Compare commits

..

153 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
114 changed files with 4059 additions and 582 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
+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'
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 26 KiB

@@ -9,12 +9,16 @@ module SpeckleConnector
module Actions
# Apply mappings for selected entities.
class ApplyMappings < Action
def initialize(entities_to_map, method, category, name, is_definition)
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
@@ -51,6 +55,9 @@ module SpeckleConnector
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
@@ -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
@@ -1,31 +1,28 @@
# frozen_string_literal: true
require_relative 'event_action'
require_relative '../../mapping/category/revit_category'
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 mapping tool.
# 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
selection = {
selection: SketchupModel::Reader::SpeckleEntitiesReader.entities_schema_details(sketchup_selection),
mappingMethods: [
'Direct Shape'
],
categories: Mapping::Category::RevitCategory.to_a
}
selection = { selection: [], mappingMethods: [], categories: [] } if sketchup_selection.none?
state.with_selection_queue(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
@@ -11,6 +11,7 @@ module SpeckleConnector
# @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)
@@ -25,7 +26,7 @@ module SpeckleConnector
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)
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
@@ -55,6 +56,7 @@ module SpeckleConnector
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
@@ -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'
@@ -29,7 +30,7 @@ module SpeckleConnector
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::SpeckleEntitiesReader.read_mapped_entities(sketchup_model.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)
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative 'action'
require_relative '../sketchup_model/reader/speckle_entities_reader'
require_relative '../sketchup_model/reader/mapper_reader'
module SpeckleConnector
module Actions
@@ -10,8 +10,8 @@ module SpeckleConnector
# @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::SpeckleEntitiesReader
.mapped_entity_details(state.speckle_state.mapped_entities.values.to_a)
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
@@ -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
@@ -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}. ===="
@@ -11,9 +11,13 @@ module SpeckleConnector
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, name, is_definition)
action = Actions::ApplyMappings.new(entities_to_map, method, category, family,
family_type, level, name, is_definition)
app.update_state!(action)
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
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module SpeckleConnector
VECTOR_Z = Geom::Vector3d.new(0, 0, 1)
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
@@ -15,7 +15,7 @@ module SpeckleConnector
Sketchup::ComponentInstance => INCLUDE_COMPONENT_ENTITY_ATTRIBUTES,
Sketchup::Group => INCLUDE_GROUP_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_FACE_ENTITY_ATTRIBUTES,
Sketchup::Face => INCLUDE_EDGE_ENTITY_ATTRIBUTES
Sketchup::Edge => INCLUDE_EDGE_ENTITY_ATTRIBUTES
}.freeze
LEVEL_SHIFT_VALUE = SpeckleObjects::Geometry.length_to_native(1.5, 'm')
@@ -4,13 +4,34 @@ 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_REVIT_DIRECTSHAPE = 'Objects.BuiltElements.Revit.DirectShape'
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'
@@ -20,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
+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)
+64 -42
View File
@@ -2,7 +2,9 @@
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'
@@ -11,8 +13,14 @@ require_relative '../speckle_objects/other/display_value'
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
@@ -26,11 +34,14 @@ 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
@@ -43,19 +54,29 @@ module SpeckleConnector
# 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 = 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
@@ -64,13 +85,23 @@ module SpeckleConnector
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
@@ -84,12 +115,10 @@ module SpeckleConnector
# 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.
layers_relation = obj['layers_relation']
# Create layers and it's folders from layers relation on the model collection.
SpeckleObjects::Relations::Layers.to_native(layers_relation, sketchup_model) if layers_relation && !from_revit
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
@@ -100,7 +129,9 @@ module SpeckleConnector
@entities_to_fill = @branch_definition.entities
end
traverse_commit_object(obj, @entities_to_fill)
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
traverse_commit_object(obj, default_commit_layer, @entities_to_fill)
create_levels_from_section_planes
check_hiding_layers_needed
try_create_instance
@@ -230,9 +261,9 @@ module SpeckleConnector
# 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, entities)
def traverse_commit_object(obj, layer, entities)
if convertible_to_native?(obj)
@state = convert_to_native(@state, obj, 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)
@@ -240,23 +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|
traverse_commit_object(obj[prop], 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, 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, entities) }
obj.each_value { |value| traverse_commit_object(value, layer, entities) }
elsif obj.is_a?(Array)
obj.each { |value| traverse_commit_object(value, entities) }
obj.each { |value| traverse_commit_object(value, layer, entities) }
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
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
@@ -264,6 +295,9 @@ 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),
@@ -272,18 +306,27 @@ module SpeckleConnector
OBJECTS_OTHER_REVIT_REVITINSTANCE => REVIT_INSTANCE.method(:to_native),
OBJECTS_OTHER_RENDERMATERIAL => RENDER_MATERIAL.method(:to_native),
OBJECTS_BUILTELEMENTS_VIEW3D => VIEW3D.method(:to_native),
OBJECTS_GIS_POLYGONELEMENT => POLYGON_ELEMENT.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
def convert_to_native(state, obj, entities = sketchup_model.entities)
def convert_to_native(state, obj, layer, entities = sketchup_model.entities)
# store this method as parameter to re-call it inner callstack
convert_to_native = method(:convert_to_native)
# Get 'to_native' method to convert upcoming speckle object to native sketchup entity
to_native_method = speckle_object_to_native(obj)
# 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, entities, &convert_to_native)
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)
@@ -291,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
@@ -340,30 +383,9 @@ module SpeckleConnector
end
# @param state [States::State] state of the application
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/CyclomaticComplexity
def convert_to_speckle_entities(state, speckle_object, entities)
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]
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
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
end
# rubocop:enable Metrics/ClassLength
end
+6 -11
View File
@@ -15,6 +15,7 @@ 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
@@ -33,8 +34,6 @@ module SpeckleConnector
new_speckle_state, model_collection = MODEL_COLLECTION.from_sketchup_model(sketchup_model, speckle_state,
@units, preferences, &convert)
# send only layers that have any object
model_collection[:layers_relation] = SpeckleObjects::Relations::Layers.from_model(sketchup_model)
return new_speckle_state, model_collection
end
@@ -65,28 +64,24 @@ module SpeckleConnector
def convert(entity, preferences, speckle_state, parent = :base)
convert = method(:convert)
unless SketchupModel::Reader::SpeckleEntitiesReader.mapped_with_schema?(entity)
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
def from_mapped_to_speckle(entity, path, preferences)
direct_shape = SpeckleObjects::BuiltElements::Revit::DirectShape
.from_entity(entity, path, @units, preferences)
return [direct_shape, [entity]]
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(face: entity, units: @units,
mesh = SpeckleObjects::Geometry::Mesh.from_face(speckle_state: speckle_state, face: entity, units: @units,
model_preferences: preferences[:model])
return speckle_state, [mesh, [entity]]
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
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module SpeckleConnector
module Mapping
module Mapper
module Category
# Revit categories.
class RevitCategory < Hash
@@ -118,7 +118,8 @@ module SpeckleConnector
VibrationManagement: 106,
Walls: 107,
StructConnectionWelds: 108,
Windows: 109
Windows: 109,
Railings: 110
}.freeze
end
# rubocop:enable Metrics/MethodLength
@@ -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
@@ -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,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
@@ -2,7 +2,7 @@
require_relative '../dictionary/speckle_schema_dictionary_handler'
require_relative '../../speckle_entities/speckle_entity'
require_relative '../../mapping/category/revit_category'
require_relative '../../mapper/category/revit_category'
require_relative '../../constants/dict_constants'
module SpeckleConnector
@@ -29,16 +29,6 @@ module SpeckleConnector
speckle_entities
end
# @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 read from attribute dictionary.
def self.read_speckle_entity(entity)
dict = entity.attribute_dictionaries.to_a.find { |d| d.name == SPECKLE_BASE_OBJECT }
@@ -60,70 +50,7 @@ module SpeckleConnector
entity.attribute_dictionaries.to_a.any? { |dict| dict.name == SPECKLE_BASE_OBJECT }
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
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.mapped_entity_details(entities)
reverse_category_dictionary = Mapping::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
{
name: speckle_schema['name'] || speckle_schema_definition['name'],
category: speckle_schema['category'] || speckle_schema_definition['category'],
categoryName: reverse_category_dictionary[speckle_schema['category']] ||
reverse_category_dictionary[speckle_schema_definition['category']],
method: speckle_schema['method'] || 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
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
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
@@ -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
@@ -2,9 +2,13 @@
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/speckle_entities_reader'
require_relative '../../../sketchup_model/reader/mapper_reader'
require_relative '../../../sketchup_model/dictionary/speckle_schema_dictionary_handler'
module SpeckleConnector
module SpeckleObjects
@@ -30,6 +34,41 @@ module SpeckleConnector
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(
@@ -39,9 +78,9 @@ module SpeckleConnector
mapped_selection = []
flat_selection_with_path.each do |entities|
entity = entities[0]
is_entity_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity)
is_entity_mapped = READER::MapperReader.mapped_with_schema?(entity)
if entity.respond_to?(:definition)
is_definition_mapped = READER::SpeckleEntitiesReader.mapped_with_schema?(entity.definition)
is_definition_mapped = READER::MapperReader.mapped_with_schema?(entity.definition)
mapped_selection.append(entities) if is_entity_mapped || is_definition_mapped
next
end
@@ -50,7 +89,7 @@ module SpeckleConnector
mapped_selection
end
def self.from_entity(entity, path, units, preferences)
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)
@@ -64,7 +103,14 @@ module SpeckleConnector
entity.definition.entities, [Sketchup::Face], path.append(entity)
)
end
base_geometries = group_faces_under_mesh_by_material(entities_with_path, units, preferences)
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
@@ -72,13 +118,13 @@ module SpeckleConnector
end
# rubocop:disable Metrics/MethodLength
def self.group_faces_under_mesh_by_material(faces_with_path, units, preferences)
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, preferences[:model], parent_material)
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]
@@ -86,7 +132,8 @@ module SpeckleConnector
mesh_group[1].append(face)
else
mesh = Geometry::Mesh.from_face(
face: face, units: units, model_preferences: preferences[:model],
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
)
@@ -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
@@ -19,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.
@@ -60,33 +60,25 @@ module SpeckleConnector
target = get_camera_target(cam, units)
direction = get_camera_direction(cam, units)
update_properties = get_scene_update_properties(page)
rendering_options = SpeckleObjects::Others::RenderingOptions.to_speckle(page.rendering_options)
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.fov, units, page.name, update_properties, rendering_options
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.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
def self.to_native(state, view, _entities, &_convert_to_native)
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'
name = view['name'] || view['id']
return state, [] if sketchup_model.pages.any? { |page| page.name == name }
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
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']
@@ -94,8 +86,22 @@ module SpeckleConnector
return state, [page]
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
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|
@@ -109,7 +115,7 @@ module SpeckleConnector
next if rendering_options[prop].nil?
rendering_options[prop] = if value.is_a?(Hash)
SpeckleObjects::Others::Color.to_native(value)
SpeckleObjects::Other::Color.to_native(value)
else
value
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
@@ -34,17 +34,33 @@ module SpeckleConnector
self[:domain] = domain
self[:units] = units
self[:layer] = layer unless layer.nil?
self[:SpeckleSchema] = speckle_schema if speckle_schema.any?
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
end
# rubocop:enable Metrics/ParameterLists
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)
Line.new(
start_pt: start_pt,
end_pt: end_pt,
domain: domain,
units: units,
layer: SketchupModel::Query::Layer.entity_path(edge),
sketchup_attributes: {},
speckle_schema: {},
application_id: edge.persistent_id.to_s
)
end
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(edge, units, model_preferences)
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 = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(edge)
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)
@@ -60,14 +76,49 @@ module SpeckleConnector
)
end
# @param _state [States::State] state of the application.
# @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, entities, &_convert_to_native)
def self.to_native(state, line, layer, entities, &_convert_to_native)
if line.key?('value')
values = line['value']
points = values.each_slice(3).to_a.map { |pt| Point.to_native(pt[0], pt[1], pt[2], line['units']) }
@@ -81,7 +132,7 @@ module SpeckleConnector
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 = line_layer unless line_layer.nil?
edge.layer = line_layer.nil? ? layer : line_layer
unless line['sketchup_attributes'].nil?
SketchupModel::Dictionary::BaseDictionaryHandler
.attribute_dictionaries_to_native(edge, line['sketchup_attributes']['dictionaries'])
@@ -3,6 +3,7 @@
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'
@@ -51,7 +52,7 @@ module SpeckleConnector
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?
self['@SpeckleSchema'] = speckle_schema if speckle_schema.any?
end
# rubocop:enable Metrics/ParameterLists
@@ -80,7 +81,7 @@ module SpeckleConnector
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity:
def self.to_native(state, mesh, entities, &convert_to_native)
def self.to_native(state, mesh, layer, entities, &convert_to_native)
model_preferences = state.user_state.preferences[:model]
# Get soft? flag of {Sketchup::Edge} object to understand smoothness of edge.
is_soften = get_soften_setting(mesh, entities)
@@ -105,7 +106,7 @@ module SpeckleConnector
native_mesh.add_polygon(polygon_points)
end
end
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'],
state, _materials = Other::RenderMaterial.to_native(state, mesh['renderMaterial'], layer,
entities, &convert_to_native)
# Find and assign material if exist
unless mesh['renderMaterial'].nil?
@@ -119,6 +120,10 @@ module SpeckleConnector
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 = mesh_layer unless mesh_layer.nil?
# Smooth edges if they already soft
@@ -129,10 +134,7 @@ module SpeckleConnector
.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
@@ -146,20 +148,18 @@ module SpeckleConnector
# @param global_transform [Geom::Transformation, nil] global transformation value of face if it is not included
# into any component.
# rubocop:disable Style/MultilineTernaryOperator
def self.from_face(face:, units:, model_preferences:, global_transform: nil, parent_material: nil)
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 = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler.speckle_schema_to_speckle(face)
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: material.nil? ? nil : Other::RenderMaterial.from_material(material),
vertices: [],
faces: [],
sketchup_attributes: att,
vertices: [], faces: [], sketchup_attributes: att,
layer: SketchupModel::Query::Layer.entity_path(face),
speckle_schema: speckle_schema,
application_id: face.persistent_id
@@ -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
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'utils'
require_relative '../base'
require_relative '../other/transform'
require_relative '../other/block_definition'
@@ -14,32 +15,14 @@ module SpeckleConnector
class PolygonElement < Base
SPECKLE_TYPE = OBJECTS_GIS_POLYGONELEMENT
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
# Handles polygon element differently from display value.
def self.to_native(state, obj, entities, &convert_to_native)
attributes = get_qgis_attributes(obj)
def self.to_native(state, obj, layer, entities, &convert_to_native)
attributes = GIS.get_qgis_attributes(obj)
obj = collect_definition_geometries(obj)
obj['name'] = get_definition_name(obj, attributes)
obj['name'] = GIS.get_definition_name(obj, attributes)
state, _definitions = Other::BlockDefinition.to_native(
state,
obj,
entities,
&convert_to_native
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model
@@ -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
@@ -75,7 +75,7 @@ module SpeckleConnector
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,
@@ -98,7 +98,7 @@ module SpeckleConnector
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def self.to_native(state, definition_obj, _entities, &convert_to_native)
def self.to_native(state, definition_obj, layer, _entities, &convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
# FIXME: Check later this is a valid check or not. Maybe unnecessary? If necessary document it!
@@ -122,13 +122,23 @@ module SpeckleConnector
definition&.entities&.clear!
definition ||= sketchup_model.definitions.add(definition_name)
ngon_faces = []
if geometry.is_a?(Array)
geometry.each do |obj|
state = convert_to_native.call(state, obj, 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, 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}")
@@ -149,6 +159,7 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
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)
@@ -5,6 +5,8 @@ require_relative 'transform'
require_relative 'block_definition'
require_relative '../base'
require_relative '../geometry/bounding_box'
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'
@@ -40,7 +42,7 @@ module SpeckleConnector
self[:renderMaterial] = render_material
self[:transform] = transform
self[:sketchup_attributes] = sketchup_attributes if sketchup_attributes.any?
self[:SpeckleSchema] = speckle_schema if speckle_schema.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
@@ -72,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,
@@ -87,6 +89,15 @@ module SpeckleConnector
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,
is_sketchup_group: false,
@@ -96,13 +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
@@ -112,7 +155,7 @@ module SpeckleConnector
# @param block [Object] block object that represents Speckle block.
# @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, block, entities, &convert_to_native)
def self.to_native(state, block, layer, entities, &convert_to_native)
# is_group = block.key?("is_sketchup_group") && block["is_sketchup_group"]
# something about this conversion is freaking out if nested block geo is a group
# so this is set to false always until I can figure this out
@@ -124,6 +167,7 @@ module SpeckleConnector
state, _definitions = BlockDefinition.to_native(
state,
block_definition,
layer,
entities,
&convert_to_native
)
@@ -133,7 +177,7 @@ module SpeckleConnector
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, entities, definition, is_group,
return add_instance_from_definition(state, block, block_layer, layer, entities, definition, is_group,
&convert_to_native)
end
@@ -169,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 unless layer.nil?
group.layer = block_layer.nil? ? layer : block_layer
group
# rubocop:enable SketchupSuggestions/AddGroup
else
instance = entities.add_instance(definition, transform)
instance.layer = layer unless layer.nil?
instance.layer = block_layer.nil? ? layer : block_layer
instance
end
@@ -190,7 +235,7 @@ 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'],
state, _materials = Other::RenderMaterial.to_native(state, block['renderMaterial'], layer,
entities, &convert_to_native)
# Retrieve material from state
@@ -226,6 +271,26 @@ module SpeckleConnector
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
@@ -22,9 +22,15 @@ module SpeckleConnector
return format_naming_convention([family, type, category, element_id]) unless element_id.nil?
return "def::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
name = def_obj['name']
return "#{name}::#{def_obj['applicationId']}" if !name.nil? && !def_obj['applicationId'].nil?
return "def::#{def_obj['id']}"
return "#{name}::#{def_obj['id']}" unless name.nil?
speckle_type = def_obj['speckle_type'].split('.').last
return "#{speckle_type}::#{def_obj['applicationId']}" unless def_obj['applicationId'].nil?
return "#{speckle_type}::#{def_obj['id']}"
end
def self.format_naming_convention(entries)
@@ -41,42 +47,55 @@ module SpeckleConnector
name
end
# Get instance name as speckle_type if it is structured as `speckle_type::application_id`
def self.get_instance_name(definition_name)
return definition_name unless definition_name.include?('::')
definition_name.split('::').first
end
# Creates a component definition and instance from a speckle object with a display value
# @param state [States::State] state of the application.
def self.to_native(state, obj, entities, &convert_to_native)
def self.to_native(state, obj, layer, entities, &convert_to_native)
# Switch displayValue with geometry
obj = collect_definition_geometries(obj)
obj['name'] = get_definition_name(obj)
state, _definitions = BlockDefinition.to_native(
state,
obj,
entities,
&convert_to_native
state, obj, layer, entities, &convert_to_native
)
definition = state.sketchup_state.sketchup_model.definitions[BlockDefinition.get_definition_name(obj)]
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'])
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?
instance.name = 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
def self.collect_definition_geometries(obj)
obj['geometry'] = obj['displayValue']
obj['geometry'] = obj['displayValue'] || obj['@displayValue']
if !obj['elements'].nil? && obj['elements'].is_a?(Array)
obj['elements'].each do |element|
# Mullions is a special case here, they are extracted as base object with @displayValue from revit..
if element['@displayValue'].nil?
obj['geometry'].append(element)
else
obj['geometry'] += element['@displayValue']
elements = obj['elements'] || obj['@elements']
# if only elements are there then assign only elements, there are some cases that RevitWalls can only
# have elements instead of display value
if obj['geometry'].nil? && !elements.nil?
obj['geometry'] = elements
else
if !elements.nil? && elements.is_a?(Array)
elements.each do |element|
# Mullions is a special case here, they are extracted as base object with @displayValue from revit..
if element['@displayValue'].nil?
obj['geometry'].append(element)
else
obj['geometry'] += element['@displayValue']
end
end
end
end
@@ -0,0 +1,25 @@
# frozen_string_literal: true
require_relative '../base'
module SpeckleConnector
module SpeckleObjects
module Other
# MappedBlockWrapper object definition for Speckle.
class MappedBlockWrapper < Base
SPECKLE_TYPE = 'Objects.Other.MappedBlockWrapper'
def initialize(category:, units:, instance:, application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:category] = category
self[:units] = units
self[:instance] = instance
end
end
end
end
end
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'color'
require_relative '../base'
module SpeckleConnector
@@ -38,10 +39,9 @@ module SpeckleConnector
# @param material [Sketchup::Material] material on the Sketchup.
def self.from_material(material)
rgba = material.color.to_a
RenderMaterial.new(
name: material.name,
diffuse: [rgba[3] << 24 | rgba[0] << 16 | rgba[1] << 8 | rgba[2]].pack('l').unpack1('l'),
diffuse: Other::Color.to_int(material.color),
opacity: material.alpha,
emissive: -16_777_216,
metalness: 0,
@@ -50,7 +50,7 @@ module SpeckleConnector
end
# @param state [States::State] state of the application.
def self.to_native(state, render_material, _entities, &_convert_to_native)
def self.to_native(state, render_material, _layer, _entities, &_convert_to_native)
return state, [] if render_material.nil?
sketchup_model = state.sketchup_state.sketchup_model
@@ -65,7 +65,7 @@ module SpeckleConnector
material = sketchup_model.materials.add(name)
material.alpha = render_material['opacity']
argb = render_material['diffuse']
material.color = Sketchup::Color.new((argb >> 16) & 255, (argb >> 8) & 255, argb & 255, (argb >> 24) & 255)
material.color = Color.from_int(argb)
new_sketchup_state = state.sketchup_state.with_materials(materials.add_material(name, material))
return state.with_sketchup_state(new_sketchup_state), [material]
end
@@ -4,7 +4,7 @@ require_relative 'color'
module SpeckleConnector
module SpeckleObjects
module Others
module Other
# Rendering options for scenes.
class RenderingOptions
# @param options [Sketchup::RenderingOptions] rendering options to convert speckle object
@@ -19,7 +19,7 @@ module SpeckleConnector
id: nil
)
self[:units] = units
self[:value] = value
self[:matrix] = value
end
def self.from_transformation(transformation, units)
@@ -11,7 +11,8 @@ module SpeckleConnector
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# rubocop:disable Metrics/ParameterLists
def initialize(name:, visible:, is_folder:, color: nil, layers_and_folders: [], application_id: nil)
def initialize(name:, visible:, is_folder:, full_path: nil, line_style: nil, color: nil, layers_and_folders: [],
application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
@@ -22,6 +23,8 @@ module SpeckleConnector
self[:color] = color
self[:visible] = visible
self[:is_folder] = is_folder
self[:full_path] = full_path unless full_path.nil?
self[:line_style] = line_style unless line_style.nil?
self[:collectionType] = 'layer'
self[:elements] = layers_and_folders if layers_and_folders.any?
end
@@ -29,25 +32,47 @@ module SpeckleConnector
# @param speckle_layer [Object] speckle layer object.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] folder to create layers in it.
# @param sketchup_model [Sketchup::Model] sketchup active model.
def self.to_native_layer(speckle_layer, folder, sketchup_model)
layer = sketchup_model.layers.add_layer(speckle_layer['name'])
layer.visible = speckle_layer['visible'] unless speckle_layer['visible'].nil?
layer.color = SpeckleObjects::Others::Color.to_native(speckle_layer['color']) if speckle_layer['color']
layer = sketchup_model.layers.add_layer(speckle_layer[:name])
layer.visible = speckle_layer[:visible] unless speckle_layer[:visible].nil?
layer.color = SpeckleObjects::Other::Color.to_native(speckle_layer[:color]) if speckle_layer[:color]
if speckle_layer[:line_style]
line_style = sketchup_model.line_styles.find { |ls| ls.name == speckle_layer[:line_style] }
layer.line_style = line_style unless line_style.nil?
end
folder.add_layer(layer) if folder.is_a?(Sketchup::LayerFolder)
end
def self.to_native_layer_folder(speckle_layer_folder, folder, sketchup_model)
speckle_layers = speckle_layer_folder['elements'].select { |layer_or_fol| layer_or_fol['elements'].nil? }
# Flat layer conversion.
def self.to_native_flat_layers(layers_relation, sketchup_model)
speckle_layers = layers_relation[:elements]
elements_to_layers(speckle_layers, sketchup_model)
end
# Converts elements to layers with it's full path.
def self.elements_to_layers(elements, sketchup_model)
elements.each do |element|
element[:name] = element[:full_path]
to_native_layer(element, sketchup_model.layers, sketchup_model)
elements_to_layers(element[:elements], sketchup_model) unless element[:elements].nil?
end
end
# Nested layer conversion with folders.
def self.to_native_layer_folder(layers_relation, folder, sketchup_model)
speckle_layers = layers_relation[:elements].select { |layer_or_fol| layer_or_fol[:elements].nil? }
speckle_layers.each do |speckle_layer|
to_native_layer(speckle_layer, folder, sketchup_model)
end
speckle_folders = speckle_layer_folder['elements'].reject { |layer_or_fol| layer_or_fol['elements'].nil? }
speckle_folders = layers_relation[:elements].reject { |layer_or_fol| layer_or_fol[:elements].nil? }
speckle_folders.each do |speckle_folder|
sub_folder = folder.add_folder(speckle_folder['name'])
sub_folder.visible = speckle_folder['visible'] unless speckle_folder['visible'].nil?
sub_folder = folder.add_folder(speckle_folder[:name])
sub_folder.visible = speckle_folder[:visible] unless speckle_folder[:visible].nil?
to_native_layer_folder(speckle_folder, sub_folder, sketchup_model)
end
end
@@ -71,7 +96,8 @@ module SpeckleConnector
name: layer.display_name,
visible: layer.visible?,
is_folder: false,
color: SpeckleObjects::Others::Color.to_speckle(layer.color),
line_style: layer.line_style.nil? ? nil : layer.line_style.name,
color: SpeckleObjects::Other::Color.to_speckle(layer.color),
application_id: layer.persistent_id
)
end
@@ -22,10 +22,60 @@ module SpeckleConnector
self[:elements] = layers
end
def self.to_native(layers_relation, sketchup_model)
folder = sketchup_model.layers
# Extract relations from commit obj to create layers in advance.
# By doing this, also checks layers will be created as flat list or nested structure according to source app.
# @param commit_obj [Hash] commit object to extract layer relations.
# @param source_app [String] source application to decide layer creation strategy.
# Currently for
# - Revit: we don't create layers in advance because we create layers according to categories.
# - SketchUp: we create layers in advance as nested.
# - Rhino: we create layers in advance as flat list with it's full path.
def self.extract_relations(commit_obj, source_app)
return nil unless commit_obj['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
SpeckleObjects::Relations::Layer.to_native_layer_folder(layers_relation, folder, sketchup_model)
elements = element_to_relation(commit_obj['elements'], source_app, [])
Layers.new(
active: commit_obj['active_layer'],
layers: elements
)
end
# rubocop:disable Metrics/CyclomaticComplexity
def self.element_to_relation(elements, source_app, parent_layers)
elements.collect do |element|
next unless element['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION
layers_tree = parent_layers.dup.append(element['name'])
full_path = ''
parent_layers.each { |parent| full_path += "#{parent}::" }
full_path += element['name']
# Add this info to commit object to check later layer_collection conversion
element['full_path'] = full_path if source_app.include?('rhino')
is_folder = element['elements'].any? { |e| e['speckle_type'] == SPECKLE_CORE_MODELS_COLLECTION }
color = element['color'] || element['displayStyle']['color'] unless element['displayStyle'].nil?
Layer.new(
name: element['name'], visible: element['visible'], is_folder: is_folder,
color: color, full_path: full_path,
layers_and_folders: element_to_relation(element['elements'], source_app, layers_tree)
)
end.compact
end
# rubocop:enable Metrics/CyclomaticComplexity
def self.to_native(obj, source_app, sketchup_model)
layers_relation = extract_relations(obj, source_app)
return if layers_relation.nil?
folder = sketchup_model.layers
is_flat = source_app.include?('rhino')
if is_flat
SpeckleObjects::Relations::Layer.to_native_flat_layers(layers_relation, sketchup_model)
else
SpeckleObjects::Relations::Layer.to_native_layer_folder(layers_relation, folder, sketchup_model)
end
active_layer = folder.to_a.find { |layer| layer.display_name == layers_relation['active_layer'] }
sketchup_model.active_layer = active_layer unless active_layer.nil?
@@ -15,15 +15,17 @@ module SpeckleConnector
family = def_obj['family']
type = def_obj['type']
category = def_obj['category']
element_id = def_obj['elementId']
id = def_obj['id']
return "#{family}-#{type}-#{category}-#{def_obj['elementId']}"
return "#{family}-#{type}-#{category}-#{element_id}-#{id}"
end
def self.to_native(state, definition, entities, &convert_to_native)
def self.to_native(state, definition, layer, entities, &convert_to_native)
definition_name = get_definition_name(definition)
definition['name'] = definition_name
definition['displayValue'] += definition['elements'] unless definition['elements'].nil?
SpeckleObjects::Other::BlockDefinition.to_native(state, definition, entities, &convert_to_native)
SpeckleObjects::Other::BlockDefinition.to_native(state, definition, layer, entities, &convert_to_native)
end
end
end
@@ -16,12 +16,13 @@ module SpeckleConnector
# @param block [Object] block object that represents Speckle block.
# @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, block, entities, &convert_to_native)
def self.to_native(state, block, layer, entities, &convert_to_native)
block_definition = block['definition']
state, _definitions = RevitDefinition.to_native(
state,
block_definition,
layer,
entities,
&convert_to_native
)
@@ -29,10 +30,11 @@ module SpeckleConnector
definition = state.sketchup_state.sketchup_model
.definitions[RevitDefinition.get_definition_name(block_definition)]
layer = state.sketchup_state.sketchup_model.layers.to_a.find { |l| l.display_name == block['category'] }
block_layer = state.sketchup_state.sketchup_model.layers.to_a
.find { |l| l.display_name == block['category'] }
return SpeckleObjects::Other::BlockInstance.add_instance_from_definition(
state, block, layer, entities, definition, false, &convert_to_native
state, block, block_layer, layer, entities, definition, false, &convert_to_native
)
end
end
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative '../../../base'
require_relative '../../../../constants/type_constants'
module SpeckleConnector
module SpeckleObjects
@@ -9,15 +10,13 @@ module SpeckleConnector
module Models
# Collection object that collect other speckle objects under it's elements.
class Collection < Base
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# @param name [String] name of the collection.
# @param collection_type [String] type of the collection like, layers, categories etc..
# @param elements [Array<Object>] elements of the collection.
# @param application_id [String, nil] id of the collection on the model.
def initialize(name:, collection_type:, elements: [], application_id: nil)
super(
speckle_type: SPECKLE_TYPE,
speckle_type: SPECKLE_CORE_MODELS_COLLECTION,
total_children_count: 0,
application_id: application_id,
id: nil
@@ -26,6 +25,16 @@ module SpeckleConnector
self[:collectionType] = collection_type
self[:elements] = elements
end
def self.to_native(state, collection, layer, entities, &convert_to_native)
collection_type = collection['collectionType']
if collection_type.include?('model')
ModelCollection.to_native(state, collection, layer, entities, &convert_to_native)
else
LayerCollection.to_native(state, collection, layer, entities, &convert_to_native)
end
end
end
end
end
@@ -0,0 +1,28 @@
# frozen_string_literal: true
require_relative 'collection'
module SpeckleConnector
module SpeckleObjects
module Speckle
module Core
module Models
# VectorLayerCollection object that collect GIS vector elements under it's elements.
class GisLayerCollection < Collection
# @param state [States::State] state of the Speckle application.
def self.to_native(state, vector_layer_collection, layer_or_folder, entities, &convert_to_native)
elements = vector_layer_collection['elements']
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer_or_folder, entities)
state = new_state
end
return state, []
end
end
end
end
end
end
end
@@ -3,6 +3,7 @@
require_relative 'collection'
require_relative '../../../../sketchup_model/query/layer'
require_relative '../../../other/color'
require_relative '../../../other/display_style'
module SpeckleConnector
module SpeckleObjects
@@ -13,7 +14,8 @@ module SpeckleConnector
class LayerCollection < Collection
SPECKLE_TYPE = 'Speckle.Core.Models.Collection'
# rubocop:disable Metrics/ParameterLists
def initialize(name:, visible:, is_folder:, color: nil, elements: [], application_id: nil)
def initialize(name:, visible:, is_folder:, display_style: nil, color: nil, elements: [],
application_id: nil)
super(
name: name,
collection_type: 'layer',
@@ -23,6 +25,7 @@ module SpeckleConnector
self[:visible] = visible
self[:is_folder] = is_folder
self[:color] = color unless color.nil?
self[:displayStyle] = display_style unless display_style.nil?
end
# rubocop:enable Metrics/ParameterLists
@@ -57,7 +60,7 @@ module SpeckleConnector
name: layer.display_name,
visible: layer.visible?,
is_folder: false,
color: SpeckleObjects::Others::Color.to_speckle(layer.color),
display_style: Other::DisplayStyle.from_layer(layer),
application_id: layer.persistent_id
)
end
@@ -76,7 +79,7 @@ module SpeckleConnector
el[:name] == folder.display_name
end
if collection_candidate.nil?
color = folder.respond_to?(:color) ? SpeckleObjects::Others::Color.to_speckle(folder.color) : nil
color = folder.respond_to?(:color) ? SpeckleObjects::Other::Color.to_speckle(folder.color) : nil
collection_candidate = LayerCollection.new(
name: folder.display_name, visible: folder.visible?,
is_folder: folder.is_a?(Sketchup::LayerFolder), color: color
@@ -90,6 +93,24 @@ module SpeckleConnector
collection
end
# rubocop:enable Metrics/CyclomaticComplexity
# @param state [States::State] state of the Speckle application.
def self.to_native(state, layer_collection, layer_or_folder, entities, &convert_to_native)
sketchup_model = state.sketchup_state.sketchup_model
elements = layer_collection['elements']
name = layer_collection['name']
name = layer_collection['full_path'] if layer_collection['full_path']
layer = sketchup_model.layers.find { |l| l.display_name == name }
layer_or_folder = layer if layer
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer_or_folder, entities)
state = new_state
end
return state, []
end
end
end
end
@@ -4,6 +4,7 @@ require_relative 'collection'
require_relative 'layer_collection'
require_relative '../../../built_elements/view3d'
require_relative '../../../built_elements/revit/direct_shape'
require_relative '../../../../mapper/mapper'
module SpeckleConnector
module SpeckleObjects
@@ -13,7 +14,9 @@ module SpeckleConnector
# ModelCollection object that collect other speckle objects under it's elements.
class ModelCollection < Collection
DIRECT_SHAPE = SpeckleObjects::BuiltElements::Revit::DirectShape
QUERY = SketchupModel::Query
VIEW3D = SpeckleObjects::BuiltElements::View3d
SPECKLE_SCHEMA_DICTIONARY_HANDLER = SketchupModel::Dictionary::SpeckleSchemaDictionaryHandler
def initialize(name:, active_layer:, elements: [], application_id: nil)
super(
name: name,
@@ -24,6 +27,27 @@ module SpeckleConnector
self[:active_layer] = active_layer
end
def self.to_native(state, model_collection, layer, entities, &convert_to_native)
elements = model_collection['elements']
views = model_collection['@Views']
if views
views.each do |view|
new_state, _converted_entities = convert_to_native.call(state, view, layer, entities)
state = new_state
end
end
elements.each do |element|
new_state, _converted_entities = convert_to_native.call(state, element, layer, entities)
state = new_state
end
active_layer = model_collection['active_layer']
state.sketchup_state.sketchup_model.active_layer = active_layer unless active_layer.nil?
return state, []
end
def self.from_sketchup_model(sketchup_model, speckle_state, units, preferences, &convert)
model_collection = ModelCollection.new(
name: 'Sketchup Model', active_layer: sketchup_model.active_layer.display_name,
@@ -31,7 +55,8 @@ module SpeckleConnector
)
# Direct shapes will pass directly to elements which are already flattened with all children
model_collection[:elements] += collect_direct_shapes(sketchup_model, units, preferences)
model_collection[:elements] += collect_mapped_entities(speckle_state, sketchup_model, units,
preferences, &convert)
# Views will pass directly to elements since they don't have any relation with layers and geometries.
model_collection[:elements] += VIEW3D.from_model(sketchup_model, units) if sketchup_model.pages.any?
@@ -52,13 +77,11 @@ module SpeckleConnector
return speckle_state, model_collection
end
def self.collect_direct_shapes(sketchup_model, units, preferences)
DIRECT_SHAPE.direct_shapes_on_selection(sketchup_model).collect do |entities|
entity = entities[0]
path = entities[1..-1]
direct_shape = DIRECT_SHAPE.from_entity(entity, path, units, preferences)
[direct_shape, [entity]]
# @param sketchup_model [Sketchup::Model] active model to retrieve and convert mapped entities.
def self.collect_mapped_entities(speckle_state, sketchup_model, units, preferences, &convert)
mapped_entities = Mapper.mapped_entities_on_selection(sketchup_model)
mapped_entities.collect do |entity_with_path|
Mapper.convert_mapped_entity(speckle_state, entity_with_path, preferences, units, &convert)
end
end
end
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require_relative '../immutable/immutable'
require_relative '../callbacks/callback_message'
require_relative '../speckle_entities/speckle_entity'
require_relative '../mapper/mapper_source'
module SpeckleConnector
module States
# State of the speckle on ruby.
class SpeckleMapperState
include Immutable::ImmutableUtils
# @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself
attr_reader :mapped_entities
# @return [Mapper::MapperSource] source of the mapper.
attr_reader :mapper_source
def initialize
@mapped_entities = Immutable::EmptyHash
@mapper_source = nil
end
def with_mapped_entity(entity)
new_mapped_entities = mapped_entities.put(entity.persistent_id, entity)
with_mapped_entities(new_mapped_entities)
end
def with_removed_mapped_entity(entity)
new_mapped_entities = mapped_entities.delete(entity.persistent_id)
with_mapped_entities(new_mapped_entities)
end
def with_mapped_entities(new_mapped_entities)
with(:@mapped_entities => new_mapped_entities)
end
def with_mapper_source(mapper_source)
# TODO: Check/Sync here parameters of the mapped entities.
with(:@mapper_source => mapper_source)
end
end
end
end
+32 -14
View File
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative 'speckle_mapper_state'
require_relative '../immutable/immutable'
require_relative '../callbacks/callback_message'
require_relative '../speckle_entities/speckle_entity'
@@ -10,13 +11,13 @@ module SpeckleConnector
class SpeckleState
include Immutable::ImmutableUtils
# @return [States::SpeckleMapperState] state of the mapper.
attr_reader :speckle_mapper_state
# @return [ImmutableHash{Integer=>SpeckleEntities::SpeckleEntity}] persistent_id of the sketchup entity and
# corresponding speckle entity
attr_reader :speckle_entities
# @return [ImmutableHash{Integer=>Sketchup::Entity}] persistent_id of the sketchup entity and itself
attr_reader :mapped_entities
# @return [Array] accounts on appdata.
attr_reader :accounts
@@ -47,10 +48,10 @@ module SpeckleConnector
@message_queue = queue
@stream_queue = stream_queue
@speckle_entities = Immutable::EmptyHash
@mapped_entities = Immutable::EmptyHash
@render_materials = Immutable::EmptyHash
@definitions = Immutable::EmptyHash
@relation = Relations::ManyToOneRelation.new
@speckle_mapper_state = SpeckleMapperState.new
end
# @param callback_name [String] name of the callback command
@@ -68,13 +69,19 @@ module SpeckleConnector
with(:@message_queue => new_queue)
end
def with_selection_queue(selection_parameters)
def with_mapper_selection_queue(selection_parameters)
new_queue = message_queue.merge({ "entitySelected":
"entitySelected(#{JSON.generate(selection_parameters)})" })
with(:@message_queue => new_queue)
end
def with_deselection_queue
def with_mapper_init_queue(init_parameters)
new_queue = message_queue.merge({ "mapperInitialized":
"mapperInitialized(#{JSON.generate(init_parameters)})" })
with(:@message_queue => new_queue)
end
def with_mapper_deselection_queue
new_queue = message_queue.merge({ "entitiesDeselected": 'entitiesDeselected()' })
with(:@message_queue => new_queue)
end
@@ -95,14 +102,29 @@ module SpeckleConnector
with(:@accounts => new_accounts)
end
def with_mapper_source(mapper_source)
new_speckle_mapper_state = speckle_mapper_state.with_mapper_source(mapper_source)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_removed_mapper_source
new_speckle_mapper_state = speckle_mapper_state.with_mapper_source(nil)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_mapped_entity(entity)
new_mapped_entities = mapped_entities.put(entity.persistent_id, entity)
with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_mapped_entity(entity)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_removed_mapped_entity(entity)
new_mapped_entities = mapped_entities.delete(entity.persistent_id)
with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_removed_mapped_entity(entity)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_mapped_entities(new_mapped_entities)
new_speckle_mapper_state = speckle_mapper_state.with_mapped_entities(new_mapped_entities)
with(:@speckle_mapper_state => new_speckle_mapper_state)
end
def with_speckle_entity(traversed_entity)
@@ -114,10 +136,6 @@ module SpeckleConnector
with(:@speckle_entities => new_speckle_entities)
end
def with_mapped_entities(new_mapped_entities)
with(:@mapped_entities => new_mapped_entities)
end
def with_relation(new_relation)
with(:@relation => new_relation)
end
+8 -3
View File
@@ -41,15 +41,20 @@ module SpeckleConnector
with(:@speckle_state => new_speckle_state)
end
def with_selection_queue(selection_parameters)
def with_mapper_selection_queue(selection_parameters)
new_speckle_state = if selection_parameters[:selection].any?
speckle_state.with_selection_queue(selection_parameters)
speckle_state.with_mapper_selection_queue(selection_parameters)
else
speckle_state.with_deselection_queue
speckle_state.with_mapper_deselection_queue
end
with(:@speckle_state => new_speckle_state)
end
def with_mapper_init_queue(init_parameters)
new_speckle_state = speckle_state.with_mapper_init_queue(init_parameters)
with(:@speckle_state => new_speckle_state)
end
def with_empty_stream_queue
new_speckle_state = speckle_state.with(:@stream_queue => {})
with(:@speckle_state => new_speckle_state)
+6 -1
View File
@@ -57,6 +57,10 @@ module SpeckleConnector
html_dialog.execute_script(data)
end
def reset_dialog_location
html_dialog.center
end
private
# @return [UI::HtmlDialog] the Sketchup interface to dialog
@@ -77,8 +81,9 @@ module SpeckleConnector
dialog.set_can_close do
true
end
File.exist?(@htm_file) ? dialog.set_file(@htm_file) : dialog.set_url('http://localhost:8081')
# File.exist?(@htm_file) ? dialog.set_file(@htm_file) : dialog.set_url('http://localhost:8081')
# dialog.set_url('http://localhost:8081') # uncomment this line if you want to use your local version of ui
dialog.set_url('https://development--speckle-sketchup-dui2.netlify.app/')
add_exec_callback(dialog)
dialog
end
+1 -1
View File
@@ -5,7 +5,7 @@ module SpeckleConnector
# An interface to Sketchup user interface. This object controls the menu `Extensions->Speckle` in Sketchup's menu,
# the Speckle toolbar and sending message to the user via Sketchup.
class SketchupUi
MENU_TITLE = 'Speckle'
MENU_TITLE = 'Speckle (Legacy)'
BEFORE_NEVER_SHOWN = -1
# @return [Sketchup::Menu] the menu of the Speckle
+7 -1
View File
@@ -16,7 +16,9 @@ require_relative '../commands/model_preferences_updated'
require_relative '../commands/activate_diffing'
require_relative '../commands/apply_mappings'
require_relative '../commands/clear_mappings'
require_relative '../commands/mapper_source_updated'
require_relative '../actions/mapper_initialized'
require_relative '../actions/reload_accounts'
require_relative '../actions/load_saved_streams'
require_relative '../actions/init_local_accounts'
@@ -29,6 +31,7 @@ require_relative '../actions/isolate_mappings_from_table'
require_relative '../actions/hide_mappings_from_table'
require_relative '../actions/select_mappings_from_table'
require_relative '../actions/show_all_entities'
require_relative '../actions/clear_mapper_source'
module SpeckleConnector
module Ui
@@ -89,7 +92,10 @@ module SpeckleConnector
isolate_mappings_from_table: Commands::ActionCommand.new(@app, Actions::IsolateMappingsFromTable),
hide_mappings_from_table: Commands::ActionCommand.new(@app, Actions::HideMappingsFromTable),
select_mappings_from_table: Commands::ActionCommand.new(@app, Actions::SelectMappingsFromTable),
show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities)
show_all_entities: Commands::ActionCommand.new(@app, Actions::ShowAllEntities),
mapper_source_updated: Commands::MapperSourceUpdated.new(@app),
clear_mapper_source: Commands::ActionCommand.new(@app, Actions::ClearMapperSource),
mapper_initialized: Commands::ActionCommand.new(@app, Actions::MapperInitialized)
}.freeze
end
# rubocop:enable Metrics/MethodLength
+1 -1
View File
@@ -1,3 +1,3 @@
VUE_APP_DEV_TOKEN=
VUE_APP_SPECKLE_NAME=SpeckleSketchup
VUE_APP_DEFAULT_SERVER=https://latest.speckle.dev
VUE_APP_DEFAULT_SERVER=https://latest.speckle.systems
+61 -33
View File
@@ -1,6 +1,7 @@
<template>
<v-app>
<v-main>
<legacy-dialog/>
<v-app-bar app flat height="50">
<v-img
class="mx-auto px-0"
@@ -15,7 +16,7 @@
>
<v-tabs-slider class="mx-sm-1"></v-tabs-slider>
<v-tab href="#streams">
{{"Streams"}}
{{ streamsText }}
</v-tab>
<v-tab href="#mapper">
{{"Mapper"}}
@@ -48,21 +49,25 @@
<v-img v-if="user.avatar" :src="user.avatar" />
<v-img v-else :src="`https://robohash.org/` + user.id + `.png?size=40x40`" />
</v-avatar>
<div>
<b>{{ user.name }}</b>
</div>
<div class="caption">
{{ user.company }}
<br />
{{ user.bio ? 'Bio: ' + user.bio : '' }}
</div>
<div>
<b>{{ user.name }}</b>
<br />
<b>{{ user.email }}</b>
</div>
<div class="caption">
<b>{{ activeAccount().serverInfo.url }}</b>
</div>
</v-card-text>
<v-card-text v-if="accounts()">
<v-divider class="my-3" />
<div v-for="account in accounts()" :key="account.id">
<v-btn
v-if="account.userInfo.id != user.id"
v-if="account.id != activeAccount().id"
rounded
large
class="my-1 elevation-0"
@@ -88,7 +93,7 @@
<v-text-field
v-model="streamSearchQuery"
prepend-inner-icon="mdi-magnify"
label="Search streams"
:label="searchText"
background-color="background"
hide-details
clearable
@@ -100,12 +105,13 @@
/>
</v-container>
<create-stream-dialog
v-if="accounts().length !== 0"
v-if="accounts() && accounts().length !== 0"
:is-f-e2-terms="preferences && preferences.user && preferences.user.fe2"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
<v-container v-if="accounts() && accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery"/>
</v-container>
<v-container v-else>
<login/>
@@ -115,7 +121,7 @@
</v-tab-item>
<v-tab-item :key="2" value="mapper">
<v-card flat>
<mapper></mapper>
<mapper :stream-text="streamText" :branch-text="branchText"></mapper>
</v-card>
</v-tab-item>
</v-tabs-items>
@@ -127,8 +133,10 @@
/*global sketchup*/
import { bus } from './main'
import userQuery from './graphql/user.gql'
import serverInfoQuery from './graphql/serverInfo.gql'
import { onLogin } from './vue-apollo'
import Login from "@/views/Login";
import LegacyDialog from './components/dialogs/LegacyDialog.vue';
global.collectPreferences = function (preferences) {
bus.$emit('update-preferences', preferences)
@@ -143,13 +151,16 @@ global.collectVersions = function (versions) {
global.loadAccounts = function (accounts) {
console.log('>>> SpeckleSketchup: Loading accounts', accounts)
localStorage.setItem('localAccounts', JSON.stringify(accounts))
let uuid = localStorage.getItem('uuid')
let selectedAccountId = localStorage.getItem('selectedAccountId')
if (accounts.length !== 0){
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] === uuid))
} else {
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
if (selectedAccountId) {
var account = accounts.find((acct) => acct['id'] === selectedAccountId)
if (account){
global.setSelectedAccount(account)
return
}
}
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
}
}
@@ -157,7 +168,8 @@ global.setSelectedAccount = function (account) {
localStorage.setItem('selectedAccount', JSON.stringify(account))
localStorage.setItem('serverUrl', account['serverInfo']['url'])
localStorage.setItem('SpeckleSketchup.AuthToken', account['token'])
localStorage.setItem('uuid', account['userInfo']['id'])
localStorage.setItem('selectedAccountId', account['id'])
localStorage.setItem('frontend2', account['serverInfo']['frontend2'])
bus.$emit('selected-account-reloaded')
}
@@ -168,13 +180,14 @@ export default {
CreateStreamDialog: () => import('@/components/dialogs/CreateStreamDialog'),
SettingsDialog: () => import('@/components/dialogs/SettingsDialog'),
GlobalToast: () => import('@/components/GlobalToast'),
Mapper: () => import('@/components/Mapper')
Mapper: () => import('@/components/Mapper'),
LegacyDialog: () => import('@/components/dialogs/LegacyDialog')
},
props: {
size: {
type: Number,
default: 42
},
}
},
data() {
return {
@@ -183,7 +196,11 @@ export default {
createStreamByIdDialog: false,
createStreamByIdText: "",
preferences: {},
tab: "streams"
tab: "streams",
searchText: '',
streamsText: 'Streams',
streamText: 'Stream',
branchText: 'Branch'
}
},
computed: {
@@ -194,8 +211,19 @@ export default {
apollo: {
user: {
query: userQuery
},
serverInfo: {
query: serverInfoQuery
}
},
beforeMount() {
// Collect accounts from 'Accounts.db' by ruby sqlite3
sketchup.exec({name: "init_local_accounts", data: {}})
// Collect preferences to render UI according to it
sketchup.exec({name: "collect_preferences", data: {}})
// Collect versions to inform mixpanel
sketchup.exec({name: "collect_versions", data: {}})
},
mounted() {
bus.$on('selected-account-reloaded', async () => {
await onLogin(this.$apollo.provider.defaultClient)
@@ -209,30 +237,30 @@ export default {
})
bus.$on('update-preferences', async (preferences) => {
let prefs = JSON.parse(preferences)
this.preferences = prefs
this.preferences = JSON.parse(preferences)
this.searchText = this.preferences.user.fe2 ? 'Search projects' : 'Search streams'
this.streamsText = this.preferences.user.fe2 ? 'Projects' : 'Streams'
this.streamText = this.preferences.user.fe2 ? 'Project' : 'Stream'
this.branchText = this.preferences.user.fe2 ? 'Model' : 'Branch'
this.$vuetify.theme.dark = this.preferences.user.dark_theme
})
// Collect versions to inform mixpanel
sketchup.exec({name: "collect_versions", data: {}})
// Collect preferences to render UI according to it
sketchup.exec({name: "collect_preferences", data: {}})
// Collect accounts from 'Accounts.db' by ruby sqlite3
sketchup.exec({name: "init_local_accounts", data: {}})
},
methods: {
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
activeAccount(){
return this.accounts().find((account) => account['isDefault'])
activeAccount() {
return JSON.parse(localStorage.getItem('selectedAccount'))
},
switchAccount(account) {
this.$mixpanel.track('Connector Action', { name: 'Account Select' })
global.setSelectedAccount(account)
// Force pushes to reload page to create ApolloClient from scratch
// setTimeout(() => {
// // timeout to wait a bit for potential sketchup.exec in the mean time calls
// location.reload()
// }, 200);
},
requestRefresh() {
sketchup.exec({name: 'reload_accounts', data: {}})
+8 -3
View File
@@ -232,22 +232,27 @@ export default {
// }
},
clearMappingsFromTableSelection(){
sketchup.exec({ name: "clear_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "clear_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Clear' })
},
isolateMappedElementsOnSketchup(){
if (this.isIsolated){
this.isIsolated = false
sketchup.exec({ name: "show_all_entities", data: {} })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Un-Isolate' })
} else {
this.isIsolated = true
sketchup.exec({ name: "isolate_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Isolate' })
}
},
hideMappedElementsOnSketchup(){
sketchup.exec({ name: "hide_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "hide_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Hide' })
},
selectMappedElementsOnSketchup(){
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
sketchup.exec({ name: "select_mappings_from_table", data: this.elementSelection })
this.$mixpanel.track('MappingsAction', { name: 'Mappings Select Elements' })
},
// Update mapped elements table whenever mapped elements has changed.
getMappedElementsTableData(){
+395 -28
View File
@@ -58,6 +58,41 @@
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="source">
<v-expansion-panel-header class="flex">
<v-container class="ma-0 pa-0">
<v-icon>
mdi-source-branch
</v-icon>
{{ `Source` }}
<v-tooltip right>
<template #activator="{ on, attrs }">
<v-btn
class="ma-0 ml-1"
height="20px"
width="20px"
icon
x-small
:color="getSourceStateIconColor()"
v-bind="attrs"
v-on="on"
@click="refreshSourceBranch"
>
<v-icon>
{{ getSourceStateIcon() }}
</v-icon>
</v-btn>
</template>
<span>{{ getSourceStateToolTip() }}</span>
</v-tooltip>
</v-container>
</v-expansion-panel-header>
<v-expansion-panel-content>
<mapper-source :stream-text="streamText" :branch-text="branchText" :source-state="this.sourceState"/>
</v-expansion-panel-content>
</v-expansion-panel>
<v-expansion-panel key="mapping">
<v-expansion-panel-header>
<div>
@@ -124,16 +159,56 @@
class="pt-0"
label="Mapper Method"
:disabled="!entitySelected"
:items="enabledMethods"
:items="availableMethods"
density="compact"
clearable
@change="onSelectedMethodChange"
></v-autocomplete>
<v-autocomplete
v-if="familySelectionActive"
v-model="selectedFamily"
class="pt-0"
label="Family"
:disabled="!entitySelected"
:items="inputFamilies"
density="compact"
clearable
@change="onSelectedFamilyChange"
></v-autocomplete>
<v-autocomplete
v-if="typeSelectionActive"
v-model="selectedFamilyType"
class="pt-0"
label="Type"
:disabled="!entitySelected"
item-value="type"
item-text="type"
:items="familyTypes"
density="compact"
clearable
></v-autocomplete>
<v-autocomplete
v-if="levelSelectionActive"
v-model="selectedLevel"
class="pt-0"
label="Base Level"
:disabled="!entitySelected"
:items="levels"
item-value="name"
item-text="name"
density="compact"
clearable
></v-autocomplete>
<v-autocomplete
v-if="categorySelectionActive"
v-model="selectedCategory"
class="pt-0"
label="Category"
:items="availableCategories"
:items="selectedMethod === 'New Revit Family' ? familyCategories : categories"
item-value="value"
item-text="key"
:disabled="!entitySelected"
@@ -142,6 +217,7 @@
></v-autocomplete>
<v-text-field
v-if="nameSelectionActive"
v-model="name"
class="pt-0"
label="Name"
@@ -153,6 +229,7 @@
<v-row justify="center" align="center">
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!entitySelected"
@click="applyMapping"
>
@@ -163,6 +240,7 @@
</v-col>
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!entitySelected"
@click="clearMapping"
>
@@ -173,10 +251,7 @@
</v-col>
</v-row>
</v-container>
</v-expansion-panel-content>
</v-expansion-panel>
@@ -204,11 +279,22 @@
/*global sketchup*/
import {bus} from "@/main";
import {groupBy} from "@/utils/groupBy";
import MappingSource from "@/components/MapperSource.vue";
import {sourceMap} from "@vue/cli-service/lib/config/terserOptions";
global.mapperSourceUpdated = function (streamId, levels, types, familyInstances) {
console.log(`Mapper source updated for ${streamId}.`)
bus.$emit('mapper-source-updated', JSON.stringify(levels), JSON.stringify(types), JSON.stringify(familyInstances))
}
global.entitySelected = function (selectionParameters) {
bus.$emit('entities-selected', JSON.stringify(selectionParameters))
}
global.mapperInitialized = function (initParameters) {
bus.$emit('mapper-initialized', JSON.stringify(initParameters))
}
global.entitiesDeselected = function () {
bus.$emit('entities-deselected')
}
@@ -219,12 +305,35 @@ global.mappedEntitiesUpdated = function (mappedEntities) {
export default {
name: "Mapper",
props: {
streamText: {
type: String,
default: ''
},
branchText: {
type: String,
default: ''
}
},
components: {
MapperSource: () => import('@/components/MapperSource.vue'),
GlobalToast: () => import('@/components/GlobalToast'),
MappedElements: () => import('@/components/MappedElements.vue')
},
data() {
return {
nativeFaceMethods: ['Floor', 'Wall'],
nativeEdgeMethods: ['Column', 'Beam', 'Brace', 'Pipe', 'Duct'],
nativeDefaultFaceMethods: ['Default Floor', 'Default Wall'],
nativeDefaultEdgeMethods: ['Default Column', 'Default Beam', 'Default Brace', 'Default Pipe', 'Default Duct'],
familySelectionActive: false,
typeSelectionActive: false,
levelSelectionActive: false,
categorySelectionActive: false,
nameSelectionActive: false,
sourceState: 'Not Set',
// Expanded indexes for selection table (Types)
selectionExpandedIndexes: [],
// Expanded indexes for mapped element table (Categories)
@@ -239,15 +348,33 @@ export default {
entitySelected: false,
selectedEntityCount: 0,
selectedEntities: [],
familyTypes: [],
lastSelectedEntity: null,
selectedMethod: null,
selectedCategory: null,
selectedFamily: null,
selectedFamilyType: null,
selectedLevel: null,
name: "",
enabledMethods: [],
availableCategories: [],
availableMethods: [],
categories: [],
familyCategories: [],
inputCategories: [],
inputFamilies: [],
inputFamilyTypes: {},
// comes from source
types: {},
levels: [],
familyInstanceTypes: {}, // comes from source as filtered
mappedEntityCount: 0,
mappedEntities: [],
panel: [1],
panel: [2],
selectionHeaders: [
{ text: 'Type', sortable: false, value: 'entityType', width: '60%' },
{ text: 'Count', sortable: false, align: 'center', value: 'count', width: '20%' },
@@ -354,9 +481,164 @@ export default {
}
},
methods:{
getSourceStateIcon(){
switch (this.sourceState){
case "Not Set":
return `mdi-cloud-off-outline`;
case "Set":
return `mdi-checkbox-marked-circle-outline`;
case "Outdated":
return `mdi-update`;
default:
break;
}
},
getSourceStateToolTip(){
switch (this.sourceState){
case "Not Set":
return 'Source disconnected.';
case "Set":
return 'Source connected.';
case "Outdated":
return 'Source branch is not up-to-date!';
default:
break;
}
},
getSourceStateIconColor(){
switch (this.sourceState){
case "Not Set":
return `grey`;
case "Set":
return `green`;
case "Outdated":
return `red`;
default:
break;
}
},
onSelectedMethodChange(){
this.hideOptionalMappingInputs()
this.updateMappingInputs()
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.$mixpanel.track('MappingsAction', { name: 'Mappings Set', schema: this.selectedMethod })
},
onSelectedFamilyChange(){
this.getTypesFromSelectedFamily();
},
updateMappingInputs(){
if (this.selectedMethod === null){
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
this.categorySelectionActive = false
this.nameSelectionActive = false
return
}
const nativeDefaultMethods = this.nativeDefaultEdgeMethods.concat(this.nativeDefaultFaceMethods)
const nativeMethods = this.nativeEdgeMethods.concat(this.nativeFaceMethods)
if (this.selectedMethod === 'Direct Shape'){
this.categorySelectionActive = true
this.nameSelectionActive = true
}
else if (this.selectedMethod === 'New Revit Family'){
this.categorySelectionActive = true
}
else if (this.selectedMethod === 'Family Instance'){
this.typeSelectionActive = true
this.familySelectionActive = true
this.levelSelectionActive = true
}
else if (nativeDefaultMethods.includes(this.selectedMethod)){
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
this.categorySelectionActive = false
this.nameSelectionActive = false
}
else if (nativeMethods.includes(this.selectedMethod)){
this.typeSelectionActive = true
this.familySelectionActive = true
this.levelSelectionActive = true
}
},
getTypesFromSelectedFamily(){
// There is no sense to set selected family type if method is not selected
if (this.selectedMethod){
if (this.sourceState !== 'Not Set'){
this.familyTypes = this.inputFamilyTypes[this.selectedFamily]
this.selectedFamilyType = this.familyTypes[0].type
if (this.selectedFamilyType === null || this.selectedFamilyType === undefined){
this.selectedFamilyType = this.familyTypes[0].type
}
}
if (this.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.inputFamilies[0]
}
if (this.familyTypes === null ||this.familyTypes === undefined){
this.familyTypes = this.inputFamilyTypes[this.selectedFamily]
}
}
},
getFamiliesFromSelectedMethod(){
switch (this.selectedMethod) {
case 'Floor':
this.inputFamilies = Object.keys(this.types['Floors']);
this.inputFamilyTypes = this.types['Floors']
break;
case 'Wall':
this.inputFamilies = Object.keys(this.types['Walls']);
this.inputFamilyTypes = this.types['Walls']
break;
case 'Column':
this.inputFamilies = Object.keys(this.types['Columns']);
this.inputFamilyTypes = this.types['Columns']
break;
case 'Beam':
this.inputFamilies = Object.keys(this.types['Beams']);
this.inputFamilyTypes = this.types['Beams']
break;
case 'Pipe':
this.inputFamilies = Object.keys(this.types['Piping System']);
this.inputFamilyTypes = this.types['Piping System']
break;
case 'Duct':
this.inputFamilies = Object.keys(this.types['Duct System']);
this.inputFamilyTypes = this.types['Duct System']
break;
case 'Family Instance':
this.inputFamilies = Object.keys(this.familyInstanceTypes);
this.inputFamilyTypes = this.familyInstanceTypes
break;
default:
break;
}
if (this.selectedMethod){
if (this.selectedFamily === null || this.selectedFamily === undefined){
this.selectedFamily = this.inputFamilies[0]
}
if (this.selectedLevel === null || this.selectedLevel === undefined){
this.selectedLevel = this.levels[0].name
}
}
},
hideOptionalMappingInputs(){
this.categorySelectionActive = false
this.nameSelectionActive = false
this.typeSelectionActive = false
this.familySelectionActive = false
this.levelSelectionActive = false
},
refreshSourceBranch(){
if (this.sourceState === 'Outdated'){
bus.$emit('refresh-source-branch')
this.$mixpanel.track('MappingsAction', { name: 'Mappings Source Update' })
}
},
clearInputs(){
this.enabledMethods = []
this.availableCategories = []
this.availableMethods = []
this.selectedEntities = []
this.selectionTableData = []
this.selectedEntityCount = 0
@@ -365,6 +647,9 @@ export default {
this.selectedCategory = null
this.entityMapped = false
this.definitionMapped = false
this.selectedLevel = null
this.selectedFamily = null
this.selectedFamilyType = null
},
getSelectionTableData(){
let groupByClass = groupBy('entityType')
@@ -403,12 +688,12 @@ export default {
return summary
},
setInputValuesFromSelection(){
// Clear all inputs if entity is not selected.
if (!this.entitySelected){
this.name = ""
this.selectedMethod = null
this.selectedCategory = null
this.clearMappingInputs()
return
}
// Check if definition card is selected and set definition mappings.
if (this.definitionSelected) {
if (!this.definitionMapped){
if (this.selectedEntityCount > 1){
@@ -416,7 +701,7 @@ export default {
}else{
this.name = this.lastSelectedEntity['definition']['entityName']
}
this.selectedMethod = 'Direct Shape'
// this.selectedMethod = 'Direct Shape'
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
@@ -426,15 +711,26 @@ export default {
}
this.selectedMethod = this.lastSelectedEntity['definition']['schema']['method']
this.selectedCategory = this.lastSelectedEntity['definition']['schema']['category']
this.selectedFamily = this.lastSelectedEntity['definition']['schema']['family']
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedFamilyType = this.lastSelectedEntity['definition']['schema']['family_type']
this.selectedLevel = this.lastSelectedEntity['definition']['schema']['level']
this.updateMappingInputs()
}
} else {
}
// Otherwise set entity mappings.
else
{
if (!this.entityMapped){
if (this.selectedEntityCount > 1){
this.name = '<Mixed>'
}else{
this.name = this.lastSelectedEntity['entityName']
}
this.selectedMethod = 'Direct Shape'
this.updateMappingInputs()
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedCategory = 49
} else {
if (this.selectedEntityCount > 1){
@@ -443,7 +739,13 @@ export default {
this.name = this.lastSelectedEntity['schema']['name']
}
this.selectedMethod = this.lastSelectedEntity['schema']['method']
this.updateMappingInputs()
this.selectedFamily = this.lastSelectedEntity['schema']['family']
this.selectedCategory = this.lastSelectedEntity['schema']['category']
this.getFamiliesFromSelectedMethod()
this.getTypesFromSelectedFamily()
this.selectedFamilyType = this.lastSelectedEntity['schema']['family_type']
this.selectedLevel = this.lastSelectedEntity['schema']['level']
}
}
},
@@ -482,10 +784,29 @@ export default {
this.mappedElementsExpandedIndexes.push(slotData.item);
}
},
inputsReadyToApply(){
if (this.selectedMethod === null || this.selectedMethod === undefined){
return false;
}
const nativeMethods = this.nativeEdgeMethods.concat(this.nativeFaceMethods)
if (this.selectedMethod === 'Direct Shape'){
return this.selectedCategory !== null
}
else if (nativeMethods.includes(this.selectedMethod)){
return this.selectedFamily !== null &&
this.selectedFamilyType !== null &&
this.selectedLevel !== null
}
else {
return true;
}
},
applyMapping(){
if (this.selectedMethod === null || this.selectedCategory === null){
if (!this.inputsReadyToApply()){
this.$eventHub.$emit('error', {
text: 'Method and category are not set.\n'
text: 'Some inputs are not set to apply mapping.\n'
})
return
}
@@ -493,6 +814,9 @@ export default {
entitiesToMap: this.selectedEntities.map((entity) => entity['entityId']),
method: this.selectedMethod,
category: this.selectedCategory,
family: this.selectedFamily,
familyType: this.selectedFamilyType,
level: this.selectedLevel,
name: this.name,
isDefinition: this.definitionSelected
}
@@ -500,6 +824,8 @@ export default {
this.$eventHub.$emit('success', {
text: 'Mapping Applied.\n'
})
this.$mixpanel.track('MappingsAction', { name: 'Mappings Applied' })
},
clearMapping(){
const mapping = {
@@ -511,32 +837,73 @@ export default {
this.$eventHub.$emit('error', {
text: 'Mapping Cleared.\n'
})
}
},
mounted() {
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('entities-selected', async (selectionParameters) => {
const selectionPars = JSON.parse(selectionParameters)
this.enabledMethods = selectionPars.mappingMethods
this.availableCategories = selectionPars.categories
this.selectedEntities = selectionPars.selection
this.$mixpanel.track('MappingsAction', { name: 'Mappings Clear' })
},
clearMappingInputs(){
this.selectedMethod = null
this.selectedCategory = null
this.name = ""
this.selectedFamily = null
this.selectedFamilyType = null
this.selectedLevel = null
this.availableMethods = null
},
getDataFromSelection(selectionParameters){
this.availableMethods = selectionParameters.mappingMethods
this.selectedEntities = selectionParameters.selection
this.selectedLevel = selectionParameters.selectedLevelName
},
updateStatesFromSelectionData(){
this.lastSelectedEntity = this.selectedEntities[this.selectedEntities.length - 1]
this.entityMapped = this.isEntitiesMapped(this.selectedEntities)
this.definitionMapped = this.isEntityDefinitionsMapped(this.selectedEntities)
this.definitionSelected = !this.entityMapped && this.definitionMapped
this.selectedEntityCount = this.selectedEntities.length
this.entitySelected = this.selectedEntityCount !== 0
}
},
mounted() {
sketchup.exec({name: "mapper_initialized", data: {}})
sketchup.exec({name: "collect_mapped_entities", data: {}})
bus.$on('mapper-initialized', async (initParameters) => {
const initPars = JSON.parse(initParameters)
this.categories = initPars.categories
this.familyCategories = initPars.familyCategories
})
bus.$on('mapper-source-updated', async (levels, types, familyInstances) => {
// Parse data to json object
this.familyInstanceTypes = JSON.parse(familyInstances)
this.levels = JSON.parse(levels)
this.types = JSON.parse(types)
})
bus.$on('entities-selected', async (selectionParameters) => {
// Parse data to json object
const selectionPars = JSON.parse(selectionParameters)
// Reset mapping inputs with nulls and empties.
this.clearMappingInputs()
// Get data from selection into objects and arrays. These data basically constructs the dropdowns.
this.getDataFromSelection(selectionPars)
// Update inner state of the mapper component according to selection data.
this.updateStatesFromSelectionData()
// Get selection table data.
this.getSelectionTableData()
// Set mapping input values from selection data.
this.setInputValuesFromSelection()
})
bus.$on('entities-deselected', async () => {
this.entitySelected = false
this.clearInputs()
this.hideOptionalMappingInputs()
})
bus.$on('mapped-entities-updated', async (mappedEntities) => {
this.mappedEntityCount = mappedEntities.length
})
bus.$on('set-source-up-to-date', (isUpToDate) => {
this.sourceState = isUpToDate
})
}
}
</script>
+223
View File
@@ -0,0 +1,223 @@
<template>
<v-container class="pa-0">
<v-autocomplete
v-model="sourceStreamId"
:label="streamText"
:items="allStreamsList"
item-text="name"
item-value="id"
density="compact"
></v-autocomplete>
<v-autocomplete
v-model="sourceBranchId"
class="pt-0 mb-n5"
:label="branchText"
:items="allBranchesList"
:disabled="sourceStreamId === null"
item-text="name"
item-value="id"
density="compact"
></v-autocomplete>
<v-container class="pa-0 mt-2">
<v-row justify="center" align="center">
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
@click="applySource"
>
<v-icon dark left>
mdi-checkbox-marked-circle
</v-icon>Apply
</v-btn>
</v-col>
<v-col cols="auto" class="pa-1 pb-2">
<v-btn
small
:disabled="!sourceApplied"
@click="clearSource"
>
<v-icon dark left>
mdi-close-circle
</v-icon>Clear
</v-btn>
</v-col>
</v-row>
</v-container>
</v-container>
</template>
<script>
/*global sketchup*/
import gql from "graphql-tag";
import streamQuery from "@/graphql/stream.gql";
import {bus} from "@/main";
import ObjectLoader from "@speckle/objectloader";
const streamLimit = 20
export default {
name: "MappingSource",
props: {
streamSearchQuery: { type: String, default: null },
sourceState: { type: String, default: 'Not Set' },
streamText: {
type: String,
default: ''
},
branchText: {
type: String,
default: ''
}
},
data() {
return {
sourceApplied: false,
sourceStreamId: null,
sourceBranchId: null,
sourceStreamName: null,
sourceBranchName: null
}
},
apollo: {
streams: {
prefetch: true,
debounce: 300,
fetchPolicy: 'cache-and-network',
query: gql`
query Streams($query: String, $limit: Int, $cursor: String) {
streams(query: $query, limit: $limit, cursor: $cursor) {
totalCount
cursor
items {
id
name
}
}
}
`,
variables() {
return {
query: this.streamSearchQuery,
limit: streamLimit,
cursor: null
}
},
update(data) {
bus.$emit('streams-loaded')
this.showMoreEnabled = data.streams?.items.length < data.streams.totalCount
return data.streams
},
},
$subscribe: {
commitCreated: {
query: gql`
subscription ($streamId: String!) {
commitCreated(streamId: $streamId)
}
`,
variables() {
return {
streamId: this.sourceStreamId
}
},
result(data) {
if (data.data.commitCreated.sourceApplication.includes('Revit')){
if (data.data.commitCreated.branchName === this.selectedBranch.name){
this.afterCommitCreated()
this.$eventHub.$emit('notification', {
text: `A new commit was created on Revit!`,
})
this.$apollo.queries.stream.refetch()
}
}
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
stream: {
query: streamQuery,
prefetch: true,
variables() {
return {
id: this.sourceStreamId,
limit: 100
}
},
skip() {
// Return true to skip the initial query execution
return this.sourceStreamId === null;
},
}
},
computed: {
selectedBranch() {
if (!this.stream) return
return this.stream.branches.items.find((branch) => branch.id === this.sourceBranchId)
},
allStreamsList() {
if (this.$apollo.loading) return
return this.streams?.items
},
allBranchesList() {
if (this.$apollo.loading) return
return this.stream?.branches.items
},
},
mounted() {
bus.$on('refresh-source-branch', () => {
this.onSourceBranchChanged()
bus.$emit('set-source-up-to-date', 'Set')
})
},
methods: {
applySource(){
bus.$emit('set-source-up-to-date', 'Set')
this.onSourceBranchChanged()
this.$eventHub.$emit('success', {
text: 'Mapper source applied.\n'
})
this.$mixpanel.track('MappingsAction', { name: 'Mappings Source Apply' })
},
clearSource(){
sketchup.exec({name:"clear_mapper_source" , data: {}})
bus.$emit('set-source-up-to-date', 'Not Set')
this.sourceApplied = false
this.sourceBranchName = null
this.sourceStreamName = null
this.sourceBranchId = null
this.sourceStreamId = null
this.$eventHub.$emit('error', {
text: 'Mapper source cleared.\n'
})
},
afterCommitCreated(){
bus.$emit('set-source-up-to-date', 'Outdated')
},
async onSourceBranchChanged() {
const commitRefId = this.selectedBranch.commits.items[0]?.referencedObject
if (!commitRefId) { return }
const loader = new ObjectLoader({
serverUrl: localStorage.getItem('serverUrl'),
token: localStorage.getItem('SpeckleSketchup.AuthToken'),
streamId: this.sourceStreamId,
objectId: commitRefId
})
let rootObj = await loader.getAndConstructObject(this.updateLoadingStage)
sketchup.exec({name:"mapper_source_updated" , data: {
base: rootObj,
stream_id: this.sourceStreamId,
commit_id: commitRefId
}})
this.sourceApplied = true
}
}
}
</script>

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