Compare commits

...

299 Commits

Author SHA1 Message Date
Mucahit Bilal GOKER e1308bfa21 legacy connector cosmetic changes (#430)
* replace logo

* change copy
2025-05-22 20:24:00 +03:00
Oğuzhan Koral ed1d1f5b44 Add legacy dialog to v2 (#420) 2025-03-19 11:24:47 +03:00
Oğuzhan Koral aff871baaa Chore(v2): v2 is legacy (#419)
* v2 is legacy

* correct the log
2025-03-19 10:26:54 +03:00
Oğuzhan Koral 0608e7048d fallback to application support for accounts db (#410) 2025-03-11 19:37:57 +03:00
Jedd Morgan 0875e55c1d Limit usage of deprecated FE1 api to only essentials (#398)
* basic queries to update

* aliasing

* Remove unused streams.gql

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2025-01-23 20:42:37 +03:00
Claire Kuang 3508bcc42e Merge pull request #395 from specklesystems/claire/cnx-699-update-github-links-to-point-to-v3
Update README.md to align with main github page
2024-11-01 18:03:35 +00:00
Claire Kuang e59bfb2e12 Update README.md to align with main github page
https://linear.app/speckle/issue/CNX-699/update-github-links-to-point-to-v3
2024-11-01 18:03:11 +00:00
Oğuzhan Koral 3bb4039c78 Update transform value to matrix on send (#394) 2024-10-28 14:19:02 +03:00
Iain Sproat bd49e19c9e chore(domains): update to *.speckle.systems domains (#343)
* chore(domains): update to *.speckle.systems domains

* Upgrade xcode to 13.4.1

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2024-07-20 12:11:45 +03:00
Oğuzhan Koral 73bcd75b78 Sketchup 2024 support (#332)
* Add sqlite3 for ruby 3.2

* Run UI from netlify URL

* Add bundle for ruby 3.2 for mac
2024-05-09 17:52:21 +03:00
Oğuzhan Koral a0578fa35d Update .gitmodules (#331)
Make https accesible to submodule
2024-05-09 15:56:27 +03:00
Alan Rynne 7de08d9f24 fix: Use correct digicert context + pass fingerprint to ISS compiler (#328)
* fix: Use correct digicert context + pass fingerprint to ISS compiler

* Update config.yml

* Update config.yml

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2024-03-14 11:27:45 +00:00
Oğuzhan Koral d047f5e6d2 Chore (URL): CNX-9018 Do not add model from multi model urls
- warn user and return
- otherwise add
2024-02-29 15:13:44 +03:00
Oğuzhan Koral 71071f817c Make FE2 terminology default (#326) 2024-02-27 17:09:38 +03:00
Oğuzhan Koral c7c864b8c0 Feat (CI): Update CI for digicert (#325)
* Update CI for digicert

* Checkout speckle-sharp-ci-tools branch if exist
2024-02-27 12:36:25 +03:00
Oğuzhan Koral 67d4862f6b Fix (Mapper): CNX-9027 mapped edges don't send at all (#324)
* Separate native revit object definitions

* Add types for native revit objects

* Pass speckle_state to line conversion

* Remove mapper functions from model collections

* Use dictionary pattern matching for mapping conversions

* Remove unnecessary argument from Mapper.to_speckle
2024-02-21 16:59:40 +03:00
Oğuzhan Koral 6a16327c30 Fix (Cache): CNX-8976 bug on initialization on clear setup (#323)
* Call sketchup functions before mount

* Change default to app.speckle.systems

* Use account id instead uuid to get selected account before
2024-02-20 23:54:15 +03:00
Oğuzhan Koral 060b1b8f41 Fix (Attributes): Fix typos 2024-02-15 16:43:32 +03:00
Oğuzhan Koral da2e228293 Fix (FE2): CNX-8877 bug access error by fe2 model url (#321)
* Disable reload

* Enrich selected account info and fix same user id issue

* Trigger sketchup only saved stream not exists

* Fix activeAccount

* Increase number of branches limit to 100

* Check regex match
2024-02-13 16:36:35 +03:00
Oğuzhan Koral ab7397bf55 Feat (FE2): CNX-8702 fe2 urls (#320)
* Recreate ApolloProvider whenever account switches

- It is a force-push of reload page whenever user switches account. It doesn't hurt

* Fix FE2 urls and streamWrapper flag argument
2024-02-03 02:36:41 +03:00
Oğuzhan Koral f79547f781 Reset UI location to center of SketchUp (#319) 2024-01-31 23:00:46 +03:00
Oğuzhan Koral 2152f1c90e Feat (Mapper): CNX-8309 Family instances (#318)
* Implement New Revit Family option

* Filter family types for 'Family Instance'

* Adjust available mapping methods according to selection

* Set hard coded categories just once and source families when source set/updated

* Implement family instance mapping

* Pass group selection info if only group selected

* Exclude definitions from conversion escape

- Previously we were using definition mappings only for direct shape conversions but now they can live in instances

* Add direct shape option to components back

* Calculate rotation of family instance

* Fix transformation matrix

* Use insertion point for instances

* Set inputs for mapped definition
2024-01-08 18:40:55 +03:00
Oğuzhan Koral 503fb4d246 Feat (GIS): support line element 2023-12-01 01:46:53 +03:00
oguzhankoral 2befefa752 Extract utils for line and polygon element 2023-11-30 15:07:31 +03:00
oguzhankoral 85e64c5076 Add line element support 2023-11-30 00:36:47 +03:00
Oğuzhan Koral 60523dc994 Fix (Collections): Support gis collections and none unit 2023-11-29 16:06:58 +03:00
oguzhankoral 6d780bf350 Rename GisLayerCollection as generic 2023-11-29 16:06:37 +03:00
oguzhankoral eec02a1f84 Support none unit 2023-11-29 15:58:26 +03:00
oguzhankoral ace2fe6fe3 WIP: Convert from 'meters' to 'm' 2023-11-29 13:26:53 +03:00
oguzhankoral 188794af8d Add support for vector layer 2023-11-29 13:26:53 +03:00
Oğuzhan Koral 92a941a944 Fix (Instancing): correct id of definition for speckle entity 2023-11-29 13:25:51 +03:00
oguzhankoral 0e1ddf2b11 Tweak definition speckle entity checks 2023-11-29 13:15:07 +03:00
Oğuzhan Koral b57fa010d1 Fix (Config): Reset configs if configSketchup somehow corrupted 2023-11-28 22:38:56 +03:00
oguzhankoral f816452b78 Reset configs if configSketchup somehow corrupted 2023-11-28 22:36:03 +03:00
Oğuzhan Koral 120083bb31 Feat (Performance): receive performance improvements 2023-11-28 13:54:36 +03:00
oguzhankoral a5bb5c4686 Remove logging 2023-11-28 13:31:19 +03:00
oguzhankoral e5e2729f0a Wrap receive into sketchup operation for performance improvement 2023-11-28 11:57:17 +03:00
oguzhankoral ba8b902f48 Merge coplanar faces at the end of the operation 2023-11-28 11:23:30 +03:00
oguzhankoral 2d67815ae6 Remove unused lines 2023-11-27 23:36:10 +03:00
oguzhankoral ec0c9066d2 Merge coplanar faces remove scoping and check normals first 2023-11-27 21:44:15 +03:00
Oğuzhan Koral 58ae858077 Update dev with changes in main 2023-11-13 19:45:20 +03:00
Oğuzhan Koral 613e7938b3 Fix (Scene): missing views from Revit 2023-11-13 14:35:09 +03:00
oguzhankoral e07ff1a445 Fix missing views from Revit
- Previously it was assuming views arrive in elements of collection
2023-11-13 14:15:32 +03:00
Oğuzhan Koral de7dd34ea2 Feat (FE2): Support fe2 terminology and urls 2023-11-13 12:03:19 +03:00
oguzhankoral 0552f695f9 Update FE2 terms for Mapper Tool 2023-11-10 16:49:51 +03:00
oguzhankoral b8d4f3d946 Parse FE2 urls to add projects via ADD BY ID OR URL 2023-11-10 15:52:06 +03:00
oguzhankoral fa112a70b1 Switch to FE2 as user preferences 2023-11-10 15:13:58 +03:00
oguzhankoral 97309ebb88 Merge remote-tracking branch 'origin/development' 2023-09-19 08:31:29 +03:00
Oğuzhan Koral 556ddc0b6f Feat (deploy): Mac support 🍎 2023-09-11 10:40:08 +03:00
Oğuzhan Koral a0dde690ea Fix intendation 2023-09-08 18:17:59 +03:00
Oğuzhan Koral a76dab5be6 Remove mac suffix from deploy manager 2023-09-08 18:16:15 +03:00
Oğuzhan Koral 2d10bc5bbf Deploy manager for mac 2023-09-08 18:13:15 +03:00
Oğuzhan Koral 4042632e0b Fix (Revit): Support curtain walls from revit 2023-09-08 13:37:07 +03:00
oguzhankoral 7ccf83e1a4 Consider speckle id for revit definition 2023-09-08 13:17:16 +03:00
oguzhankoral 019cd0756f Convert revit walls to native instead with display value 2023-09-08 13:16:50 +03:00
József L. Kiss 0e5f9f80be CI integration of mac build (#301)
* patcher

* patch-version

* fix 1

* fix 2

* patch_version

* revert

* mac installer

* slname

* checkout

* powershell

* SEMVER

* python

* installername

* git checkout

* cd speckle-sharp-ci-tools

* zip

* zip location

* white space

* remove checktou

---------

Co-authored-by: József L. Kiss <>
2023-09-07 17:05:02 +03:00
József L. Kiss fc6767860a Add release version of sqlit3 bundle 2023-08-17 13:05:13 +02:00
oguzhankoral 5b5b4be7b2 Mac AppData folder 2023-08-16 17:22:25 +03:00
oguzhankoral 45351d082e Add sqlite3_27.bundle for mac 2023-08-16 16:30:43 +03:00
Oğuzhan Koral 22ccd07491 Release 2.15 2023-07-25 14:48:22 +03:00
Oğuzhan Koral 2cf9ee647b Fix (Mapper): Add offset parameter to walls 2023-07-25 14:26:21 +03:00
oguzhankoral efb567824b Add offset parameter to walls 2023-07-12 13:01:16 +03:00
Oğuzhan Koral f0aac39486 Chore (Mixpanel): tracking for mapper actions 2023-07-06 10:03:08 +03:00
oguzhankoral f278055805 Correct mappings set and applied 2023-07-03 12:27:23 +03:00
oguzhankoral 6f2e36fd11 Add mixpanel tracking for mapper 2023-07-03 12:03:37 +03:00
Oğuzhan Koral 119d80ffc8 Fix (Mapper): Definition mapping 2023-06-29 22:42:36 +03:00
oguzhankoral 771c3df864 Fix definition mapping 2023-06-29 22:39:31 +03:00
Oğuzhan Koral 7d1963e458 Feat (Mapper): Revit and default wall support 2023-06-26 12:23:52 +03:00
oguzhankoral dde85972b3 Add default wall 2023-06-26 12:20:32 +03:00
oguzhankoral 5e061da910 Correct tooltips according to source state 2023-06-26 11:49:34 +03:00
oguzhankoral 46bea345de RevitWall from baseline 2023-06-26 11:33:09 +03:00
Oğuzhan Koral bc53462ad6 Chore (Mapper): Rename Floor with RevitFloor 2023-06-26 10:47:50 +03:00
oguzhankoral 884df40a1d Rename Floor with RevitFloor 2023-06-26 10:45:40 +03:00
Oğuzhan Koral b23168c067 Fix (Mapper): evaluate mapping source family data 2023-06-26 10:07:48 +03:00
oguzhankoral 5568212f15 Correct type names from source 2023-06-25 20:48:16 +03:00
oguzhankoral 79db79d799 Remove braces from mapper 2023-06-25 20:47:54 +03:00
oguzhankoral 18a4008efd Set selected level name string instead of object 2023-06-25 20:20:17 +03:00
oguzhankoral 83e4abd1ee Update family types when family updated 2023-06-23 18:37:47 +03:00
oguzhankoral 243bcfba72 Make dropdowns functional 2023-06-23 18:33:39 +03:00
Oğuzhan Koral af2c8c560f Feat (Mapper): mapper method by selection and floor creation 2023-06-22 17:27:49 +03:00
oguzhankoral 6a37f3871c Add new types as constants 2023-06-22 17:18:00 +03:00
oguzhankoral 759a388448 Register levels as speckle entity 2023-06-22 17:17:47 +03:00
oguzhankoral 360e89d7ce Separate default floor with native floor 2023-06-22 17:17:23 +03:00
oguzhankoral 64655a3284 Pass speckle state to speckle conversions to detect levels 2023-06-22 17:16:47 +03:00
oguzhankoral c58356bde8 Rename with_mapper_selection_queue with better one 2023-06-22 17:16:06 +03:00
oguzhankoral f74117632d Pass stream id to level object to create it as speckle entity 2023-06-22 17:15:23 +03:00
oguzhankoral 5f2b8b8e2b Parse family type level info from command to action 2023-06-22 17:15:00 +03:00
oguzhankoral f0ce7481fd Register clear mapper source action to commands 2023-06-22 17:14:38 +03:00
oguzhankoral 6e7a5c6140 Apply and clear mapping source by buttons instead of auto-apply 2023-06-22 17:13:52 +03:00
oguzhankoral 079c18ee19 Apply mappings also for family, type and level 2023-06-22 17:13:25 +03:00
oguzhankoral 26ef6a3815 Add clearing mapper source action 2023-06-22 17:12:18 +03:00
oguzhankoral cf6dcefe6c Separate mapper data collection from Sketchup selection 2023-06-22 17:11:41 +03:00
oguzhankoral 35590eb979 Add revit parameter object 2023-06-22 17:11:07 +03:00
oguzhankoral 583a8b8a76 Separate mapper related read functions 2023-06-20 08:18:08 +03:00
Oğuzhan Koral c6e8a664de Feat (Mapper): Fetch mapper source 2023-06-19 15:10:43 +03:00
oguzhankoral 24a5e0a579 Update levels geometrically when we have new commit 2023-06-19 12:57:48 +03:00
oguzhankoral a242c197fb Store mapper source in ruby state 2023-06-16 23:10:50 +03:00
oguzhankoral 08bdd23149 Add state for mapper 2023-06-16 21:57:13 +03:00
oguzhankoral c5b35b2d98 Notify user when selected source has new update 2023-06-16 16:23:27 +03:00
oguzhankoral 5407fecd1f Fetch source branch last commit and pass to ruby 2023-06-15 16:10:10 +03:00
oguzhankoral 12d2821d26 Add mapping source selection 2023-06-15 12:09:39 +03:00
Oğuzhan Koral 2e6d58e6a3 Feat (Mapper): mapping for default floor 2023-06-14 22:20:21 +03:00
oguzhankoral 0f0c0fd5ae Check material nil 2023-06-14 16:50:04 +03:00
oguzhankoral ef9ec1c223 Send floors with global coordinates as flat list 2023-06-14 01:11:37 +03:00
oguzhankoral f4387bae30 Send @SpeckleSchema as detached property 2023-06-13 11:50:35 +03:00
oguzhankoral 49b238a23a Extract mapped elements on selection 2023-06-13 11:49:24 +03:00
oguzhankoral 4b75c01b28 Convert sketchup faces to built elements' floor 2023-06-12 13:36:09 +03:00
Oğuzhan Koral e7f641046b Fix (Send): Do not send hidden geometries in components 2023-06-09 15:26:16 +03:00
oguzhankoral 9aaabe0fab Do not send hidden geometries in components 2023-06-09 15:24:40 +03:00
Oğuzhan Koral b6e4b711bf Feat (Edge): support rhino curve types 2023-06-09 11:19:57 +03:00
oguzhankoral 58fcfd210b Support arc and circle 2023-06-09 11:12:04 +03:00
oguzhankoral 5868b9c234 Support polycurves as displayValues 2023-06-09 09:55:52 +03:00
Oğuzhan Koral 0dc6d9cf9d Fix (DisplayValue): Use name of display value first 2023-06-08 23:42:05 +03:00
oguzhankoral 67f50cf2fd If displayValue has name use it 2023-06-08 23:39:54 +03:00
Oğuzhan Koral 8b26a4d49a Chore (DisplayValue): Better component names for displayValues 2023-06-08 23:15:07 +03:00
oguzhankoral a1d0bb0aa1 Instance name as speckle_type only 2023-06-08 23:08:25 +03:00
oguzhankoral bfe08560b1 Use speckle type instead def 2023-06-08 22:31:28 +03:00
Oğuzhan Koral ac3ac24272 Fix (Scene): Orthogonal view receiving as zoomed in 2023-06-08 14:39:07 +03:00
oguzhankoral 03e7191d0e Remove unnecessary lines on to_native 2023-06-08 14:36:39 +03:00
oguzhankoral b3a42f8723 Extract camera creation fuction to fix rubocop warnings 2023-06-08 14:34:18 +03:00
oguzhankoral 9c4b740300 Send 35mm focal length if camera is orthogonal 2023-06-08 14:27:05 +03:00
oguzhankoral 2a12bdadf2 Set camera height for isometric views 2023-06-08 14:14:34 +03:00
Oğuzhan Koral c90e8ad4d2 Feat (Face): extrudable brep faces 2023-06-08 12:23:21 +03:00
oguzhankoral 4a52c51c86 Rename returning single faces as ngon 2023-06-08 12:19:09 +03:00
oguzhankoral 0efc817ddc Remove remaining orphan edges after clean up 2023-06-07 13:06:16 +03:00
oguzhankoral fee54fc98c Do not smooth and soft single faces 2023-06-07 13:05:58 +03:00
oguzhankoral 210f751396 Return added entities too from conversion 2023-06-07 13:05:35 +03:00
Oğuzhan Koral 2e2bc3fe29 Feat (Layers): support flat layers 2023-06-06 13:35:05 +03:00
oguzhankoral a6f05f86d1 Document layer strategies 2023-06-06 13:33:37 +03:00
oguzhankoral fc144e4848 Convert layers as flat list from Rhino 2023-06-06 11:43:22 +03:00
oguzhankoral cef9531428 Log function to help write texts to local file 2023-06-06 11:42:54 +03:00
oguzhankoral abd4faefbf Move speckle entity creation from speckle object to SpeckleEntity class 2023-06-06 08:17:31 +03:00
Oğuzhan Koral 966f7aaed5 Fix (Collection): fix blender receive 2023-05-29 18:12:02 +03:00
oguzhankoral 51b59fa995 Fix typo on including string 2023-05-29 18:09:48 +03:00
oguzhankoral 0b713736bd Include also detached @elements props to displayValue 2023-05-29 18:06:05 +03:00
oguzhankoral 9e33581c66 Accept also detached @displayValue props 2023-05-29 18:05:29 +03:00
oguzhankoral b97792b596 Check only collection is model or not 2023-05-29 18:04:56 +03:00
Matteo Cominetti c0746f8eff Merge pull request #270 from specklesystems/oguzhan/hash-only-hostname-of-server-url 2023-05-25 18:17:37 +01:00
oguzhankoral a826a9d692 Hash hostname of the serverUrl 2023-05-25 20:09:32 +03:00
Oğuzhan Koral 6d04203d37 Feat (Collections): Eliminate relations for layer info 2023-05-24 15:57:47 +03:00
oguzhankoral 33b2ed8a94 Fix performance penalty 2023-05-24 14:38:27 +03:00
oguzhankoral 4f16da7ad0 Do not extract relations if from revit 2023-05-24 01:16:51 +03:00
oguzhankoral 36f92c7655 Add network object support 2023-05-24 01:00:06 +03:00
oguzhankoral 1d4f5a759e Eliminate relations for model collections 2023-05-23 23:31:02 +03:00
oguzhankoral 28af9bc811 Enable rescue block for to_native 2023-05-23 12:07:48 +03:00
oguzhankoral cf04cd4094 Check view up and direction parallel or not 2023-05-23 11:50:08 +03:00
oguzhankoral 23e9efb28a Pass layer prop through to_native methods 2023-05-23 11:49:51 +03:00
Oğuzhan Koral 57322df29c Fix (Layer): Do not send line_style if none 2023-05-22 13:57:23 +03:00
oguzhankoral fff82d34c6 Do not send line_style if none 2023-05-22 13:56:10 +03:00
Oğuzhan Koral 7211860c21 Chore (Layer): Support line styles for layers 2023-05-22 11:46:52 +03:00
oguzhankoral 9fc69044f8 Support line styles for layers 2023-05-22 11:42:51 +03:00
Oğuzhan Koral 76467c3e81 Fix (Blocks): Eliminate nil block geometries 2023-05-19 19:36:28 +03:00
oguzhankoral 4cfc04e2f3 Eliminate nil block geometries 2023-05-19 19:13:15 +03:00
Oğuzhan Koral 6bfc7771d0 Fix (Face): Merge coplanar doesn't work some cases 2023-05-19 12:26:24 +03:00
oguzhankoral 7055135257 Return nil if layer path nil 2023-05-19 12:20:55 +03:00
Oğuzhan Koral 85312cf20d Fix (Mapper): inconsistency on mapped elements table state 2023-05-16 13:36:27 +03:00
oguzhankoral 4051cae5b0 Clear cached element and category selection when mapped elements updated 2023-05-16 13:27:02 +03:00
oguzhankoral c64bb6dedf Include instances for button reactions
- Clear, Isolate, Hide, Select
2023-05-16 13:25:41 +03:00
Oğuzhan Koral 7bb17ae0db Fix (Collections): Add layer property to objects as full path 2023-05-16 08:39:06 +03:00
oguzhankoral 3244eb9b15 Add layer property to objects as full path
This is for receiving on Rhino
2023-05-16 08:33:42 +03:00
Oğuzhan Koral 6e173847ba Fix (Performance): Smooth only non-planar quad meshes 2023-05-15 13:53:56 +03:00
oguzhankoral 8cbc1f763d Smooth only non-planar quad meshes 2023-05-15 13:40:31 +03:00
Oğuzhan Koral 2d45abeb28 Release 2.14.0
Merging 2.13 hotfixes into dev branch
2023-05-15 13:17:54 +03:00
oguzhankoral 2fd0ad77bc Fix or disable rubocop issues 2023-05-15 13:14:32 +03:00
oguzhankoral 53c1167831 Remove orthogonality checks 2023-05-15 13:04:22 +03:00
oguzhankoral 48d4a1d7bb Add plane_utils to calculate planarity of 4 points for quad meshes 2023-05-15 13:04:19 +03:00
oguzhankoral 4bbbba6e89 Split all quad meshes 2023-05-15 13:03:22 +03:00
oguzhankoral 0a13575325 Disable rubocop for to_native 2023-05-15 13:02:12 +03:00
Oğuzhan Koral d6075209df Fix (Update): Check if branch instance is deleted 2023-05-12 12:53:19 +03:00
oguzhankoral 9d1b39422b Extract entities_updated method 2023-05-12 12:39:37 +03:00
oguzhankoral 38e297d32b Correct condition for is_update_commit 2023-05-12 11:31:23 +03:00
oguzhankoral 459ef3c9f1 Check if branch instance is deleted 2023-05-12 11:21:34 +03:00
Oğuzhan Koral 3bb12487ba Feat (QGIS): Attribute support for qgis commits 2023-05-12 10:13:44 +03:00
oguzhankoral 8b2428c60d Workaround for QGIS geometry objects or arrays 2023-05-10 17:26:47 +03:00
oguzhankoral f9befd9051 Write qgis attributes to components 2023-05-10 12:43:29 +03:00
oguzhankoral 1020a9bd5d Wrap PolygonElement object into component 2023-05-10 12:07:40 +03:00
Oğuzhan Koral 2844c4ea86 Fix (Instance): Disable aligning axes of displayValue components 2023-05-08 16:54:52 +03:00
oguzhankoral 3344188fee Disable aligning axes of displayValue components 2023-05-08 16:44:10 +03:00
Oğuzhan Koral 67138c2f78 Fix (Update): Update behavior for Revit > Sketchup 2023-05-08 13:21:31 +03:00
oguzhankoral f59d19ef21 Check children changed or not for upcoming blocks 2023-05-08 13:13:53 +03:00
Oğuzhan Koral d0fa7e638f Feat (Collections): Model collections 2023-05-08 09:46:44 +03:00
oguzhankoral 63011e466e Change relation speckle types to collections 2023-05-08 09:34:47 +03:00
oguzhankoral 92d1976300 Split collections into helper classes 2023-05-04 20:08:17 +03:00
oguzhankoral a0cdc9fa07 Check sketchup entities for respomding true to layer set, if so set 2023-05-04 12:22:02 +03:00
oguzhankoral 60f0006597 Remove old layer helper methods 2023-05-04 12:06:56 +03:00
oguzhankoral 67a3c62a08 Align instance axes that created from displayValues without transform 2023-05-03 18:23:58 +03:00
oguzhankoral 21d5dc1e0b Remove layer argument passing for all to_native methods 2023-04-27 17:59:39 +01:00
oguzhankoral 0d810f59f9 Place received objects into correct layers 2023-04-27 17:39:15 +01:00
oguzhankoral dba60b700f Layer relations to native layer tree 2023-04-27 17:21:20 +01:00
oguzhankoral ad9a56bd20 Remove unused methods 2023-04-27 16:16:15 +01:00
oguzhankoral 82585e9104 Convert views to native with traversal method 2023-04-27 16:14:20 +01:00
oguzhankoral dfbb241b0b Model collections on send 2023-04-27 14:56:59 +01:00
oguzhankoral f69ea91f45 Add query for layers 2023-04-26 16:44:11 +01:00
oguzhankoral 768d916092 Convert scenes to view3d on class 2023-04-26 16:44:00 +01:00
oguzhankoral c0acecae10 Query methods for direct shapes 2023-04-26 16:43:31 +01:00
oguzhankoral f741d1c7e4 Clean converter from object specific functions 2023-04-26 16:43:03 +01:00
oguzhankoral d25c70f1d1 Do not load bootstrap again when reload triggered 2023-04-26 16:42:06 +01:00
oguzhankoral 6f954467ef Move collections into related namespace 2023-04-26 16:41:37 +01:00
oguzhankoral 867987a5f5 Group meshes also according to layers 2023-04-26 14:39:52 +01:00
oguzhankoral 61e0a6351c Init collection class 2023-04-14 10:09:44 +03:00
oguzhankoral 558264c23b Add documentation to app methods 2023-04-13 13:57:37 +03:00
oguzhankoral cf7aa371af Move view related methods to related class 2023-04-12 20:04:17 +03:00
oguzhankoral ffedaf6a73 Fix bug on views receive 2023-04-12 14:02:50 +03:00
oguzhankoral d9ae272b36 Proof of concept of layer relations
# Conflicts:
#	speckle_connector/src/convertors/to_speckle.rb
#	speckle_connector/src/speckle_objects/geometry/mesh.rb
#	speckle_connector/src/speckle_objects/other/block_definition.rb
#	speckle_connector/src/speckle_objects/other/block_instance.rb
2023-04-12 12:56:59 +03:00
Oğuzhan Koral 3c5d1f5c9e Fix (References): missing namespaces 2023-04-12 11:09:03 +03:00
oguzhankoral b69b8b7585 Fix missing references after couple of implementation 2023-04-12 10:44:15 +03:00
Oğuzhan Koral 8c7e498fb2 Fix (Security): Bump sqlite3 from 5.0.8 to 5.1.5 in /ui 2023-04-10 14:42:56 +03:00
dependabot[bot] 359413a296 Bump sqlite3 from 5.0.8 to 5.1.5 in /ui
Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.0.8 to 5.1.5.
- [Release notes](https://github.com/TryGhost/node-sqlite3/releases)
- [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.0.8...v5.1.5)

---
updated-dependencies:
- dependency-name: sqlite3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 11:19:39 +00:00
Oğuzhan Koral cef7d894c4 Fix (Security): Bump git from 1.12.0 to 1.18.0 2023-04-10 14:15:16 +03:00
dependabot[bot] 7d9cc1aacc Bump git from 1.12.0 to 1.18.0
Bumps [git](https://github.com/ruby-git/ruby-git) from 1.12.0 to 1.18.0.
- [Release notes](https://github.com/ruby-git/ruby-git/releases)
- [Changelog](https://github.com/ruby-git/ruby-git/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ruby-git/ruby-git/compare/v1.12.0...v1.18.0)

---
updated-dependencies:
- dependency-name: git
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 11:12:22 +00:00
Oğuzhan Koral a6780a756b Feat (Mapper): Mapper tool for direct shapes 2023-04-10 14:05:44 +03:00
Oğuzhan Koral 4e37d17b42 Feat (Mapper): Mapped elements table and it's buttons 2023-04-10 13:48:19 +03:00
oguzhankoral 793923348f Switch button for isolate/show-all accoding to state 2023-04-10 13:37:04 +03:00
oguzhankoral 700db1788d Improve performance dramatically of isolate 2023-04-08 00:02:52 +03:00
oguzhankoral 68a5a44702 Make functional buttons for mapped elements 2023-04-07 20:16:00 +03:00
oguzhankoral 464891f5a1 Manage state of selected elements and existing elements 2023-04-07 11:33:05 +03:00
oguzhankoral d2e5db2680 Custom data table header for child tables 2023-04-06 22:59:46 +03:00
oguzhankoral a9c41d0545 Connect data relationship between parent and childeren data tables 2023-04-05 17:19:21 +03:00
oguzhankoral 0bc458d307 Connect nested data tables with their v-model 2023-04-05 13:54:05 +03:00
oguzhankoral 36c7cc285e Update vuetifyjs to 2.6.10 2023-04-05 13:52:56 +03:00
oguzhankoral f9fbd31a0f Wrap data table with container 2023-04-03 16:43:19 +03:00
oguzhankoral 8789c1f855 Extract mapped elements from mapper component 2023-04-03 15:41:04 +03:00
oguzhankoral f65073480a Apply mixed mapping name 2023-04-03 15:04:20 +03:00
oguzhankoral 157ed831a7 Shrink v-cards for map selection between component-definition 2023-03-31 23:19:19 +03:00
oguzhankoral 0799a59a15 Get info from definition about number of instances 2023-03-31 23:18:23 +03:00
Oğuzhan Koral 60ea3e83e1 Fix (DirectShape): Nested objects material 2023-03-31 19:57:46 +03:00
oguzhankoral 4b874deb50 Don't add mapped entities to layer groups 2023-03-31 19:55:12 +03:00
oguzhankoral 6726b9ad50 Fix nested material issıe 2023-03-31 19:54:29 +03:00
Oğuzhan Koral 75e6fe609e Fix (Mapper): Direct shape objects on viewer 2023-03-31 15:28:05 +03:00
oguzhankoral b40d1ccf7d Send directShapes collection as detached 2023-03-31 15:26:13 +03:00
Oğuzhan Koral 4f7ba23904 Feat (Mapper): Enable multiple mapping apply and clear 2023-03-31 14:33:41 +03:00
oguzhankoral 49084706f2 Enable multiple mapping apply and clear 2023-03-31 14:31:49 +03:00
Oğuzhan Koral 1852e5f6bf Chore (Mapper): Show default values for selected entity 2023-03-30 20:33:59 +03:00
oguzhankoral be0ed5428a Apply default values from selected entity 2023-03-30 20:31:16 +03:00
oguzhankoral f428efde6a Mark mapped cards and make active one 2023-03-30 19:56:49 +03:00
oguzhankoral e79f04f33e Remove unnecessary statement 2023-03-30 15:59:33 +03:00
oguzhankoral 04db8c281f Remove selected rows which was unneccessary 2023-03-30 15:59:03 +03:00
oguzhankoral 2681698186 Disable definition option for groups
- This is uncessary since each group has it's own unique definition on sketchup
2023-03-30 15:49:04 +03:00
Oğuzhan Koral 1682c66da6 Feat (Mapper): Read mapped elements on document load 2023-03-29 23:20:29 +03:00
oguzhankoral 38bc1032d8 Read mapped entities on document load model 2023-03-29 23:18:05 +03:00
Oğuzhan Koral ba37eef9ca Feat (Mapper): Group meshes also for base geometries of direct shapes 2023-03-29 22:49:30 +03:00
oguzhankoral 3db179882a Group direct shape meshes by material 2023-03-29 22:45:45 +03:00
oguzhankoral ba8f25a807 Move mesh related methods from definition 2023-03-29 22:44:06 +03:00
oguzhankoral eac3d3089b Find entities on active path 2023-03-29 22:43:13 +03:00
Oğuzhan Koral 0454a9e147 Fix (Mapper): Group global transformations for direct shapes 2023-03-29 21:03:13 +03:00
oguzhankoral f390cd1c68 Fix/disable rubocop issues 2023-03-29 21:02:49 +03:00
oguzhankoral aaa1ba5aa9 Apply parent's material to direct shape faces 2023-03-29 20:58:57 +03:00
oguzhankoral fb9556abd9 Fix sub elements transform 2023-03-29 20:58:35 +03:00
Oğuzhan Koral 3cfb3b1f84 Feat (Mapper): Show mapped elements on table 2023-03-29 14:54:21 +03:00
oguzhankoral b071bab137 Collect mapped entities when mapper mounted 2023-03-29 14:49:48 +03:00
oguzhankoral 5dbc68ea76 State management for mapped elements 2023-03-29 14:45:09 +03:00
oguzhankoral e76fbc3b77 Notify user after apply or clear mapping 2023-03-29 13:12:59 +03:00
oguzhankoral 09ca0514d1 Remove mapping word from buttons 2023-03-29 12:57:12 +03:00
oguzhankoral faec32a5bb Remove attribute dictionary completely if mappings cleared 2023-03-29 12:35:56 +03:00
oguzhankoral 1f5793ab79 Nil check for reading objects from scratch 2023-03-29 11:50:03 +03:00
oguzhankoral df22bd1cec Fallback to definition schema of instance 2023-03-29 11:47:21 +03:00
oguzhankoral 61dba4e78d Return false if entities are missing for base_and_entities 2023-03-29 11:44:58 +03:00
Oğuzhan Koral bb678117f8 Feat (Mapper): UI communication with Sketchup 2023-03-28 21:36:12 +03:00
oguzhankoral 2dc08e71b7 Collect also instances that definitions' mapped 2023-03-28 21:02:21 +03:00
oguzhankoral df2f844e74 Styles for entity selection to map 2023-03-28 20:11:52 +03:00
oguzhankoral 621c602fc0 Make narrower scrool bar 2023-03-28 20:11:15 +03:00
oguzhankoral f809169757 Fetch definition schema from selection 2023-03-28 20:10:59 +03:00
oguzhankoral 848b135612 Clear mappings 2023-03-28 20:10:26 +03:00
oguzhankoral db6af66705 Fill inputs according to selection 2023-03-28 02:34:55 +03:00
oguzhankoral 728e1f5a86 Send categories as key-value pair objects 2023-03-28 02:34:38 +03:00
oguzhankoral d02d95bc9e Apply mapping command and action 2023-03-28 02:34:13 +03:00
oguzhankoral c1180c5373 Trigger sketchup action to apply mappings 2023-03-28 01:49:17 +03:00
oguzhankoral 4e01fb64e6 Track objects from selection table and prepare mapping 2023-03-28 01:46:21 +03:00
oguzhankoral d0fcb1da34 Add theme colors for mapper active entity cards 2023-03-28 01:45:44 +03:00
oguzhankoral 48352d06b3 Extend selected entity data from sketchup 2023-03-28 01:45:18 +03:00
oguzhankoral d995652569 Get info from last selected entity 2023-03-27 18:57:42 +03:00
oguzhankoral 5a958fa51c Split class names with space 2023-03-27 18:57:13 +03:00
oguzhankoral cfb9e2cacd Print mapped count to selection table 2023-03-27 18:12:08 +03:00
oguzhankoral 7932fc1cab Send entity persistent_id to UI 2023-03-27 18:11:40 +03:00
oguzhankoral e0fc1715b5 Create data table for selection 2023-03-27 14:54:19 +03:00
oguzhankoral 1cfd6caa64 Read object schemas from selection with their type 2023-03-27 14:54:01 +03:00
oguzhankoral 54bf18888d Helper method definition to group objects by property value 2023-03-27 14:53:23 +03:00
Oğuzhan Koral 6b60fc4259 Feat (Observers): Selection observers to collect data 2023-03-24 19:37:20 +03:00
oguzhankoral 216af8697c Create accordion for mapper 2023-03-24 17:33:42 +03:00
oguzhankoral d97e7314d0 Add categories to fake data 2023-03-24 17:33:11 +03:00
oguzhankoral 67d9dda143 Sample interactions 2023-03-24 13:06:55 +03:00
oguzhankoral 5c60e303cb Move mapped_with_schema method to reader 2023-03-24 12:36:45 +03:00
oguzhankoral e36b8f3a56 Create selection observer to trigger UI 2023-03-24 12:36:21 +03:00
Oğuzhan Koral 1e7a19b463 Chore (UI): UI components for mapping tool 2023-03-24 12:30:55 +03:00
oguzhankoral 8004249a29 Init mapper with tab 2023-03-23 16:08:36 +03:00
Oğuzhan Koral 6cd800e4ef Feat (Mapping): Send direct shape PoC 2023-03-23 12:54:42 +03:00
oguzhankoral e182bc7ed2 Clean unused methods 2023-03-22 21:38:23 +03:00
oguzhankoral c338d51c9d Skip faces into definitions if it is mapped 2023-03-22 21:19:11 +03:00
oguzhankoral c1912250df Do definition check in advance and that's why definitions argument removed 2023-03-22 21:18:50 +03:00
oguzhankoral 994c41980e Init definitions and render_materials for later caching 2023-03-22 21:18:03 +03:00
oguzhankoral 385b7a514d Transform mesh if global_transformation defined 2023-03-22 21:17:25 +03:00
oguzhankoral 3aaade9228 Entity queries for parental relationships 2023-03-22 21:16:58 +03:00
oguzhankoral b4dd19b711 Split direct shape conversion from generic conversion 2023-03-22 21:06:53 +03:00
oguzhankoral 9e9b83b4ba Init direct shape definition 2023-03-22 21:06:14 +03:00
oguzhankoral 5ff5114669 Add revit categories dictionary 2023-03-21 19:55:44 +03:00
oguzhankoral f63039f3fb Change module name with revit specific 2023-03-21 19:53:47 +03:00
oguzhankoral b39399463d Add speckle schema to objects for to speckle 2023-03-21 14:17:36 +03:00
oguzhankoral 8217ef7fbb Create speckle schema dictionary handler 2023-03-21 14:17:18 +03:00
oguzhankoral b4851c34e1 Separate base dictionary_handler from root 2023-03-21 14:16:52 +03:00
144 changed files with 6938 additions and 869 deletions
+160 -33
View File
@@ -26,21 +26,17 @@ jobs:
build-connector: # Reusable job for basic connectors
executor:
name: win/default # comes with python 3.7.3
shell: cmd.exe
shell: powershell.exe
parameters:
slug:
type: string
default: ""
environment:
SSM: 'C:\Program Files\DigiCert\DigiCert One Signing Manager Tools'
steps:
- checkout
- attach_workspace:
at: ./
- run:
name: Create Innosetup signing cert
shell: powershell.exe
command: |
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
- run:
name: Set Environment Variable
shell: powershell.exe
@@ -52,30 +48,134 @@ jobs:
python patch_version.py $semver
environment:
WORKFLOW_NUM: << pipeline.number >>
- run:
name: Build Installer
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p
shell: cmd.exe #does not work in powershell
#- run:
# name: Patch
# shell: powershell.exe
# command:
# | # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
# $tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
# $semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
# $ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
# $channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
# $version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
# New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
# echo $version
# python patch_version.py $semver
# speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
- unless: # Build installers unsigned on non-tagged builds
condition: << pipeline.git.tag >>
steps:
- run:
name: Build Installer
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p
shell: cmd.exe # does not work in powershell
- when: # Setup certificates and build installers signed for tagged builds
condition: << pipeline.git.tag >>
steps:
- run:
name: "Digicert Signing Manager Setup"
command: |
cd C:\
curl.exe -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:$env:SM_API_KEY" -o smtools-windows-x64.msi
msiexec.exe /i smtools-windows-x64.msi /quiet /qn | Wait-Process
- run:
name: "Create Auth & OV Signing Cert"
command: |
cd C:\
echo $env:SM_CLIENT_CERT_FILE_B64 > certificate.txt
certutil -decode certificate.txt certificate.p12
- run:
name: "Sync Certs"
command: |
& $env:SSM\smksp_cert_sync.exe
- run:
name: "Build Installer"
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p /DSIGN_INSTALLER /DCODE_SIGNING_CERT_FINGERPRINT=%SM_CODE_SIGNING_CERT_SHA1_HASH%
shell: cmd.exe
- persist_to_workspace:
root: ./
paths:
- speckle-sharp-ci-tools/Installers
build-connector-mac:
macos:
xcode: 13.4.1
parameters:
projname:
type: string
default: ""
slug:
type: string
default: ""
installer:
type: boolean
default: false
converter-files:
type: string
default: ""
installername:
type: string
default: ""
build-config:
type: string
default: Release
bundlename:
type: string
default: ""
steps:
- checkout
- attach_workspace:
at: ./
- run:
name: Install dotnet
command: |
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel Current
$HOME/.dotnet/dotnet --version
$HOME/.dotnet/dotnet --list-runtimes
$HOME/.dotnet/dotnet --list-sdks
- run:
name: Create installer target dir
command: |
mkdir -p speckle-sharp-ci-tools/Installers/<< parameters.slug >>
- run:
name: Set Environment Variable
command: |
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;)
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
VER=$(echo "$SEMVER" | sed -e 's/-.*//')
VERSION=$(echo $VER.$WORKFLOW_NUM)
python3 patch_version.py $SEMVER
environment:
WORKFLOW_NUM: << pipeline.number >>
- run:
name: Zip Connector files
command: |
zip -r << parameters.slug >>-mac.zip "./speckle_connector" "./speckle_connector.rb"
# Copy installer files
- run:
name: Copy files to installer
command: |
mkdir -p speckle-sharp-ci-tools/Mac/<< parameters.installername >>/.installationFiles/
cp << parameters.slug >>-mac.zip speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles
# Create installer
- run:
name: Exit if External PR
command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi
- run:
name: Build Mac installer
command: ~/.dotnet/dotnet publish speckle-sharp-ci-tools/Mac/<<parameters.installername>>/<<parameters.installername>>.sln -r osx-x64 -c Release
- run:
name: Zip installer
command: |
cd speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/
zip -r <<parameters.slug>>.zip ./
- store_artifacts:
path: speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/<<parameters.slug>>.zip
- run:
name: Copy to installer location
command: |
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "2.0.999"; fi;)
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
VER=$(echo "$SEMVER" | sed -e 's/-.*//')
VERSION=$(echo $VER.$WORKFLOW_NUM)
cp speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/osx-x64/publish/<<parameters.slug>>.zip speckle-sharp-ci-tools/Installers/<< parameters.slug >>/<<parameters.slug>>-$SEMVER.zip
environment:
WORKFLOW_NUM: << pipeline.number >>
- when:
condition: << pipeline.git.tag >>
steps:
- persist_to_workspace:
root: ./
paths:
- speckle-sharp-ci-tools/Installers
get-ci-tools: # Clones our ci tools and persists them to the workspace
docker:
- image: cimg/base:2021.01
@@ -91,14 +191,21 @@ jobs:
- run:
name: Clone
command: git clone git@github.com:specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
- run:
name: Checkout branch
command: |
cd speckle-sharp-ci-tools
if [ -z "$CIRCLE_TAG" ]
then
git checkout ${CIRCLE_BRANCH} || git checkout main
else
git checkout ${CIRCLE_TAG} || git checkout main
fi
- persist_to_workspace:
root: ./
paths:
- speckle-sharp-ci-tools
- persist_to_workspace:
root: ./
paths:
- speckle-sharp-ci-tools
deploy-manager2:
docker:
- image: mcr.microsoft.com/dotnet/sdk:6.0
@@ -144,7 +251,17 @@ workflows:
filters:
tags:
only: /.*/
context: innosetup
context: digicert-keylocker
- build-connector-mac:
slug: sketchup
requires:
- get-ci-tools
- build-ui
filters:
tags:
only: /.*/
installername: SpeckleSketchUpInstall
- deploy-manager2:
context: do-spaces-speckle-releases
@@ -152,11 +269,21 @@ workflows:
os: Win
extension: exe
requires:
- get-ci-tools
- build-ui
- build-connector
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
- deploy-manager2:
context: do-spaces-speckle-releases
slug: sketchup
os: OSX
extension: zip
requires:
- build-connector-mac
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "_sqlite3"]
path = _sqlite3
url = git@github.com:specklesystems/sketchup-sqlite3.git
url = https://github.com/specklesystems/sketchup-sqlite3.git
+3 -4
View File
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.1)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
axiom-types (0.1.1)
@@ -26,7 +26,7 @@ GEM
path_expander (~> 1.0)
ruby_parser (~> 3.1, > 3.1.0)
sexp_processor (~> 4.8)
git (1.12.0)
git (1.18.0)
addressable (~> 2.8)
rchardet (~> 1.8)
ice_nine (0.11.2)
@@ -48,8 +48,7 @@ GEM
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
psych (3.3.4)
public_suffix (5.0.0)
public_suffix (5.0.1)
rainbow (3.1.1)
rake (13.0.6)
rchardet (1.8.0)
+15 -36
View File
@@ -2,44 +2,14 @@
<img src="https://user-images.githubusercontent.com/2679513/131189167-18ea5fe1-c578-47f6-9785-3748178e4312.png" width="150px"/><br/>
Speckle | SketchUp
</h1>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
> Speckle is the first AEC data hub that connects with your favorite AEC tools. Speckle exists to overcome the challenges of working in a fragmented industry where communication, creative workflows, and the exchange of data are often hindered by siloed software and processes. It is here to make the industry better.
<h3 align="center">
Connector for SketchUp
</h3>
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
<p align="center"><a href="https://github.com/specklesystems/speckle-blender/"><img src="https://circleci.com/gh/specklesystems/speckle-blender.svg?style=svg&amp;circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29" alt="Speckle-Next"></a> </p>
# About Speckle
What is Speckle? Check our ![YouTube Video Views](https://img.shields.io/youtube/views/B9humiSpHzM?label=Speckle%20in%201%20minute%20video&style=social)
### Features
- **Object-based:** say goodbye to files! Speckle is the first object based platform for the AEC industry
- **Version control:** Speckle is the Git & Hub for geometry and BIM data
- **Collaboration:** share your designs collaborate with others
- **3D Viewer:** see your CAD and BIM models online, share and embed them anywhere
- **Interoperability:** get your CAD and BIM models into other software without exporting or importing
- **Real time:** get real time updates and notifications and changes
- **GraphQL API:** get what you need anywhere you want it
- **Webhooks:** the base for a automation and next-gen pipelines
- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more!
### Try Speckle now!
Give Speckle a try in no time by:
- [![speckle XYZ](https://img.shields.io/badge/https://-speckle.xyz-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://speckle.xyz) ⇒ creating an account at our public server
- [![create a droplet](https://img.shields.io/badge/Create%20a%20Droplet-0069ff?style=flat-square&logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click
### Resources
- [![Community forum users](https://img.shields.io/badge/community-forum-green?style=for-the-badge&logo=discourse&logoColor=white)](https://speckle.community) for help, feature requests or just to hang with other speckle enthusiasts, check out our community forum!
- [![website](https://img.shields.io/badge/tutorials-speckle.systems-royalblue?style=for-the-badge&logo=youtube)](https://speckle.systems) our tutorials portal is full of resources to get you started using Speckle
- [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=for-the-badge&logo=read-the-docs&logoColor=white)](https://speckle.guide/user/blender.html) reference on almost any end-user and developer functionality
# Repo structure
@@ -66,6 +36,15 @@ This repo is split into three parts:
After building `sqlite3.sln` file, compiled `sqlite3.so` (for Windows) and `sqlite3.bundle` (for OSX) dynamic library files are created
by solution to place them into source code into `speckle_connector/src/ext`. Building this project should be only
happen when SketchUp starts to support newer Ruby versions (currently it is `2.7`).
### Other repos
Make sure to also check and ⭐️ these other Speckle next generation repositories:
- [`speckle-sharp-connectors`](https://github.com/specklesystems/speckle-sharp-connectors): .NET connectors and desktop UI
- [`speckle-sharp-sdk`](https://github.com/specklesystems/speckle-sharp-sdk): our .NET SDK for next gen connectors and development
- [`speckle-powerbi`](https://github.com/specklesystems/speckle-powerbi): PowerBi connector
- and more [connectors & tooling](https://github.com/specklesystems/)!
## Contribution Guide
@@ -194,4 +173,4 @@ To track your code quality locally,
2. To check overall state of repository by RubyCritic, run below
```ruby
rake rubycritic
```
```
+1 -1
View File
@@ -24,7 +24,7 @@ module SpeckleConnector
# Run from localhost or from build files
DEV_MODE = false
puts("Loading Speckle Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
puts("Loading Speckle (Legacy) Connector v#{CONNECTOR_VERSION} from #{DEV_MODE ? 'dev' : 'build'}")
unless file_loaded?(__FILE__)
ex = SketchupExtension.new('Speckle SketchUp', File.join(PATH, 'bootstrap'))
+1
View File
@@ -3,6 +3,7 @@
require 'sketchup'
require 'pathname'
require 'speckle_connector/debug'
require_relative 'src/log/log'
require_relative 'src/ui/sketchup_ui'
require_relative 'src/ui/ui_controller'
require_relative 'src/commands/menu_command_handler'
+3 -1
View File
@@ -15,7 +15,9 @@ module SpeckleConnector
def self.reload
load(__FILE__)
pattern = File.join(__dir__, '**/*.rb')
Dir.glob(pattern).each { |file| load(file) }
# TODO: Here is a opportunity to improve reloading process.
# We can cache last edited time of the each file later to check which file need to be reloaded.
Dir.glob(pattern).each { |file| load(file) unless file.include?('bootstrap') }
.size
end
# rubocop:enable SketchupSuggestions/FileEncoding
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 26 KiB

@@ -0,0 +1,73 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Apply mappings for selected entities.
class ApplyMappings < Action
def initialize(entities_to_map, method, category, family,
family_type, level, name, is_definition)
super()
@entities_to_map = entities_to_map
@method = method
@category = category
@name = name
@family = family
@family_type = family_type
@level = level
@is_definition = is_definition
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
sketchup_model.entities
else
sketchup_model.active_path.last.definition.entities
end
# Collect entities from entity ids that comes from UI as list
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id) }
# Switch to definitions if all entities are component instance and UI flag shows that
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
entities_to_map = entities_to_map.collect(&:definition).uniq
end
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
entities_to_map.each do |entity|
name = if @name == '<Mixed>'
entity.respond_to?(:name) ? entity.name : ''
else
@name
end
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :category, @category)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :name, name)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :method, @method)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family, @family)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :family_type, @family_type)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.set_attribute(entity, :level, @level)
speckle_state = speckle_state.with_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { apply: true })
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
end
end
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mapper source.
class ClearMapperSource < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
new_speckle_state = state.speckle_state.with_removed_mapper_source
erase_levels(state)
state.with_speckle_state(new_speckle_state)
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
def self.erase_levels(state)
levels = state.sketchup_state.sketchup_model.definitions.select do |definition|
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.get_attribute(definition, :speckle_type) ==
OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
levels.each do |level|
level.entities.clear!
level.instances.each(&:erase!)
end
end
end
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mappings for selected entities.
class ClearMappings < Action
def initialize(entities_to_map, is_definition)
super()
@entities_to_map = entities_to_map
@is_definition = is_definition
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def update_state(state)
sketchup_model = state.sketchup_state.sketchup_model
entities = if sketchup_model.active_path.nil?
sketchup_model.entities
else
sketchup_model.active_path.last.definition.entities
end
# Collect entities from entity ids that comes from UI as list
entities_to_map = entities.select { |e| @entities_to_map.include?(e.persistent_id) }
# Switch to definitions if all entities are component instance and UI flag shows that
if entities_to_map.all? { |e| e.is_a?(Sketchup::ComponentInstance) } && @is_definition
entities_to_map = entities_to_map.collect(&:definition).uniq
end
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
entities_to_map.each do |entity|
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
speckle_state = speckle_state.with_removed_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { clear: true })
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
end
end
end
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'mapped_entities_updated'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Clear mappings for selected entities from mapped elements table.
class ClearMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
speckle_state = state.speckle_state
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.remove_dictionary(entity)
speckle_state = speckle_state.with_removed_mapped_entity(entity)
end
new_state = MappedEntitiesUpdated.update_state(state.with_speckle_state(speckle_state))
Events::SelectionEventAction.update_state(new_state, { clear: true })
end
end
end
end
@@ -0,0 +1,30 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../mapper_selection_changed'
require_relative '../../mapper/category/revit_category'
require_relative '../../sketchup_model/reader/speckle_entities_reader'
require_relative '../../sketchup_model/reader/mapper_reader'
require_relative '../../sketchup_model/query/entity'
module SpeckleConnector
module Actions
module Events
# Update selected speckle objects when the selection changes for mapper tool.
class SelectionEventAction < EventAction
# @param state [States::State] the current state of Speckle application.
# @return [States::State] the new updated state object
def self.update_state(state, event_data)
return state unless event_data&.any?
# Get sketchup selection
sketchup_selection = state.sketchup_state.sketchup_model.selection
# Collect and return mapper selection info.
# Later we can add more selection info for different scopes.
MapperSelectionChanged.new(sketchup_selection).update_state(state)
end
end
end
end
end
@@ -0,0 +1,36 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Hide entities that selected from mapped elements table.
class HideMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
if entity.is_a?(Sketchup::ComponentDefinition)
entity.instances.each do |instance|
instance.hidden = true
end
end
entity.hidden = true
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
end
end
end
@@ -23,6 +23,7 @@ module SpeckleConnector
preferences = Preferences.read_preferences(sketchup_state.sketchup_model)
user_state_with_preferences = state.user_state.with_preferences(preferences)
state = States::State.new(user_state_with_preferences, speckle_state, sketchup_state, false)
# This is where we attach observers to related model objects like selection, entities..
Actions::LoadSketchupModel.update_state(state, sketchup_state.sketchup_model)
end
@@ -0,0 +1,64 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Isolate entities that selected from mapped elements table.
class IsolateMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.update_state(state, data)
sketchup_model = state.sketchup_state.sketchup_model
# Hide all entities first
sketchup_model.entities.each do |ent|
ent.hidden = true
end
# Flat entities to isolate mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(sketchup_model.entities)
comp_flat_entities = flat_entities.grep(Sketchup::ComponentInstance) + flat_entities.grep(Sketchup::Group) +
flat_entities.grep(Sketchup::ComponentDefinition)
face_edge_flat_entities = flat_entities.grep(Sketchup::Face) + flat_entities.grep(Sketchup::Edge)
# Collect entity ids to clear mappings
selected_elements = data.collect { |_, entities| entities['selectedElements'] }.flatten
comps_or_groups, faces_or_edges = selected_elements.partition do |e|
e['entityType'] == 'Component' || e['entityType'] == 'Definition' || e['entityType'] == 'Group'
end
faces_or_edges_ids = faces_or_edges.collect { |e| e['entityId'] }
face_edge_flat_entities.select { |e| faces_or_edges_ids.include?(e.persistent_id) }.each do |entity|
entity.hidden = false
end
comps_or_groups_ids = comps_or_groups.collect { |e| e['entityId'] }
comp_flat_entities.select { |e| comps_or_groups_ids.include?(e.persistent_id) }.each do |entity|
if entity.is_a?(Sketchup::ComponentDefinition)
entity.instances.each do |instance|
instance.hidden = false
end
end
entity.hidden = false
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
end
end
@@ -3,6 +3,7 @@
require_relative 'action'
require_relative 'initialize_materials'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../preferences/preferences'
require_relative '../states/state'
require_relative '../states/sketchup_state'
@@ -28,6 +29,9 @@ module SpeckleConnector
# Read speckle entities
new_speckle_entities = SketchupModel::Reader::SpeckleEntitiesReader.read(sketchup_model.entities)
new_speckle_state = new_state.speckle_state.with_speckle_entities(Immutable::Hash.new(new_speckle_entities))
# Read mapped entities
new_mapped_entities = SketchupModel::Reader::MapperReader.read_mapped_entities(sketchup_model.entities)
new_speckle_state = new_speckle_state.with_mapped_entities(Immutable::Hash.new(new_mapped_entities))
new_state = new_state.with_speckle_state(new_speckle_state)
# Read preferences from database and model.
@@ -42,8 +46,8 @@ module SpeckleConnector
# @param sketchup_model [Sketchup::Model] the model to attach observers to
# @param observers [Hash{Class=>}] the observer objects indexed by their class that will be attached
def self.attach_observers(sketchup_model, observers)
# selection = sketchup_model.selection
# selection.add_observer(observers[SELECTION_OBSERVER_NAME])
selection = sketchup_model.selection
selection.add_observer(observers[SELECTION_OBSERVER])
# layers = sketchup_model.layers
# layers.add_observer(observers[LAYERS_OBSERVER_NAME])
entities = sketchup_model.entities
@@ -0,0 +1,20 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/reader/mapper_reader'
module SpeckleConnector
module Actions
# Triggers when mapped entities updated.
class MappedEntitiesUpdated < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data = nil)
mapped_entities = SketchupModel::Reader::MapperReader
.mapped_entity_details(state.speckle_state.speckle_mapper_state.mapped_entities.values.to_a)
state.with_mapped_entities_queue(mapped_entities)
end
end
end
end
@@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperInitialized < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
init_parameters = {
categories: Mapper::Category::RevitCategory.to_a,
familyCategories: Mapper::Category::RevitFamilyCategory.to_a
}.freeze
state.with_mapper_init_queue(init_parameters)
end
end
end
end
@@ -0,0 +1,200 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../mapper/category/revit_category'
require_relative '../mapper/category/revit_family_category'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Actions
# Collects mapper selection info.
class MapperSelectionChanged < Action
READER = SketchupModel::Reader
DICTIONARY = SketchupModel::Dictionary
def initialize(selection)
super()
@selection = selection
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
# Get mapping info from selection.
mapping = get_mapping_info(state, @selection)
state.with_mapper_selection_queue(mapping)
end
def filter_out_levels(selection)
selection.reject do |e|
DICTIONARY::SpeckleEntityDictionaryHandler
.get_attribute(e, :speckle_type) == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
end
def get_mapping_info(state, selection)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
selection = filter_out_levels(selection)
grouped_by_type = group_by_type(selection)
supported_entity_count = grouped_by_type.length
# Return empty method list if there is no supported entity to map.
return EMPTY_SELECTION if supported_entity_count == 0
# Return Direct Shape itself if multiple kinds of element are selected like Edge and Face.
# OR single type is equal to only direct shape supports.
return multiple_supported_selection_info(selection) if supported_entity_count > 1
# FIXME: Distinguish selection info according to selection elegantly!!!
if grouped_by_type.keys.first == Sketchup::ComponentInstance
return component_selection_info(selection, source_exist)
end
return group_selection_info(selection) if grouped_by_type.keys.first == Sketchup::Group
if supported_entity_count > 1 ||
(supported_entity_count == 1 &&
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES.include?(grouped_by_type.keys.first))
if source_exist
return direct_shape_selection_info_with_source(selection, [])
else
return direct_shape_selection_info(selection, source_exist)
end
end
# Only single type selections remained after this point.
return face_selection_info(state, grouped_by_type.values.first) if grouped_by_type.keys.first == Sketchup::Face
return edge_selection_info(state, grouped_by_type.values.first) if grouped_by_type.keys.first == Sketchup::Edge
EMPTY_SELECTION
end
MAPPER_SUPPORTED_ENTITY_TYPES = [
Sketchup::ComponentInstance,
Sketchup::Group,
Sketchup::Face,
Sketchup::Edge
].freeze
MAPPER_DIRECT_SHAPE_SUPPORTED_ENTITY_TYPES = [
Sketchup::ComponentInstance,
Sketchup::Group
].freeze
EMPTY_SELECTION = {
selection: [],
mappingMethods: []
}.freeze
def multiple_supported_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def component_selection_info(selection, source_exist)
if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family', 'Family Instance']
}.freeze
else
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape', 'New Revit Family']
}.freeze
end
end
def group_selection_info(selection)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape']
}.freeze
end
def direct_shape_selection_info(selection, source_exist)
methods = ['Direct Shape', 'New Revit Family']
methods.append('Family Instance') if source_exist
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: methods
}.freeze
end
def direct_shape_selection_info_with_default(selection, methods)
{
selection: SketchupModel::Reader::MapperReader.entities_schema_details(selection),
mappingMethods: ['Direct Shape'] + methods
}.freeze
end
def direct_shape_selection_info_with_source(filtered_selection, methods)
instances = @selection.grep(Sketchup::ComponentInstance)
selected_level = instances.find do |i|
DICTIONARY::SpeckleEntityDictionaryHandler
.get_attribute(i, :speckle_type) == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
selected_level_name = nil
if selected_level
selected_level_name = DICTIONARY::SpeckleEntityDictionaryHandler.get_attribute(selected_level, :name)
end
{
selection: READER::MapperReader.entities_schema_details(filtered_selection),
mappingMethods: ['Direct Shape'] + methods,
categories: Mapper::Category::RevitCategory.to_a,
selectedLevelName: selected_level_name
}.freeze
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
def face_selection_info(state, faces)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
grouped_by_verticality = faces.group_by { |face| face.normal.perpendicular?(VECTOR_Z) }
return direct_shape_selection_info(faces, source_exist) if grouped_by_verticality.length == 2
if source_exist
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_source(faces, ['Wall'])
else
direct_shape_selection_info_with_source(faces, ['Floor'])
end
else
if grouped_by_verticality.keys.first
direct_shape_selection_info_with_default(faces, ['Default Wall'])
else
direct_shape_selection_info_with_default(faces, ['Default Floor'])
end
end
end
def edge_selection_info(state, edges)
source_exist = !state.speckle_state.speckle_mapper_state.mapper_source.nil?
if source_exist
methods = ['Column', 'Beam', 'Pipe', 'Duct']
direct_shape_selection_info_with_source(edges, methods)
else
default_methods = ['Default Column', 'Default Beam', 'Default Pipe', 'Default Duct']
direct_shape_selection_info_with_default(edges, default_methods)
end
end
def group_by_type_old(selection)
selection.group_by(&:class).filter_map do |group|
[group.first, group] if MAPPER_SUPPORTED_ENTITY_TYPES.include?(group.first)
end.to_h
end
def group_by_type(selection)
selection.select { |s| MAPPER_SUPPORTED_ENTITY_TYPES.include?(s.class) }.group_by(&:class)
end
end
end
end
@@ -0,0 +1,77 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../constants/type_constants'
require_relative '../mapper/mapper_source'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
module SpeckleConnector
module Actions
# Action to update mapper source.
class MapperSourceUpdated < Action
def initialize(base, stream_id, commit_id)
super()
@base = base
@stream_id = stream_id
@commit_id = commit_id
end
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
levels = convert_levels(state, @base['@Levels'])
types = convert_types(@base['@Types'])
family_instances = convert_family_instance_types(@base['@Types'])
mapper_source = Mapper::MapperSource.new(@stream_id, @commit_id, levels, types)
new_speckle_state = state.speckle_state.with_mapper_source(mapper_source)
state = state.with_speckle_state(new_speckle_state)
state.with_add_queue('mapperSourceUpdated', @stream_id, [
{ is_string: false, val: levels.to_json },
{ is_string: false, val: types.to_json },
{ is_string: false, val: family_instances.to_json }
])
end
def convert_types(types)
types.collect do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
type = type[1..-1] if type[0] == '@'
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
[type, elements]
end.compact.to_h
end
def convert_family_instance_types(types)
family_instance_types = {}
types.each do |type, type_elements|
next if type_elements.nil? || !type_elements.is_a?(Array) || type == '__closure'
# skip type if there is no any revit symbol element type
symbol_element_types = type_elements.select do |t|
t['speckle_type'] == OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE &&
t['placementType'] == 'OneLevelBased'
end
next if symbol_element_types.empty?
elements = type_elements.map do |type_element|
SpeckleObjects::BuiltElements::Revit::RevitElementType.to_native(type_element)
end
elements = elements.group_by { |e| e[:family] }
family_instance_types.merge!(elements)
end
family_instance_types
end
def convert_levels(state, levels)
levels.collect do |level|
SpeckleObjects::BuiltElements::Level.to_native(state, level, @stream_id)
end
end
end
end
end
@@ -4,6 +4,7 @@ require_relative 'action'
require_relative 'events/app_event_action'
require_relative 'events/entities_event_action'
require_relative 'events/model_event_action'
require_relative 'events/selection_event_action'
require_relative '../constants/observer_constants'
module SpeckleConnector
@@ -13,11 +14,11 @@ module SpeckleConnector
RUN_ORDER = {
APP_OBSERVER => Events::AppEventAction,
ENTITIES_OBSERVER => Events::EntitiesEventAction,
MODEL_OBSERVER => Events::ModelEventAction
MODEL_OBSERVER => Events::ModelEventAction,
# MATERIALS_OBSERVER => Events::MaterialsEventAction,
# LAYERS_OBSERVER => Events::LayerEventAction,
# PAGES_OBSERVER => Events::PagesEventAction,
# SELECTION_OBSERVER => Events::SelectionEventAction
SELECTION_OBSERVER => Events::SelectionEventAction
}.freeze
def self.update_state(state, events)
@@ -3,6 +3,7 @@
require_relative 'action'
require_relative '../convertors/units'
require_relative '../convertors/to_native'
require_relative '../convertors/clean_up'
module SpeckleConnector
module Actions
@@ -26,7 +27,12 @@ module SpeckleConnector
converter = Converters::ToNative.new(state, @stream_id, @stream_name, @branch_name, @source_app)
# Have side effects on the sketchup model. It effects directly on the entities by adding new objects.
start_time = Time.now.to_f
state.sketchup_state.sketchup_model.start_operation('Receive Speckle Objects', true)
state = converter.receive_commit_object(@base)
if state.user_state.model_preferences[:merge_coplanar_faces]
Converters::CleanUp.merge_coplanar_faces(converter.converted_faces)
end
state.sketchup_state.sketchup_model.commit_operation
elapsed_time = (Time.now.to_f - start_time).round(3)
puts "==== Converting to Native executed in #{elapsed_time} sec ===="
puts "==== Source application is #{@source_app}. ===="
@@ -0,0 +1,37 @@
# frozen_string_literal: true
require_relative 'action'
require_relative 'events/selection_event_action'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module Actions
# Select entities that selected from mapped elements table.
class SelectMappingsFromTable < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, data)
# Clear first selection
state.sketchup_state.sketchup_model.selection.clear
# Flat entities to clear mappings
flat_entities = SketchupModel::Query::Entity.flat_entities(state.sketchup_state.sketchup_model.entities)
# Collect entity ids to clear mappings
entity_ids = data.collect { |_, entities| entities['selectedElements'].collect { |e| e['entityId'] } }.flatten
# Store speckle state to update with mapped entities.
flat_entities.each do |entity|
next unless entity_ids.include?(entity.persistent_id)
if entity.is_a?(Sketchup::ComponentDefinition)
state.sketchup_state.sketchup_model.selection.add(entity.instances)
end
state.sketchup_state.sketchup_model.selection.add(entity)
end
Events::SelectionEventAction.update_state(state, { clear: true })
end
end
end
end
@@ -0,0 +1,20 @@
# frozen_string_literal: true
require_relative 'action'
module SpeckleConnector
module Actions
# Show all entities on the model.
class ShowAllEntities < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
# Show all entities first
state.sketchup_state.sketchup_model.entities.each do |ent|
ent.hidden = false
end
state
end
end
end
end
@@ -32,16 +32,20 @@ module SpeckleConnector
ui_controller.update_ui(state)
end
# Attach observers to application when speckle initialized via menu commands.
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
end
# Send messages to HtmlDialog if any.
def send_messages!
queue = @state.speckle_state.message_queue
queue.each_value { |value| ui_controller.user_interfaces[Ui::SPECKLE_UI_ID].dialog.execute_script(value) }
update_state!(Actions::ClearQueue)
end
def add_observer_handler!(observer_handler)
@observer_handler = observer_handler
end
# This is the only function application state will be switched by calling upcoming action with it's parameters
# if any.
def update_state!(action, *parameters)
old_state = @state
@state = action.update_state(old_state, *parameters)
@@ -0,0 +1,25 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/apply_mappings'
module SpeckleConnector
module Commands
# Command to apply mapping for selected entities.
class ApplyMappings < Command
def _run(data)
entities_to_map = data['entitiesToMap']
method = data['method']
category = data['category']
family = data['family']
family_type = data['familyType']
level = data['level']
name = data['name']
is_definition = data['isDefinition']
action = Actions::ApplyMappings.new(entities_to_map, method, category, family,
family_type, level, name, is_definition)
app.update_state!(action)
end
end
end
end
@@ -0,0 +1,18 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/clear_mappings'
module SpeckleConnector
module Commands
# Command to clear mapping for selected entities.
class ClearMappings < Command
def _run(data)
entities_to_map = data['entitiesToClearMap']
is_definition = data['isDefinition']
action = Actions::ClearMappings.new(entities_to_map, is_definition)
app.update_state!(action)
end
end
end
end
@@ -12,7 +12,7 @@ module SpeckleConnector
# This is the command where we show UI to user.
class InitializeSpeckle < Command
def dialog_title
"Speckle #{CONNECTOR_VERSION}"
"Speckle (Legacy) #{CONNECTOR_VERSION}"
end
private
@@ -0,0 +1,19 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../actions/mapper_source_updated'
module SpeckleConnector
module Commands
# Command to update mapper source.
class MapperSourceUpdated < Command
def _run(data)
base = data['base']
stream_id = data['stream_id']
commit_id = data['commit_id']
action = Actions::MapperSourceUpdated.new(base, stream_id, commit_id)
app.update_state!(action)
end
end
end
end
@@ -0,0 +1,27 @@
# frozen_string_literal: true
require_relative 'command'
require_relative '../states/initial_state'
require_relative '../ui/vue_view'
require_relative '../actions/initialize_speckle'
require_relative '../observers/factory'
module SpeckleConnector
module Commands
# Command to reset Speckle UI window location onto center of SketchUp window.
class ResetWindowLocation < Command
private
def _run
app = self.app
vue_view = app.ui_controller.user_interfaces[Ui::SPECKLE_UI_ID]
if vue_view
vue_view.dialog.reset_dialog_location
else
puts "Speckle UI didn't initialized!"
end
end
end
end
end
@@ -3,6 +3,7 @@
require_relative 'menu_command_handler'
require_relative 'action_command'
require_relative 'initialize_speckle'
require_relative 'reset_window_location'
require_relative '../actions/one_click_send'
module SpeckleConnector
@@ -10,6 +11,7 @@ module SpeckleConnector
# Speckle menu commands that adds them to Sketchup menu and toolbar.
class SpeckleMenuCommands
CMD_INITIALIZE_SPECKLE = :initialize_speckle
CMD_RESET_WINDOW_LOCATION_SPECKLE = :reset_window_location_speckle
CMD_SEND_TO_SPECKLE = :send_to_speckle
CMD_RECEIVE_FROM_SPECKLE = :receive_from_speckle
@@ -26,31 +28,29 @@ module SpeckleConnector
commands.add_to_menu!(CMD_INITIALIZE_SPECKLE, speckle_menu)
commands.add_to_toolbar!(CMD_INITIALIZE_SPECKLE, speckle_toolbar)
# commands[CMD_SEND_TO_SPECKLE] = send_command(app)
# commands.add_to_menu!(CMD_SEND_TO_SPECKLE, speckle_menu)
# commands.add_to_toolbar!(CMD_SEND_TO_SPECKLE, speckle_toolbar)
commands[CMD_RESET_WINDOW_LOCATION_SPECKLE] = reset_window_location_command(app)
commands.add_to_menu!(CMD_RESET_WINDOW_LOCATION_SPECKLE, speckle_menu)
end
def self.initialize_speckle_command(app)
cmd = MenuCommandHandler.sketchup_command(
InitializeSpeckle.new(app), 'Initialize Speckle'
InitializeSpeckle.new(app), 'Initialize Speckle (Legacy)'
)
cmd.tooltip = 'Launch Connector'
cmd.status_bar_text = 'Opens the Speckle Connector window'
cmd.status_bar_text = 'Opens the Speckle (Legacy) Connector window'
cmd.small_icon = '../../img/s2logo.png'
cmd.large_icon = '../../img/s2logo.png'
cmd
end
def self.send_command(app)
def self.reset_window_location_command(app)
cmd = MenuCommandHandler.sketchup_command(
ActionCommand.new(app, Actions::OneClickSend), 'Send to Speckle'
ResetWindowLocation.new(app), 'Reset Window Location'
)
cmd.tooltip = 'Send to Speckle'
cmd.status_bar_text = 'Send to Speckle'
cmd.small_icon = '../../img/Sender.png'
cmd.large_icon = '../../img/Sender.png'
cmd.set_validation_proc { MenuCommandHandler.speckle_started(app) }
cmd.tooltip = 'Bring Speckle window onto center of SketchUp window'
cmd.status_bar_text = 'Bring Speckle window onto center of SketchUp window'
cmd.small_icon = '../../img/s2logo.png'
cmd.large_icon = '../../img/s2logo.png'
cmd
end
end
@@ -2,6 +2,9 @@
module SpeckleConnector
SPECKLE_BASE_OBJECT = 'Speckle_Base_Object'
SPECKLE_MAPPING_TOOL_SCHEMA = 'Speckle_Mapping_Tool_Schema'
SPECKLE_SCHEMA = 'Speckle_Schema'
SPECKLE_ID = 'speckle_id'
SPECKLE_TYPE = 'speckle_type'
APPLICATION_ID = 'application_id'
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module SpeckleConnector
VECTOR_Z = Geom::Vector3d.new(0, 0, 1)
end
@@ -4,4 +4,5 @@ module SpeckleConnector
APP_OBSERVER = 'SpeckleConnector::Observers::AppObserver'
ENTITIES_OBSERVER = 'SpeckleConnector::Observers::EntitiesObserver'
MODEL_OBSERVER = 'SpeckleConnector::Observers::ModelObserver'
SELECTION_OBSERVER = 'SpeckleConnector::Observers::SelectionObserver'
end
@@ -14,7 +14,9 @@ module SpeckleConnector
path = ENV.fetch('APPDATA')
Pathname.new(File.join(path, 'Speckle')).cleanpath.to_s
when OS_MAC
File.join(Dir.home, 'Library/Application Support/Speckle')
primary_path = File.join(Dir.home, '.config/Speckle')
fallback_path = File.join(Dir.home, 'Library/Application Support/Speckle')
Dir.exist?(primary_path) ? primary_path : fallback_path
else
raise 'Speckle could not determine your Appdata path'
end
@@ -11,6 +11,13 @@ module SpeckleConnector
INCLUDE_COMPONENT_ENTITY_ATTRIBUTES = :include_component_entity_attributes
MERGE_COPLANAR_FACES = :merge_coplanar_faces
ENTITY_KEYS_FOR_INCLUDING_ATTRIBUTES = {
Sketchup::ComponentInstance => INCLUDE_COMPONENT_ENTITY_ATTRIBUTES,
Sketchup::Group => INCLUDE_GROUP_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_FACE_ENTITY_ATTRIBUTES,
Sketchup::Edge => INCLUDE_EDGE_ENTITY_ATTRIBUTES
}.freeze
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
DEFAULT_MODEL_PREFERENCES = {
@@ -3,10 +3,35 @@
module SpeckleConnector
BASE_OBJECT = 'Base'
OBJECTS_GIS_POLYGONELEMENT = 'Objects.GIS.PolygonElement'
OBJECTS_GIS_LINEELEMENT = 'Objects.GIS.LineElement'
OBJECTS_BUILTELEMENTS_VIEW3D = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
OBJECTS_BUILTELEMENTS_NETWORK = 'Objects.BuiltElements.Network'
OBJECTS_BUILTELEMENTS_REVIT_LEVEL = 'Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel'
OBJECTS_BUILTELEMENTS_DEFAULT_FLOOR = 'Objects.BuiltElements.Floor'
OBJECTS_BUILTELEMENTS_REVIT_FLOOR = 'Objects.BuiltElements.Floor:Objects.BuiltElements.Revit.RevitFloor'
OBJECTS_BUILTELEMENTS_DEFAULT_WALL = 'Objects.BuiltElements.Wall'
OBJECTS_BUILTELEMENTS_REVIT_WALL = 'Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall'
OBJECTS_BUILTELEMENTS_DEFAULT_COLUMN = 'Objects.BuiltElements.Column'
OBJECTS_BUILTELEMENTS_REVIT_COLUMN = 'Objects.BuiltElements.Column:Objects.BuiltElements.Revit.RevitColumn'
OBJECTS_BUILTELEMENTS_DEFAULT_BEAM = 'Objects.BuiltElements.Beam'
OBJECTS_BUILTELEMENTS_REVIT_BEAM = 'Objects.BuiltElements.Beam:Objects.BuiltElements.Revit.RevitBeam'
OBJECTS_BUILTELEMENTS_DEFAULT_PIPE = 'Objects.BuiltElements.Pipe'
OBJECTS_BUILTELEMENTS_REVIT_PIPE = 'Objects.BuiltElements.Pipe:Objects.BuiltElements.Revit.RevitPipe'
OBJECTS_BUILTELEMENTS_DEFAULT_DUCT = 'Objects.BuiltElements.Duct'
OBJECTS_BUILTELEMENTS_REVIT_DUCT = 'Objects.BuiltElements.Duct:Objects.BuiltElements.Revit.RevitDuct'
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
OBJECTS_BUILTELEMENTS_REVIT_FAMILY_INSTANCE = 'Objects.BuiltElements.Revit.FamilyInstance'
OBJECTS_BUILTELEMENTS_REVIT_PARAMETER = 'Objects.BuiltElements.Revit.Parameter'
OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType'
OBJECTS_BUILTELEMENTS_REVIT_REVITSYMBOLELEMENTTYPE = 'Objects.BuiltElements.Revit.RevitElementType:Objects.BuiltElements.Revit.RevitSymbolElementType'
OBJECTS_GEOMETRY_LINE = 'Objects.Geometry.Line'
OBJECTS_GEOMETRY_POLYLINE = 'Objects.Geometry.Polyline'
OBJECTS_GEOMETRY_POLYCURVE = 'Objects.Geometry.Polycurve'
OBJECTS_GEOMETRY_ARC = 'Objects.Geometry.Arc'
OBJECTS_GEOMETRY_CIRCLE = 'Objects.Geometry.Circle'
OBJECTS_GEOMETRY_MESH = 'Objects.Geometry.Mesh'
OBJECTS_GEOMETRY_BREP = 'Objects.Geometry.Brep'
@@ -16,4 +41,9 @@ module SpeckleConnector
OBJECTS_OTHER_REVIT_REVITINSTANCE = 'Objects.Other.Revit.RevitInstance'
OBJECTS_OTHER_BLOCKDEFINITION = 'Objects.Other.BlockDefinition'
OBJECTS_OTHER_RENDERMATERIAL = 'Objects.Other.RenderMaterial'
OBJECTS_OTHER_DISPLAYSTYLE = 'Objects.Other.DisplayStyle'
SPECKLE_CORE_MODELS_COLLECTION = 'Speckle.Core.Models.Collection'
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.RasterLayer'
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER = 'Speckle.Core.Models.Collection:Objects.GIS.VectorLayer'
end
@@ -228,6 +228,8 @@ module SpeckleConnector
return false unless is_array && value.length == 2
return false if value[1].nil?
value[1].all? { |v| v.is_a?(Sketchup::Entity) }
end
+35 -25
View File
@@ -11,12 +11,31 @@ module SpeckleConnector
# @param entities [Sketchup::Entities] entities to remove edges between that make entities coplanar.
# @note Merging coplanar faces idea originated from [CleanUp](https://github.com/thomthom/cleanup) plugin
# which is developed by [Thomas Thomassen](https://github.com/thomthom).
def self.merge_coplanar_faces(entities)
def self.merge_coplanar_entities(entities)
edges = []
faces = entities.collect { |entity| entity if entity.is_a? Sketchup::Face }.compact
faces = merged_faces(faces)
faces.each { |face| face.edges.each { |edge| edges << edge } }
edges.uniq!
edges.each { |edge| remove_edge_have_coplanar_faces(edge, faces, false) }
# Remove remaining orphan edges
edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
merged_faces(faces)
end
def self.merge_coplanar_faces(faces)
edges = []
faces = faces.reject(&:deleted?)
faces.each { |face| face.edges.each { |edge| edges << edge } }
edges.uniq!
edges.each { |edge| remove_edge_have_coplanar_faces(edge) }
# Remove remaining orphan edges
# edges.reject(&:deleted?).select { |edge| edge.faces.empty? }.each(&:erase!)
merged_faces(faces)
end
@@ -33,43 +52,34 @@ module SpeckleConnector
# - Whether UV texture map is aligned between faces or not.
# - Finally, if faces are coplanar by correcting these checks, then removes edge from Sketchup.active_model.
# @param edge [Sketchup::Edge] edge to check.
# @param faces [Array<Sketchup::Face>] scoped faces to check 'edge.faces' both (first and second)
# belongs to this faces or not. If any of this faces does not involve this scoped faces, then do not delete.
# @param ignore_materials [Boolean] whether ignore materials or not.
# Returns true if the given edge separating two coplanar faces.
# Return false otherwise.
# rubocop:disable Metrics/AbcSize
def self.remove_edge_have_coplanar_faces(edge, faces, ignore_materials)
def self.remove_edge_have_coplanar_faces(edge)
return false unless edge.valid? && edge.is_a?(Sketchup::Edge)
return false unless edge.faces.size == 2
# Check scoped faces have this edges
if edge.faces.size == 2
is_first = faces.include?(edge.faces[0])
is_second = faces.include?(edge.faces[1])
return false unless is_first && is_second
end
face_1, face_2 = edge.faces
return false if face_duplicate?(face_1, face_2)
# Check for troublesome faces which might lead to missing geometry if merged.
return false unless edge_safe_to_merge?(edge)
begin
return false unless face_1.normal.samedirection?(face_2.normal)
return false if face_duplicate?(face_1, face_2)
# Check for troublesome faces which might lead to missing geometry if merged.
return false unless edge_safe_to_merge?(edge)
# Check materials match.
unless ignore_materials
return false unless (face_1.material == face_2.material) && (face_1.back_material == face_2.back_material)
# Verify UV mapping match.
return false if !face_1.material.nil? && !continuous_uv?(face_1, face_2, edge) && face_1.material.texture.nil?
end
# Check faces are coplanar or not.
return false unless faces_coplanar?(face_1, face_2)
# Check faces are coplanar or not.
return false unless faces_coplanar?(face_1, face_2)
edge.erase!
true
edge.erase!
true
rescue StandardError => e
puts "Failed to merge coplanar faces by removing edge with error: #{e}"
false
end
end
# rubocop:enable Metrics/AbcSize
# Determines if two faces are overlapped.
def self.face_duplicate?(face_1, face_2, overlapping: false)
+137 -150
View File
@@ -2,16 +2,26 @@
require_relative 'converter'
require_relative '../constants/type_constants'
require_relative '../speckle_entities/speckle_entity'
require_relative '../speckle_objects/gis/polygon_element'
require_relative '../speckle_objects/gis/line_element'
require_relative '../speckle_objects/other/transform'
require_relative '../speckle_objects/other/render_material'
require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/other/block_instance'
require_relative '../speckle_objects/other/display_value'
require_relative '../speckle_objects/other/revit/revit_instance'
require_relative '../speckle_objects/revit/revit_instance'
require_relative '../speckle_objects/geometry/point'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/polycurve'
require_relative '../speckle_objects/geometry/arc'
require_relative '../speckle_objects/geometry/circle'
require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/network'
require_relative '../speckle_objects/speckle/core/models/collection'
require_relative '../speckle_objects/speckle/core/models/gis_layer_collection'
require_relative '../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module Converters
@@ -24,31 +34,49 @@ module SpeckleConnector
# @return [String] source application of received object that will be converted to native
attr_reader :source_app
attr_reader :converted_faces
def initialize(state, stream_id, stream_name, branch_name, source_app)
super(state, stream_id)
@stream_name = stream_name
@branch_name = branch_name
@source_app = source_app.downcase
@converted_faces = []
end
# Module aliases
GEOMETRY = SpeckleObjects::Geometry
OTHER = SpeckleObjects::Other
REVIT = SpeckleObjects::Revit
BUILTELEMENTS = SpeckleObjects::BuiltElements
GIS = SpeckleObjects::GIS
# Class aliases
POINT = GEOMETRY::Point
LINE = GEOMETRY::Line
POLYCURVE = GEOMETRY::Polycurve
ARC = GEOMETRY::Arc
CIRCLE = GEOMETRY::Circle
MESH = GEOMETRY::Mesh
BLOCK_DEFINITION = OTHER::BlockDefinition
BLOCK_INSTANCE = OTHER::BlockInstance
REVIT_INSTANCE = OTHER::Revit::RevitInstance
REVIT_INSTANCE = REVIT::Other::RevitInstance
REVIT_WALL = BUILTELEMENTS::RevitWall
RENDER_MATERIAL = OTHER::RenderMaterial
DISPLAY_VALUE = OTHER::DisplayValue
VIEW3D = BUILTELEMENTS::View3d
POLYGON_ELEMENT = GIS::PolygonElement
LINE_ELEMENT = GIS::LineElement
COLLECTION = SpeckleObjects::Speckle::Core::Models::Collection
GIS_LAYER_COLLECTION = SpeckleObjects::Speckle::Core::Models::GisLayerCollection
BASE_OBJECT_PROPS = %w[applicationId id speckle_type totalChildrenCount].freeze
CONVERTABLE_SPECKLE_TYPES = %w[
Objects.Geometry.Line
Objects.Geometry.Polyline
Objects.Geometry.Polycurve
Objects.Geometry.Arc
Objects.Geometry.Circle
Objects.Geometry.Mesh
Objects.Geometry.Brep
Objects.Other.BlockInstance
@@ -56,44 +84,92 @@ module SpeckleConnector
Objects.Other.BlockDefinition
Objects.Other.RenderMaterial
Objects.Other.Instance:Objects.Other.BlockInstance
Objects.BuiltElements.View:Objects.BuiltElements.View3D
Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall
Objects.BuiltElements.Network
Objects.GIS.PolygonElement
Objects.GIS.LineElement
Speckle.Core.Models.Collection
Speckle.Core.Models.Collection:Objects.GIS.RasterLayer
Speckle.Core.Models.Collection:Objects.GIS.VectorLayer
].freeze
def from_revit
@from_revit ||= source_app.include?('revit')
end
def from_rhino
@from_rhino ||= source_app.include?('rhino')
end
def from_sketchup
@from_sketchup ||= source_app.include?('sketchup')
end
def from_qgis
@from_qgis ||= source_app.include?('qgis')
end
# ReceiveObjects action call this method by giving everything that comes from server.
# Upcoming object is a referencedObject of selected commit to receive.
# UI is responsible currently to fetch objects from ObjectLoader module by calling getAndConstruct method.
# @param obj [Object] speckle commit object.
def receive_commit_object(obj)
# First create layers on the sketchup before starting traversing
# @Named Views are exception here. It does not mean a layer. But it is anti-pattern for now.
filtered_layer_containers = obj.keys.filter_map { |key| key if key.start_with?('@') && key != '@Named Views' }
create_layers(filtered_layer_containers, sketchup_model.layers) unless from_revit
# Convert views to sketchup scenes
SpeckleObjects::BuiltElements::View3d.to_native(obj, sketchup_model)
# Get default commit layer from sketchup model which will be used as fallback
unless from_revit
# Create layers and it's folders from layers relation on the model collection.
SpeckleObjects::Relations::Layers.to_native(obj, source_app, sketchup_model)
end
# By default entities to fill is sketchup model's entities.
@entities_to_fill = sketchup_model.entities
# Navigate to branch entities if commit doesn't come from sketchup
unless from_sketchup
@branch_definition = branch_definition
@entities_to_fill = @branch_definition.entities
end
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
@entities_to_fill = entities_to_fill(obj)
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer, @entities_to_fill)
traverse_commit_object(obj, default_commit_layer, @entities_to_fill)
create_levels_from_section_planes
check_hiding_layers_needed
try_create_instance
@state
end
# Creating instance from @branch_definition only available for non-sketchup commits since we wrap commits
# under instance.
# There is also another use case that maybe definition is exist in file but user might be deleted it before.
# If this is the case we can add instance by checking number of instances.
# rubocop:disable Style/GuardClause
def try_create_instance
if !from_sketchup && (!@is_update_commit || @branch_definition.instances.empty?)
instance = sketchup_model.entities.add_instance(@branch_definition, Geom::Transformation.new)
BLOCK_INSTANCE.align_instance_axes(instance) if from_qgis
end
end
# rubocop:enable Style/GuardClause
def levels_layer
@levels_layer ||= sketchup_model.layers.add('Levels')
end
def clear_levels
instances = @entities_to_fill.grep(Sketchup::ComponentInstance)
instances.each do |instance|
speckle_type = instance.get_attribute(SPECKLE_BASE_OBJECT, 'speckle_type')
next if speckle_type.nil?
sketchup_model.definitions.remove(instance.definition) if speckle_type == OBJECTS_BUILTELEMENTS_REVIT_LEVEL
end
end
# Create levels from section planes that already created for this commit object.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def create_levels_from_section_planes
clear_levels if @is_update_commit
return unless from_revit
section_planes = @entities_to_fill.grep(Sketchup::SectionPlane)
@@ -105,7 +181,9 @@ module SpeckleConnector
section_planes.each do |section_plane|
level_name = "#{@definition_name}-#{section_plane.name}"
definition = sketchup_model.definitions.add(level_name)
@entities_to_fill.add_instance(definition, Geom::Transformation.new)
instance = @entities_to_fill.add_instance(definition, Geom::Transformation.new)
att = section_plane.attribute_dictionary(SPECKLE_BASE_OBJECT).to_h
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_hash(instance, att)
elevation = section_plane.bounds.center.z
c1_e = Geom::Point3d.new(c_1.x, c_1.y, elevation - LEVEL_SHIFT_VALUE)
c2_e = Geom::Point3d.new(c_2.x, c_2.y, elevation - LEVEL_SHIFT_VALUE)
@@ -122,8 +200,17 @@ module SpeckleConnector
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# @return [Sketchup::ComponentDefinition] branch definition to fill objects in it.
def branch_definition
@definition_name = "#{@branch_name}-#{@stream_name}"
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
@is_update_commit = !definition.nil?
definition = sketchup_model.definitions.add(@definition_name) if definition.nil?
definition
end
def entities_to_fill(_obj)
return sketchup_model.entities if from_sketchup
return sketchup_model.entities unless from_revit
@definition_name = "#{@branch_name}-#{@stream_name}"
definition = sketchup_model.definitions.find { |d| d.name == @definition_name }
@@ -131,7 +218,7 @@ module SpeckleConnector
definition = sketchup_model.definitions.add(@definition_name)
sketchup_model.entities.add_instance(definition, Geom::Transformation.new)
end
definition.entities
definition
end
LAYERS_WILL_BE_HIDDEN = [
@@ -169,82 +256,14 @@ module SpeckleConnector
['Objects.BuiltElements.Revit.Parameter'].include?(obj['speckle_type'])
end
# Create actual Sketchup layers from layer_paths that taken from Speckle base object.
# @param layer_paths [Array<String>] layer paths to decompose it to folders and it's layers.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] folder to create folders and layers under it.
def create_layers(layer_paths, folder)
# Strip leading '@'
layers_with_folders = layer_paths.map { |layer| layer[1..-1] }
# Split layer_paths according to having parent folder or not.
layers_with_head_folder, headless_layers = layers_with_folders.partition { |layer| layer.include?('::') }
# Create array of array that split with '::'
folder_layer_arrays = layers_with_head_folder.collect { |folder_layer| folder_layer.split('::') }
# Add headless layers into `Sketchup.active_model.layers`
create_headless_layers(headless_layers, folder)
# Create layers that have parent folder(s)- this method is recursive until all tree is created.
create_folder_layers(folder_layer_arrays, folder)
end
# @param page [Sketchup::Page] scene to update -update properties-
def set_page_update_properties(page, update_properties)
update_properties.each do |prop, value|
page.instance_variable_set(:"@#{prop}", value)
end
end
# @param rendering_options [Sketchup::RenderingOptions] rendering options of scene (page)
def set_rendering_options(rendering_options, speckle_rendering_options)
speckle_rendering_options.each do |prop, value|
next if rendering_options[prop].nil?
rendering_options[prop] = if value.is_a?(Hash)
SpeckleObjects::Others::Color.to_native(value)
else
value
end
end
end
# @param headless_layers [Array<String>] headless layer names.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_headless_layers(headless_layers, folder)
headless_layers.each do |layer_name|
# Add layer first to the layers object of sketchup model.
layer = sketchup_model.layers.add(layer_name)
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer_name }
end
end
# Create layers with it's parent folders.
# @param folder [Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_folder_layers(folder_layer_arrays, folder)
folder_layer_arrays.each do |folder_layer_array|
create_folder_layer(folder_layer_array, folder)
end
end
# Create layers that have parent folder(s)- this method is recursive (self-caller) until all tree is created.
def create_folder_layer(folder_array, folder)
if folder_array.length > 1
# add folder if it is not exist.
folder.add_folder(folder_array[0]) unless folder.folders.any? { |f| f.display_name == folder_array[0] }
new_folder = folder.folders.find { |f| f.display_name == folder_array[0] }
create_folder_layer(folder_array[1..-1], new_folder)
else
# Add layer first to the layers object of sketchup model.
layer = sketchup_model.layers.add(folder_array[0])
folder.add_layer(layer) unless folder.layers.any? { |l| l.display_name == layer }
end
end
# Traversal method to create Sketchup objects from upcoming base object.
# @param obj [Hash, Array] object might be source base object or it's sub objects, because this method is a
# self-caller method means that call itself according to conditions inside of it.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def traverse_commit_object(obj, commit_folder, layer, entities)
def traverse_commit_object(obj, layer, entities)
if convertible_to_native?(obj)
@state = convert_to_native(@state, obj, layer, entities)
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
elsif obj.is_a?(Hash) && obj.key?('speckle_type')
return if ignored_speckle_type?(obj)
@@ -252,57 +271,23 @@ module SpeckleConnector
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']}. Continuing traversal.")
props = obj.keys.filter_map { |key| key unless key.start_with?('_') }
props.each do |prop|
layer_path = prop if prop.start_with?('@') && obj[prop].is_a?(Array)
layer = find_layer(layer_path, commit_folder, layer)
traverse_commit_object(obj[prop], commit_folder, layer, entities)
traverse_commit_object(obj[prop], layer, entities)
end
else
# puts(">>> Found #{obj['speckle_type']}: #{obj['id']} with displayValue.")
@state = convert_to_native(@state, obj, layer, entities)
@state, _converted_entities = convert_to_native(@state, obj, layer, entities)
end
elsif obj.is_a?(Hash)
obj.each_value { |value| traverse_commit_object(value, commit_folder, layer, entities) }
obj.each_value { |value| traverse_commit_object(value, layer, entities) }
elsif obj.is_a?(Array)
obj.each { |value| traverse_commit_object(value, commit_folder, layer, entities) }
obj.each { |value| traverse_commit_object(value, layer, entities) }
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# Find layer of the Speckle object by checking iteratively into folder.
# @param layer_path [String] complete layer_path to retrieve
# @param folder [Sketchup::LayerFolder, Sketchup::Layers] entry folder to search layer
# @param fallback_layer [Sketchup::Layer] fallback layer to assign object later if any error occur.
# @return [Sketchup::Layer] layer according to path
# @example
# "@folder_1::folder_2::layer_1"
# # it will return the layer object which has display name as `layer_1`.
def find_layer(layer_path, folder, fallback_layer)
begin
# Split folders and it's tail layer (last one is layer, others are folders.)
layer_path_array = layer_path[1..-1].split('::')
# Get sub folders as array, might be empty if `layer_path_array` has only 1 entry
sub_folders = layer_path_array.length > 1 ? layer_path_array[0..-2] : []
# Get exact layer name from last entry
layer_name = layer_path_array.last
# Iterate sub folders to find new sub folder to switch it.
# It help to search in the tree by switching the target search folder.
# Finally we can reach the layer name.
sub_folders.each do |sub_folder|
# Try to find sub folder into source folder passes by argument
s_f = folder.folders.find { |f| f.display_name == sub_folder }
# Switch source folder if any exist
folder = s_f unless s_f.nil?
end
# Find finally the layer into related folder
folder.layers.find { |l| l.display_name == layer_name }
rescue StandardError
return fallback_layer
end
end
def speckle_object_to_native(obj)
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil?
return DISPLAY_VALUE.method(:to_native) unless obj['displayValue'].nil? && obj['@displayValue'].nil?
SPECKLE_OBJECT_TO_NATIVE[obj['speckle_type']]
end
@@ -310,13 +295,25 @@ module SpeckleConnector
SPECKLE_OBJECT_TO_NATIVE = {
OBJECTS_GEOMETRY_LINE => LINE.method(:to_native),
OBJECTS_GEOMETRY_POLYLINE => LINE.method(:to_native),
OBJECTS_GEOMETRY_POLYCURVE => POLYCURVE.method(:to_native),
OBJECTS_GEOMETRY_ARC => ARC.method(:to_native),
OBJECTS_GEOMETRY_CIRCLE => CIRCLE.method(:to_native),
OBJECTS_GEOMETRY_MESH => MESH.method(:to_native),
OBJECTS_GEOMETRY_BREP => MESH.method(:to_native),
OBJECTS_OTHER_BLOCKDEFINITION => BLOCK_DEFINITION.method(:to_native),
OBJECTS_OTHER_BLOCKINSTANCE => BLOCK_INSTANCE.method(:to_native),
OBJECTS_OTHER_BLOCKINSTANCE_FULL => BLOCK_INSTANCE.method(:to_native),
OBJECTS_OTHER_REVIT_REVITINSTANCE => REVIT_INSTANCE.method(:to_native),
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native)
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native),
OBJECTS_BUILTELEMENTS_VIEW3D => VIEW3D.method(:to_native),
OBJECTS_BUILTELEMENTS_REVIT_WALL => REVIT_WALL.method(:to_native),
OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE => BUILTELEMENTS::Revit::DirectShape.method(:to_native),
OBJECTS_BUILTELEMENTS_NETWORK => BUILTELEMENTS::Network.method(:to_native),
OBJECTS_GIS_POLYGONELEMENT => POLYGON_ELEMENT.method(:to_native),
OBJECTS_GIS_LINEELEMENT => LINE_ELEMENT.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION => COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_RASTER_LAYER => GIS_LAYER_COLLECTION.method(:to_native),
SPECKLE_CORE_MODELS_COLLECTION_VECTOR_LAYER => GIS_LAYER_COLLECTION.method(:to_native)
}.freeze
# @param state [States::State] state of the speckle application
@@ -328,6 +325,8 @@ module SpeckleConnector
# Call 'to_native' method by passing this method itself to handle nested 'to_native' conversions.
# It returns updated state and converted entities.
state, converted_entities = to_native_method.call(state, obj, layer, entities, &convert_to_native)
faces = converted_entities.select { |e| e.is_a?(Sketchup::Face) }
@converted_faces += faces if faces.any?
if from_revit
# Create levels as section planes if they exists
create_levels(state, obj)
@@ -335,11 +334,11 @@ module SpeckleConnector
create_layers_from_categories(state, obj, converted_entities)
end
# Create speckle entities from sketchup entities to achieve continuous traversal.
convert_to_speckle_entities(state, obj, converted_entities)
SpeckleEntities::SpeckleEntity.from_speckle_object(state, obj, converted_entities, stream_id)
rescue StandardError => e
puts("Failed to convert #{obj['speckle_type']} (id: #{obj['id']})")
puts(e)
return state
return state, []
end
# rubocop:disable Metrics/CyclomaticComplexity
@@ -349,13 +348,13 @@ module SpeckleConnector
layer = sketchup_model.layers.find { |l| l.display_name == speckle_object['category'] }
unless layer.nil?
entities.each { |entity| entity.layer = layer } if layer
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
return state
end
layer = sketchup_model.layers.add(speckle_object['category'])
unless layer.nil?
entities.each { |entity| entity.layer = layer } if layer
entities.each { |entity| entity.layer = layer if entity.respond_to?(:layer) } if layer
state
end
state
@@ -365,39 +364,27 @@ module SpeckleConnector
# @param state [States::State] state of the speckle application
def create_levels(state, speckle_object)
return state if speckle_object['level'].nil?
return state unless speckle_object['level']['speckle_type'].include?('Objects.BuiltElements.Level')
level = speckle_object['level']
return state if level.nil?
return state unless level['speckle_type'].include?('Objects.BuiltElements.Level')
level_name = speckle_object['level']['name'] || speckle_object['level']['id']
level_name = level['name'] || level['id']
is_exist = @entities_to_fill.grep(Sketchup::SectionPlane).any? { |sp| sp.name == level_name }
return state if is_exist
elevation = SpeckleObjects::Geometry.length_to_native(speckle_object['level']['elevation'],
speckle_object['level']['units'])
elevation = SpeckleObjects::Geometry.length_to_native(level['elevation'], level['units'])
section_plane = @entities_to_fill.add_section_plane([0, 0, elevation + LEVEL_SHIFT_VALUE], [0, 0, -1])
section_plane.name = level_name
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
section_plane, level['applicationId'], level['id'], level['speckle_type'], [], @stream_id
)
state
end
# @param state [States::State] state of the application
def convert_to_speckle_entities(state, speckle_object, entities)
speckle_id = speckle_object['id']
application_id = speckle_object['applicationId']
speckle_type = speckle_object['speckle_type']
children = speckle_object['__closure'].nil? ? [] : speckle_object['__closure']
speckle_state = state.speckle_state
entities.each do |entity|
next if entity.is_a?(Sketchup::Material)
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
!state.user_state.user_preferences[:register_speckle_entity]
ent = SpeckleEntities::SpeckleEntity.new(entity, speckle_id, application_id, speckle_type, children,
[stream_id])
ent.write_initial_base_data
speckle_state = speckle_state.with_speckle_entity(ent)
end
state.with_speckle_state(speckle_state)
def convert_to_speckle_entities(state, speckle_objects_with_entities)
return state if speckle_objects_with_entities.empty?
end
end
# rubocop:enable Metrics/ClassLength
+34 -142
View File
@@ -10,63 +10,31 @@ require_relative '../speckle_objects/other/block_instance'
require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/other/rendering_options'
require_relative '../speckle_objects/built_elements/view3d'
require_relative '../speckle_objects/built_elements/revit/direct_shape'
require_relative '../speckle_objects/relations/layers'
require_relative '../speckle_objects/speckle/core/models/model_collection'
require_relative '../constants/path_constants'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/query/entity'
module SpeckleConnector
module Converters
# Converts sketchup entities to speckle objects.
class ToSpeckle < Converter
MODEL_COLLECTION = SpeckleObjects::Speckle::Core::Models::ModelCollection
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
SPECKLE_ENTITIES_READER = SketchupModel::Reader::SpeckleEntitiesReader
VIEW3D = SpeckleObjects::BuiltElements::View3d
# Convert selected objects by putting them into related array that grouped by layer.
# @return [Hash{Symbol=>Array}] layers -which only have objects- to hold it's objects under the base object.
def convert_selection_to_base(preferences)
layers = add_all_layers
state = speckle_state
sketchup_model.selection.each do |entity|
new_speckle_state, converted_object_with_entity = convert(entity, preferences, state)
state = new_speckle_state
layer_name = entity_layer_path(entity)
layers[layer_name].push(converted_object_with_entity)
end
# send only+ layers that have any object
base_object_properties = layers.reject { |_layer_name, objects| objects.empty? }
add_views(base_object_properties) if sketchup_model.pages.any?
return state, SpeckleObjects::Base.with_detached_layers(base_object_properties)
end
convert = method(:convert)
new_speckle_state, model_collection = MODEL_COLLECTION.from_sketchup_model(sketchup_model, speckle_state,
@units, preferences, &convert)
# Add views from pages.
# @param base_object_properties [Hash] dynamically attached base object properties.
def add_views(base_object_properties)
views = []
sketchup_model.pages.each do |page|
cam = page.camera
origin = get_camera_origin(cam)
target = get_camera_target(cam)
direction = get_camera_direction(cam)
update_properties = get_scene_update_properties(page)
rendering_options = SpeckleObjects::Others::RenderingOptions.to_speckle(page.rendering_options)
view = SpeckleObjects::BuiltElements::View3d.new(
page.name, origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, @units),
cam.perspective?, cam.fov, @units, page.name, update_properties, rendering_options
)
views.append(view)
end
base_object_properties['@Named Views'] = views
end
# Get scene properties
# @param page [Sketchup::Page] page on sketchup.
def get_scene_update_properties(page)
{
use_axes: page.use_axes?,
use_camera: page.use_camera?,
use_hidden_geometry: page.use_hidden_geometry?,
use_hidden_layers: page.use_hidden_layers?,
use_hidden_objects: page.use_hidden_objects?,
use_rendering_options: page.use_rendering_options?,
use_section_planes: page.use_section_planes?,
use_shadow_info: page.use_shadow_info?,
use_style: page.use_style?
}
return new_speckle_state, model_collection
end
# Serialized and traversed information to send batches.
@@ -93,17 +61,28 @@ module SpeckleConnector
# @param entity [Sketchup::Entity] sketchup entity to convert Speckle.
# @param speckle_state [States::SpeckleState] the current speckle state of the {States::State}
# @param parent [Symbol, String] parent of the Sketchup Entity to be converted.
# rubocop:disable Metrics/MethodLength
def convert(entity, preferences, speckle_state, parent = :base)
convert = method(:convert)
unless SketchupModel::Reader::MapperReader.mapped_with_schema?(entity) &&
!entity.is_a?(Sketchup::ComponentDefinition)
return from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
end
return speckle_state, nil
end
# rubocop:disable Metrics/MethodLength
def from_native_to_speckle(entity, preferences, speckle_state, parent, &convert)
if entity.is_a?(Sketchup::Edge)
line = SpeckleObjects::Geometry::Line.from_edge(entity, @units, preferences[:model]).to_h
line = SpeckleObjects::Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity,
units: @units, model_preferences: preferences[:model]).to_h
return speckle_state, [line, [entity]]
end
if entity.is_a?(Sketchup::Face)
mesh = SpeckleObjects::Geometry::Mesh.from_face(entity, @units, preferences[:model])
mesh = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity, units: @units,
model_preferences: preferences[:model])
return speckle_state, [mesh, [entity]]
end
@@ -124,9 +103,13 @@ module SpeckleConnector
end
if entity.is_a?(Sketchup::ComponentDefinition)
# Local caching
return speckle_state, [definitions[entity.guid], [entity]] if definitions.key?(entity.guid)
new_speckle_state, block_definition = SpeckleObjects::Other::BlockDefinition.from_definition(
entity, @units, @definitions, preferences, speckle_state, parent, &convert
entity, @units, preferences, speckle_state, parent, &convert
)
definitions[entity.guid] = block_definition
speckle_state = new_speckle_state
return speckle_state, [block_definition, [entity]]
end
@@ -134,97 +117,6 @@ module SpeckleConnector
return speckle_state, nil
end
# rubocop:enable Metrics/MethodLength
# Create layers -> {Hash{Symbol=>Array}} from sketchup model with empty array as hash entry values.
# This method add first headless layers (not belong to any folder),
# then goes through each folder, their sub-folders and their layers.
# @return [Hash{Symbol=>Array}] layers from sketchup model with empty array as hash entry values.
def add_all_layers
# add headless layers
layer_objects = add_layers(sketchup_model.layers.layers)
# add layers from folders
add_layers_from_folders(sketchup_model.layers.folders, layer_objects)
layer_objects
end
# @param layers [Array<Sketchup::Layer>] layers in sketchup model
# @return [Hash{Symbol=>Array}] layers with empty array value.
def add_layers(layers, layer_objects = {}, parent_name = '')
layers.each do |layer|
layer_name = parent_name.empty? ? "@#{layer.display_name}" : "#{parent_name}::#{layer.display_name}"
layer_objects[layer_name] = []
end
layer_objects
end
# @param folders [Array<Sketchup::LayerFolder>] layer folders in sketchup model.
# @param layer_objects [Hash{Symbol=>Array}] layer objects to fill in.
# @param parent_name [String] parent folder name to structure layer path before send to Speckle.
# ex: "@#{parent_name}::#{layer_name}"
def add_layers_from_folders(folders, layer_objects, parent_name = '')
folders.each do |folder|
folder_name = parent_name.empty? ? "@#{folder.display_name}" : "#{parent_name}::#{folder.display_name}"
add_layers(folder.layers, layer_objects, folder_name)
add_layers_from_folders(folder.folders, layer_objects, folder_name) unless folder.folders.empty?
end
end
# Find layer path of given Sketchup entity.
# @param entity [Sketchup::Entity] entity to find root layer.
# @return [String] layer path of Sketchup entity.
def entity_layer_path(entity)
layer_name = entity.layer.display_name
if entity.layer.folder.nil?
"@#{layer_name}"
else
folders = folder_name(entity.layer.folder)
path = ''
folders.reverse.each do |folder|
path += "#{folder}::"
end
"@#{path}#{layer_name}"
end
end
# Nested method to retrieve sub-folders until nothing found.
# @return [Array<String>] folder names as list from bottom to top. Might need to be reversed if you want to see
# from top to bottom.
def folder_name(folder, folders = [])
if folder.folder.nil?
folders.push(folder.display_name)
else
folder_name(folder.folder, folders.push(folder.display_name))
end
end
private
def get_camera_direction(cam)
SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(cam.direction[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[2], @units),
@units
)
end
def get_camera_target(cam)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.target[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[2], @units),
@units
)
end
def get_camera_origin(camera)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.eye[0], @units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[1], @units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[2], @units),
@units
)
end
end
end
end
Binary file not shown.
Binary file not shown.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
# frozen_string_literal: true
module SpeckleConnector
# Helper module for logging.
module Log
def self.write_to_file(text, file_name = 'log', path = "#{ENV['HOME']}/Desktop")
file_path = path + "/#{file_name}.json"
File.delete(file_path) if File.exist?(file_path)
File.write(file_path, text)
end
end
end
@@ -0,0 +1,142 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapper
module Category
# Revit categories.
class RevitCategory < Hash
class << self
# rubocop:disable Metrics/MethodLength
def dictionary
{
AbutmentFoundations: 0,
AbutmentPiles: 1,
AbutmentWalls: 2,
BridgeAbutments: 3,
DuctTerminal: 4,
Alignments: 5,
StructConnectionAnchors: 6,
ApproachSlabs: 7,
BridgeArches: 8,
AudioVisualDevices: 9,
StairsRailingBaluster: 10,
BridgeBearings: 11,
StructConnectionBolts: 12,
BridgeCables: 13,
BridgeDecks: 14,
BridgeFraming: 15,
CableTrayFitting: 16,
CableTrayRun: 17,
CableTray: 18,
Casework: 19,
Ceilings: 20,
Columns: 21,
CommunicationDevices: 22,
ConduitFitting: 23,
Conduit: 24,
Coordination_Model: 25,
BridgeFramingCrossBracing: 26,
CurtainWallPanels: 27,
CurtaSystem: 28,
CurtainWallMullions: 29,
DataDevices: 30,
BridgeFramingDiaphragms: 31,
Doors: 32,
DuctAccessory: 33,
DuctFitting: 34,
PlaceHolderDucts: 35,
DuctSystem: 36,
DuctCurves: 37,
ElectricalEquipment: 38,
ElectricalFixtures: 39,
Entourage: 40,
ExpansionJoints: 41,
FireAlarmDevices: 42,
FireProtection: 43,
Floors: 44,
FoodServiceEquipment: 45,
Furniture: 46,
FurnitureSystems: 47,
GenericAnnotation: 48,
GenericModel: 49,
BridgeGirders: 50,
Hardscape: 51,
LightingDevices: 52,
LightingFixtures: 53,
Lines: 54,
Mass: 55,
MechanicalEquipment: 56,
MedicalEquipment: 57,
NurseCallDevices: 58,
Parking: 59,
Parts: 60,
PierCaps: 61,
PierColumns: 62,
BridgeFoundations: 63,
PierPiles: 64,
BridgeTowers: 65,
PierWalls: 66,
BridgePiers: 67,
PipeAccessory: 68,
PipeFitting: 69,
PlaceHolderPipes: 70,
PipeSegments: 71,
PipeCurves: 72,
PipingSystem: 73,
Planting: 74,
StructConnectionPlates: 75,
PlumbingFixtures: 76,
StructConnectionProfiles: 77,
StairsRailing: 78,
Ramps: 79,
Roads: 80,
Roofs: 81,
SecurityDevices: 82,
StructConnectionShearStuds: 83,
Signage: 84,
Site: 85,
SpecialityEquipment: 86,
Sprinklers: 87,
Stairs: 88,
StructuralFramingSystem: 89,
StructuralColumns: 90,
StructConnections: 91,
FabricAreas: 92,
StructuralFoundation: 93,
StructuralFraming: 94,
Rebar: 95,
Coupler: 96,
StructuralStiffener: 97,
StructuralTendons: 98,
StructuralTruss: 99,
TemporaryStructure: 100,
Topography: 101,
BridgeFramingTrusses: 102,
VerticalCirculation: 103,
VibrationDampers: 104,
VibrationIsolators: 105,
VibrationManagement: 106,
Walls: 107,
StructConnectionWelds: 108,
Windows: 109,
Railings: 110
}.freeze
end
# rubocop:enable Metrics/MethodLength
def reverse_dictionary
dictionary.collect { |k, v| [v, k] }.to_h
end
def to_a
dictionary.collect { |k, v| { key: k, value: v } }.to_a
end
def reverse_to_a
dictionary.collect { |k, v| { key: v, value: k } }.to_a
end
end
end
end
end
end
@@ -0,0 +1,79 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapper
module Category
# Revit categories for families.
class RevitFamilyCategory < Hash
class << self
# rubocop:disable Metrics/MethodLength
def dictionary
{
AudioVisualDevices: 9,
CableTrayFitting: 16,
Casework: 19,
Columns: 21,
CommunicationDevices: 22,
ConduitFitting: 23,
DataDevices: 30,
Doors: 32,
DuctAccessory: 33,
ElectricalEquipment: 38,
ElectricalFixtures: 39,
Entourage: 40,
FireAlarmDevices: 42,
FireProtection: 43,
FoodServiceEquipment: 45,
Furniture: 46,
FurnitureSystems: 47,
GenericAnnotation: 48,
GenericModel: 49,
Hardscape: 51,
LightingDevices: 52,
LightingFixtures: 53,
Lines: 54,
Mass: 55,
MechanicalEquipment: 56,
MedicalEquipment: 57,
NurseCallDevices: 58,
Parking: 59,
PipeAccessory: 68,
PipeFitting: 69,
Planting: 74,
PlumbingFixtures: 76,
Roads: 80,
SecurityDevices: 82,
Signage: 84,
Site: 85,
SpecialityEquipment: 86,
Sprinklers: 87,
StructuralFramingSystem: 89,
StructuralColumns: 90,
StructConnections: 91,
StructuralFoundation: 93,
StructuralFraming: 94,
StructuralStiffener: 97,
TemporaryStructure: 100,
VerticalCirculation: 103,
Windows: 109,
Railings: 110
}.freeze
end
# rubocop:enable Metrics/MethodLength
def reverse_dictionary
dictionary.collect { |k, v| [v, k] }.to_h
end
def to_a
dictionary.collect { |k, v| { key: k, value: v } }.to_a
end
def reverse_to_a
dictionary.collect { |k, v| { key: v, value: k } }.to_a
end
end
end
end
end
end
+127
View File
@@ -0,0 +1,127 @@
# frozen_string_literal: true
require_relative '../speckle_objects/built_elements/revit/revit_floor'
require_relative '../speckle_objects/built_elements/revit/revit_wall'
require_relative '../speckle_objects/built_elements/revit/direct_shape'
require_relative '../speckle_objects/built_elements/revit/revit_column'
require_relative '../speckle_objects/built_elements/revit/revit_beam'
require_relative '../speckle_objects/built_elements/revit/revit_pipe'
require_relative '../speckle_objects/built_elements/revit/revit_duct'
require_relative '../speckle_objects/built_elements/default_floor'
require_relative '../speckle_objects/built_elements/default_wall'
require_relative '../speckle_objects/built_elements/default_column'
require_relative '../speckle_objects/built_elements/default_beam'
require_relative '../speckle_objects/built_elements/default_duct'
require_relative '../speckle_objects/built_elements/default_pipe'
require_relative '../speckle_objects/other/mapped_block_wrapper'
require_relative '../sketchup_model/query/entity'
require_relative '../sketchup_model/reader/mapper_reader'
require_relative '../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
# Mapper is a tool to convert SketchUp entities to other applications' native objects.
module Mapper
QUERY = SketchupModel::Query
MAPPER_READER = SketchupModel::Reader::MapperReader
SPECKLE_SCHEMA_DICTIONARY_HANDLER = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
# Collects mapped entities on selection as flat list.
def self.mapped_entities_on_selection(sketchup_model)
flat_selection_with_path = QUERY::Entity.flat_entities_with_path(
sketchup_model.selection,
[Sketchup::Edge, Sketchup::Face, Sketchup::ComponentInstance, Sketchup::Group], [sketchup_model]
)
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = MAPPER_READER.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = MAPPER_READER.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
mapped_selection.append(entities) if is_entity_mapped
end
mapped_selection
end
def self.convert_mapped_entity(speckle_state, entity_with_path, preferences, units, &convert)
entity = entity_with_path[0]
method = get_method(entity)
return nil if method.nil?
path = entity_with_path[1..-1]
if face_mapping?(entity, method)
global_transformation = QUERY::Entity.global_transformation(entity, path)
face = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity,
units: units, model_preferences: preferences,
global_transform: global_transformation)
return [face, [entity]]
end
if edge_mapping?(entity, method)
global_transformation = QUERY::Entity.global_transformation(entity, path)
edge = SpeckleObjects::Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity,
units: units, model_preferences: preferences,
global_transformation: global_transformation)
return [edge, [entity]]
end
if method == 'Direct Shape'
direct_shape = DIRECT_SHAPE.from_entity(speckle_state, entity, path, units, preferences)
return [direct_shape, [entity]]
end
if ['New Revit Family', 'Family Instance'].include?(method)
_speckle_state, block_instance = SpeckleObjects::Other::BlockInstance.from_component_instance(
entity, units, preferences, speckle_state, path: path, &convert
)
return [block_instance, [entity]]
end
nil
end
NATIVE_MAPPING_TO_SPECKLE = {
'Default Column' => SpeckleObjects::BuiltElements::DefaultColumn.method(:to_speckle_schema),
'Default Beam' => SpeckleObjects::BuiltElements::DefaultBeam.method(:to_speckle_schema),
'Default Pipe' => SpeckleObjects::BuiltElements::DefaultPipe.method(:to_speckle_schema),
'Default Duct' => SpeckleObjects::BuiltElements::DefaultDuct.method(:to_speckle_schema),
'Column' => SpeckleObjects::BuiltElements::RevitColumn.method(:to_speckle_schema),
'Beam' => SpeckleObjects::BuiltElements::RevitBeam.method(:to_speckle_schema),
'Pipe' => SpeckleObjects::BuiltElements::RevitPipe.method(:to_speckle_schema),
'Duct' => SpeckleObjects::BuiltElements::RevitDuct.method(:to_speckle_schema)
}.freeze
def self.to_speckle(speckle_state, entity, units, global_transformation: nil)
speckle_schema = SPECKLE_SCHEMA_DICTIONARY_HANDLER.speckle_schema_to_speckle(entity)
return speckle_schema if speckle_schema.nil?
to_speckle_schema_method = NATIVE_MAPPING_TO_SPECKLE[speckle_schema['method']]
return speckle_schema if to_speckle_schema_method.nil?
to_speckle_schema_method.call(speckle_state, entity, units, global_transformation: global_transformation)
end
def self.get_method(entity)
method = SPECKLE_SCHEMA_DICTIONARY_HANDLER.get_attribute(entity, 'method')
return method if method
if entity.is_a?(Sketchup::ComponentInstance)
method = SPECKLE_SCHEMA_DICTIONARY_HANDLER.get_attribute(entity.definition, 'method')
end
method
end
def self.face_mapping?(entity, method)
(method.include?('Floor') || method.include?('Wall')) && entity.is_a?(Sketchup::Face)
end
def self.edge_mapping?(entity, method)
(method.include?('Column') || method.include?('Beam') || method.include?('Pipe') || method.include?('Duct')) &&
entity.is_a?(Sketchup::Edge)
end
end
end
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative '../immutable/immutable'
require_relative '../speckle_objects/built_elements/level'
require_relative '../speckle_objects/built_elements/revit/revit_element_type'
module SpeckleConnector
# Mapper is a tool to convert SketchUp entities to other applications' native objects.
module Mapper
# Mapper source object that collects information about stream id and commit id to identify source in the branch,
# also contains levels and family types to be able to map objects with them.
class MapperSource
# @return [String] stream id of the mapper source.
attr_reader :stream_id
# @return [String] commit id of the mapper source.
attr_reader :commit_id
# @return [Array<SpeckleObjects::BuiltElements::Level>] levels in the source branch.
attr_reader :levels
# @return [ImmutableHash{String=>Array<SpeckleObjects::BuiltElements::Revit::RevitElementType>}] revit element
# types.
attr_reader :types
def initialize(stream_id, commit_id, levels, types)
@stream_id = stream_id
@commit_id = commit_id
@levels = levels
@types = types
end
end
end
end
+3 -1
View File
@@ -5,6 +5,7 @@ require_relative 'entities_observer'
require_relative 'observer_handler'
require_relative 'model_observer'
require_relative 'event_handler'
require_relative 'selection_observer'
require_relative '../constants/observer_constants'
module SpeckleConnector
@@ -22,7 +23,8 @@ module SpeckleConnector
{
APP_OBSERVER => AppObserver.new(handler),
ENTITIES_OBSERVER => EntitiesObserver.new(handler),
MODEL_OBSERVER => ModelObserver.new(handler)
MODEL_OBSERVER => ModelObserver.new(handler),
SELECTION_OBSERVER => SelectionObserver.new(handler)
}.freeze
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
require_relative 'event_observer'
module SpeckleConnector
module Observers
# @see https://ruby.sketchup.com/Sketchup/SelectionObserver.html
class SelectionObserver < Sketchup::SelectionObserver
include EventObserver
# rubocop:disable Naming/MethodName
# @param _selection (Sketchup::Selection)
# @param _entity (Sketchup::Entity)
def onSelectionAdded(_selection, _entity)
push_selection_event(:onSelectionAdded)
end
# @param _selection (Sketchup::Selection)
def onSelectionBulkChange(_selection)
push_selection_event(:onSelectionBulkChange)
end
# @param _selection (Sketchup::Selection)
def onSelectionCleared(_selection)
push_selection_event(:onSelectionCleared)
end
# @param _selection (Sketchup::Selection)
def onSelectionRemoved(_selection, _entity)
push_selection_event(:onSelectionRemoved)
end
# Due to a SketchUp bug, this method is called by the wrong name.
alias onSelectedRemoved onSelectionRemoved
# rubocop:enable Naming/MethodName
private
# Selection changes need to be registered only once
def push_selection_event(event_name)
# Don't push anything if the selection event was already registered
selection_events = observer_handler.events[self.class]
return if selection_events&.any?
push_event(event_name)
end
end
end
end
@@ -12,9 +12,9 @@ module SpeckleConnector
include Immutable::ImmutableUtils
DICT_HANDLER = SketchupModel::Dictionary::SpeckleModelDictionaryHandler
# rubocop:disable Layout/LineLength
DEFAULT_CONFIG = "('configSketchup', '{\"dark_theme\":false, \"diffing\":false, \"register_speckle_entity\":false}');"
DEFAULT_CONFIG = "('configSketchup', '{\"dark_theme\":false, \"diffing\":false, \"register_speckle_entity\":false, \"fe2\":true}');"
# rubocop:enable Layout/LineLength
DEFAULT_PREFERENCES = '{"dark_theme":false, "diffing":false, "register_speckle_entity": false}'
DEFAULT_PREFERENCES = '{"dark_theme":false, "diffing":false, "register_speckle_entity": false, "fe2": true}'
# @param sketchup_model [Sketchup::Model] active model.
def self.read_preferences(sketchup_model)
@@ -34,10 +34,16 @@ module SpeckleConnector
def self.data_complete?(row_data)
return false if row_data.empty?
data = JSON.parse(row_data.first.first)
return false if data['dark_theme'].nil? || data['diffing'].nil? || data['register_speckle_entity'].nil?
begin
data = JSON.parse(row_data.first.first)
if data['dark_theme'].nil? || data['fe2'].nil? || data['diffing'].nil? || data['register_speckle_entity'].nil?
return false
end
true
true
rescue StandardError
false
end
end
# Validates current preferences. If there are incomplete data then this method resets it with default preferences.
@@ -65,11 +71,13 @@ module SpeckleConnector
data_hash = JSON.parse(row_data).to_h
# Get current theme value
dark_theme = data_hash['dark_theme']
fe2 = data_hash['fe2']
diffing = data_hash['diffing']
register_speckle_entity = data_hash['register_speckle_entity']
{
dark_theme: dark_theme,
fe2: fe2,
diffing: diffing,
register_speckle_entity: register_speckle_entity
}.freeze
@@ -0,0 +1,89 @@
# frozen_string_literal: true
require 'delegate'
require_relative 'dictionary_handler'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes for Speckle objects on SketchUp model.
class BaseDictionaryHandler < DictionaryHandler
IGNORED_DICTIONARY_NAMES = [
SPECKLE_BASE_OBJECT,
'IFC 4',
'IFC 2x3'
].freeze
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
def self.attribute_dictionaries_to_speckle(entity, model_preferences)
dictionaries = {}
return dictionaries unless model_preferences[INCLUDE_ENTITY_ATTRIBUTES]
klass = get_entity_setting_type(entity)
return dictionaries unless model_preferences[ENTITY_KEYS_FOR_INCLUDING_ATTRIBUTES[klass]]
return dictionaries if entity.attribute_dictionaries.nil?
entity.attribute_dictionaries.each do |att_dict|
dict_name = att_dict == '' ? 'empty_dictionary_name' : att_dict.name
dictionaries[dict_name] = att_dict.to_h unless IGNORED_DICTIONARY_NAMES.include?(att_dict.name)
end
dictionaries
end
# @param entity [Sketchup::Entity] entity to set attribute dictionaries
# rubocop:disable Metrics/CyclomaticComplexity
def self.attribute_dictionaries_to_native(entity, dictionaries)
return if dictionaries.nil?
classification_to_native(entity, dictionaries) if entity.is_a?(Sketchup::ComponentDefinition)
dictionaries.each do |dict_name, entries|
next unless entries.is_a?(Hash)
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
entries.each do |key, value|
set_attribute(entity, key, value, dict_name)
rescue StandardError => e
puts("Failed to write key: #{key} value: #{value} to dictionary #{dict_name}")
puts(e)
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# Classification is ComponentDefinition specific, so they can be added only definition by add_classification
# method.
# @param definition_entity [Sketchup::ComponentDefinition] definition to add callback
def self.classification_to_native(definition_entity, dictionaries)
applied_schema_types = dictionaries['AppliedSchemaTypes']
return if applied_schema_types.nil?
applied_schema_types.each do |key, value|
definition_entity.add_classification(key, value)
end
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
SPECKLE_BASE_OBJECT
end
# Gets entity type for including entity attributes setting.
# @param entity [Sketchup::Entity] entity to find setting entity.
# @return [Sketchup::Face, Sketchup::Edge, Sketchup::Group, Sketchup::ComponentInstance]
def self.get_entity_setting_type(entity)
klass = entity.class
if entity.is_a?(Sketchup::ComponentDefinition)
klass = if entity.group?
Sketchup::Group
else
Sketchup::ComponentInstance
end
end
klass
end
end
end
end
end
@@ -1,65 +1,13 @@
# frozen_string_literal: true
require 'delegate'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes from the groups and other entities that represents Speckle objects on SketchUp model.
class DictionaryHandler
DICTIONARY_NAME = 'Speckle_Base_Object'
IGNORED_DICTIONARY_NAMES = [
DICTIONARY_NAME,
'IFC 4',
'IFC 2x3'
].freeze
# @param entity [Sketchup::Entity] entity to get attribute dictionaries
def self.attribute_dictionaries_to_speckle(entity)
dictionaries = {}
return dictionaries if entity.attribute_dictionaries.nil?
entity.attribute_dictionaries.each do |att_dict|
dict_name = att_dict == '' ? 'empty_dictionary_name' : att_dict.name
dictionaries[dict_name] = att_dict.to_h unless IGNORED_DICTIONARY_NAMES.include?(att_dict.name)
end
dictionaries
end
# @param entity [Sketchup::Entity] entity to set attribute dictionaries
# rubocop:disable Metrics/CyclomaticComplexity
def self.attribute_dictionaries_to_native(entity, dictionaries)
return if dictionaries.nil?
classification_to_native(entity, dictionaries) if entity.is_a?(Sketchup::ComponentDefinition)
dictionaries.each do |dict_name, entries|
next unless entries.is_a?(Hash)
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
entries.each do |key, value|
set_attribute(entity, key, value, dict_name)
rescue StandardError => e
puts("Failed to write key: #{key} value: #{value} to dictionary #{dict_name}")
puts(e)
end
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# Classification is ComponentDefinition specific, so they can be added only definition by add_classification
# method.
# @param definition_entity [Sketchup::ComponentDefinition] definition to add callback
def self.classification_to_native(definition_entity, dictionaries)
applied_schema_types = dictionaries['AppliedSchemaTypes']
return if applied_schema_types.nil?
applied_schema_types.each do |key, value|
definition_entity.add_classification(key, value)
end
end
# @param entity [Sketchup::Entity] the sketchup entity of Speckle object
# @param key [Symbol] the name of the attribute
# @param dictionary_name [String, Symbol] the name of the attribute dictionary
@@ -105,9 +53,15 @@ module SpeckleConnector
dictionary.delete_key(key)
end
# @param entity [Sketchup::Entity] the sketchup entity of Speckle object
# @param dictionary_name [String, Symbol] the name of the attribute dictionary to remove
def self.remove_dictionary(entity, dictionary_name = self.dictionary_name)
entity.attribute_dictionaries.delete(dictionary_name)
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
DICTIONARY_NAME
raise NotImplementedError 'Implement this in subclass'
end
end
end
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require_relative 'dictionary_handler'
require_relative 'base_dictionary_handler'
require_relative '../../constants/dict_constants'
require_relative '../../constants/type_constants'
@@ -9,6 +9,8 @@ module SpeckleConnector
module Dictionary
# Dictionary handler of the speckle entity.
class SpeckleEntityDictionaryHandler < DictionaryHandler
DICTIONARY_NAME = SPECKLE_BASE_OBJECT
# Writes initial data while speckle entity is creating first time.
# @param sketchup_entity [Sketchup::Entity] Sketchup entity to write data into it's attribute dictionary.
# rubocop:disable Metrics/ParameterLists
@@ -26,6 +28,11 @@ module SpeckleConnector
set_hash(sketchup_entity, initial_dict_data)
end
# rubocop:enable Metrics/ParameterLists
# @return [String] the name of the dictionary to read from
def self.dictionary_name
DICTIONARY_NAME
end
end
end
end
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require_relative 'dictionary_handler'
require_relative 'base_dictionary_handler'
require_relative '../../constants/dict_constants'
require_relative '../../constants/type_constants'
@@ -8,7 +8,7 @@ module SpeckleConnector
module SketchupModel
module Dictionary
# Dictionary handler of the speckle model.
class SpeckleModelDictionaryHandler < DictionaryHandler
class SpeckleModelDictionaryHandler < BaseDictionaryHandler
DICTIONARY_NAME = 'Speckle'
# Writes initial data while speckle entity is creating first time.
# @param sketchup_model [Sketchup::Model] Sketchup model to write data into it's attribute dictionary.
@@ -0,0 +1,29 @@
# frozen_string_literal: true
require 'delegate'
require_relative 'dictionary_handler'
require_relative '../../constants/dict_constants'
module SpeckleConnector
module SketchupModel
module Dictionary
# Read and write attributes for Speckle objects' schema on SketchUp model.
class SpeckleSchemaDictionaryHandler < DictionaryHandler
def self.speckle_schema_to_speckle(entity)
schema = {}
return schema if entity.attribute_dictionaries.nil?
schema_dict = entity.attribute_dictionaries.find { |dict| dict.name == dictionary_name }
return schema if schema_dict.nil?
schema_dict.to_h
end
# @return [String] the name of the dictionary to read from
def self.dictionary_name
SPECKLE_MAPPING_TOOL_SCHEMA
end
end
end
end
end
@@ -0,0 +1,113 @@
# frozen_string_literal: true
module SpeckleConnector
module SketchupModel
# Query operations in sketchup model.
module Query
# Queries for entity.
class Entity
class << self
# Creates flat list for entities that defined in classes property. It searches from top to bottom to collect
# entities.
# @param entities_to_flat [Sketchup::Entities] entities to flat their children, grandchildren and so on..
# @param classes [Array<Class>] objects types to collect as flat list.
def flat_entities(entities_to_flat,
classes = [Sketchup::Edge, Sketchup::Face, Sketchup::ComponentInstance,
Sketchup::Group, Sketchup::ComponentDefinition])
entities = []
entities_to_flat.each do |entity|
entities.append(entity) if classes.include?(entity.class)
if entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
entities.append(entity.definition) if classes.include?(Sketchup::ComponentDefinition)
entities += flat_entities(entity.definition.entities, classes)
end
end
entities
end
# Create array for each entity with their path.
# @param entities_to_flat [Sketchup::Entities, Array<Sketchup::Entity>] entities to flat with their path.
# @param classes [Array<Class>] classes to flat. Put class into this array if you want to find their paths.
# @param path [Array<Object>] path for entity that we are in.
# @return [Array<Object>] entity with it's path as flat array. See example.
# @example
# path[0] is entity itself
# path[1..-1] rest as path from top to bottom
def flat_entities_with_path(entities_to_flat,
classes = [Sketchup::Edge, Sketchup::Face, Sketchup::ComponentInstance,
Sketchup::Group, Sketchup::ComponentDefinition],
path = [])
entities = []
entities_to_flat.each do |entity|
# Collect object itself
entities.append([entity] + path) if classes.include?(entity.class)
# entities[entity] = path if classes.include?(entity.class) && entities[entity].nil?
# Skip unless entity is a container entity like group or component.
next unless entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
# Add entity definition also with it's path.
entities[entity.definition] = path if classes.include?(Sketchup::ComponentDefinition)
# Collect sub-objects if object is a container at the same time.
sub_entities = flat_entities_with_path(entity.definition.entities.to_a,
classes, path + [entity])
entities += sub_entities
end
entities
end
# Calculates global transformation of entity by multiplying path entries from bottom to top by reversing path.
# @param entity [Sketchup::Entity] entity to find global transformation.
# @param path [Array<Object>] path that parents of entity that has transformation value to calculate global
# transformation of the entity.
# @return [Geom::Transformation] global transformation of the entity.
def global_transformation(entity, path)
# If entity is face, use Identity
global = entity.respond_to?(:transformation) ? entity.transformation : Geom::Transformation.new
path.reverse.each do |local|
global = local.transformation * global if local.respond_to?(:transformation)
end
global
end
# Global transformation search for entity that lies on only one instance.
# @param entity [Sketchup::Entity] entity to find global transformation.
def global_transformation_from_bottom(entity)
# If entity is face, use Identity
transformation = entity.respond_to?(:transformation) ? entity.transformation : Geom::Transformation.new
parent = parent_or_model(entity)
until parent.is_a?(Sketchup::Model) || parent.nil?
transformation = parent.transformation * transformation
parent = parent_or_model(parent)
end
transformation
end
# Parent search for entity from bottom to top. It is not ideal if entity lives in different instances.
def parent_or_model(entity)
parent = entity.parent
return parent if parent.is_a?(Sketchup::Model)
instances = parent.instances
if instances.length > 1
puts 'Parent has more than one instance'
instances.each(&:make_unique)
instances = instances.select { |instance| instance.definition.entities.include?(entity) }
end
instances.first
end
# Finds first material of parents from bottom to top.
def parent_material(path)
material = nil
path.reverse.each do |local|
material = local.material if local.respond_to?(:material)
return material unless material.nil?
end
material
end
end
end
end
end
end
@@ -0,0 +1,44 @@
# frozen_string_literal: true
module SpeckleConnector
module SketchupModel
# Query operations in sketchup model.
module Query
# Queries for layer and it's parents.
class Layer
class << self
# @param layer [Sketchup::Layer] layer to get folder path of the layer
# @return [Array<Sketchup::Folder>] path of the layer
def path(layer)
parent_folders = []
folder = layer.folder
until folder.nil?
parent_folders.append(folder)
folder = folder.folder
end
parent_folders.reverse
end
# @param entity [Sketchup::Entity] entity to find path.
def entity_path(entity, separation = '::')
path = path(entity.layer)
full_path = path.append(entity.layer)
full_path_string = ''
full_path.each_with_index do |layer, i|
full_path_string += layer.display_name
full_path_string += separation unless i == full_path.length - 1
end
full_path_string
end
# @param string_layer_path [String] string layer path to split.
def entity_layer_from_path(string_layer_path, separation = '::')
return string_layer_path if string_layer_path.nil?
string_layer_path.split(separation).last
end
end
end
end
end
end
@@ -0,0 +1,95 @@
# frozen_string_literal: true
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapper/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
# Reader model for sketchup model.
module Reader
# Reader module for mapper.
module MapperReader
# @param entities [Sketchup::Entities] entities to collect mapped entities.
# @return [Hash{String=>Sketchup::Entity}] mapped entities with persistent id.
def self.read_mapped_entities(entities)
mapped_entities = {}
Query::Entity.flat_entities(entities).each do |entity|
mapped_entities[entity.persistent_id] = entity if mapped_with_schema?(entity)
end
mapped_entities
end
# @param entity [Sketchup::Entity] sketchup entity to check whether mapped with speckle schema or not.
def self.mapped_with_schema?(entity)
is_entity_mapped = !Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity).nil?
return is_entity_mapped if is_entity_mapped
return is_entity_mapped unless entity.is_a?(Sketchup::ComponentInstance)
!Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition).nil?
end
def self.get_schema(entity)
Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(entity)
end
def self.entities_schema_details(entities)
entities.collect do |entity|
entity_selection_details = entity_selection_details(entity)
if entity.is_a?(Sketchup::ComponentInstance)
entity_selection_details = entity_selection_details.merge(
{ definition: entity_selection_details(entity.definition) }
)
end
entity_selection_details
end
end
def self.entity_selection_details(entity)
sanitized_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split
is_definition = entity.is_a?(Sketchup::ComponentDefinition)
entity_type = is_definition ? sanitized_type.last : sanitized_type.first
speckle_schema = get_schema(entity)
{
name: speckle_schema['name'],
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity_type,
schema: speckle_schema,
numberOfInstances: is_definition ? entity.instances.length : 1
}
end
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapper::Category::RevitCategory.reverse_dictionary
entities.collect do |entity|
speckle_schema = get_schema(entity)
speckle_schema_definition = entity.respond_to?(:definition) ? get_schema(entity.definition) : nil
entity_type = entity.class.name.split('::').last.gsub(/(?<=[a-z])(?=[A-Z])/, ' ').split.first
category = get_map_attribute(speckle_schema, speckle_schema_definition, 'category')
{
name: get_map_attribute(speckle_schema, speckle_schema_definition, 'name'),
category: category,
categoryName: category.nil? ? '' : reverse_category_dictionary[category],
method: get_map_attribute(speckle_schema, speckle_schema_definition, 'method'),
entityName: entity.respond_to?(:name) ? entity.name : '',
entityId: entity.persistent_id,
entityType: entity.is_a?(Sketchup::ComponentDefinition) ? 'Definition' : entity_type,
schema: speckle_schema,
definitionSchema: speckle_schema_definition
}
end
end
def self.get_map_attribute(schema, definition_schema, attribute)
return schema[attribute] if schema[attribute]
return definition_schema[attribute] if !definition_schema.nil? && definition_schema[attribute]
nil
end
end
end
end
end
@@ -1,6 +1,8 @@
# frozen_string_literal: true
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapper/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
@@ -11,6 +13,7 @@ module SpeckleConnector
# Reader module for speckle entities.
module SpeckleEntitiesReader
# @param entities [Sketchup::Entities] entities to collect speckle entities.
# @return [Hash{String=>Sketchup::Entity}] speckle entities with persistent id.
def self.read(entities)
speckle_entities = {}
entities.each do |entity|
@@ -46,6 +49,8 @@ module SpeckleConnector
entity.attribute_dictionaries.to_a.any? { |dict| dict.name == SPECKLE_BASE_OBJECT }
end
end
end
end
@@ -1,5 +1,7 @@
# frozen_string_literal: true
require_relative '../../constants/geo_constants'
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
@@ -24,6 +26,13 @@ module SpeckleConnector
end
adj_faces.uniq
end
# @param face [Sketchup::Face] face to get max z distance for all vertices.
def self.max_z(face)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
points_z_values.max - points_z_values.min
end
end
end
end
@@ -0,0 +1,84 @@
# frozen_string_literal: true
module SpeckleConnector
# Operations related to {SketchupModel}.
module SketchupModel
# Works directly with/on SketchUp Entities of different kinds (Groups, Faces, Edges, ...).
module Utils
# Static methods to do plane calculations with sketchup geom objects like Point3d and Vector3d.
class Plane
LENGTH_TOLERANCE = 1e-8
# Create plane from 3 points
# @param origin [Geom::Point3d] the point on the plane that wil become the origin of the local coordinate system
# @param point_1 [Geom::Point3d] the point that defines first direction
# @param point_2 [Geom::Point3d] the third point on the plane
# @return [Plane] the parametrization of the plane that goes through the given points
def self.from_points(origin, point_1, point_2)
direction_x = origin.vector_to(point_1).normalize
direction_x = direction_x.normalize
normal = direction_x.cross(origin.vector_to(point_2))
direction_y = direction_x.cross(normal.normalize)
new(origin: origin, direction_u: direction_x, direction_v: direction_y)
end
# @return [Geom::Vector3d] the direction of the u-axis on the plane
attr_reader :direction_u
# @return [Geom::Vector3d] the direction of the v-axis on the plane
attr_reader :direction_v
# @return [Geom::Point3d] the origin of the local coordinate system on the plane
attr_reader :origin
# @param origin [Geom::Point3d] the origin of the coordinate system on the plane
# @param direction_u [Geom::Vector3d] the direction of the x-axis
# @param direction_v [Geom::Vector3d] the direction of the y-axis
def initialize(origin:, direction_u:, direction_v:)
@origin = origin
@direction_u = direction_u
@direction_v = direction_v
end
# Get the point object in global coordinates for the point on the plane with local coordinates (u,v).
# @param coordinate_u [Float] the u-coordinate on the plane
# @param coordinate_v [Float] the v-coordinate on the plane
# @return [Geom::Point3d] the point in space that corresponds to the given (u, v) coordinates
def point_at(coordinate_u, coordinate_v)
scaled_direction_u = Geom::Vector3d.new(direction_u.x * coordinate_u,
direction_u.y * coordinate_u,
direction_u.z * coordinate_u)
scaled_direction_v = Geom::Vector3d.new(direction_v.x * coordinate_v,
direction_v.y * coordinate_v,
direction_v.z * coordinate_v)
origin + scaled_direction_u + scaled_direction_v
end
# Find local (u, v) coordinates of the projection of the given point to the plane
# @param point [Geom::Point3d] the point that will be projected to the plane
# @return [(Float, Float)] the local coordinates on the plane that correspond to the projected point
def plane_coordinates(point)
origin_to_point = origin.vector_to(point)
coordinate_u = origin_to_point.dot(direction_u)
coordinate_v = origin_to_point.dot(direction_v)
[coordinate_u, coordinate_v]
end
# Project a given point to the plane
# @param point [Geom::Point3d] the point that will be projected to the plane
# @return [Geom::Point3d] the projected point on the plane
def project_to_plane(point)
coordinate_u, coordinate_v = plane_coordinates(point)
point_at(coordinate_u, coordinate_v)
end
# Check if the given point lies on the plane
# @param point [Geom::Point3d] the point to check
# @return [Boolean] whether the point lies on the plane
def on_plane?(point)
point.distance(project_to_plane(point)).to_m < LENGTH_TOLERANCE
end
end
end
end
end
@@ -51,9 +51,9 @@ module SpeckleConnector
@sketchup_entity = sketchup_entity
@application_id = application_id
@id = speckle_id
@total_children_count = children.length
@total_children_count = children.nil? ? 0 : children.length
@speckle_type = speckle_type
@speckle_children_objects = children
@speckle_children_objects = children.nil? ? [] : children
end
# rubocop:enable Metrics/ParameterLists
@@ -119,6 +119,41 @@ module SpeckleConnector
def valid?
sketchup_entity.valid?
end
# @param state [States::State] state of the application
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_speckle_object(state, speckle_object, entities, stream_id)
return state, [] if entities.empty?
speckle_id = speckle_object['id']
application_id = speckle_object['applicationId']
speckle_type = speckle_object['speckle_type']
children = speckle_object['__closure'].nil? ? [] : speckle_object['__closure']
speckle_state = state.speckle_state
entities.each do |entity|
next if entity.is_a?(Sketchup::Material) || entity.is_a?(Sketchup::Page)
next if (entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)) &&
!state.user_state.user_preferences[:register_speckle_entity]
if entity.is_a?(Sketchup::ComponentDefinition)
definition = speckle_object['definition'] || speckle_object['@block_definition'] ||
speckle_object['block_definition']
if definition
speckle_id = definition['id']
speckle_type = definition['speckle_type']
end
end
ent = SpeckleEntity.new(entity, speckle_id, application_id, speckle_type, children, [stream_id])
ent.write_initial_base_data
speckle_state = speckle_state.with_speckle_entity(ent)
end
new_state = state.with_speckle_state(speckle_state)
return new_state, entities
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
end
end
@@ -0,0 +1,39 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../geometry/line'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Beam object.
class DefaultBeam < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_BEAM
def initialize(base_line:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:units] = units
end
# @param edge [Sketchup::Edge] edge to get speckle schema for beam.
def self.to_speckle_schema(_speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
DefaultBeam.new(
base_line: base_line,
units: units,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,39 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../geometry/line'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Column object.
class DefaultColumn < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_COLUMN
def initialize(base_line:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:units] = units
end
# @param edge [Sketchup::Edge] edge to get speckle schema for column.
def self.to_speckle_schema(_speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
DefaultColumn.new(
base_line: base_line,
units: units,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,39 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../geometry/line'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Duct object.
class DefaultDuct < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_DUCT
def initialize(base_line:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:units] = units
end
# @param edge [Sketchup::Edge] edge to get speckle schema for duct.
def self.to_speckle_schema(_speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
DefaultDuct.new(
base_line: base_line,
units: units,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,53 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../built_elements/revit/parameter'
require_relative '../other/render_material'
require_relative '../geometry/line'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Floor object.
class DefaultFloor < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_FLOOR
def initialize(outline:, voids:, units:, material:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:outline] = outline
self[:voids] = voids
self[:units] = units
self[:renderMaterial] = material
end
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(_speckle_state, face, units, global_transformation: nil)
outline = Geometry::Polyline.from_loop(face.loops.first, units, global_transformation: global_transformation)
voids = []
if face.loops.length > 1
voids = face.loops[1..face.loops.length - 1].collect do |loop|
Geometry::Polyline.from_loop(loop, units, global_transformation: global_transformation)
end
end
material = face.material || face.back_material
DefaultFloor.new(
outline: outline,
voids: voids,
units: units,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,39 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../geometry/line'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Pipe object.
class DefaultPipe < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_PIPE
def initialize(base_line:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:units] = units
end
# @param edge [Sketchup::Edge] edge to get speckle schema for pipe.
def self.to_speckle_schema(_speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
DefaultPipe.new(
base_line: base_line,
units: units,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,52 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../built_elements/revit/parameter'
require_relative '../other/render_material'
require_relative '../geometry/length'
require_relative '../geometry/line'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/utils/face_utils'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Default Wall object.
class DefaultWall < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_DEFAULT_WALL
def initialize(base_line:, height:, flipped:, units:, material:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:baseLine] = base_line
self[:height] = height
self[:flipped] = flipped
self[:units] = units
self[:renderMaterial] = material
end
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(_speckle_state, face, units, global_transformation: nil)
base_line = Geometry::Line.base_line_from_face(face, units, global_transformation: global_transformation)
material = face.material || face.back_material
DefaultWall.new(
base_line: base_line,
height: Geometry.length_to_speckle(SketchupModel::Utils::FaceUtils.max_z(face), units),
flipped: false,
units: units,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,84 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/render_material'
require_relative '../geometry/line'
require_relative '../geometry/length'
require_relative '../geometry/polyline'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/speckle_entity_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Level object.
class Level < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_LEVEL
def initialize(name:, elevation:, units:, element_id:, application_id: nil, id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:name] = name
self[:elevation] = elevation
self[:units] = units
self[:elementId] = element_id
self[:referenceOnly] = true
self[:createView] = false
end
# @param state [States::State] state of the application.
def self.to_native(state, speckle_level, stream_id)
sketchup_model = state.sketchup_state.sketchup_model
levels_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == 'Levels' }
levels_layer = sketchup_model.layers.add('Levels') if levels_layer.nil?
name = speckle_level['name']
elevation = speckle_level['elevation']
units = speckle_level['units']
element_id = speckle_level['elementId']
application_id = speckle_level['applicationId']
id = speckle_level['id']
skp_elevation = Geometry.length_to_native(elevation, units)
definition_name = "#{name}-#{application_id}"
definition = sketchup_model.definitions.find { |definition| definition.name == definition_name }
definition.entities.clear! unless definition.nil?
definition = sketchup_model.definitions.add(definition_name) if definition.nil?
instance = sketchup_model.entities.add_instance(definition, Geom::Transformation.new)
instance.locked = true
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
instance, application_id, id, SPECKLE_TYPE, [], stream_id
)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_attribute(instance, :name, name)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.write_initial_base_data(
definition, application_id, id, SPECKLE_TYPE, [], stream_id
)
SketchupModel::Dictionary::SpeckleEntityDictionaryHandler.set_attribute(definition, :name, name)
c1_e = Geom::Point3d.new(0, 10.m, skp_elevation)
c2_e = Geom::Point3d.new(0, 0, skp_elevation)
c3_e = Geom::Point3d.new(10.m, 0, skp_elevation)
cline_1 = definition.entities.add_cline(c1_e, c2_e)
cline_2 = definition.entities.add_cline(c2_e, c3_e)
text = definition.entities.add_text(" #{name}", c1_e)
[cline_1, cline_2, text, definition, instance].each { |o| o.layer = levels_layer }
Level.new(
name: name,
elevation: elevation,
units: units,
element_id: element_id,
application_id: application_id,
id: speckle_level['id']
)
end
end
end
end
end
@@ -0,0 +1,23 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Network object represents scenes on Sketchup.
class Network < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_NETWORK
def self.to_native(state, network, layer, entities, &convert_to_native)
network['elements'].each do |element|
state, _converted_entities = convert_to_native.call(state, element['elements'], layer, entities)
end
return state, []
end
end
end
end
end
@@ -0,0 +1,152 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../other/render_material'
require_relative '../../other/block_instance'
require_relative '../../other/block_definition'
require_relative '../../other/transform'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/query/entity'
require_relative '../../../sketchup_model/reader/mapper_reader'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Direct shape definition for Revit mappings.
class DirectShape < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_DIRECTSHAPE
READER = SketchupModel::Reader
QUERY = SketchupModel::Query
DICTIONARY = SketchupModel::Dictionary
def initialize(name:, category:, units:, base_geometries:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:category] = category
self[:units] = units
self[:baseGeometries] = base_geometries
end
def self.get_direct_shape_name(direct_shape)
if direct_shape['name'] == ''
direct_shape['applicationId'].to_s
else
"#{direct_shape['name']}::#{direct_shape['applicationId']}"
end
end
# @param state [States::State] state of the application.
def self.to_native(state, direct_shape, layer, entities, &convert_to_native)
direct_shape['geometry'] = direct_shape['baseGeometries']
direct_shape['name'] = get_direct_shape_name(direct_shape)
state, _definitions = Other::BlockDefinition.to_native(
state, direct_shape, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(direct_shape)]
instance = entities.add_instance(definition, Geom::Transformation.new)
instance.name = direct_shape['name'] unless direct_shape['name'].nil?
DICTIONARY::SpeckleSchemaDictionaryHandler.set_hash(
instance,
{
name: direct_shape['name'], category: direct_shape['category'], method: 'Direct Shape'
}
)
new_speckle_state = state.speckle_state.with_mapped_entity(instance)
state = state.with_speckle_state(new_speckle_state)
instance.layer = layer unless layer.nil?
return state, [instance, definition]
end
# Collects direct shapes on selection as flat list.
def self.direct_shapes_on_selection(sketchup_model)
flat_selection_with_path = QUERY::Entity.flat_entities_with_path(
sketchup_model.selection,
[Sketchup::Face, Sketchup::ComponentInstance, Sketchup::Group], [sketchup_model]
)
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = READER::MapperReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = READER::MapperReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
mapped_selection.append(entities) if is_entity_mapped
end
mapped_selection
end
def self.from_entity(speckle_state, entity, path, units, model_preferences)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity)
if schema.nil? && entity.respond_to?(:definition)
schema = DICTIONARY::SpeckleSchemaDictionaryHandler.attribute_dictionary(entity.definition)
end
entities_with_path = []
entities_with_path.append([entity] + path) if entity.is_a?(Sketchup::Face) || entity.is_a?(Sketchup::Edge)
# Collect here flat list
if entity.is_a?(Sketchup::ComponentInstance) || entity.is_a?(Sketchup::Group)
entities_with_path += QUERY::Entity
.flat_entities_with_path(
entity.definition.entities, [Sketchup::Face], path.append(entity)
)
end
base_geometries = if entity.is_a?(Sketchup::Edge)
[Geometry::Line.from_edge(speckle_state: speckle_state, edge: entity, units: units,
model_preferences: model_preferences,
global_transformation: nil)]
else
group_faces_under_mesh_by_material(speckle_state, entities_with_path, units,
model_preferences)
end
DirectShape.new(
name: schema[:name], category: schema[:category], units: units,
base_geometries: base_geometries, application_id: entity.persistent_id
)
end
# rubocop:disable Metrics/MethodLength
def self.group_faces_under_mesh_by_material(speckle_state, faces_with_path, units, model_preferences)
mesh_groups = {}
faces_with_path.each do |face_with_path|
face = face_with_path[0]
entity_path = face_with_path[1..-1]
parent_material = QUERY::Entity.parent_material(entity_path)
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, model_preferences, parent_material)
if mesh_groups.key?(mesh_group_id)
mesh_group = mesh_groups[mesh_group_id]
mesh_group[0].face_to_mesh(face, QUERY::Entity.global_transformation(face, entity_path))
mesh_group[1].append(face)
else
mesh = Geometry::Mesh.from_face(
speckle_state: speckle_state,
face: face, units: units, model_preferences: model_preferences,
global_transform: QUERY::Entity.global_transformation(face, entity_path),
parent_material: parent_material
)
mesh_groups[mesh_group_id] = [mesh, [face]]
end
end
# Update mesh overwrites points and polygons into base object.
mesh_groups.each { |_, mesh| mesh.first.update_mesh }
mesh_groups.values
end
# rubocop:enable Metrics/MethodLength
end
end
end
end
end
@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Family instance for Revit mappings.
class FamilyInstance < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_FAMILY_INSTANCE
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, level:, units:, base_point:, rotation:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:units] = units
self[:basePoint] = base_point
self[:rotation] = rotation
end
# rubocop:enable Metrics/ParameterLists
end
end
end
end
end
@@ -0,0 +1,26 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Revit parameter.
class Parameter < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_PARAMETER
def initialize(name:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:name] = name
end
end
end
end
end
end
@@ -0,0 +1,63 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../geometry/line'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit base object.
class RevitBeam < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_BEAM
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, base_line:, level:, units:, parameters:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to get speckle schema for beam.
def self.to_speckle_schema(speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = [edge.start.position, edge.end.position].map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitBeam.new(
family: schema['family'],
type: schema['family_type'],
base_line: base_line,
level: level,
units: units,
parameters: parameters,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,63 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../geometry/line'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit column object.
class RevitColumn < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_COLUMN
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, base_line:, level:, units:, parameters:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to get speckle schema for column.
def self.to_speckle_schema(speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = [edge.start.position, edge.end.position].map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitColumn.new(
family: schema['family'],
type: schema['family_type'],
base_line: base_line,
level: level,
units: units,
parameters: parameters,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,67 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../geometry/line'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit duct object.
class RevitDuct < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_DUCT
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, height:, width:, base_line:, level:, units:, parameters:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:height] = height
self[:width] = width
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to get speckle schema for duct.
def self.to_speckle_schema(speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = [edge.start.position, edge.end.position].map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitDuct.new(
family: schema['family'],
type: schema['family_type'],
height: schema['height'],
width: schema['width'],
base_line: base_line,
level: level,
units: units,
parameters: parameters,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,42 @@
# frozen_string_literal: true
require_relative '../../base'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
module Revit
# Revit element type.
class RevitElementType < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_REVITELEMENTTYPE
# rubocop:disable Metrics/ParameterLists
def initialize(category:, family:, type:, element_id:, application_id: nil, id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: id
)
self[:category] = category
self[:family] = family
self[:type] = type
self[:elementId] = element_id
end
# rubocop:enable Metrics/ParameterLists
def self.to_native(revit_element_type)
RevitElementType.new(
category: revit_element_type['category'],
family: revit_element_type['family'],
type: revit_element_type['type'],
element_id: revit_element_type['elementId'],
application_id: revit_element_type['applicationId'],
id: revit_element_type['id']
)
end
end
end
end
end
end
@@ -0,0 +1,77 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../other/render_material'
require_relative '../../geometry/line'
require_relative '../../geometry/polyline'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit floor object.
class RevitFloor < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_FLOOR
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, outline:, voids:, level:, units:, material:, parameters:nil, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:level] = level
self[:outline] = outline
self[:voids] = voids
self[:units] = units
self[:parameters] = parameters
self[:renderMaterial] = material
end
# rubocop:enable Metrics/ParameterLists
# @param face [Sketchup::Face] face to get speckle schema for floor.
def self.to_speckle_schema(speckle_state, face, units, global_transformation: nil)
outline = Geometry::Polyline.from_loop(face.loops.first, units, global_transformation: global_transformation)
voids = []
if face.loops.length > 1
voids = face.loops[1..face.loops.length - 1].collect do |loop|
Geometry::Polyline.from_loop(loop, units, global_transformation: global_transformation)
end
end
material = face.material || face.back_material
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
parameters = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = face.vertices.collect(&:position).map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitFloor.new(
family: schema['family'],
type: schema['family_type'],
outline: outline,
voids: voids,
level: level,
units: units,
parameters: parameters,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,65 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../geometry/line'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit pipe object.
class RevitPipe < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_PIPE
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, diameter:, base_line:, level:, units:, parameters:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:diameter] = diameter
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to get speckle schema for pipe.
def self.to_speckle_schema(speckle_state, edge, units, global_transformation: nil)
base_line = Geometry::Line.to_speckle_schema(edge: edge, units: units)
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = [edge.start.position, edge.end.position].map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitPipe.new(
family: schema['family'],
type: schema['family_type'],
diameter: schema['diameter'],
base_line: base_line,
level: level,
units: units,
parameters: parameters,
application_id: edge.persistent_id
)
end
end
end
end
end
@@ -0,0 +1,102 @@
# frozen_string_literal: true
require_relative '../../base'
require_relative '../../built_elements/revit/parameter'
require_relative '../../other/render_material'
require_relative '../../geometry/line'
require_relative '../../geometry/length'
require_relative '../../../constants/type_constants'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../../sketchup_model/utils/face_utils'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# Revit wall object.
class RevitWall < Base
SPECKLE_TYPE = OBJECTS_BUILTELEMENTS_REVIT_WALL
# rubocop:disable Metrics/ParameterLists
def initialize(family:, type:, base_line:, height:, flipped:, level:, units:, material:, parameters: nil, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:family] = family
self[:type] = type
self[:height] = height
self[:flipped] = flipped
self[:level] = level
self[:baseLine] = base_line
self[:units] = units
self[:parameters] = parameters
self[:renderMaterial] = material
end
# rubocop:enable Metrics/ParameterLists
def self.to_native(state, wall, layer, entities, &convert_to_native)
obj = Other::DisplayValue.collect_definition_geometries(wall)
obj['name'] = Other::DisplayValue.get_definition_name(obj)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = Other::DisplayValue.get_instance_name(obj['name']) unless obj['name'].nil?
instance.layer = layer unless layer.nil?
# Align instance axes that created from display value. (without any transform)
# BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
# @param face [Sketchup::Face] face to get speckle schema for wall.
def self.to_speckle_schema(speckle_state, face, units, global_transformation: nil)
base_line = Geometry::Line.base_line_from_face(face, units, global_transformation: global_transformation)
material = face.material || face.back_material
schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face).to_h
source_exist = !speckle_state.speckle_mapper_state.mapper_source.nil?
level = nil
parameters = nil
if source_exist
level = speckle_state.speckle_mapper_state.mapper_source.levels.find { |l| l[:name] == schema['level'] }
parameters = Base.new
offset_parameter = BuiltElements::Revit::Parameter.new(name: 'Height Offset From Level')
level_z = Geometry.length_to_native(level[:elevation], level[:units])
min_z = face.vertices.collect(&:position).map(&:z).min
offset_parameter['value'] = Geometry.length_to_speckle(min_z - level_z, units)
offset_parameter['units'] = units
parameters['Height Offset From Level'] = offset_parameter
end
RevitWall.new(
family: schema['family'],
type: schema['family_type'],
base_line: base_line,
height: Geometry.length_to_speckle(SketchupModel::Utils::FaceUtils.max_z(face), units),
flipped: false,
level: level,
units: units,
parameters: parameters,
material: material.nil? ? nil : Other::RenderMaterial.from_material(face.material || face.back_material),
application_id: face.persistent_id
)
end
def self.get_wall_height(face, units)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
Geometry.length_to_speckle(points_z_values.max - points_z_values.min, units)
end
end
end
end
end
@@ -2,6 +2,7 @@
require_relative '../base'
require_relative '../../constants/type_constants'
require_relative '../../speckle_objects/geometry/length'
require_relative '../../speckle_objects/geometry/point'
require_relative '../../speckle_objects/geometry/vector'
@@ -18,7 +19,7 @@ module SpeckleConnector
# @param direction [SpeckleObjects::Geometry::Vector] direction of the view from eye to target.
# @param up_direction [SpeckleObjects::Geometry::Vector] up direction of the view.
# @param is_perspective [Boolean] whether view is perspective or not.
# @param lens [Boolean] fov value of the view camera.
# @param lens [Numeric] focal length value of the view camera.
# @param units [String] units of the camera.
# @param application_id [String] application_id of the view.
# @param update_properties [Hash{Symbol=>boolean}] properties of the view.
@@ -45,48 +46,123 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# Collects scenes as views from sketchup model.
# @param sketchup_model [Sketchup::Model] sketchup model to collect views from pages.
# @param units [String] units of the model.
def self.from_model(sketchup_model, units)
sketchup_model.pages.collect { |page| from_page(page, units) }
end
# @param page [Sketchup::Page] page to convert speckle view.
def self.from_page(page, units)
cam = page.camera
origin = get_camera_origin(cam, units)
target = get_camera_target(cam, units)
direction = get_camera_direction(cam, units)
update_properties = get_scene_update_properties(page)
rendering_options = SpeckleObjects::Other::RenderingOptions.to_speckle(page.rendering_options)
View3d.new(
page.name, origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, units),
cam.perspective?, cam.perspective? ? cam.focal_length : 35, units, page.name,
update_properties, rendering_options
)
end
# @param state [States::State] state of the speckle app.
# @param obj [Hash] commit object.
# @param sketchup_model [Sketchup::Model] active sketchup model.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(obj, sketchup_model)
views = collect_views(obj)
return if views.empty?
def self.to_native(state, view, _layer, _entities, &_convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
return state, [] unless view['speckle_type'] == 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
views.each do |view|
next unless view['speckle_type'] == 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
name = view['name'] || view['id']
return state, [] if sketchup_model.pages.any? { |page| page.name == name }
name = view['name'] || view['id']
next if sketchup_model.pages.any? { |page| page.name == name }
camera = create_camera(view)
sketchup_model.active_view.camera = camera
sketchup_model.pages.add(name)
page = sketchup_model.pages[name]
set_page_update_properties(page, view['update_properties']) if view['update_properties']
set_rendering_options(page.rendering_options, view['rendering_options']) if view['rendering_options']
return state, [page]
end
origin = view['origin']
target = view['target']
lens = view['lens'] || 50
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
# Set camera position before creating scene on it.
my_camera = Sketchup::Camera.new(origin, target, [0, 0, 1], !view['isOrthogonal'], lens)
sketchup_model.active_view.camera = my_camera
sketchup_model.pages.add(name)
page = sketchup_model.pages[name]
set_page_update_properties(page, view['update_properties']) if view['update_properties']
set_rendering_options(page.rendering_options, view['rendering_options']) if view['rendering_options']
def self.create_camera(view)
origin = view['origin']
target = view['target']
focal_length = view['lens'] || 35
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
view_direction = (origin - target).normalize
up = view_direction.parallel?([0, 0, 1]) ? [0, 1, 0] : [0, 0, 1]
# Set camera position before creating scene on it.
is_perspective = !view['isOrthogonal']
camera = Sketchup::Camera.new(origin, target, up, is_perspective)
camera.focal_length = focal_length if is_perspective
camera.height = (origin - target).length * 2 unless is_perspective
camera
end
# @param page [Sketchup::Page] scene to update -update properties-
def self.set_page_update_properties(page, update_properties)
update_properties.each do |prop, value|
page.instance_variable_set(:"@#{prop}", value)
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
def self.collect_views(obj)
views = []
views += obj.filter_map do |_key, value|
if value.is_a?(Array) &&
value.any? { |v| v['speckle_type'] == OBJECTS_BUILTELEMENTS_VIEW3D }
value
end
# @param rendering_options [Sketchup::RenderingOptions] rendering options of scene (page)
def self.set_rendering_options(rendering_options, speckle_rendering_options)
speckle_rendering_options.each do |prop, value|
next if rendering_options[prop].nil?
rendering_options[prop] = if value.is_a?(Hash)
SpeckleObjects::Other::Color.to_native(value)
else
value
end
end
views.flatten.select { |view| view['speckle_type'] == OBJECTS_BUILTELEMENTS_VIEW3D }
end
# Get scene properties
# @param page [Sketchup::Page] page on sketchup.
def self.get_scene_update_properties(page)
{
use_axes: page.use_axes?,
use_camera: page.use_camera?,
use_hidden_geometry: page.use_hidden_geometry?,
use_hidden_layers: page.use_hidden_layers?,
use_hidden_objects: page.use_hidden_objects?,
use_rendering_options: page.use_rendering_options?,
use_section_planes: page.use_section_planes?,
use_shadow_info: page.use_shadow_info?,
use_style: page.use_style?
}
end
def self.get_camera_direction(camera, units)
SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(camera.direction[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.direction[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.direction[2], units),
units
)
end
def self.get_camera_target(camera, units)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.target[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.target[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.target[2], units),
units
)
end
def self.get_camera_origin(camera, units)
SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(camera.eye[0], units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[1], units),
SpeckleObjects::Geometry.length_to_speckle(camera.eye[2], units),
units
)
end
end
end
@@ -0,0 +1,31 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Arc object definition for Speckle.
class Arc < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_ARC
# @param [States::State] state of the current application.
# @param arc [Object] object represents Speckle Arc.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, arc, layer, entities, &_convert_to_native)
plane = arc['plane']
units = arc['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
x_axis = Vector.to_native(plane['xdir']['x'], plane['xdir']['y'], plane['xdir']['z'], units)
radius = Geometry.length_to_native(arc['radius'], units)
edges = entities.add_arc(origin, x_axis, normal, radius, arc['startAngle'], arc['endAngle'])
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -0,0 +1,33 @@
# frozen_string_literal: true
require_relative 'point'
require_relative 'vector'
require_relative 'length'
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Circle object definition for Speckle.
class Circle < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_CIRCLE
# @param [States::State] state of the current application.
# @param circle [Object] object represents Speckle Circle.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
def self.to_native(state, circle, layer, entities, &_convert_to_native)
plane = circle['plane']
units = circle['units']
origin = Point.to_native(plane['origin']['x'], plane['origin']['y'], plane['origin']['z'], units)
normal = Vector.to_native(plane['normal']['x'], plane['normal']['y'], plane['normal']['z'], units)
radius = Geometry.length_to_native(circle['radius'], units)
edges = entities.add_circle(origin, normal, radius)
edges.each { |edge| edge.layer = layer }
return state, edges
end
end
end
end
end
@@ -11,6 +11,10 @@ module SpeckleConnector
end
def self.length_to_native(length, units)
if units == 'none'
units = SpeckleConnector::Converters::
SKETCHUP_UNITS[Sketchup.active_model.options['UnitsOptions']['LengthUnit']]
end
length.__send__(SpeckleConnector::Converters::SKETCHUP_UNIT_STRINGS[units])
end
end
@@ -5,7 +5,8 @@ require_relative 'point'
require_relative 'bounding_box'
require_relative '../base'
require_relative '../primitive/interval'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -17,11 +18,11 @@ module SpeckleConnector
# @param start_pt [Geometry::Point] start point speckle object of the speckle line.
# @param end_pt [Geometry::Point] end point speckle object of the speckle line.
# @param domain [Primitive::Interval] interval speckle object of the speckle line -represents domain.
# @param bbox [Geometry::BoundingBox] bounding box speckle object of the speckle line.
# @param units [String] units of the speckle line.
# @param application_id [String, nil] entity id of the {Sketchup::Edge} that represents to the speckle line.
# rubocop:disable Metrics/ParameterLists
def initialize(start_pt:, end_pt:, domain:, bbox:, units:, sketchup_attributes: {}, application_id: nil)
def initialize(start_pt:, end_pt:, domain:, units:, layer:,
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: 'Objects.Geometry.Line',
total_children_count: 0,
@@ -31,39 +32,92 @@ module SpeckleConnector
self[:start] = start_pt
self[:end] = end_pt
self[:domain] = domain
self[:bbox] = bbox
self[:units] = units
self[:layer] = layer unless layer.nil?
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
end
# rubocop:enable Metrics/ParameterLists
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(edge, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes] && model_preferences[:include_edge_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(edge)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
def self.to_speckle_schema(edge:, units:)
start_pt = Geometry::Point.from_vertex(edge.start.position, units)
end_pt = Geometry::Point.from_vertex(edge.end.position, units)
domain = Primitive::Interval.from_numeric(0, Float(edge.length), units)
bbox = Geometry::BoundingBox.from_bounds(edge.bounds, units)
Line.new(
start_pt: start_pt,
end_pt: end_pt,
domain: domain,
bbox: bbox,
units: units,
sketchup_attributes: att,
layer: SketchupModel::Query::Layer.entity_path(edge),
sketchup_attributes: {},
speckle_schema: {},
application_id: edge.persistent_id.to_s
)
end
# @param _state [States::State] state of the application.
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(speckle_state:, edge:, units:, model_preferences:, global_transformation: nil)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(edge, model_preferences)
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = Mapper.to_speckle(speckle_state, edge, units, global_transformation: global_transformation)
start_pt = Geometry::Point.from_vertex(edge.start.position, units)
end_pt = Geometry::Point.from_vertex(edge.end.position, units)
domain = Primitive::Interval.from_numeric(0, Float(edge.length), units)
Line.new(
start_pt: start_pt,
end_pt: end_pt,
domain: domain,
units: units,
layer: SketchupModel::Query::Layer.entity_path(edge),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: edge.persistent_id.to_s
)
end
# @param edge [Sketchup::Face] face to get base line from face.
def self.base_line_from_face(face, units, global_transformation: nil)
points = face.vertices.collect(&:position)
points_z_values = points.collect(&:z)
height = Geometry.length_to_speckle(points_z_values.max - points_z_values.min, units)
min_z = points_z_values.min
projected_points = points.map { |p| Geom::Point3d.new(p.x, p.y, min_z) }
distance_with_points = Struct.new(:distance, :point_1, :point_2)
lines_with_distances = []
projected_points.each do |p|
projected_points.each do |p_other|
next if p_other == p
lines_with_distances.append(distance_with_points.new(p.distance(p_other), p, p_other))
end
end
lines_with_distances.sort_by!(&:distance).reverse!
p_1 = lines_with_distances.first.point_1
p_2 = lines_with_distances.first.point_2
unless global_transformation.nil?
p_1 = p_1.transform!(global_transformation)
p_2 = p_2.transform!(global_transformation)
end
Line.new(
start_pt: Geometry::Point.from_vertex(p_1, units),
end_pt: Geometry::Point.from_vertex(p_2, units),
domain: Primitive::Interval.from_numeric(0, Geometry.length_to_speckle(p_1.distance(p_2), units), units),
units: units,
layer: SketchupModel::Query::Layer.entity_path(face),
sketchup_attributes: {},
speckle_schema: {},
application_id: face.persistent_id.to_s
)
end
# @param state [States::State] state of the application.
# @param line [Object] object represents Speckle line.
# @param layer [Sketchup::Layer] layer to add {Sketchup::Edge} into it.
# @param entities [Sketchup::Entities] entities collection to add {Sketchup::Edge} into it.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(state, line, layer, entities, &_convert_to_native)
if line.key?('value')
values = line['value']
@@ -75,25 +129,28 @@ module SpeckleConnector
end_pt = Point.to_native(line['end']['x'], line['end']['y'], line['end']['z'], line['units'])
edges = entities.add_edges(start_pt, end_pt)
end
line_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(line['layer'])
line_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == line_layer_name }
edges.each do |edge|
edge.layer = layer
edge.layer = line_layer.nil? ? layer : line_layer
unless line['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
end
end
return state, edges
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
def self.test_line(start_point, end_point, units)
domain = Primitive::Interval.from_numeric(0, 5, units)
bbox = Geometry::BoundingBox.test_bounds(units)
Line.new(
start_pt: start_point,
end_pt: end_point,
domain: domain,
bbox: bbox,
layer: 'test',
application_id: '',
units: units
)
@@ -3,8 +3,14 @@
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../other/render_material'
require_relative '../../mapper/mapper'
require_relative '../../sketchup_model/query/entity'
require_relative '../../convertors/clean_up'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/utils/plane_utils'
require_relative '../../sketchup_model/query/layer'
module SpeckleConnector
module SpeckleObjects
@@ -29,7 +35,8 @@ module SpeckleConnector
# @param faces [Array] faces of the speckle mesh.
# @param sketchup_attributes [Hash] additional information about speckle mesh.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, render_material:, vertices:, faces:, sketchup_attributes:, application_id: nil)
def initialize(units:, render_material:, vertices:, faces:,
sketchup_attributes:, layer:, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -40,14 +47,35 @@ module SpeckleConnector
@polygons = []
@units = units
self[:units] = units
self[:layer] = layer
self[:renderMaterial] = render_material
# self[:bbox] = bbox
self[:'@(31250)vertices'] = vertices
self[:'@(62500)faces'] = faces
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
end
# rubocop:enable Metrics/ParameterLists
# Checks 4 points are planar or not.
def self.check_4_points_planar(points)
plane = SketchupModel::Utils::Plane.from_points(points[0], points[1], points[2])
plane.on_plane?(points[3])
end
# Add quad mesh to sketchup native mesh by checking planarity.
# @param native_mesh [Geom::Mesh] sketchup mesh to convert them later faces.
# @param polygon_points [Array<Geom::Point3d>] sketchup points to add them with polygon to mesh.
def self.add_quad_mesh(native_mesh, polygon_points)
is_planar = check_4_points_planar(polygon_points)
if is_planar
native_mesh.add_polygon(polygon_points)
else
native_mesh.add_polygon([polygon_points[0], polygon_points[1], polygon_points[2]])
native_mesh.add_polygon([polygon_points[0], polygon_points[2], polygon_points[3]])
end
is_planar
end
# @param entities [Sketchup::Entities] entities to add
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
@@ -63,15 +91,23 @@ module SpeckleConnector
# Initialize native PolygonMesh object later to add polygon inside it.
native_mesh = Geom::PolygonMesh.new(mesh['vertices'].count / 3)
faces = mesh['faces']
has_any_non_planar_quad_mesh = false
while faces.count > 0
num_pts = faces.shift
# 0 -> 3, 1 -> 4 to preserve backwards compatibility
num_pts += 3 if num_pts < 3
indices = faces.shift(num_pts)
native_mesh.add_polygon(indices.map { |index| points[index] })
polygon_points = indices.map { |index| points[index] }
# Quad mesh
if num_pts == 4
is_planar = add_quad_mesh(native_mesh, polygon_points)
has_any_non_planar_quad_mesh = true unless is_planar
else
native_mesh.add_polygon(polygon_points)
end
end
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'],
layer, entities, &convert_to_native)
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'], layer,
entities, &convert_to_native)
# Find and assign material if exist
unless mesh['renderMaterial'].nil?
material_name = mesh['renderMaterial']['name'] || mesh['renderMaterial']['id']
@@ -82,17 +118,23 @@ module SpeckleConnector
# Add faces from mesh with material and smooth setting
entities.add_faces_from_mesh(native_mesh, smooth_flags, material, material)
added_faces = entities.grep(Sketchup::Face).last(native_mesh.polygons.length)
mesh_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(mesh['layer'])
mesh_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == mesh_layer_name }
# Merge only added faces in this scope
# if model_preferences[:merge_coplanar_faces]
# added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
# end
added_faces.each do |face|
face.layer = layer
face.layer = mesh_layer unless mesh_layer.nil?
# Smooth edges if they already soft
# FIXME: Below line should be reconsidered. It might be a good to know here mesh comes from NURBS or not.
face.edges.each { |edge| edge.smooth = true if edge.soft? } if has_any_non_planar_quad_mesh
unless mesh['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(face, mesh['sketchup_attributes']['dictionaries'])
end
end
# Merge only added faces in this scope
if model_preferences[:merge_coplanar_faces]
added_faces = Converters::CleanUp.merge_coplanar_faces(added_faces)
end
return state, added_faces
end
# rubocop:enable Metrics/MethodLength
@@ -101,37 +143,45 @@ module SpeckleConnector
# rubocop:enable Metrics/PerceivedComplexity:
# @param face [Sketchup::Face] face to convert mesh
# @param units [String] model units to send Speckle.
# @param model_preferences [Hash{Symbol=>Boolean}] model preferences to check include attributes or not.
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component.
# rubocop:disable Style/MultilineTernaryOperator
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_face(face, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes] && model_preferences[:include_face_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(face)
end
def self.from_face(speckle_state:, face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(face, model_preferences)
has_any_soften_edge = face.edges.any?(&:soft?)
att = dictionaries.any? ? { is_soften: has_any_soften_edge, dictionaries: dictionaries }
: { is_soften: has_any_soften_edge }
speckle_schema = Mapper.to_speckle(speckle_state, face, units, global_transformation: global_transform)
material = face.material || face.back_material || parent_material
speckle_mesh = Mesh.new(
units: units,
render_material: face.material.nil? && face.back_material.nil? ? nil : Other::RenderMaterial
.from_material(face.material || face.back_material),
# bbox: Geometry::BoundingBox.from_bounds(face.bounds, units),
vertices: [],
faces: [],
sketchup_attributes: att,
render_material: material.nil? ? nil : Other::RenderMaterial.from_material(material),
vertices: [], faces: [], sketchup_attributes: att,
layer: SketchupModel::Query::Layer.entity_path(face),
speckle_schema: speckle_schema,
application_id: face.persistent_id
)
speckle_mesh.face_to_mesh(face)
speckle_mesh.face_to_mesh(face, global_transform)
speckle_mesh.update_mesh
speckle_mesh
end
# rubocop:enable Style/MultilineTernaryOperator
# rubocop:enable Metrics/CyclomaticComplexity
def face_to_mesh(face)
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component. So it's mesh will be transformed into global coordinates to represent it correctly in
# Speckle viewer or other connectors.
def face_to_mesh(face, global_transform = nil)
mesh = face.loops.count > 1 ? face.mesh : nil
mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh)
mesh.nil? ? face_indices_to_array(face) : mesh_faces_to_array(mesh)
if global_transform.nil?
mesh.nil? ? face_vertices_to_array(face) : mesh_points_to_array(mesh)
mesh.nil? ? face_indices_to_array(face) : mesh_faces_to_array(mesh)
else
mesh_points_to_array(face.mesh, global_transform)
mesh_faces_to_array(face.mesh, global_transform)
end
end
# Collects indexed Sketchup vertices into flat array for Speckle use.
@@ -176,7 +226,8 @@ module SpeckleConnector
# Get a flat array of vertices from a sketchup polygon mesh
# @param mesh [Geom::PolygonMesh] mesh to get points.
def mesh_points_to_array(mesh)
def mesh_points_to_array(mesh, global_transform = nil)
mesh.transform!(global_transform) unless global_transform.nil?
mesh.points.each do |pt|
# FIXME: Enable previous line when viewer supports shared vertices
# vertices.push(pt) unless vertices.any? { |point| point == pt }
@@ -186,7 +237,8 @@ module SpeckleConnector
# Get an array of face indices from a sketchup polygon mesh
# @param mesh [Geom::PolygonMesh] mesh to convert into polygons.
def mesh_faces_to_array(mesh)
def mesh_faces_to_array(mesh, global_transform = nil)
mesh.transform!(global_transform) unless global_transform.nil?
mesh.polygons.each do |poly|
global_polygon_array = [poly.count]
poly.each do |index|
@@ -225,6 +277,39 @@ module SpeckleConnector
end
points
end
# Mesh group id helps to determine how to group faces into meshes.
# @param face [Sketchup::Face] face to get mesh group id.
def self.get_mesh_group_id(face, model_preferences, parent_material = nil)
if model_preferences[:include_entity_attributes] &&
model_preferences[:include_face_entity_attributes] &&
attribute_dictionary?(face)
return face.persistent_id.to_s
end
material = face.material || face.back_material || parent_material
layer_name = face.layer.display_name
return layer_name if material.nil?
return material.entityID.to_s + layer_name
end
def self.attribute_dictionary?(face)
any_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return any_attribute_dictionary unless any_attribute_dictionary
# If there are any attribute dictionary, then make sure that they are not ignored ones.
all_attribute_dictionary_ignored = face.attribute_dictionaries.all? do |dict|
ignored_dictionaries.include?(dict.name)
end
!all_attribute_dictionary_ignored
end
def self.ignored_dictionaries
[
'Speckle_Base_Object'
]
end
end
end
end
@@ -0,0 +1,21 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Polycurve object definition for Speckle.
# It basically groups the lines-curves under it's `segments` property.
class Polycurve < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_POLYCURVE
def self.to_native(state, polycurve, layer, entities, &convert_to_native)
polycurve['displayValue'] = polycurve['segments']
convert_to_native.call(state, polycurve, layer, entities)
end
end
end
end
end
@@ -0,0 +1,65 @@
# frozen_string_literal: true
require_relative 'length'
require_relative 'point'
require_relative 'bounding_box'
require_relative '../base'
require_relative '../primitive/interval'
module SpeckleConnector
module SpeckleObjects
module Geometry
# Polyline object definition for Speckle.
class Polyline < Base
SPECKLE_TYPE = OBJECTS_GEOMETRY_POLYLINE
# @param value [Array<Numeric>] polygon vertex coordinates as flat list.
# @param domain [Primitive::Interval] domain of the polyline.
# @param length [Numeric] length of the polyline.
# @param closed [Boolean] whether polyline is closed or not.
# @param units [String] units of the polyline.
# @param application_id [String] application id of the polyline which corresponds to persistent_id of the Loop.
# rubocop:disable Metrics/ParameterLists
def initialize(value:, domain:, length:, closed:, units:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:value] = value
self[:domain] = domain
self[:length] = length
self[:closed] = closed
self[:units] = units
end
# rubocop:enable Metrics/ParameterLists
# @param loop [Sketchup::Loop] loop to convert closed speckle polyline.
def self.from_loop(loop, units, global_transformation: nil)
points = loop.vertices.collect do |vertex|
position = vertex.position
position = vertex.position.transform!(global_transformation) unless global_transformation.nil?
position
end
values = points.collect do |p|
[Geometry.length_to_speckle(p.x, units),
Geometry.length_to_speckle(p.y, units),
Geometry.length_to_speckle(p.z, units)]
end.flatten
loop_length = loop.edges.sum(&:length)
length = Geometry.length_to_speckle(loop_length, units)
domain = Primitive::Interval.from_lengths(0, loop_length, units)
Polyline.new(
value: values,
domain: domain,
length: length,
units: units,
closed: true,
application_id: loop.persistent_id.to_s
)
end
end
end
end
end
@@ -0,0 +1,207 @@
# frozen_string_literal: true
module SpeckleConnector
module SpeckleObjects
module Geometry
module Units
MILLIMETERS = 'mm'
CENTIMETERS = 'cm'
METERS = 'm'
KILOMETERS = 'km'
INCHES = 'in'
FEET = 'ft'
YARDS = 'yd'
MILES = 'mi'
NONE = 'none'
# USInches = "us_in" the smelliest ones, can add later if people scream "USA #1"
USFEET = 'us_ft' # it happened, absolutely gross
SUPPORTED_UNITS = [MILLIMETERS, CENTIMETERS, METERS, KILOMETERS,
INCHES, FEET, USFEET, YARDS, MILES, NONE].freeze
CONVERSION_TABLE = {
MILLIMETERS => {
CENTIMETERS => 0.1,
METERS => 0.001,
KILOMETERS => 1e-6,
INCHES => 0.0393701,
FEET => 0.00328084,
USFEET => 0.0032808333,
YARDS => 0.00109361,
MILES => 6.21371e-7
},
CENTIMETERS => {
MILLIMETERS => 10,
METERS => 0.01,
KILOMETERS => 1e-5,
INCHES => 0.393701,
FEET => 0.0328084,
USFEET => 0.0328083333,
YARDS => 0.0109361,
MILES => 6.21371e-6
},
METERS => {
MILLIMETERS => 1000,
CENTIMETERS => 100,
KILOMETERS => 0.001,
INCHES => 39.3701,
FEET => 3.28084,
USFEET => 3.28083333,
YARDS => 1.09361,
MILES => 0.000621371
},
KILOMETERS => {
MILLIMETERS => 1e6,
CENTIMETERS => 100000,
METERS => 1000,
INCHES => 39370.1,
FEET => 3280.84,
USFEET => 3280.83333,
YARDS => 1093.61,
MILES => 0.621371
},
INCHES => {
MILLIMETERS => 25.4,
CENTIMETERS => 2.54,
METERS => 0.0254,
KILOMETERS => 2.54e-5,
FEET => 0.0833333,
USFEET => 0.0833331667,
YARDS => 0.027777694,
MILES => 1.57828e-5
},
FEET => {
MILLIMETERS => 304.8,
CENTIMETERS => 30.48,
METERS => 0.3048,
KILOMETERS => 0.0003048,
INCHES => 12,
USFEET => 0.999998,
YARDS => 0.333332328,
MILES => 0.000189394
},
USFEET => {
MILLIMETERS => 120000.0 / 3937.0,
CENTIMETERS => 12000.0 / 3937.0,
METERS => 1200.0 / 3937.0,
KILOMETERS => 1.2 / 3937.0,
INCHES => 12.000024,
FEET => 1.000002,
YARDS => 1.000002 / 3.0,
MILES => 1.000002 / 5280.0
},
YARDS => {
MILLIMETERS => 914.4,
CENTIMETERS => 91.44,
METERS => 0.9144,
KILOMETERS => 0.0009144,
INCHES => 36,
FEET => 3,
USFEET => 2.999994,
MILES => 1.0 / 1760.0
},
MILES => {
MILLIMETERS => 1.609e6,
CENTIMETERS => 160934,
METERS => 1609.34,
KILOMETERS => 1.60934,
INCHES => 63360,
FEET => 5280,
USFEET => 5279.98944002112,
YARDS => 1759.99469184
},
NONE => { NONE => 1 }
}.freeze
def self.unit_supported?(unit)
SUPPORTED_UNITS.include?(unit)
end
# USYards = "us_yd" the smelliest ones, can add later if people scream "USA #1"
# USMiles = "us_mi" the smelliest ones, can add later if people scream "USA #1"
def self.get_conversion_factor(from, to)
from = get_units_from_string(from)
to = get_units_from_string(to)
CONVERSION_TABLE[from][to] || 1
end
def self.get_units_from_string(unit)
return nil if unit.nil?
case unit.downcase
when 'mm', 'mil', 'millimeter', 'millimeters', 'millimetres'
MILLIMETERS
when 'cm', 'centimetre', 'centimeter', 'centimetres', 'centimeters'
CENTIMETERS
when 'm', 'meter', 'metre', 'meters', 'metres'
METERS
when 'inches', 'inch', 'in'
INCHES
when 'feet', 'foot', 'ft'
FEET
when 'ussurveyfeet'
USFEET
when 'yard', 'yards', 'yd'
YARDS
when 'miles', 'mile', 'mi'
MILES
when 'kilometers', 'kilometer', 'km'
KILOMETERS
when 'none'
NONE
else
raise "Cannot understand what unit #{unit} is."
end
end
def self.get_encoding_from_unit(unit)
case unit
when MILLIMETERS
1
when CENTIMETERS
2
when METERS
3
when KILOMETERS
4
when INCHES
5
when FEET
6
when YARDS
7
when MILES
8
else
0
end
end
def self.get_unit_from_encoding(unit)
case unit
when 1
MILLIMETERS
when 2
CENTIMETERS
when 3
METERS
when 4
KILOMETERS
when 5
INCHES
when 6
FEET
when 7
YARDS
when 8
MILES
else
NONE
end
end
end
end
end
end
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'length'
require_relative '../base'
module SpeckleConnector
@@ -25,6 +26,14 @@ module SpeckleConnector
self[:z] = z
self[:units] = units
end
def self.to_native(x, y, z, units)
Geom::Vector3d.new(
Geometry.length_to_native(x, units),
Geometry.length_to_native(y, units),
Geometry.length_to_native(z, units)
)
end
end
end
end
@@ -0,0 +1,68 @@
# frozen_string_literal: true
require_relative 'utils'
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
# Line element in GIS tools.
class LineElement < Base
SPECKLE_TYPE = OBJECTS_GIS_LINEELEMENT
# Handles polygon element differently from display value.
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = GIS.get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = GIS.get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = obj['name'] unless obj['name'].nil?
SketchupModel::Dictionary::DictionaryHandler.set_hash(instance, attributes, 'qgis')
SketchupModel::Dictionary::DictionaryHandler.set_hash(definition, attributes, 'qgis')
# Align instance axes that created from display value. (without any transform)
Other::BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
def self.collect_definition_geometries(obj)
geometries = []
# FIXME: This type check needed because of QGIS. It can send geometries both way, object or array..
# This is something need to be fixed by QGIS.
if obj['geometry'].is_a?(Array)
obj['geometry'].each do |geometry|
geometries << geometry
end
else
geometries += obj['geometry']
end
geometries.each do |geo|
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
geo['sketchup_attributes'] = { 'is_soften' => false }
end
end
obj['geometry'] = geometries
obj
end
end
end
end
end
@@ -0,0 +1,70 @@
# frozen_string_literal: true
require_relative 'utils'
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
# BoundingBox object definition for Speckle.
class PolygonElement < Base
SPECKLE_TYPE = OBJECTS_GIS_POLYGONELEMENT
# Handles polygon element differently from display value.
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = GIS.get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = GIS.get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
.definitions[Other::BlockDefinition.get_definition_name(obj)]
Other::BlockInstance.find_and_erase_existing_instance(definition, obj['id'], obj['applicationId'])
t_arr = obj['transform']
transform = t_arr.nil? ? Geom::Transformation.new : Other::Transform.to_native(t_arr, obj['units'])
instance = entities.add_instance(definition, transform)
instance.name = obj['name'] unless obj['name'].nil?
SketchupModel::Dictionary::DictionaryHandler.set_hash(instance, attributes, 'qgis')
SketchupModel::Dictionary::DictionaryHandler.set_hash(definition, attributes, 'qgis')
# Align instance axes that created from display value. (without any transform)
Other::BlockInstance.align_instance_axes(instance)
return state, [instance, definition]
end
def self.collect_definition_geometries(obj)
geometries = []
# FIXME: This type check needed because of QGIS. It can send geometries both way, object or array..
# This is something need to be fixed by QGIS.
if obj['geometry'].is_a?(Array)
obj['geometry'].each do |geometry|
display_value = geometry['displayValue']
geometries += display_value
end
else
geometries += obj['geometry']['displayValue']
end
geometries.each do |geo|
if geo['speckle_type'] && geo['speckle_type'] == OBJECTS_GEOMETRY_MESH
geo['sketchup_attributes'] = { 'is_soften' => false }
end
end
obj['geometry'] = geometries
obj
end
end
end
end
end
@@ -0,0 +1,29 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
require_relative '../other/block_instance'
require_relative '../../constants/type_constants'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
module SpeckleConnector
module SpeckleObjects
module GIS
def self.get_definition_name(obj, attributes)
return obj['name'] unless obj['name'].nil?
return attributes['name'] unless attributes['name'].nil?
return "def::#{obj['id']}"
end
def self.get_qgis_attributes(obj)
attributes = obj['attributes'].to_h
speckle_properties = %w[id speckle_type totalChildrenCount units applicationId]
speckle_properties.each { |key| attributes.delete(key) }
attributes
end
end
end
end
@@ -7,7 +7,8 @@ require_relative '../base'
require_relative '../geometry/point'
require_relative '../geometry/mesh'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -22,7 +23,7 @@ module SpeckleConnector
# @param application_id [String, NilClass] application id of the block definition.
# rubocop:disable Metrics/ParameterLists
def initialize(geometry:, name:, units:, always_face_camera:, sketchup_attributes: {},
application_id: nil)
speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -33,7 +34,8 @@ module SpeckleConnector
self[:name] = name
self[:always_face_camera] = always_face_camera
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
# FIXME: Since geometry sends with @ as detached, block basePlane renders on viewer.
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
# '@@' means that it is a detached property.
self['@@geometry'] = geometry
end
# rubocop:enable Metrics/ParameterLists
@@ -42,32 +44,19 @@ module SpeckleConnector
# instance
# @param units [String] units of the Sketchup model
# @param definitions [Hash{String=>BlockDefinition}] all converted {BlockDefinition}s on the converter.
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/ParameterLists
def self.from_definition(definition, units, definitions, preferences, speckle_state, parent, &convert)
guid = definition.guid
return definitions[guid] if definitions.key?(guid)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
if definition.group?
if preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(definition)
end
elsif preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
end
end
def self.from_definition(definition, units, preferences, speckle_state, parent, &convert)
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(definition, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(definition)
# TODO: Solve logic
geometry = if definition.entities[0].is_a?(Sketchup::Edge) || definition.entities[0].is_a?(Sketchup::Face)
new_speckle_state, geo = group_entities_to_speckle(
definition, preferences, speckle_state, parent, &convert
definition.entities, preferences, speckle_state, parent, &convert
)
speckle_state = new_speckle_state
geo
@@ -83,21 +72,18 @@ module SpeckleConnector
end
end
# FIXME: Decide how to approach base point of the definition instead origin.
block_definition = BlockDefinition.new(
units: units,
name: definition.name,
geometry: geometry,
geometry: geometry.compact,
always_face_camera: definition.behavior.always_face_camera?,
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: definition.persistent_id.to_s
)
return speckle_state, block_definition
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/ParameterLists
def self.get_definition_name(def_obj)
@@ -120,7 +106,12 @@ module SpeckleConnector
definition_name = get_definition_name(definition_obj)
application_id = definition_obj['applicationId']
definition = sketchup_model.definitions[definition_name]
if definition && (definition.name == definition_name || definition.guid == application_id)
# Check any entities of definition changed
entities_updated = entities_updated?(definition, definition_obj)
if definition && !entities_updated &&
(definition.name == definition_name || definition.guid == application_id)
return state, [definition]
end
@@ -130,21 +121,30 @@ module SpeckleConnector
sketchup_attributes = definition_obj['sketchup_attributes']
definition&.entities&.clear!
definition ||= sketchup_model.definitions.add(definition_name)
definition.layer = layer
ngon_faces = []
if geometry.is_a?(Array)
geometry.each do |obj|
state = convert_to_native.call(state, obj, layer, definition.entities)
state, added_entities = convert_to_native.call(state, obj, layer, definition.entities)
if added_entities.length == 1 && added_entities.first.is_a?(Sketchup::Face)
ngon_faces.append(added_entities.first)
end
end
end
ngon_faces.each do |f|
f.edges.each do |e|
e.soft = false
e.smooth = false
end
end
if geometry.is_a?(Hash) && !definition_obj['speckle_type'].nil?
state = convert_to_native.call(state, geometry, layer, definition.entities)
state, _converted_entities = convert_to_native.call(state, geometry, layer, definition.entities)
end
# puts("definition finished: #{name} (#{application_id})")
# puts(" entity count: #{definition.entities.count}")
definition.behavior.always_face_camera = always_face_camera
unless sketchup_attributes.nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(definition, sketchup_attributes['dictionaries'])
end
return state, [definition]
@@ -158,21 +158,22 @@ module SpeckleConnector
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.group_entities_to_speckle(definition, preferences, speckle_state, parent, &convert)
orphan_edges = definition.entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
def self.group_entities_to_speckle(entities, preferences, speckle_state, parent, &convert)
entities = entities.reject(&:hidden?)
orphan_edges = entities.grep(Sketchup::Edge).filter { |edge| edge.faces.none? }
lines = orphan_edges.collect do |orphan_edge|
new_speckle_state, converted = convert.call(orphan_edge, preferences, speckle_state, parent)
speckle_state = new_speckle_state
converted
end
nested_blocks = definition.entities.grep(Sketchup::ComponentInstance).collect do |component_instance|
nested_blocks = entities.grep(Sketchup::ComponentInstance).collect do |component_instance|
new_speckle_state, converted = convert.call(component_instance, preferences, speckle_state, parent)
speckle_state = new_speckle_state
converted
end
nested_groups = definition.entities.grep(Sketchup::Group).collect do |group|
nested_groups = entities.grep(Sketchup::Group).collect do |group|
new_speckle_state, converted = convert.call(group, preferences, speckle_state, parent)
speckle_state = new_speckle_state
converted
@@ -180,7 +181,9 @@ module SpeckleConnector
if preferences[:model][:combine_faces_by_material]
mesh_groups = {}
definition.entities.grep(Sketchup::Face).collect do |face|
entities.grep(Sketchup::Face).collect do |face|
next unless SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.attribute_dictionary(face).nil?
new_speckle_state = group_meshes_by_material(
face, mesh_groups, speckle_state, preferences, parent, &convert
)
@@ -192,7 +195,7 @@ module SpeckleConnector
return speckle_state, lines + nested_blocks + nested_groups + mesh_groups.values
else
meshes = []
definition.entities.grep(Sketchup::Face).collect do |face|
entities.grep(Sketchup::Face).collect do |face|
new_speckle_state, converted = convert.call(face, preferences, speckle_state, parent)
meshes.append(converted)
speckle_state = new_speckle_state
@@ -209,7 +212,7 @@ module SpeckleConnector
# rubocop:disable Metrics/ParameterLists
def self.group_meshes_by_material(face, mesh_groups, speckle_state, preferences, parent, &convert)
# convert material
mesh_group_id = get_mesh_group_id(face, preferences[:model])
mesh_group_id = Geometry::Mesh.get_mesh_group_id(face, preferences[:model])
new_speckle_state, converted = convert.call(face, preferences, speckle_state, parent)
mesh_groups[mesh_group_id] = converted unless mesh_groups.key?(mesh_group_id)
mesh_group = mesh_groups[mesh_group_id]
@@ -219,36 +222,17 @@ module SpeckleConnector
end
# rubocop:enable Metrics/ParameterLists
# Mesh group id helps to determine how to group faces into meshes.
# @param face [Sketchup::Face] face to get mesh group id.
def self.get_mesh_group_id(face, model_preferences)
if model_preferences[:include_entity_attributes] &&
model_preferences[:include_face_entity_attributes] &&
attribute_dictionary?(face)
return face.persistent_id.to_s
# It is important check for hosted elements that wrapped into component in sketchup.
# Their definition name might be stay same but their speckle ids should be checked
# to compare they updated or not.
def self.entities_updated?(definition, speckle_definition)
children_changed = false
unless definition.nil?
# TODO: Here we need to check later if definition invalid or not.
previous_speckle_id = definition.get_attribute(SPECKLE_BASE_OBJECT, 'speckle_id')
children_changed = previous_speckle_id != speckle_definition['id']
end
material = face.material || face.back_material
return 'none' if material.nil?
return material.entityID.to_s
end
def self.attribute_dictionary?(face)
any_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return any_attribute_dictionary unless any_attribute_dictionary
# If there are any attribute dictionary, then make sure that they are not ignored ones.
all_attribute_dictionary_ignored = face.attribute_dictionaries.all? do |dict|
ignored_dictionaries.include?(dict.name)
end
!all_attribute_dictionary_ignored
end
def self.ignored_dictionaries
[
'Speckle_Base_Object'
]
children_changed
end
end
end
@@ -5,7 +5,11 @@ require_relative 'transform'
require_relative 'block_definition'
require_relative '../base'
require_relative '../geometry/bounding_box'
require_relative '../../sketchup_model/dictionary/dictionary_handler'
require_relative '../other/mapped_block_wrapper'
require_relative '../built_elements/revit/family_instance'
require_relative '../../sketchup_model/dictionary/base_dictionary_handler'
require_relative '../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
require_relative '../../sketchup_model/query/layer'
module SpeckleConnector
module SpeckleObjects
@@ -23,8 +27,8 @@ module SpeckleConnector
# @param sketchup_attributes [Hash{Symbol=>Object}] sketchup attributes of the block instance.
# @param application_id [String] application id of the block instance.
# rubocop:disable Metrics/ParameterLists
def initialize(units:, is_sketchup_group:, name:, render_material:, transform:, block_definition:,
sketchup_attributes: {}, application_id: nil)
def initialize(units:, is_sketchup_group:, name:, render_material:, transform:, block_definition:, layer:,
sketchup_attributes: {}, speckle_schema: {}, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -33,10 +37,12 @@ module SpeckleConnector
)
self[:units] = units
self[:name] = name
self[:layer] = layer
self[:is_sketchup_group] = is_sketchup_group
self[:renderMaterial] = render_material
self[:transform] = transform
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:speckle_schema] = speckle_schema if speckle_schema.any?
# FIXME: Since blockDefinition sends with @ as detached, block basePlane renders on viewer.
self['@@definition'] = block_definition
end
@@ -47,12 +53,10 @@ module SpeckleConnector
new_speckle_state, block_definition = convert.call(group.definition, preferences, speckle_state,
group.persistent_id)
speckle_state = new_speckle_state
dictionaries = {}
if preferences[:model][:include_entity_attributes] && preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(group)
end
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(group, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(group)
block_instance = BlockInstance.new(
units: units,
is_sketchup_group: true,
@@ -60,7 +64,9 @@ module SpeckleConnector
render_material: group.material.nil? ? nil : RenderMaterial.from_material(group.material),
transform: Other::Transform.from_transformation(group.transformation, units),
block_definition: block_definition,
layer: SketchupModel::Query::Layer.entity_path(group),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: group.guid
)
return speckle_state, block_instance
@@ -68,7 +74,7 @@ module SpeckleConnector
# @param component_instance [Sketchup::ComponentInstance] component instance to convert Speckle BlockInstance
# rubocop:disable Metrics/MethodLength
def self.from_component_instance(component_instance, units, preferences, speckle_state, &convert)
def self.from_component_instance(component_instance, units, preferences, speckle_state, path: nil, &convert)
new_speckle_state, block_definition = convert.call(
component_instance.definition,
preferences,
@@ -77,13 +83,20 @@ module SpeckleConnector
)
speckle_state = new_speckle_state
dictionaries = {}
if preferences[:model][:include_entity_attributes] &&
preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(component_instance)
end
dictionaries = SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_speckle(component_instance, preferences[:model])
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(component_instance)
if speckle_schema.empty?
speckle_schema = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
.speckle_schema_to_speckle(component_instance.definition)
end
# transform into global if any path provided
transformation = component_instance.transformation
transformation = SketchupModel::Query::Entity.global_transformation(component_instance, path) if path
block_instance = BlockInstance.new(
units: units,
@@ -94,11 +107,45 @@ module SpeckleConnector
else
RenderMaterial.from_material(component_instance.material)
end,
transform: Other::Transform.from_transformation(component_instance.transformation, units),
transform: Other::Transform.from_transformation(transformation, units),
block_definition: block_definition,
layer: SketchupModel::Query::Layer.entity_path(component_instance),
sketchup_attributes: att,
speckle_schema: speckle_schema,
application_id: component_instance.persistent_id.to_s
)
if speckle_schema
case speckle_schema['method']
when 'New Revit Family'
# duplicate already converted one to attach without speckle schema into mapped block wrapper
copy_block_instance = block_instance.clone(freeze: true)
block_instance['@SpeckleSchema'] = SpeckleObjects::Other::MappedBlockWrapper.new(
category: speckle_schema['category'],
units: units,
instance: copy_block_instance,
application_id: component_instance.persistent_id.to_s
)
when 'Family Instance'
level = speckle_state.speckle_mapper_state.mapper_source
.levels.find { |l| l[:name] == speckle_schema['level'] }
family = speckle_schema['family']
type = speckle_schema['family_type']
block_instance['@SpeckleSchema'] = SpeckleObjects::BuiltElements::Revit::FamilyInstance.new(
family: family,
type: type,
level: level,
units: units,
base_point: SpeckleObjects::Geometry::Point.from_vertex(
component_instance.definition.insertion_point.transform(transformation),
units
),
rotation: calculate_rotation(transformation.to_a),
application_id: component_instance.persistent_id.to_s
)
end
end
return speckle_state, block_instance
end
# rubocop:enable Metrics/MethodLength
@@ -128,7 +175,10 @@ module SpeckleConnector
definition = state.sketchup_state.sketchup_model
.definitions[BlockDefinition.get_definition_name(block_definition)]
return add_instance_from_definition(state, block, layer, entities, definition, is_group, &convert_to_native)
block_layer_name = SketchupModel::Query::Layer.entity_layer_from_path(block['layer'])
block_layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == block_layer_name }
return add_instance_from_definition(state, block, block_layer, layer, entities, definition, is_group,
&convert_to_native)
end
def self.get_transform_matrix(block)
@@ -163,18 +213,19 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/ParameterLists
def self.add_instance_from_definition(state, block, layer, entities, definition, is_group, &convert_to_native)
def self.add_instance_from_definition(state, block, block_layer, layer, entities, definition, is_group,
&convert_to_native)
t_arr = get_transform_matrix(block)
transform = Other::Transform.to_native(t_arr, block['units'])
instance = if is_group
# rubocop:disable SketchupSuggestions/AddGroup
group = entities.add_group(definition.entities.to_a)
group.layer = layer
group.layer = block_layer.nil? ? layer : block_layer
group
# rubocop:enable SketchupSuggestions/AddGroup
else
instance = entities.add_instance(definition, transform)
instance.layer = layer
instance.layer = block_layer.nil? ? layer : block_layer
instance
end
@@ -184,8 +235,8 @@ module SpeckleConnector
puts("Failed to create instance for speckle block instance #{block['id']}") if instance.nil?
# Transform already applied to instance unless is group
instance.transformation = transform if is_group
state, _materials = Other::RenderMaterial.to_native(state, block['renderMaterial'],
layer, entities, &convert_to_native)
state, _materials = Other::RenderMaterial.to_native(state, block['renderMaterial'], layer,
entities, &convert_to_native)
# Retrieve material from state
unless block['renderMaterial'].nil?
@@ -196,7 +247,7 @@ module SpeckleConnector
instance.name = block['name'] unless block['name'].nil?
unless block['sketchup_attributes'].nil?
SketchupModel::Dictionary::DictionaryHandler
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(instance, block['sketchup_attributes']['dictionaries'])
end
return state, [instance, definition]
@@ -206,6 +257,40 @@ module SpeckleConnector
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/ParameterLists
# Instances that created from display value that has no any transform value.
# Because of this reason their definition created with origin axis. We basically create transformation
# vector between bounds min to origin, to move definition axis to bounds min. Otherwise they looks weird in
# sketchup and might be cumbersome when we want to add new entities into definition.
# @param instance [Sketchup::ComponentInstance] instance to align axis to it's bounds
def self.align_instance_axes(instance)
bounds = instance.bounds
transform = Geom::Transformation.translation(bounds.min.vector_to(Geom::Point3d.new(0, 0, 0)))
entities = instance.definition.entities
entities.transform_entities(transform, entities.to_a)
instance_transform = instance.transformation
instance.transform!(instance_transform * transform.inverse * instance_transform.inverse)
end
def self.calculate_rotation(matrix)
# Ensure the matrix is a flat array with 16 elements
unless matrix.is_a?(Array) && matrix.size == 16
raise ArgumentError, 'Matrix must be an array with 16 elements'
end
# Extract the elements of the 2x2 rotation sub-matrix
cos_theta = matrix[0] # First column, first row
sin_theta = matrix[1] # Second column, first row
# Calculate the rotation angle in radians
theta = Math.atan2(sin_theta, cos_theta)
# Ensure the angle is between -π and π
theta -= 2 * Math::PI while theta > Math::PI
theta += 2 * Math::PI while theta < -Math::PI
theta
end
end
end
end
@@ -2,7 +2,7 @@
module SpeckleConnector
module SpeckleObjects
module Others
module Other
# Color object transformations between sketchup and speckle.
class Color
# @param color [Sketchup::Color] color to convert speckle object
@@ -16,9 +16,24 @@ module SpeckleConnector
end
def self.to_native(speckle_color)
return from_int(speckle_color) if speckle_color.is_a?(Numeric)
Sketchup::Color.new(speckle_color['red'], speckle_color['green'],
speckle_color['blue'], speckle_color['alpha'])
end
# @param color [Sketchup::Color] color to convert speckle object
# @return [Numeric] int value of the color
def self.to_int(color)
rgba = color.to_a
[rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack('l').unpack1('l').to_i
end
# @param argb [Numeric] int value of the corresponding color
# @return [Sketchup::Color] sketchup color
def self.from_int(argb)
Sketchup::Color.new((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >> 24) & 255)
end
end
end
end
@@ -0,0 +1,35 @@
# frozen_string_literal: true
require_relative 'color'
require_relative '../base'
require_relative '../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
module Other
# DisplayStyle object for layer.
class DisplayStyle < Base
def initialize(name:, color:, line_type:)
super(
speckle_type: OBJECTS_OTHER_DISPLAYSTYLE,
total_children_count: 0,
application_id: nil,
id: nil
)
self[:name] = name
self[:color] = color
self[:linetype] = line_type unless line_type.nil?
end
# @param layer [Sketchup::Layer] layer to get display style.
def self.from_layer(layer)
DisplayStyle.new(
name: '',
color: Color.to_int(layer.color),
line_type: layer.line_style.nil? ? nil : layer.line_style.name
)
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More