Compare commits

..

100 Commits

Author SHA1 Message Date
Jedd Morgan 27a7d72de3 Merge pull request #1051 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev -> main
2025-08-26 17:20:06 +01:00
Jedd Morgan 678f113d05 Merge pull request #1050 from specklesystems/jrm/main-to-dev2
Update dev with main
2025-08-26 16:54:05 +01:00
Jedd Morgan 92da66bbbb Merge branch 'dev' into jrm/main-to-dev2 2025-08-26 16:53:25 +01:00
Oğuzhan Koral 79a5228899 Fix: invert boolean flag (#1049)
* Introduce global config

* invert boolean flag
2025-08-26 18:52:21 +03:00
Adam Hathcock 4d9411de42 fix(revit): Revit files persist model card data to a file like Tekla instead of into the file (#1045)
* Revit files persist model card data to a file like Tekla instead of into the file

* fmt

* fixes logger

* Update Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 16:31:48 +01:00
Oğuzhan Koral 3780747992 Introduce global config (#1041) 2025-08-26 09:54:50 +00:00
Mucahit Bilal GOKER 4514b1b831 Merge pull request #1039 from specklesystems/bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit
feat(revit): Add Area Scheme
2025-08-24 18:29:02 +03:00
Björn Steinhagen 2bbbbf6204 Merge branch 'dev' into bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit 2025-08-24 17:16:49 +02:00
Björn Steinhagen e1b5dea3f7 fix: csharpier 2025-08-24 17:15:29 +02:00
Mucahit Bilal GOKER 2d2c274030 Merge pull request #1040 from specklesystems/bilal/cnx-2121-duplicated-geometry-when-parts-visibility-is-on
fix(revit): Exclude host element from view filter when parts are enabled
2025-08-24 18:15:11 +03:00
Björn Steinhagen 81dd72a281 Merge branch 'dev' into bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit 2025-08-24 17:12:17 +02:00
Björn Steinhagen b82349478c Merge branch 'dev' into bilal/cnx-2121-duplicated-geometry-when-parts-visibility-is-on 2025-08-24 17:09:08 +02:00
bimgeek 7d0690f7a0 bjorn pasha asked for these changes 2025-08-24 18:08:55 +03:00
bimgeek 62a0cb895d pasha bjorns comments 2025-08-24 17:56:21 +03:00
Claire Kuang f28ce73d33 Merge pull request #1035 from specklesystems/claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision
fix(rhino/grasshopper): always use double precision meshes and current doc mesh settings
2025-08-22 16:56:26 +01:00
bimgeek 15425c5328 no need for 2 db queries 2025-08-22 15:12:33 +03:00
bimgeek 7c645e3c51 collector disposal 2025-08-22 15:05:35 +03:00
bimgeek 795d068175 exclude parts from view filter 2025-08-22 14:57:43 +03:00
Claire Kuang 90c2bd2873 Merge branch 'dev' into claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision 2025-08-22 12:45:36 +01:00
Claire Kuang bd7a3c7c43 Merge pull request #1037 from specklesystems/claire/revit-snapping
feat(revit): adds snapping for mesh vertices and nurbs curves
2025-08-22 12:36:10 +01:00
Claire Kuang ea976309bc Merge branch 'dev' into claire/revit-snapping 2025-08-22 12:31:33 +01:00
Claire Kuang 1b5787274a Merge pull request #1010 from specklesystems/claire/cnx-2167-material-quantity-extraction-for-revit-railings
feat(revit): adds material quantities for railings
2025-08-22 12:31:17 +01:00
Björn 7e595deabc Merge branch 'dev' into claire/revit-snapping 2025-08-22 11:42:22 +02:00
Claire Kuang 66091b2b73 Merge branch 'dev' into claire/cnx-2167-material-quantity-extraction-for-revit-railings 2025-08-22 10:37:30 +01:00
Claire Kuang 4f8d8d4f07 Merge pull request #1036 from specklesystems/bjorn/cnx-2212-grasshopper-deconstruct-node-should-encapsulate-all-input
fix(grasshopper): handle multiple objects with different fields in deconstruct node
2025-08-22 09:57:20 +01:00
bimgeek 4fba12f966 add area scheme switch statement 2025-08-21 19:47:52 +03:00
Claire Kuang 348975c33d Merge branch 'dev' into claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision 2025-08-21 10:33:54 +01:00
Björn cd6888868e fix: flickering, dynamic outputs and docstrings 2025-08-21 10:10:07 +02:00
Björn f2d4e64005 Merge remote-tracking branch 'origin/dev' into bjorn/cnx-2212-grasshopper-deconstruct-node-should-encapsulate-all-input 2025-08-21 08:54:14 +02:00
Björn Steinhagen a92b88f6d3 fix: replace list access with progressive field discovery in deconstruct component 2025-08-21 08:19:10 +02:00
dependabot[bot] abfdbdeffa chore(deps): bump actions/checkout from 4 to 5 (#1034)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 22:24:20 +01:00
Claire Kuang efe66e7e98 Merge branch 'dev' into claire/cnx-2167-material-quantity-extraction-for-revit-railings 2025-08-20 16:28:47 +01:00
Claire Kuang c3fa1bb0dc Update LocalToGlobalToDirectShapeConverter.cs 2025-08-20 15:32:12 +01:00
Claire Kuang e487981e5b adds snapping for mesh, curve, and points 2025-08-20 15:06:44 +01:00
Claire Kuang 9a6dda629b also fixes an issue with sending low res meshes.
uses current doc settings to convert display meshes for breps etc
2025-08-19 16:32:03 +01:00
Björn 46e7d6e432 chore: re-add comments 2025-08-19 17:04:12 +02:00
Björn b9f4845fa7 feat: handle multiple inputs 2025-08-19 16:49:20 +02:00
Björn 36863efc5a refactor: update SolveInstance to collect multiple input objects 2025-08-19 16:01:41 +02:00
Björn a0ce883a3f feat: DeconstructSpeckleParam input to accept multiple objects 2025-08-19 15:48:25 +02:00
Claire Kuang bc0fe17d08 Update MeshToSpeckleConverter.cs 2025-08-19 14:38:33 +01:00
Claire Kuang 2e52409db6 Update DisplayMeshExtractor.cs 2025-08-19 14:31:21 +01:00
Claire Kuang f434cde7b3 removes model far from origin logic from rhino 2025-08-19 14:23:12 +01:00
Claire Kuang 3e596cac29 Update MeshToSpeckleConverter.cs 2025-08-19 10:00:10 +01:00
Jedd Morgan 876d5c1bfe fix(rhino-importer): Do not save to objects sqlite cache (#1033)
* First pass

* ifc importer to not save objects to sqlite
2025-08-18 15:50:03 +01:00
Oğuzhan Koral 3424de9130 Merge pull request #1032 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev to main
2025-08-18 11:33:50 +01:00
Jedd Morgan 279e900105 feat(file_import)!: .NET job processor (#992)
* First Pass

* commit transaction

* wip1

* rhino round2

* wip

* net8

* Got the importer importing!

* Refactor to separate containers

* New queuing queries

* generate solutions

* Generate solutions

* fixed tests

* Rhino headless imports

* minor fixes

* logging

* fix activity factory

* sketchup configs

* Add more logging

* Format

* Clean up the diff a bit

* relock

* delete bad launchsettings
2025-08-18 10:29:15 +00:00
Claire Kuang ac7398be49 fix(grasshopper): fix casting issues for model objects (#1031)
* adds missing path and properties to model object casting

* slight optimization to not retrieve material twice when color is inherited from material

* enables casting of non-geometrybase geometry like points
2025-08-18 11:24:41 +01:00
Björn Steinhagen 0bfeef637b feat(rhino): add layer mapping for revit integration (#1027)
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* feat: Add layer mapping support and rename object mapping methods

* feat: object-layer mapping detection for UI updates

* feat: add layer hierarchy mapping resolution

* refactor: unused methods

* feat: layer dropdown

* feat(rhino): add effective object resolution for layer mappings

* fix: event handling

* feat: generic model, duhh

* refactor: move records to mapper namespace

* refactor: consolidate Rhino layer and object utilities into helpers

* refactor: move `GetEffectiveObjectsForLayerMapping` to `RevitMappingResolver`

* chore: update category list (#1028)

* fix:  `ModifyAttributes` for object mapping changes

* fix: lol no need for static

* refactor: DI for helper class

* refactor: hardcoded list in dui

* fix: updating mapped layers

* fix: handle object addition events to update mappings on copy

* feat: poc (#1030)

* fix: no static!
2025-08-15 15:16:38 +00:00
Jonathon Broughton 0b5984b410 feat(Navisworks): CNX-2238 – Add Revit interop-lite category mapping to Navisworks connector (BETA) (#1023) 2025-08-12 08:30:49 +01:00
Björn Steinhagen ad1b6fd74c feat(rhino): add vertex normals optimization setting (#1022)
* chore: create settings class

* chore: send settings

* chore: converter reacts to setting

* fix: knock-on effect

* fix: format

* fix: importer needs param

* feat: adds seperate setting for sendTextureCoordinates

* refactor: grouped setting
2025-08-08 14:13:32 +03:00
Jedd Morgan f1f17eea3d Merge pull request #1021 from specklesystems/jrm/dev-to-main
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Dev -> Main
2025-08-07 15:16:38 +01:00
Jedd Morgan 642607acad Merge branch 'main' into jrm/dev-to-main 2025-08-07 15:09:09 +01:00
Claire Kuang 7f3b23e71e Merge branch 'dev' into claire/cnx-2167-material-quantity-extraction-for-revit-railings 2025-08-07 09:00:26 +01:00
Jedd Morgan d2ed8d3ea9 PR to prepare for rhino importer pr (cleans the diff) (#1020)
* Some changes to clean the diff

* fix build
2025-08-06 17:05:05 +01:00
Björn Steinhagen 1d8f9dd97f feat(rhino): implement Revit category mapper for interop lite (#1018)
* feat: add `RevitBuiltInCategoryStore` for Interop Lite mapper (#1004)

* feat: revitmapper rhino connector binding (#1016)

* chore: `RhinoMapperBinding` class

* chore: `RhinoMapperBinding` structure

* chore: implement `GetAvailableCategories`

* chore: implement `AssignToCategory`

* refactor: common code to helper method

* chore: implement `ClearAllCategoryAssignments`

* chore: implement `GetCurrentMappings`

* chore: implement `GetObjectsByCategory`

* chore: implement event handling

* fix: compiler errors

* chore: service registration

* docs: cleanup

* fix: extend DirectShape category mapping to all geometry objects (#1017)

* fix: filter mapper events to only mapped objects

* refactor: simplify RhinoMapperBinding following existing patterns

* chore: remove unused method

* fix: add DocumentModelStore dependency for event handling

* refactor: mapper store

* fix: list sorted alphabetically

* fix: refresh mapper table on document switch

* chore: note

* docs: note
2025-08-06 14:58:36 +03:00
Björn Steinhagen a7c82c4958 fix(grasshopper): update workspace type to match SDK LimitedWorkspace changes (#1015)
* fix: update workspace type to match SDK `LimitedWorkspace` changes

* fix: types

* chore: SDK version bump

* chore: regenerate package lock files
2025-08-04 11:02:14 +02:00
Adam Hathcock 81555d1657 WorksetId can be null for Revit so account for it (#1011)
* WorksetId can be null for Revit so account for it

* use a primative value instead of the revit object
2025-08-01 07:56:59 +00:00
Claire Kuang 9b0a6c3202 Merge branch 'dev' into claire/cnx-2167-material-quantity-extraction-for-revit-railings 2025-07-31 17:08:45 +01:00
Claire Kuang 2aee54e8c7 Merge pull request #1013 from Guanyu1997/gwa
Gwa
2025-07-31 17:08:27 +01:00
Claire Kuang e3248efeb4 Merge branch 'dev' into gwa 2025-07-31 16:55:52 +01:00
Adam Hathcock 35bbf2d6c9 Rethrow raw operation exceptions that are probably CancelledTaskExceptions (#1014) 2025-07-31 15:55:32 +00:00
Claire Kuang 4129b1a579 fixes max width issues 2025-07-31 16:53:58 +01:00
Claire Kuang ef90a94c34 addresses pr comments 2025-07-31 16:37:27 +01:00
Claire Kuang 71df86750c fixes build errors 2025-07-31 16:28:43 +01:00
Guanyu1997 7f2649a5dd Merge branch 'dev' into gwa 2025-07-31 16:10:10 +02:00
Claire Kuang de662e4a2b adds material quantities for pipes 2025-07-30 21:00:17 +01:00
Claire Kuang 2cb7211734 Merge pull request #1009 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev to main
2025-07-30 12:42:01 +01:00
Claire Kuang 82c84bee97 feat(autocad): adds xdata to properties (#1006)
* adds xdata extractor

* changes xdata to list value
2025-07-30 12:20:57 +01:00
Claire Kuang 3e6ceb3546 fix(rhino): models with duplicate collection names can now be received (#1008)
* layer baker cache now only works with lowercase keys

* changes dict to use a stringcomparer
2025-07-30 11:18:47 +01:00
Jedd Morgan 2d13849b2c Chore(rhino): Quick easy nullability fixes (#1007)
* Quick easy nullability fixes

* Fix post merge changes
2025-07-30 10:42:48 +01:00
kekesidavid 952d95851a parameterextractor now extracts system type params (#1003)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-07-29 17:31:43 +00:00
Claire Kuang 84fc2801ef fix(grasshopper): adds casting of model objects in the case of non-baked objects (#1005)
* adds casting of model object in case of no baked geometry id

* adds block def casting for non-baked definitions also

* creates appid in case of empty for non-baked objects

* Update SpeckleGeometryWrapperGoo.ModelObjects.cs
2025-07-29 15:32:12 +01:00
Claire Kuang 07f272f453 updates load to assign dataobject geometries the color and mat of the dataobject (#993)
Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-07-24 16:51:38 +01:00
Claire Kuang 8085065027 fix(autocad): converts invalid property value types to string (#994)
* Update ExtensionDictionaryExtractor.cs

* updates value logic
2025-07-24 13:18:05 +00:00
Adam Hathcock 31e26ca9d0 GH perf: Reduce allocations of scoped items (#989)
* Reduce allocations of scoped items

* update SDK to 3.4.6

* add SpeckleSolveInstance

* fix SDK update

* Update SDK to 3.4.8

* Just setup a context if things are used without first setitng one up

* fixes material wrapper casting

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-07-24 11:05:25 +00:00
Adam Hathcock 088cbb3b97 Merge pull request #1001 from specklesystems/main
(hotfix from main) (no squash) Main to Dev
2025-07-24 10:42:09 +01:00
Oğuzhan Koral 57fd7de027 No more netlify URL (#1002) 2025-07-23 16:49:39 +00:00
Claire Kuang 85fc828036 Update DisplayMeshExtractor.cs (#1000) 2025-07-23 15:59:22 +00:00
Adam Hathcock c288ea0088 Remove the trace around a single convert as it is spammy (#996) 2025-07-23 15:45:45 +00:00
Jedd Morgan 81924e2027 feat(logging)!: Expose static session guid and align user ids with DUI (#997)
* Add static guid

* fixed compile

* format

* fix build!

* Fix tests

* Use strings

* Update to keep the service id
2025-07-23 16:34:49 +01:00
Jedd Morgan c9b637b92e Fix project building on linux (#999) 2025-07-23 14:24:18 +00:00
kekesidavid 4779d406b8 fix(rhino): replacing invalid chars in block definition names (#990)
* removing invalid chars from block definition names

* renamed function

* review comments

* cleanup

* review comments, RhinoUtils made static
2025-07-23 11:56:37 +02:00
Jonathon Broughton a945e35a2a fix(Navisworks): include GUIDs and other non-dictionary properties in flat hierarchy (#995)
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* Flattens pseudo class properties

Handles properties that are not dictionaries by adding them to a "pseudo class properties" category.
This commit flattens those properties into the main dictionary for easier access.

Also filters specific property names like "ClassName" and "DisplayName".

* Avoids processing empty property sets

Prevents processing of objects with no category dictionaries, improving performance and avoiding potential errors.

Changes `bannedNamesForProps` from `List` to `HashSet` for faster lookups.

* Uncomments continue statement.

Re-enables the `continue` statement within the hierarchical property handler.

This ensures that the loop proceeds to the next iteration when a specific condition is met, which prevents unintended behavior.
2025-07-23 08:41:50 +01:00
Guanyu1997 509d3275af text
correct ToHost and ToSpeckle method
2025-07-18 13:32:50 +02:00
Guanyu1997 c562190973 text
text conversion
2025-07-18 12:16:12 +02:00
Adam Hathcock e7ee172f90 Merge pull request #988 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Dev to Main for release (NO SQUASH)
2025-07-16 16:36:09 +01:00
Adam Hathcock d49b1eea33 Merge pull request #987 from specklesystems/main-dev
Main to dev
2025-07-16 16:22:47 +01:00
Adam Hathcock d8afd74171 Merge remote-tracking branch 'origin/dev' into main-dev
# Conflicts:
#	Connectors/Rhino/Speckle.Connectors.GrasshopperShared/Components/Objects/SpeckleBlockInstancePassthrough.cs
2025-07-16 16:16:28 +01:00
Oğuzhan Koral b88f50ced6 Feat(gh): Add optional version message for senders (#986)
* Add optional version message for senders

* Rework to not have optional parameters

---------

Co-authored-by: Adam Hathcock <adam@hathcock.uk>
2025-07-16 15:37:13 +01:00
Adam Hathcock e130045930 feat: Rhino importer with CLI parameters (#910)
* Make a Rhino 8 importer

* adjust things to not require SDK changes

* something like this

* rhino importer cli sketch

* fix deps and solutions

* things build

* move to files

* change signatures of things to not require sendinfo or accounts

* formatting

* Fix test

* Reuse some account

* Fix logging and possible error

* formatting

* add active doc disposal

* add global try/catch

* merge fix

* add rhino importer

* add SLN and use it

* have to put back the extension rename

* SDK update

* Try out loading the plugin manually

* don't need the mac SLN

* fix lock

* fix lock again?

* Use the location of the assembly, not current

* Fix lock file

* fix lock on windows

---------

Co-authored-by: Chuck Driesler <cdriesler.iv@gmail.com>
2025-07-15 22:06:56 +03:00
Adam Hathcock ae72cc3adb Update the SDK to 3.4.5 (#982) 2025-07-15 17:25:48 +03:00
Claire Kuang 816539ce18 chore(grasshopper): renames model link to speckle model (#985) 2025-07-15 11:30:26 +02:00
Claire Kuang c6cdb0d893 remove new appid on passthrough mutation (#984) 2025-07-15 08:54:57 +00:00
Claire Kuang dd026e24a3 feat(grasshopper): updates properties nodes to only work with properties inputs (#983)
* updates query properties and properties selector to accept properties as input

* Update AccountManagerComponent.cs
2025-07-15 09:18:44 +01:00
Adam Hathcock 76015ed30c Null check a nurbs trim to avoid exception (#965)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-07-14 09:48:34 +01:00
Claire Kuang b9fbf60df5 feat(grasshopper): add gh specific type support to props (#981)
* adds additional raw converters to grasshopper

* adds plane, vector, and interval to supported property value types

* Update Speckle.Converters.RhinoShared.projitems

* merge conflict resolutions
2025-07-11 16:03:41 +01:00
Claire Kuang 2f8477e072 feat(grasshopper): add support for dataobjects (#979)
* chore(grasshopper): rename speckle object to speckle geometry (#968)

* renames speckleobjectwrapper to specklegeometrywrapper

* renames files

* feat: add `SpeckleDataObjectWrapper`, `SpeckleDataObjectWrapperGoo`, `SpeckleDataObjectWrapperParam` (#969)

* feat(grasshopper): implement `DataObject` casting with property sync (#970)

* feat: `CastFrom` start with `SpeckleGeometryWrapper`

* feat: `Name` and `Properties` syncing on `DataObject` level

* feat: next cast phase

* fix: parameterless constructor

* fix: `SpeckleGeometryWrapperGoo` cast

* refactor: pr review comments

* feat: `Path` and `Parent`

* fix: deep copy on the geo

* feat(grasshopper): load data objects (#972)

* load now creates dataobjects

includes fixes to expand collection and query objects

* adds helper and fixes filter speckle objects node

* feat: add preview support for `DataObject` (#973)

* feat(grasshopper): add baking support for `SpeckleDataObjectWrapper` (#974)

* feat: add baking

* fix: pr comment

* feat(grasshopper): adds dataobject passthrough node (#975)

* adds passthrough node

also fixes deconstruct

* Update LocalToGlobalMapHandler.cs

* feat(grasshopper): adds dataobject icons (#977)

* adds icon pngs

* changes icons and icon order

* feat(grasshopper): add DataObject publishing support (#978)

* feat: publish `DataObject` first pass

* feat: `DataObject` consideration for `PropertyGroupPathsSelector`

* docs: remaining todo

* feat: add `DataObject` support to `GetObjectProperties` component

* refactor: pr review comments

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-07-11 09:28:46 +01:00
Jonathon Broughton 973a91ac5a fix(navisworks): CNX-2102 - Refines origin mode validation logic to include fallback default (#967)
* Refines origin mode handling logic

Improves validation of origin mode settings to ensure
valid values are processed correctly. If the origin
mode is null or invalid, a default is returned, reducing
errors and clarifying logic.

Cache eviction has been maintained for previous types
only when necessary, enhancing performance efficiency.

* Simplifies the retrieval of the origin mode setting by consolidating conditions into a single if statement.

* Improves cache eviction logic by removing redundant checks and ensuring defaults are applied correctly.
2025-07-08 17:42:17 +00:00
Claire Kuang 60a39b775c adds list properties (#971) 2025-07-08 10:33:04 +01:00
Jonathon Broughton f84b5e7c90 Fixes AnimationViewpointCut causing blocks in publishing (#966)
Simplifies the handling of saved viewpoints and group items.

Removes legacy defensive checks and clarifies conditions for adding viewpoints.

Ensures that only non-empty groups are processed, improving efficiency.

Relates to ongoing feature enhancements.

Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2025-07-03 17:49:17 +01:00
283 changed files with 8156 additions and 2495 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
file_version: ${{ steps.set-version.outputs.file_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -83,7 +83,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -293,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -60,7 +60,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
)]
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -95,28 +95,19 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using (var convertActivity = _activityFactory.Start("Converting object"))
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
sendInfo.ProjectId
);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(entity, applicationId, objectCollection, instanceProxies, projectId);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
if (results.All(x => x.Status == Status.ERROR))
@@ -3,6 +3,15 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{41BC679F-887F-44CF-971D-A5502EE87DB0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Local|AnyCPU' ">
<OutputPath>bin\Local\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"/>
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -70,7 +70,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
/// </remarks>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -89,8 +89,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using var _2 = _activityFactory.Start("Convert");
var result = ConvertCsiObject(csiObject, rootObjectCollection);
results.Add(result);
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -356,14 +356,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -236,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -265,9 +265,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -266,9 +266,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +360,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -73,6 +73,7 @@ public class NavisworksSendBinding : ISendBinding
new IncludeInternalPropertiesSetting(false),
new ConvertHiddenElementsSetting(false),
new PreserveModelHierarchySetting(false),
new RevitCategoryMappingSetting(false)
];
public async Task Send(string modelCardId) =>
@@ -93,7 +94,8 @@ public class NavisworksSendBinding : ISendBinding
visualRepresentationMode: _toSpeckleSettingsManagerNavisworks.GetVisualRepresentationMode(modelCard),
convertHiddenElements: _toSpeckleSettingsManagerNavisworks.GetConvertHiddenElements(modelCard),
includeInternalProperties: _toSpeckleSettingsManagerNavisworks.GetIncludeInternalProperties(modelCard),
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard)
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard),
mappingToRevitCategories: _toSpeckleSettingsManagerNavisworks.GetMappingToRevitCategories(modelCard)
)
);
@@ -151,22 +151,22 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
foreach (NAV.SavedItem item in ((NAV.GroupItem)parentItem).Children)
{
switch (item.IsGroup)
switch (item)
{
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
// case false when item is NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
// // This will need to be the documented behaviour.
// break;
case false:
collectedSets.Add((NAV.SavedViewpoint)item);
// case NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// Legacy defensive behaviour: skip viewpoints without visibility overrides.
// Essentially, send everything, or whatever the current view state for hidden elements is.
// break;
case NAV.SavedViewpointAnimationCut:
// Skip animation cuts.
break;
default: // handles item.IsGroup == true
if (((NAV.GroupItem)item).Children.Count > 0) // Don't add empty groups
{
CollectSavedViews(item, collectedSets);
}
case NAV.SavedViewpoint savedViewpoint:
collectedSets.Add(savedViewpoint);
break;
case NAV.GroupItem groupItem when groupItem.Children.Count > 0:
CollectSavedViews(groupItem, collectedSets);
break;
// No action for empty groups or unknown types.
}
}
}
@@ -32,18 +32,9 @@ public class NavisworksRootObjectBuilder(
internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current;
/// <summary>
/// Asynchronously builds a Speckle object hierarchy from Navisworks model items.
/// </summary>
/// <param name="navisworksModelItems">The list of Navisworks items to convert.</param>
/// <param name="sendInfo">Information about the send operation.</param>
/// <param name="onOperationProgressed">Progress reporting callback.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>A result containing the root collection and conversion results.</returns>
/// <exception cref="SpeckleException">Thrown when no objects can be converted.</exception>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -54,7 +45,7 @@ public class NavisworksRootObjectBuilder(
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, sendInfo, onOperationProgressed);
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
// 2. Initialize root collection
var rootCollection = InitializeRootCollection();
@@ -62,7 +53,7 @@ public class NavisworksRootObjectBuilder(
// 3. Convert all model items and store results
var (convertedElements, conversionResults) = await ConvertModelItemsAsync(
navisworksModelItems,
sendInfo,
projectId,
onOperationProgressed,
cancellationToken
);
@@ -80,7 +71,7 @@ public class NavisworksRootObjectBuilder(
private static void ValidateInputs(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed
)
{
@@ -94,9 +85,11 @@ public class NavisworksRootObjectBuilder(
throw new ArgumentNullException(nameof(navisworksModelItems));
}
if (onOperationProgressed == null || sendInfo == null)
if (onOperationProgressed == null || projectId == null)
{
throw new ArgumentNullException(onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(sendInfo));
throw new ArgumentNullException(
onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(projectId)
);
}
}
@@ -109,7 +102,7 @@ public class NavisworksRootObjectBuilder(
private Task<(Dictionary<string, Base?> converted, List<SendConversionResult> results)> ConvertModelItemsAsync(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -122,7 +115,7 @@ public class NavisworksRootObjectBuilder(
foreach (var item in navisworksModelItems)
{
cancellationToken.ThrowIfCancellationRequested();
var converted = ConvertNavisworksItem(item, convertedBases, sendInfo);
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
results.Add(converted);
processedCount++;
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
@@ -310,7 +303,7 @@ public class NavisworksRootObjectBuilder(
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases,
SendInfo sendInfo
string projectId
)
{
string applicationId = elementSelectionService.GetModelItemPath(navisworksItem);
@@ -318,7 +311,7 @@ public class NavisworksRootObjectBuilder(
try
{
Base converted = sendConversionCache.TryGetValue(applicationId, sendInfo.ProjectId, out ObjectReference? cached)
Base converted = sendConversionCache.TryGetValue(applicationId, projectId, out ObjectReference? cached)
? cached
: rootToSpeckleConverter.Convert(navisworksItem);
@@ -0,0 +1,12 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connector.Navisworks.Operations.Send.Settings;
public class RevitCategoryMappingSetting(bool value) : ICardSetting
{
public string? Id { get; set; } = "mappingToRevitCategories";
public string? Title { get; set; } = "Map to Revit Categories";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
public object? Value { get; set; } = value;
}
@@ -18,6 +18,7 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
private readonly Dictionary<string, bool?> _convertHiddenElementsCache = [];
private readonly Dictionary<string, bool?> _includeInternalPropertiesCache = [];
private readonly Dictionary<string, bool?> _preserveModelHierarchyCache = [];
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
{
@@ -63,23 +64,41 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
throw new ArgumentNullException(nameof(modelCard));
}
var originString = modelCard.Settings?.First(s => s.Id == "originMode").Value as string;
if (originString is not null && OriginModeSetting.OriginModeMap.TryGetValue(originString, out OriginMode origin))
var originString = modelCard.Settings?.FirstOrDefault(s => s.Id == "originMode")?.Value as string;
if (!OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
{
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out OriginMode previousType))
{
if (previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
return OriginMode.ModelOrigin; // Default to ModelOrigin if not specified or invalid
}
throw new ArgumentException($"Invalid origin mode value: {originString}");
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousType) && previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
}
public bool GetMappingToRevitCategories(SenderModelCard modelCard)
{
if (modelCard == null)
{
throw new ArgumentNullException(nameof(modelCard));
}
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public bool GetConvertHiddenElements(SenderModelCard modelCard)
@@ -25,10 +25,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenEleementsSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\OriginModeSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\PreserveModelHierarchySetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\RevitCategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManagerNavisworks.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\VisualRepresentationSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\NavisworksSelectionFilter.cs"/>
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -226,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -251,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -321,14 +321,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -3,48 +3,45 @@ using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.SQLite;
namespace Speckle.Connectors.Revit.HostApp;
// POC: should be interfaced out
internal sealed class RevitDocumentStore : DocumentModelStore
{
// POC: move to somewhere central?
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
private readonly ILogger<RevitDocumentStore> _logger;
private readonly IAppIdleManager _idleManager;
private readonly RevitContext _revitContext;
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
private readonly IdStorageSchema _idStorageSchema;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
public RevitDocumentStore(
ILogger<DocumentModelStore> logger,
IAppIdleManager idleManager,
RevitContext revitContext,
IJsonSerializer jsonSerializer,
DocumentModelStorageSchema documentModelStorageSchema,
IdStorageSchema idStorageSchema,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext,
IRevitTask revitTask
IRevitTask revitTask,
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
ILogger<RevitDocumentStore> logger
)
: base(logger, jsonSerializer)
{
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
_idleManager = idleManager;
_revitContext = revitContext;
_documentModelStorageSchema = documentModelStorageSchema;
_idStorageSchema = idStorageSchema;
_topLevelExceptionHandler = topLevelExceptionHandler;
_threadContext = threadContext;
_logger = logger;
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
@@ -101,80 +98,36 @@ internal sealed class RevitDocumentStore : DocumentModelStore
return;
}
_threadContext
.RunOnMain(() =>
{
//if not the same active document then don't save the current cards to a bad document!
if (!EnsureActiveDocumentIsSame(document))
{
return;
}
using Transaction t = new(document, "Speckle Write State");
t.Start();
using DataStorage ds = GetSettingsDataStorage(document) ?? DataStorage.Create(document);
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
string serializedModels = Serialize();
stateEntity.Set("contents", serializedModels);
using Entity idEntity = new(_idStorageSchema.GetSchema());
idEntity.Set("Id", s_revitDocumentStoreId);
ds.SetEntity(idEntity);
ds.SetEntity(stateEntity);
t.Commit();
})
.FireAndForget();
}
private bool EnsureActiveDocumentIsSame(Document document)
{
var localDoc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (localDoc == null)
try
{
return false;
var key = document.ProjectInformation.UniqueId.NotNull();
_jsonCacheManager.UpdateObject(key, modelCardState);
}
catch (Exception ex) when (!ex.IsFatal())
{
var key = document.ProjectInformation.UniqueId.NotNull();
_logger.LogError(ex, "Failed to save model card state for document {DocumentId}", key);
}
return localDoc.Equals(document);
}
protected override void LoadState()
{
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
// POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save)
if (document == null)
{
return;
}
var stateEntity = GetSpeckleEntity(document);
if (stateEntity == null || !stateEntity.IsValid())
{
ClearAndSave();
return;
}
string modelsString = stateEntity.Get<string>("contents");
LoadFromString(modelsString);
}
private DataStorage? GetSettingsDataStorage(Document doc)
{
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
if (!settingIdEntity.IsValid())
{
continue;
}
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
}
return null;
var key = document.ProjectInformation.UniqueId.NotNull();
var state = _jsonCacheManager.GetObject(key);
LoadFromString(state);
}
private Entity? GetSpeckleEntity(Document? doc)
@@ -2,7 +2,9 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converters.RevitShared.Extensions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -75,8 +77,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
//this used to throw an exception, but we don't want to fail loudly if the view is not found
return [];
}
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var elementsInView = viewCollector.ToElements();
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -125,4 +127,52 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
// Below function collects list of ids to exclude from final list.
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
{
var elementsToExclude = new HashSet<ElementId>();
foreach (var element in elements)
{
// check if element is a part
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
{
try
{
// get source element ids from the part
var sourceIds = part.GetSourceElementIds();
if (sourceIds != null)
{
foreach (var sourceId in sourceIds)
{
elementsToExclude.Add(sourceId.HostElementId);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
// silently continue processing other Parts if one fails
// this follows the pattern used elsewhere in the codebase
}
}
}
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
{
var idsToExclude = GetSourceElementIdsToExclude(allElements);
return allElements.Where(e => !idsToExclude.Contains(e.Id));
}
return allElements;
}
}
@@ -33,17 +33,17 @@ public class RevitRootObjectBuilder(
{
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<DocumentToConvert> documentElementContexts,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(documentElementContexts, sendInfo, onOperationProgressed, ct))
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
);
private RootObjectBuilderResult BuildSync(
IReadOnlyList<DocumentToConvert> documentElementContexts,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -183,10 +183,7 @@ public class RevitRootObjectBuilder(
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
if (
!hasTransform
&& sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value)
)
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
cacheHitCount++;
@@ -38,6 +38,10 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -61,6 +65,10 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -104,6 +112,11 @@ public class RevitThreadContext : ThreadContext
ex = e;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -37,9 +37,9 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)operations\receive\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\RevitHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)operations\receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\TransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\IRevitSendFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitCategoriesFilter.cs" />
@@ -61,4 +61,4 @@
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
</ItemGroup>
</Project>
</Project>
+1 -1
View File
@@ -5,7 +5,7 @@
</ItemGroup>
<Target AfterTargets="Build" Name="WarnIfPublicReleaseVersionInstalled" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Warning
Text="Conflicting Rhino plugin detected - Do you have a public release installed?"
Text="Conflicting Rhino plugin detected - Do you have a public release installed? at '@(PublicReleasePath)'"
Condition="Exists(@(PublicReleasePath))" />
</Target>
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.rhino7": {
@@ -382,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -403,14 +403,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.4, )",
"Speckle.Sdk": "[3.4.4, )",
"Speckle.Sdk.Dependencies": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.logging": {
@@ -337,13 +337,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.4, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -382,18 +381,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "zLYhOAPKdaFYN+YOjDDKyXK/9WMvv+EB2bmemdIf+xU8SzKeXOLMqf+Zo2pokkc1Wjc7ZmggphBbmSBkHmS9Dw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.4"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "tzQR3tcTFGzzu2R1EQbG2JM+Us57JGFm+tH9wScSrDQE1X5XRjJfaxf1st09D26Ale2B+pVst/AVxr9OlLs1Kw==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -403,14 +402,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.4"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.4, )",
"resolved": "3.4.4",
"contentHash": "c5fRdts5l/xS842CjhxOCOvrCq7tZ6eO3x2SB1GRECBzpQ9Y9I2Yn4FXOrgznFSb5HFur+ReJsZZH7Ml8pW/iQ=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -158,10 +158,19 @@ public class CreateCollection : VariableParameterComponentBase
{
foreach (var obj in objects)
{
// deep copy to avoid mutations
if (obj?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper objWrapper)
// handle data objects directly (deep copy to avoid mutations)
// NOTE: DataObject first, since a DataObject with one geo is castable to speckle geometry
if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
{
SpeckleObjectWrapper wrapper = objWrapper.DeepCopy();
var dataObjectWrapper = dataObjectWrapperGoo.Value.DeepCopy();
dataObjectWrapper.Path = childPath;
dataObjectWrapper.Parent = parentCollection;
parentCollection.Elements.Add(dataObjectWrapper);
}
// handle geometry objects (deep copy to avoid mutations)
else if (obj?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper objWrapper)
{
SpeckleGeometryWrapper wrapper = objWrapper.DeepCopy();
wrapper.Path = childPath;
wrapper.Parent = parentCollection;
parentCollection.Elements.Add(wrapper);
@@ -55,8 +55,8 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
// Separate objects and collections
// Note: SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper,
// so it will be included in objects
var objects = wrapper.Elements.OfType<SpeckleObjectWrapper>().ToList();
var collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
List<SpeckleWrapper> objects = wrapper.GetAtomicObjects().ToList();
List<SpeckleCollectionWrapper> collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
@@ -66,7 +66,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
Name = "_objects",
NickName = "_objs",
Description =
"Some collections may contain a mix of objects and other collections. Here we output the atomic objects from within this collection.",
"Some collections may contain a mix of objects and other collections. These are the objects directly contained in this collection.",
Access = GH_ParamAccess.list
};
@@ -113,10 +113,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
else
{
// Create appropriate Goo types for child objects
List<IGH_Goo> childObjectGoos = childWrapper
.Elements.OfType<SpeckleObjectWrapper>()
.Select(obj => obj.CreateGoo())
.ToList();
List<IGH_Goo> childObjectGoos = childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
outputValue = childObjectGoos;
}
@@ -8,8 +8,8 @@ public static class ComponentCategories
// categories
public const string OPERATIONS = " Models";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
public const string PARAMETERS = " Params";
public const string DEVELOPER = "Dev";
}
@@ -37,90 +37,190 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
protected override void SolveInstance(IGH_DataAccess da)
{
object data = new();
da.GetData(0, ref data);
List<OutputParamWrapper> outputParams = new();
switch (data)
// on first iteration, discover all fields from all objects to create stable output structure
if (da.Iteration == 0)
{
case SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null:
// get children elements from the wrapper to override the elements prop while parsing
List<IGH_Goo> children = collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList();
outputParams = ParseSpeckleWrapper(collectionGoo.Value, children);
break;
case SpeckleObjectWrapperGoo objectGoo when objectGoo.Value != null:
outputParams = ParseSpeckleWrapper(objectGoo.Value);
break;
case SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null:
outputParams = ParseSpeckleWrapper(blockInstanceGoo.Value);
break;
case SpeckleBlockDefinitionWrapperGoo blockDef:
outputParams = ParseSpeckleWrapper(blockDef.Value);
break;
case SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null:
outputParams = ParseSpeckleWrapper(materialGoo.Value);
break;
HashSet<string> allFields = DiscoverAllFieldsFromInput();
case SpecklePropertyGroupGoo propGoo:
Name = $"properties ({propGoo.Value.Count})";
outputParams = new();
foreach (var key in propGoo.Value.Keys)
if (allFields.Count > 0)
{
var requiredOutputs = CreateOutputParamsFromFieldNames(allFields);
if (OutputMismatch(requiredOutputs))
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value is SpecklePropertyGoo prop
? prop.Value
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
outputParams.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(requiredOutputs));
return;
}
break;
default:
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return;
}
}
// process current object normally
object data = new();
if (!da.GetData(0, ref data))
{
return;
}
var outputParams = DeconstructObject(data);
if (outputParams == null)
{
return;
}
// set component name based on the current object
NickName = Name;
if (da.Iteration == 0 && OutputMismatch(outputParams))
// set output data - fill missing fields with nulls for objects that don't have all fields
SetOutputData(da, outputParams);
}
/// <summary>
/// Discovers all unique field names from all input objects by looking at volatile data directly.
/// </summary>
private HashSet<string> DiscoverAllFieldsFromInput()
{
HashSet<string> allFields = new();
foreach (var item in Params.Input[0].VolatileData.AllData(true))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(outputParams);
}
);
}
else
{
for (int i = 0; i < outputParams.Count; i++)
var objectOutputs = DeconstructObject(item);
if (objectOutputs != null)
{
var outParam = Params.Output[i];
var outParamWrapper = outputParams[i];
switch (outParam.Access)
foreach (var output in objectOutputs)
{
case GH_ParamAccess.item:
da.SetData(i, outParamWrapper.Value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outParamWrapper.Value as IList);
break;
allFields.Add(output.Param.Name);
}
}
}
return allFields;
}
/// <summary>
/// Creates output parameter wrappers from a set of field names, all with item access.
/// </summary>
private List<OutputParamWrapper> CreateOutputParamsFromFieldNames(HashSet<string> fieldNames) =>
fieldNames
.OrderBy(name => name)
.Select(fieldName => CreateOutputParamByKeyValue(fieldName, null, GH_ParamAccess.item))
.ToList();
/// <summary>
/// Deconstructs a single object into its constituent fields/properties.
/// </summary>
private List<OutputParamWrapper>? DeconstructObject(object data) =>
data switch
{
// get children elements from wrapper to override elements prop while parsing
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
=> ParseSpeckleWrapper(
collectionGoo.Value,
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
),
// get geometries from wrapper to override displayValue prop while parsing
SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null
=> ParseSpeckleWrapper(
dataObjectGoo.Value,
null,
dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList()
),
SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null => ParseSpeckleWrapper(objectGoo.Value),
SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null
=> ParseSpeckleWrapper(blockInstanceGoo.Value),
SpeckleBlockDefinitionWrapperGoo blockDef when blockDef.Value != null => ParseSpeckleWrapper(blockDef.Value),
SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null => ParseSpeckleWrapper(materialGoo.Value),
SpecklePropertyGroupGoo propGoo when propGoo.Value != null => ParsePropertyGroup(propGoo),
_ => HandleUnsupportedType(data)
};
/// <summary>
/// Handles SpecklePropertyGroupGoo objects by extracting their key-value pairs.
/// </summary>
private List<OutputParamWrapper> ParsePropertyGroup(SpecklePropertyGroupGoo propGoo)
{
Name = $"properties ({propGoo.Value.Count})";
List<OutputParamWrapper> objectOutputs = new();
foreach (var key in propGoo.Value.Keys)
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
objectOutputs.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
}
return objectOutputs;
}
/// <summary>
/// Handles unsupported object types by logging an error and returning null.
/// </summary>
private List<OutputParamWrapper>? HandleUnsupportedType(object data)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return null;
}
/// <summary>
/// Sets output data for the current iteration, filling missing fields with null values.
/// Uses a lookup dictionary for efficient field matching.
/// </summary>
private void SetOutputData(IGH_DataAccess da, List<OutputParamWrapper> currentOutputs)
{
if (Params.Output.Count == 0)
{
return;
}
// create a lookup for current outputs by field name
var outputLookup = currentOutputs.ToDictionary(o => o.Param.Name, o => o.Value);
// set data for each output parameter
for (int i = 0; i < Params.Output.Count; i++)
{
var outputParam = Params.Output[i];
// set the value if it exists, otherwise set null
object? value = outputLookup.TryGetValue(outputParam.Name, out var fieldValue) ? fieldValue : null;
switch (outputParam.Access)
{
case GH_ParamAccess.item:
da.SetData(i, value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, value as IList ?? new List<object?>());
break;
}
}
}
private List<OutputParamWrapper> ParseSpeckleWrapper(SpeckleWrapper wrapper, List<IGH_Goo>? collElements = null)
private List<OutputParamWrapper> ParseSpeckleWrapper(
SpeckleWrapper wrapper,
List<IGH_Goo>? elements = null,
List<IGH_Goo>? displayValue = null
)
{
Name = string.IsNullOrEmpty(wrapper.Name) ? wrapper.Base.speckle_type : wrapper.Name;
return CreateOutputParamsFromBase(wrapper.Base, collElements);
return CreateOutputParamsFromBase(wrapper.Base, elements, displayValue);
}
private List<OutputParamWrapper> CreateOutputParamsFromBase(Base @base, List<IGH_Goo>? collElements = null)
private List<OutputParamWrapper> CreateOutputParamsFromBase(
Base @base,
List<IGH_Goo>? elements = null,
List<IGH_Goo>? displayValue = null
)
{
List<OutputParamWrapper> result = new();
if (@base == null)
@@ -128,118 +228,146 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return result;
}
// cycle through base props
// process each property of the Base object
foreach (var prop in @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic))
{
// Convert and add to corresponding output structure
var value = prop.Value;
switch (value)
// skip internal dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
case null:
result.Add(CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item));
break;
continue;
}
case IList list:
List<object> nativeObjects = new();
// override list value if base is a collection and this is the elements prop, since this is empty if coming from a collectionwrapper
if (@base is Collection && prop.Key == "elements" && collElements != null)
{
list = collElements;
}
foreach (var x in list)
{
switch (x)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base xBase:
nativeObjects.AddRange(ConvertOrCreateWrapper(xBase));
break;
default:
nativeObjects.Add(x);
break;
}
}
result.Add(CreateOutputParamByKeyValue(prop.Key, nativeObjects, GH_ParamAccess.list));
break;
case Dictionary<string, object?> dict: // this should be treated a properties dict
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
break;
case SpeckleWrapper wrapper:
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
break;
case Base baseValue:
result.Add(CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list));
break;
default:
// we don't want to output dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
continue;
}
result.Add(CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item));
break;
var outputParam = CreateOutputParamForProperty(prop, @base, elements, displayValue);
if (outputParam != null)
{
result.Add(outputParam);
}
}
return result;
}
private List<SpeckleObjectWrapperGoo> ConvertOrCreateWrapper(Base @base)
/// <summary>
/// Creates an output parameter for a single property, handling different value types appropriately.
/// </summary>
private OutputParamWrapper CreateOutputParamForProperty(
KeyValuePair<string, object?> prop,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
) =>
prop.Value switch
{
null => CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item),
IList list => CreateListOutputParam(prop.Key, list, @base, elements, displayValue),
Dictionary<string, object?> dict => CreateDictionaryOutputParam(prop.Key, dict),
SpeckleWrapper wrapper => CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item),
Base baseValue => CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list),
_ => CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item)
};
/// <summary>
/// Creates an output parameter for list properties, with special handling for collection elements and display values.
/// </summary>
private OutputParamWrapper CreateListOutputParam(
string key,
IList list,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
)
{
// override list value for special cases
IList actualList = key switch
{
"elements" when @base is Collection && elements != null => elements,
"displayValue" when @base is Speckle.Objects.Data.DataObject && displayValue != null => displayValue,
_ => list
};
List<object> nativeObjects = new();
foreach (var item in actualList)
{
switch (item)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base baseItem:
nativeObjects.AddRange(ConvertOrCreateWrapper(baseItem));
break;
default:
nativeObjects.Add(item);
break;
}
}
return CreateOutputParamByKeyValue(key, nativeObjects, GH_ParamAccess.list);
}
/// <summary>
/// Creates an output parameter for dictionary properties, converting them to SpecklePropertyGroupGoo.
/// </summary>
private OutputParamWrapper CreateDictionaryOutputParam(string key, Dictionary<string, object?> dict)
{
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
return CreateOutputParamByKeyValue(key, propertyGoo, GH_ParamAccess.item);
}
/// <summary>
/// Converts a Speckle Base object to host geometry or creates a wrapper if conversion fails.
/// Returns a list of SpeckleGeometryWrapperGoo objects.
/// </summary>
private List<SpeckleGeometryWrapperGoo> ConvertOrCreateWrapper(Base @base)
{
try
{
// convert the base and create a wrapper for each result
List<(GeometryBase, Base)> convertedBase = SpeckleConversionContext.ConvertToHost(@base);
List<SpeckleObjectWrapperGoo> convertedWrappers = new();
foreach ((GeometryBase g, Base b) in convertedBase)
{
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = b,
GeometryBase = g,
Name = b["name"] as string ?? "",
Color = null,
Material = null
};
convertedWrappers.Add(new(convertedWrapper));
}
return convertedWrappers;
// attempt conversion to host geometry
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
return convertedBase.Select(CreateGeometryWrapper).ToList();
}
catch (ConversionException)
{
// some classes, like RawEncoding, have no direct conversion or fallback value.
// when this is the case, wrap it to allow users to further expand the object.
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new() { new SpeckleObjectWrapperGoo(convertedWrapper) };
// fallback: create wrapper without conversion for objects that can't be converted
return new List<SpeckleGeometryWrapperGoo> { CreateFallbackWrapper(@base) };
}
}
/// <summary>
/// Creates a SpeckleGeometryWrapperGoo from a converted geometry and base object pair.
/// </summary>
private SpeckleGeometryWrapperGoo CreateGeometryWrapper((object geometry, Base @base) converted)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = converted.@base,
GeometryBase = converted.geometry as GeometryBase,
Name = converted.@base["name"] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
/// <summary>
/// Creates a fallback wrapper for Base objects that cannot be converted to host geometry.
/// </summary>
private SpeckleGeometryWrapperGoo CreateFallbackWrapper(Base @base)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
private OutputParamWrapper CreateOutputParamByKeyValue(string key, object? value, GH_ParamAccess access)
{
Param_GenericObject param =
@@ -272,19 +400,17 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Output;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// TODO: better, nicer handling of creation/removal
// remove all existing output parameters
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
// add new output parameters
foreach (var newParam in outputParams)
{
var param = new Param_GenericObject
@@ -297,11 +423,15 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
Params.RegisterOutputParam(param);
}
// notify Grasshopper of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
/// <summary>
/// Determines if the current output parameter structure differs from the required structure.
/// </summary>
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
{
if (Params.Output.Count != outputParams.Count)
@@ -309,10 +439,10 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return true;
}
var count = 0;
foreach (var newParam in outputParams)
for (int i = 0; i < outputParams.Count; i++)
{
var oldParam = Params.Output[count];
var newParam = outputParams[i];
var oldParam = Params.Output[i];
if (
oldParam.NickName != newParam.Param.NickName
|| oldParam.Name != newParam.Param.Name
@@ -321,7 +451,6 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
{
return true;
}
count++;
}
return false;
@@ -17,7 +17,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_create;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public CreateSpeckleProperties()
: base(
@@ -73,7 +73,10 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
continue;
}
var propertyValue = ExtractPropertyValue(da, i, paramName);
ISpecklePropertyGoo? propertyValue =
Params.Input[i].Access == GH_ParamAccess.item
? ExtractPropertyValue(da, i, paramName)
: ExtractPropertyListValue(da, i, paramName);
if (propertyValue != null)
{
@@ -114,6 +117,26 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
return propertyGoo;
}
private ISpecklePropertyGoo? ExtractPropertyListValue(IGH_DataAccess da, int index, string paramName)
{
List<object?> value = new();
da.GetDataList(index, value);
var propertyGoo = new SpecklePropertyGoo();
if (!propertyGoo.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter '{paramName}' contains invalid data type. Only strings, numbers, and booleans are supported."
);
return null;
}
return propertyGoo;
}
protected override void AppendComponentSpecificMenuItems(ToolStripDropDown menu)
{
Menu_AppendSeparator(menu);
@@ -85,7 +85,7 @@ public class FilterSpeckleObjects : GH_Component
return;
}
List<SpeckleObjectWrapper?> objects = inputObjects
List<SpeckleWrapper?> objects = inputObjects
.Select(o => o.ToSpeckleObjectWrapper())
.Where(o => o is not null)
.ToList();
@@ -108,11 +108,11 @@ public class FilterSpeckleObjects : GH_Component
string speckleId = "";
dataAccess.GetData(5, ref speckleId);
List<SpeckleObjectWrapper> matchedObjects = new();
List<SpeckleObjectWrapper> removedObjects = new();
List<SpeckleWrapper> matchedObjects = new();
List<SpeckleWrapper> removedObjects = new();
for (int i = 0; i < objects.Count; i++)
{
SpeckleObjectWrapper wrapper = objects[i]!;
SpeckleWrapper wrapper = objects[i]!;
// filter by name
if (!MatchesSearchPattern(name, wrapper.Name))
@@ -129,12 +129,21 @@ public class FilterSpeckleObjects : GH_Component
}
else
{
foreach (string key in wrapper.Properties.Value.Keys)
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
? dataObjPropWrapper.Properties
: wrapper is SpeckleGeometryWrapper geoPropWrapper
? geoPropWrapper.Properties
: null;
if (properties is not null)
{
if (MatchesSearchPattern(property, key))
foreach (string key in properties.Value.Keys)
{
foundProperty = true;
break;
if (MatchesSearchPattern(property, key))
{
foundProperty = true;
break;
}
}
}
}
@@ -146,10 +155,13 @@ public class FilterSpeckleObjects : GH_Component
}
// filter by material name
if (!MatchesSearchPattern(material, wrapper.Material?.Name ?? ""))
if (wrapper is SpeckleGeometryWrapper geoWrapper)
{
removedObjects.Add(wrapper);
continue;
if (!MatchesSearchPattern(material, geoWrapper.Material?.Name ?? ""))
{
removedObjects.Add(wrapper);
continue;
}
}
// filter by application id
@@ -1,179 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class GetObjectProperties : GH_Component, IGH_VariableParameterComponent
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public GetObjectProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the properties inside Speckle Objects at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"Speckle Objects to retrieve properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
List<string> paths = new();
da.GetDataList(1, paths);
if (paths.Count == 0)
{
return;
}
if (OutputMismatch(paths))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(paths);
}
);
}
else
{
SpeckleObjectWrapperGoo objectWrapperGoo = new();
da.GetData(0, ref objectWrapperGoo);
// flatten object properties, if any
SpecklePropertyGroupGoo properties = objectWrapperGoo.Value.Properties;
if (properties.Value.Count == 0)
{
return;
}
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
for (int i = 0; i < paths.Count; i++)
{
var name = paths[i];
if (FindProperty(flattenedProps, name) is SpecklePropertyGoo prop)
{
da.SetData(i, prop.Value);
}
else
{
da.SetData(i, null);
}
}
}
}
// attempts to find a property by concatenated key, or returns null if not
private SpecklePropertyGoo? FindProperty(Dictionary<string, SpecklePropertyGoo> props, string unifiedPath)
{
if (!props.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
{
return null;
}
return currentGoo;
}
private bool OutputMismatch(List<string> outputParams)
{
if (Params.Output.Count != outputParams.Count)
{
return true;
}
var count = 0;
foreach (var newParam in outputParams)
{
var oldParam = Params.Output[count];
if (oldParam.NickName != newParam || oldParam.Name != newParam)
{
return true;
}
count++;
}
return false;
}
private void CreateOutputs(List<string> outputParams)
{
// Ensure we have the required count of output parameters
while (Params.Output.Count != outputParams.Count)
{
if (Params.Output.Count > outputParams.Count) // if too many, unregister
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
if (Params.Output.Count < outputParams.Count) // if too little, add some
{
var param = new Param_GenericObject
{
Name = "newParam",
NickName = "newParam",
MutableNickName = false,
Access = GH_ParamAccess.item
};
Params.RegisterOutputParam(param);
}
}
// now unify names and nicknames
int index = 0;
foreach (var newParam in outputParams)
{
Params.Output[index].NickName = newParam;
Params.Output[index].Name = newParam;
index++;
}
// now we can update the output params
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new Param_GenericObject
{
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
MutableNickName = true,
Optional = true
};
myParam.NickName = myParam.Name;
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
public void VariableParameterMaintenance() { }
}
@@ -4,9 +4,6 @@ using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
#if RHINO8_OR_GREATER
using Grasshopper.Rhinoceros.Model;
#endif
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
@@ -23,35 +20,28 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
protected override Bitmap Icon => Resources.speckle_inputs_property;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
protected override void LoadVolatileData()
{
var objectPropertyGroups = VolatileData
var propertyGroups = VolatileData
.AllData(true)
.OfType<SpeckleObjectWrapperGoo>()
.Select(goo => goo.Value.Properties)
.Where(goo => goo is SpecklePropertyGroupGoo)
.Select(goo =>
goo switch
{
SpecklePropertyGroupGoo geometryGoo => geometryGoo,
_ => throw new InvalidOperationException("Unexpected goo type")
}
)
.ToList();
#if RHINO8_OR_GREATER
// support model objects direct piping also
if (objectPropertyGroups.Count != VolatileData.DataCount)
{
var modelObjects = VolatileData
.AllData(true)
.OfType<ModelObject>()
.Select(mo => new SpeckleObjectWrapperGoo(mo).Value.Properties)
.ToList();
objectPropertyGroups.AddRange(modelObjects);
}
#endif
if (objectPropertyGroups.Count == 0)
if (propertyGroups.Count == 0)
{
return;
}
var paths = GetPropertyPaths(objectPropertyGroups);
var paths = GetPropertyPaths(propertyGroups);
m_data.Clear();
m_data.AppendRange(paths.Select(s => new GH_String(s)));
}
@@ -0,0 +1,99 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class QueryProperties : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public QueryProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the Properties at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Speckle Properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Values", "V", "The values of the specified keys", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a Speckle Properties item");
return;
}
List<string> keys = new();
if (!da.GetDataList(1, keys))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a key");
return;
}
if (properties == null || properties.Value.Count == 0 || keys.Count == 0)
{
return;
}
List<object?> values = new();
foreach (string key in keys)
{
ISpecklePropertyGoo? value = GetValueByPath(properties, key);
values.Add(value);
}
da.SetDataList(0, values);
}
public static ISpecklePropertyGoo? GetValueByPath(SpecklePropertyGroupGoo data, string path)
{
string[] keys = path.Split('.');
ISpecklePropertyGoo? current = data;
foreach (var key in keys)
{
if (current is SpecklePropertyGroupGoo dict)
{
if (dict.Value.TryGetValue(key, out ISpecklePropertyGoo? next))
{
current = next;
}
else
{
return null;
}
}
else
{
return null; // Current is not a dictionary, path is invalid
}
}
return current;
}
}
@@ -91,7 +91,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
private List<int>? _outputFilterIndices;
// Caches the list of all objects by geometrybase type
private readonly Dictionary<ObjectType, List<SpeckleObjectWrapper>> _filterDict = new();
private readonly Dictionary<ObjectType, List<SpeckleGeometryWrapper>> _filterDict = new();
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
@@ -109,7 +109,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
// filter by collection path
// Note: the collection paths selector will omit the target collection from the path of nested collections.
// the discard ("_objects") will be used to indicate objects found directly in the target collection.
List<SpeckleObjectWrapper> filteredObjects;
List<SpeckleWrapper> filteredObjects;
SpeckleCollectionWrapper? targetCollectionWrapper = null;
if (!string.IsNullOrEmpty(path))
{
@@ -120,23 +120,25 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
return;
}
filteredObjects = targetCollectionWrapper.Elements.OfType<SpeckleObjectWrapper>().ToList();
filteredObjects = targetCollectionWrapper.GetAtomicObjects(true).ToList();
}
else
{
filteredObjects = GetAllObjectsFromCollection(collectionWrapperGoo.Value).ToList();
filteredObjects = collectionWrapperGoo.Value.GetAtomicObjects(true).ToList();
}
// sort objects by filters
// sort geometry objects by filters
var geometryObjects = filteredObjects.OfType<SpeckleGeometryWrapper>().ToList();
if (_filterDict.Count == 0)
{
SortObjectsByGeometryBaseType(filteredObjects);
SortObjectsByGeometryBaseType(geometryObjects);
}
// Set output objects
for (int i = 0; i < Params.Output.Count; i++)
{
List<SpeckleObjectWrapper> outputValues = i == 0 ? filteredObjects : _filterDict[Filters[i - 1]];
List<SpeckleWrapper> outputValues =
i == 0 ? filteredObjects : _filterDict[Filters[i - 1]].Select(o => (SpeckleWrapper)o).ToList();
List<IGH_Goo> outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
{
@@ -151,7 +153,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
}
// Sort the input objects by the FilterType enums, based on the type of their geometryBase
private void SortObjectsByGeometryBaseType(List<SpeckleObjectWrapper> objs)
private void SortObjectsByGeometryBaseType(List<SpeckleGeometryWrapper> objs)
{
if (_filterDict.Count > 0)
{
@@ -168,7 +170,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
{
if (
wrapper.GeometryBase?.ObjectType is ObjectType objType
&& _filterDict.TryGetValue(objType, out List<SpeckleObjectWrapper>? value)
&& _filterDict.TryGetValue(objType, out List<SpeckleGeometryWrapper>? value)
)
{
value.Add(wrapper);
@@ -176,25 +178,6 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
}
}
private IEnumerable<SpeckleObjectWrapper> GetAllObjectsFromCollection(SpeckleCollectionWrapper collectionWrapper)
{
foreach (ISpeckleCollectionObject element in collectionWrapper.Elements)
{
switch (element)
{
case SpeckleCollectionWrapper childCollectionWrapper:
foreach (var item in GetAllObjectsFromCollection(childCollectionWrapper))
{
yield return item;
}
break;
case SpeckleObjectWrapper objectWrapper:
yield return objectWrapper;
break;
}
}
}
private SpeckleCollectionWrapper? FindCollectionAtPath(SpeckleCollectionWrapper root, string unifiedPath)
{
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
@@ -8,20 +8,18 @@ using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
public class SpeckleBlockDefinitionPassthrough : GH_Component
public class SpeckleBlockDefinitionPassthrough()
: SpeckleSolveInstance(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public SpeckleBlockDefinitionPassthrough()
: base(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_def;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -35,9 +33,9 @@ public class SpeckleBlockDefinitionPassthrough : GH_Component
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"Objects",
"O",
"Objects to include in the Block Definition. Speckle objects and instances or Model objects and instances are accepted.",
"Geometry",
"G",
"Geometry to include in the Block Definition. Speckle geometry and instances or Model objects and instances are accepted.",
GH_ParamAccess.list
);
Params.Input[1].Optional = true;
@@ -56,7 +54,7 @@ public class SpeckleBlockDefinitionPassthrough : GH_Component
GH_ParamAccess.item
);
pManager.AddGenericParameter("Objects", "O", "Objects contained in the Block Definition", GH_ParamAccess.list);
pManager.AddGenericParameter("Geometry", "G", "Geometry contained in the Block Definition", GH_ParamAccess.list);
pManager.AddTextParameter("Name", "N", "Name of the Block Definition", GH_ParamAccess.item);
}
@@ -71,7 +69,7 @@ public class SpeckleBlockDefinitionPassthrough : GH_Component
if (inputDefinition == null && inputObjects.Count == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Objects.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Geometry.");
return;
}
@@ -91,10 +89,10 @@ public class SpeckleBlockDefinitionPassthrough : GH_Component
// process geometry
if (inputObjects.Count > 0)
{
List<SpeckleObjectWrapper> processedObjects = new();
List<SpeckleGeometryWrapper> processedObjects = new();
foreach (IGH_Goo goo in inputObjects)
{
if (goo.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
if (goo.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
{
processedObjects.Add(gooWrapper);
}
@@ -8,20 +8,18 @@ using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
public class SpeckleBlockInstancePassthrough : GH_Component
public class SpeckleBlockInstancePassthrough()
: SpeckleSolveInstance(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public SpeckleBlockInstancePassthrough()
: base(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -0,0 +1,172 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("5CE8AA40-7706-4893-853D-4C77604548FA")]
public class SpeckleDataObjectPassthrough()
: SpeckleSolveInstance(
"Speckle Data Object",
"SDO",
"Create or modify a Speckle Data Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_dataobject;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Input Speckle DataObject. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
int geoIndex = pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object. Speckle Geometry and Grasshopper geometry are accepted.",
GH_ParamAccess.list
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object.",
GH_ParamAccess.list
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
// process the object
// deep copy so we don't mutate the object
SpeckleDataObjectWrapperGoo inputObject = new();
SpeckleDataObjectWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
result = inputObject.Value.DeepCopy();
}
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
if (!da.GetDataList(1, inputGeometry) && result == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle DataObject or Geometries.");
return;
}
foreach (var inputGeo in inputGeometry)
{
if (inputGeo.Value is SpeckleBlockInstanceWrapper)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"DataObjects cannot contain Block Instances.");
return;
}
}
string? inputName = null;
da.GetData(2, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(3, ref inputProperties);
// process geometry
if (result == null)
{
result = new SpeckleDataObjectWrapperGoo().Value;
}
if (inputGeometry.Count > 0)
{
result.Geometries.Clear();
foreach (var inputGeo in inputGeometry)
{
// deep copy so we don't mutate the input geo which may be speckle geometry
SpeckleGeometryWrapper mutatingGeo = inputGeo.Value.DeepCopy();
// assign fields before adding, otherwise they will be out of sync with wrapper
mutatingGeo.Base[Constants.NAME_PROP] = result.Name;
mutatingGeo.Properties = result.Properties;
mutatingGeo.Parent = result.Parent;
mutatingGeo.Path = result.Path;
result.Geometries.Add(mutatingGeo);
}
}
// process name
if (inputName != null)
{
result.Name = inputName;
}
// process properties
if (inputProperties != null)
{
result.Properties = inputProperties;
}
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.CreateGoo());
da.SetDataList(1, result.Geometries);
da.SetData(2, result.Name);
da.SetData(3, result.Properties);
da.SetData(4, path);
}
}
@@ -4,31 +4,30 @@ using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class SpeckleObjectPassthrough : GH_Component
public class SpeckleGeometryPassthrough()
: SpeckleSolveInstance(
"Speckle Geometry",
"SG",
"Create or modify a Speckle Geometry",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public SpeckleObjectPassthrough()
: base(
"Speckle Object",
"SO",
"Create or modify a Speckle Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_object;
public override GH_Exposure Exposure => GH_Exposure.primary;
protected override Bitmap Icon => Resources.speckle_objects_geometry;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddGenericParameter(
"Object",
"O",
"Input Object. Speckle Objects and Model Objects are accepted.",
"Speckle Geometry",
"SG",
"Input Speckle Geometry. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
@@ -36,31 +35,36 @@ public class SpeckleObjectPassthrough : GH_Component
int geoIndex = pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Object.",
"Geometry of the Speckle Geometry.",
GH_ParamAccess.item
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Object. Speckle Properties and User Content are accepted.",
"The properties of the Speckle Geometry. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
int colorIndex = pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
int colorIndex = pManager.AddColourParameter(
"Color",
"c",
"The color of the Speckle Geometry",
GH_ParamAccess.item
);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Object. Display Materials, Model Materials, and Speckle Materials are accepted.",
"The material of the Speckle Geometry. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
@@ -78,39 +82,34 @@ public class SpeckleObjectPassthrough : GH_Component
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Object", "O", "Speckle Object", GH_ParamAccess.item);
pManager.AddGenericParameter("Speckle Geometry", "SG", "Speckle Geometry", GH_ParamAccess.item);
pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
pManager.AddGeometryParameter("Geometry", "G", "Geometry of the Speckle Geometry.", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Object",
"The properties of the Speckle Geometry",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Speckle Object.",
"The material of the Speckle Geometry.",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Object, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
@@ -120,10 +119,10 @@ public class SpeckleObjectPassthrough : GH_Component
// process the object
// deep copy so we don't mutate the object
IGH_Goo? inputObject = null;
SpeckleObjectWrapper? result = null;
SpeckleGeometryWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
if (inputObject?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
if (inputObject?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
{
result = gooWrapper.DeepCopy();
}
@@ -139,7 +138,7 @@ public class SpeckleObjectPassthrough : GH_Component
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Object or Geometry.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
return;
}
@@ -159,9 +158,9 @@ public class SpeckleObjectPassthrough : GH_Component
// deep copy so we don't mutate the input geo which may be speckle objects
if (inputGeometry != null)
{
if (inputGeometry.ToSpeckleObjectWrapper() is SpeckleObjectWrapper geoWrapper)
if (inputGeometry.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper geoWrapper)
{
SpeckleObjectWrapper mutatingGeo = geoWrapper.DeepCopy();
SpeckleGeometryWrapper mutatingGeo = geoWrapper.DeepCopy();
if (result is null)
{
result = mutatingGeo;
@@ -190,44 +189,43 @@ public class SpeckleObjectPassthrough : GH_Component
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"{inputGeometry.TypeName} is not a valid type for Speckle Objects."
$"{inputGeometry.TypeName} is not a valid type for Speckle Geometry."
);
return;
}
}
result.NotNull();
// process name
if (inputName != null)
{
result!.Name = inputName;
result.Name = inputName;
}
// process properties
if (inputProperties != null)
{
result!.Properties = inputProperties;
result.Properties = inputProperties;
}
// process color (no mutation)
if (inputColor != null)
{
result!.Color = inputColor;
result.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
result!.Material = inputMaterial.Value;
result.Material = inputMaterial.Value;
}
// no need to process application Id.
// New definitions should have a new appID generated in the new() constructor, and we want to preserve old appID otherwise for changetracking.
// get the path
string path =
result!.Path.Count > 1
? string.Join(Constants.LAYER_PATH_DELIMITER, result!.Path)
: result!.Path.FirstOrDefault();
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.CreateGoo());
@@ -240,7 +238,7 @@ public class SpeckleObjectPassthrough : GH_Component
}
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
private void MatchNonGeometryProps(SpeckleObjectWrapper wrapper, SpeckleObjectWrapper wrapperToMatch)
private void MatchNonGeometryProps(SpeckleGeometryWrapper wrapper, SpeckleGeometryWrapper wrapperToMatch)
{
wrapper.Name = wrapperToMatch.Name;
wrapper.ApplicationId = wrapperToMatch.ApplicationId;
@@ -10,11 +10,23 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// CreateSpeckleProperties passthrough component by key value pairs
/// </summary>
[Guid("FED2298C-0D2B-4868-94B5-B8D17F9385A5")]
public class SpecklePropertiesPassthrough : GH_Component
public class SpecklePropertiesPassthrough : SpeckleSolveInstance
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_properties;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
private enum PropertyMode
{
@@ -38,18 +50,6 @@ public class SpecklePropertiesPassthrough : GH_Component
}
}
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Input properties", GH_ParamAccess.item);
@@ -95,13 +95,19 @@ public class SpecklePropertiesPassthrough : GH_Component
return;
}
// validate that keys and values are of same length
if (inputKeys.Count != inputValues.Count)
// validate that keys and values are of valid length
if ((Mode == PropertyMode.Merge || Mode == PropertyMode.Replace) && inputKeys.Count != inputValues.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Keys and values are mismatched in length");
return;
}
if (Mode == PropertyMode.Remove && (inputKeys.Count == 0 || inputValues.Count > 0))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Only input keys to remove");
return;
}
// process the properties
Dictionary<string, ISpecklePropertyGoo> result =
inputProperties is null || Mode == PropertyMode.Replace
@@ -126,7 +132,7 @@ public class SpecklePropertiesPassthrough : GH_Component
for (int i = 0; i < inputKeys.Count; i++)
{
string key = inputKeys[i];
object? value = inputValues[i];
object? value = Mode == PropertyMode.Remove ? null : inputValues[i];
ISpecklePropertyGoo? convertedValue = null;
switch (value)
{
@@ -142,7 +148,7 @@ public class SpecklePropertiesPassthrough : GH_Component
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Values contain an invalid data type. Only strings, numbers, booleans, and other Speckle properties are supported."
$"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
);
return;
}
@@ -167,13 +173,7 @@ public class SpecklePropertiesPassthrough : GH_Component
result.Add(key, convertedValue);
break;
case PropertyMode.Remove:
if (result.TryGetValue(key, out ISpecklePropertyGoo existingValue))
{
if (existingValue.Equals(convertedValue))
{
result.Remove(key);
}
}
result.Remove(key);
break;
}
}
@@ -203,8 +203,7 @@ public class SpecklePropertiesPassthrough : GH_Component
modeItem.ToolTipText = "Existing properties will be cleared and replaced by input keyvalue pairs.";
break;
case PropertyMode.Remove:
modeItem.ToolTipText =
"Existing keyvalue pairs that match the input keyvalue pairs will be removed from properties.";
modeItem.ToolTipText = "Existing keyvalue pairs that match the input keys will be removed from properties.";
break;
}
}
@@ -29,8 +29,8 @@ public class AccountManagerComponent : GH_Component, IDisposable
public AccountManagerComponent()
: base(
"Accounts",
"A",
"Sign In",
"SI",
"Sign in to a Speckle Account",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
@@ -271,7 +271,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
ResetApiClient(UrlModelResource);
}
private void ApiClient_OnVersionCreated(object sender, ProjectVersionsUpdatedMessage e)
private void ApiClient_OnVersionCreated(object? sender, ProjectVersionsUpdatedMessage e)
{
HandleNewCommit();
}
@@ -25,9 +25,6 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
[Guid("52481972-7867-404F-8D9F-E1481183F355")]
public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
{
public GhContextMenuButton ProjectContextMenuButton { get; set; }
public GhContextMenuButton ModelContextMenuButton { get; set; }
public SendAsyncComponent()
: base(
"Publish",
@@ -57,6 +54,8 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
public SpeckleUrlModelResource? OutputParam { get; set; }
public bool HasMultipleInputs { get; set; }
public string? VersionMessage { get; private set; }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
@@ -67,6 +66,8 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
"The collection model object to send",
GH_ParamAccess.item
);
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
@@ -275,6 +276,10 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
return;
}
RootCollectionWrapper = rootCollectionWrapper;
string? versionMessage = null;
da.GetData(2, ref versionMessage);
VersionMessage = versionMessage;
}
}
@@ -402,7 +407,13 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
SendOperationResult? result = await sendOperation
.Execute(new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper }, sendInfo, progress, CancellationToken)
.Execute(
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
sendInfo,
Parent.VersionMessage,
progress,
CancellationToken
)
.ConfigureAwait(false);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
@@ -49,6 +49,8 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
public string? Url { get; private set; }
public string? VersionMessage { get; private set; }
protected override Bitmap Icon => Resources.speckle_operations_syncpublish;
protected override void RegisterInputParams(GH_InputParamManager pManager)
@@ -61,7 +63,8 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
"The model collection to publish",
GH_ParamAccess.item
);
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
pManager.AddBooleanParameter("Run", "r", "Run the publish operation", GH_ParamAccess.item);
}
@@ -86,8 +89,12 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
da.GetData(1, ref rootCollectionWrapper);
string? versionMessage = null;
da.GetData(2, ref versionMessage);
VersionMessage = versionMessage;
bool run = false;
da.GetData(2, ref run);
da.GetData(3, ref run);
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper, run);
}
@@ -177,8 +184,14 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
using var client = clientFactory.Create(account);
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
var result = await sendOperation
.Execute(new List<SpeckleCollectionWrapperGoo>() { input.Input }, sendInfo, progress, cancellationToken)
await sendOperation
.Execute(
new List<SpeckleCollectionWrapperGoo>() { input.Input },
sendInfo,
VersionMessage,
progress,
cancellationToken
)
.ConfigureAwait(false);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
@@ -36,8 +36,8 @@ public class SpeckleSelectModelComponent : GH_Component
public SpeckleSelectModelComponent()
: base(
"Speckle Model URL",
"URL",
"Speckle Model",
"SM",
"User selectable model from Speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
@@ -1,3 +1,5 @@
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Wizard;
public class SearchToolStripMenuItem
@@ -124,7 +126,7 @@ public class SearchToolStripMenuItem
ParentDropDown.ItemClicked += (sender, args) =>
{
// we are not closing the dropdown only if user clicked the first item of the dropdown which is TextBox that we use for search
if (args.ClickedItem.Name == SearchItemId)
if (args.ClickedItem.NotNull().Name == SearchItemId)
{
return;
}
@@ -22,7 +22,7 @@ public class SpeckleOperationWizard
public Account? SelectedAccount { get; private set; }
public List<Account>? Accounts { get; }
public Workspace? SelectedWorkspace { get; private set; }
public LimitedWorkspace? SelectedWorkspace { get; private set; }
public Project? SelectedProject { get; private set; }
public Model? SelectedModel { get; private set; }
public Version? SelectedVersion { get; private set; }
@@ -416,38 +416,42 @@ public class SpeckleOperationWizard
{
return;
}
using IClient client = _clientFactory.Create(SelectedAccount);
var activeWorkspace = client.ActiveUser.GetActiveWorkspace().Result;
Workspace? selectedWorkspace =
LimitedWorkspace? selectedWorkspace =
SelectedWorkspace
?? activeWorkspace
?? (WorkspaceMenuHandler.Workspaces?.items.Count > 0 ? WorkspaceMenuHandler.Workspaces?.items[0] : null);
SelectedWorkspace = selectedWorkspace;
WorkspaceMenuHandler.RedrawMenuButton(SelectedWorkspace);
}
private void OnWorkspaceSelected(object sender, WorkspaceSelectedEventArgs e)
private void OnWorkspaceSelected(object? sender, WorkspaceSelectedEventArgs e)
{
SelectedWorkspace = e.SelectedWorkspace;
ResetProjects();
_refreshComponent.Invoke();
}
private void OnProjectSelected(object sender, ProjectSelectedEventArgs e)
private void OnProjectSelected(object? sender, ProjectSelectedEventArgs e)
{
SelectedProject = e.SelectedProject;
ResetModels();
_refreshComponent.Invoke();
}
private void OnModelSelected(object sender, ModelSelectedEventArgs e)
private void OnModelSelected(object? sender, ModelSelectedEventArgs e)
{
SelectedModel = e.SelectedModel;
ResetVersions(true);
_refreshComponent.Invoke();
}
private void OnVersionSelected(object sender, VersionSelectedEventArgs e)
private void OnVersionSelected(object? sender, VersionSelectedEventArgs e)
{
SelectedVersion = e.SelectedVersion;
IsLatestVersion = e.IsLatest;
@@ -3,9 +3,9 @@ using Speckle.Sdk.Api.GraphQL.Models;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Wizard;
public class WorkspaceSelectedEventArgs(Workspace? model) : EventArgs
public class WorkspaceSelectedEventArgs(LimitedWorkspace? model) : EventArgs
{
public Workspace? SelectedWorkspace { get; } = model;
public LimitedWorkspace? SelectedWorkspace { get; } = model;
}
public class WorkspaceMenuHandler
@@ -15,7 +15,7 @@ public class WorkspaceMenuHandler
public bool IsPersonalProjects { get; set; }
private SearchToolStripMenuItem? _searchItem;
private readonly Func<Task> _createWorkspace;
private Workspace? SelectedWorkspace { get; set; }
private LimitedWorkspace? SelectedWorkspace { get; set; }
public ResourceCollection<Workspace>? Workspaces { get; set; }
public Bitmap? Logo { get; private set; }
@@ -103,7 +103,7 @@ public class WorkspaceMenuHandler
);
}
private void OnWorkspaceSelected(Workspace? workspace)
private void OnWorkspaceSelected(LimitedWorkspace? workspace)
{
IsPersonalProjects = workspace == null;
_menu?.Close();
@@ -112,7 +112,7 @@ public class WorkspaceMenuHandler
WorkspaceSelected?.Invoke(this, new WorkspaceSelectedEventArgs(workspace));
}
public void RedrawMenuButton(Workspace? workspace)
public void RedrawMenuButton(LimitedWorkspace? workspace)
{
var suffix = WorkspaceContextMenuButton.Enabled
? "Left-click to select another workspace."
@@ -0,0 +1,19 @@
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
namespace Speckle.Connectors.GrasshopperShared.Components;
public abstract class SpeckleSolveInstance(
string name,
string nickname,
string description,
string category,
string subCategory
) : GH_Component(name, nickname, description, category, subCategory)
{
protected override void BeforeSolveInstance() => SpeckleConversionContext.SetupCurrent();
protected override void AfterSolveInstance() => SpeckleConversionContext.EndCurrent();
protected abstract override void SolveInstance(IGH_DataAccess da);
}
@@ -0,0 +1,11 @@
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.HostApp.Extras;
public class ListAccessStateTag : GH_StateTag
{
public override string Description => "This parameter is set to List access";
public override string Name => "List Access";
public override Bitmap Icon => Resources.speckle_state_access;
}
@@ -128,12 +128,45 @@ public static class GrasshopperHelpers
}
/// <summary>
/// Attempts to cast an IGH_Goo to a Speckle Object Wrapper
/// Gets all of the atomic objects inside a collection wrapper.
/// </summary>
/// <param name="coll"></param>
/// <param name="recurse">Will recurse into sub collections to get atomic objects</param>
/// <returns></returns>
public static IEnumerable<SpeckleWrapper> GetAtomicObjects(this SpeckleCollectionWrapper coll, bool recurse = false)
{
foreach (var element in coll.Elements)
{
switch (element)
{
case SpeckleDataObjectWrapper dataObject:
yield return dataObject;
break;
case SpeckleGeometryWrapper geo: // covers both instances and geo
yield return geo;
break;
case SpeckleCollectionWrapper subColl:
if (recurse)
{
foreach (var subElement in subColl.GetAtomicObjects(recurse))
{
yield return subElement;
}
}
break;
default:
break;
}
}
}
/// <summary>
/// Attempts to cast an IGH_Goo to a Speckle Geometry Wrapper
/// </summary>
/// <param name="goo"></param>
/// <returns>A reference to the Speckle Object Wrapper from the goo, if any</returns>
/// <returns>A reference to the Speckle Geometry Wrapper from the goo, if any</returns>
/// <remarks>This method **does not** deep copy the return value</remarks>
public static SpeckleObjectWrapper? ToSpeckleObjectWrapper(this IGH_Goo goo)
public static SpeckleGeometryWrapper? ToSpeckleGeometryWrapper(this IGH_Goo goo)
{
SpeckleBlockInstanceWrapperGoo instanceGoo = new();
if (instanceGoo.CastFrom(goo))
@@ -142,7 +175,35 @@ public static class GrasshopperHelpers
}
else
{
SpeckleObjectWrapperGoo objGoo = new();
SpeckleGeometryWrapperGoo objGoo = new();
return objGoo.CastFrom(goo) ? objGoo.Value : null;
}
}
/// <summary>
/// Attempts to cast an IGH_Goo to a Speckle Geometry or DataObject Wrapper
/// </summary>
/// <param name="goo"></param>
/// <returns>A reference to the Speckle Wrapper from the goo, if any</returns>
/// <remarks>This method **does not** deep copy the return value</remarks>
public static SpeckleWrapper? ToSpeckleObjectWrapper(this IGH_Goo goo)
{
// first preserve data objects as they are
// this is processed first because data objects with 1 display value can be cast to geometry wrappers
if (goo is SpeckleDataObjectWrapperGoo dataObject)
{
return dataObject.Value;
}
// then try to process as geometry
SpeckleBlockInstanceWrapperGoo instanceGoo = new();
if (instanceGoo.CastFrom(goo))
{
return instanceGoo.Value;
}
else
{
SpeckleGeometryWrapperGoo objGoo = new();
return objGoo.CastFrom(goo) ? objGoo.Value : null;
}
}
@@ -3,6 +3,7 @@ using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.HostApp;
@@ -10,25 +11,59 @@ namespace Speckle.Connectors.GrasshopperShared.HostApp;
/// <summary>
/// Handles grasshopper wide converters. We don't need new converters, unless the document changes - this class should handle this (untested).
/// </summary>
public static class SpeckleConversionContext
public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter, IRootToHostConverter hostConverter)
{
public static Base ConvertToSpeckle(GeometryBase geo)
private static IServiceScope? s_scope;
private static SpeckleConversionContext? s_currentContext;
public static SpeckleConversionContext Current
{
using var scope = PriorityLoader.CreateScopeForActiveDocument();
return scope.ServiceProvider.GetRequiredService<IRootToSpeckleConverter>().Convert(geo);
get
{
if (s_currentContext == null)
{
SetupCurrent();
}
return s_currentContext.NotNull();
}
}
public static List<(GeometryBase, Base)> ConvertToHost(Base input)
public static void SetupCurrent()
{
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var result = scope.ServiceProvider.GetRequiredService<IRootToHostConverter>().Convert(input);
if (s_currentContext != null)
{
return;
}
s_scope = PriorityLoader.CreateScopeForActiveDocument();
s_currentContext = s_scope.Get<SpeckleConversionContext>();
}
public static void EndCurrent()
{
if (s_currentContext == null)
{
return;
}
s_currentContext = null;
s_scope?.Dispose();
s_scope = null;
}
public Base ConvertToSpeckle(object geo) => speckleConverter.Convert(geo);
public List<(object, Base)> ConvertToHost(Base input)
{
var result = hostConverter.Convert(input);
return result switch
{
GeometryBase geometry => [(geometry, input)],
List<GeometryBase> geometryList => geometryList.Select(o => (o, input)).ToList(),
IEnumerable<(GeometryBase, Base)> fallbackConversionResult => fallbackConversionResult.ToList(),
_ => throw new SpeckleException("Failed to convert input to rhino")
List<GeometryBase> geometryList => geometryList.Select(o => ((object)o, input)).ToList(),
IEnumerable<(GeometryBase, Base)> fallbackConversionResult
=> fallbackConversionResult.Select(o => ((object)o.Item1, o.Item2)).ToList(),
object obj => [(obj, input)],
_ => throw new SpeckleException("Failed to convert input to grasshopper")
};
}
}
@@ -40,7 +40,7 @@ internal sealed class GrasshopperBlockUnpacker
public HashSet<string> UnpackBlocks(
IReadOnlyCollection<TraversalContext> blockComponents,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies,
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
GrasshopperCollectionRebuilder collectionRebuilder
)
{
@@ -95,7 +95,7 @@ internal sealed class GrasshopperBlockUnpacker
/// </summary>
private void CreateBlocksInDependencyOrder(
List<(Collection[] path, IInstanceComponent component)> sortedComponents,
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
GrasshopperCollectionRebuilder collectionRebuilder,
HashSet<string> consumedObjectIds
)
@@ -159,11 +159,11 @@ internal sealed class GrasshopperBlockUnpacker
private SpeckleBlockDefinitionWrapper? CreateBlockDefinitionWrapper(
InstanceDefinitionProxy definitionProxy,
string definitionId,
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
HashSet<string> consumedObjectIds
)
{
var definitionObjects = new List<SpeckleObjectWrapper>();
var definitionObjects = new List<SpeckleGeometryWrapper>();
var currentDefinitionObjectIds = new HashSet<string>();
foreach (var objectId in definitionProxy.objects)
@@ -25,7 +25,7 @@ internal sealed class GrasshopperCollectionRebuilder
}
public void AppendSpeckleGrasshopperObject(
SpeckleObjectWrapper speckleGrasshopperObjectWrapper,
ISpeckleCollectionObject speckleGrasshopperObjectWrapper,
List<Collection> collectionPath,
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker
@@ -43,7 +43,7 @@ internal sealed class GrasshopperCollectionRebuilder
{
// first check if cache already has this collection
string fullPath = string.Concat(path);
if (_cache.TryGetValue(fullPath, out SpeckleCollectionWrapper col))
if (_cache.TryGetValue(fullPath, out SpeckleCollectionWrapper? col))
{
return col;
}
@@ -58,7 +58,7 @@ internal sealed class GrasshopperCollectionRebuilder
string key = string.Concat(currentLayerPath);
// check cache
if (_cache.TryGetValue(key, out SpeckleCollectionWrapper currentCol))
if (_cache.TryGetValue(key, out SpeckleCollectionWrapper? currentCol))
{
previousCollectionWrapper = currentCol;
continue;
@@ -118,7 +118,9 @@ internal sealed class GrasshopperCollectionRebuilder
{
// Remove consumed objects from this level
collection.Elements.RemoveAll(element =>
element is SpeckleObjectWrapper obj && obj.ApplicationId != null && consumedObjectIds.Contains(obj.ApplicationId)
element is SpeckleGeometryWrapper obj
&& obj.ApplicationId != null
&& consumedObjectIds.Contains(obj.ApplicationId)
);
// Recurse into child collections
@@ -1,5 +1,5 @@
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Logging;
using Speckle.Sdk.Api;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
@@ -41,7 +41,8 @@ public class GrasshopperReceiveOperation
// 2 - Check account exist
var account = receiveInfo.Account;
using IClient apiClient = _clientFactory.Create(account);
using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail());
using var userScope = UserActivityScope.AddUserScope(account);
Speckle.Sdk.Api.GraphQL.Models.Version? version = await apiClient
.Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken)
@@ -18,7 +18,7 @@ using Speckle.Sdk.Models.Instances;
/// </remarks>
internal sealed class LocalToGlobalMapHandler
{
public Dictionary<string, SpeckleObjectWrapper> ConvertedObjectsMap { get; } = new();
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
private readonly TraversalContextUnpacker _traversalContextUnpacker;
@@ -53,7 +53,7 @@ internal sealed class LocalToGlobalMapHandler
try
{
List<(GeometryBase, Base)> converted = SpeckleConversionContext.ConvertToHost(obj);
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
if (converted.Count == 0)
{
@@ -69,51 +69,102 @@ internal sealed class LocalToGlobalMapHandler
_materialUnpacker
);
// Extract name and properties
SpecklePropertyGroupGoo propertyGroup = new();
string name = "";
if (obj is Speckle.Objects.Data.DataObject dataObject)
{
// get color and mat on dataobject first
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(
dataObject.applicationId ?? "",
out var cachedDataObjColor
)
? cachedDataObjColor
: null;
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
dataObject.applicationId ?? "",
out var cachedDataObjMaterial
)
? cachedDataObjMaterial
: null;
// get geometries
List<SpeckleGeometryWrapper> geometries = new();
foreach ((object convertedObj, Base original) in converted)
{
if (convertedObj is GeometryBase geometryBase)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = original,
GeometryBase = geometryBase,
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: dataObjColor,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: dataObjMat,
};
geometries.Add(wrapper);
}
}
SpecklePropertyGroupGoo propertyGroup = new();
propertyGroup.CastFrom(dataObject.properties);
name = dataObject.name;
// remove the displayvalue of the original dataobject since these are now processed and stored on the wrapper
// to prevent storing of duplicate Base
dataObject.displayValue.Clear();
var dataObjectWrapper = new SpeckleDataObjectWrapper()
{
Base = dataObject,
Geometries = geometries,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
Name = dataObject.name,
Properties = propertyGroup,
ApplicationId = dataObject.applicationId,
};
// Add to collections (not to map since these won't be definition objects)
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
}
else
{
SpecklePropertyGroupGoo propertyGroup = new();
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
if (obj[Constants.NAME_PROP] is string objName)
foreach ((object convertedObj, Base original) in converted)
{
name = objName;
if (convertedObj is GeometryBase geometryBase)
{
var wrapper = new SpeckleGeometryWrapper()
{
Base = original,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = obj[Constants.NAME_PROP] as string ?? "",
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: null,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: null,
ApplicationId = objId
};
// Always add to both map and collections
ConvertedObjectsMap[objId] = wrapper;
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
}
}
}
foreach ((GeometryBase geometryBase, Base original) in converted)
{
var wrapper = new SpeckleObjectWrapper()
{
Base = original,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = name,
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: null,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: null,
ApplicationId = objId
};
// Always add to both map and collections
ConvertedObjectsMap[objId] = wrapper;
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
}
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -10,9 +10,9 @@ namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
/// </summary>
internal sealed class GrasshopperBlockPacker
{
private readonly IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> _instanceObjectsManager;
private readonly IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> _instanceObjectsManager;
public GrasshopperBlockPacker(IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> instanceObjectsManager)
public GrasshopperBlockPacker(IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> instanceObjectsManager)
{
_instanceObjectsManager = instanceObjectsManager;
}
@@ -31,7 +31,7 @@ internal sealed class GrasshopperBlockPacker
/// </summary>
/// <param name="blockInstance">The block instance to process</param>
/// <param name="depth">Current nesting depth (0 = top level, increases for nested instances)</param>
public List<SpeckleObjectWrapper>? ProcessInstance(SpeckleBlockInstanceWrapper? blockInstance, int depth = 0)
public List<SpeckleGeometryWrapper>? ProcessInstance(SpeckleBlockInstanceWrapper? blockInstance, int depth = 0)
{
if (blockInstance?.Definition == null)
{
@@ -51,7 +51,7 @@ internal sealed class GrasshopperBlockPacker
/// Processes a block definition, adding it and its objects to InstanceObjectsManager.
/// Updates maxDepth for existing definitions when encountered at greater depths.
/// </summary>
private List<SpeckleObjectWrapper>? ProcessDefinition(SpeckleBlockDefinitionWrapper definition, int depth = 0)
private List<SpeckleGeometryWrapper>? ProcessDefinition(SpeckleBlockDefinitionWrapper definition, int depth = 0)
{
// Use wrapper's id as definitive identifier. Create if empty.
definition.ApplicationId ??= Guid.NewGuid().ToString();
@@ -73,7 +73,7 @@ internal sealed class GrasshopperBlockPacker
}
// Process objects recursively
var objectsToAdd = new List<SpeckleObjectWrapper>();
var objectsToAdd = new List<SpeckleGeometryWrapper>();
var currentObjectIds = new List<string>(); // Track current object IDs for proxy update
foreach (var obj in definition.Objects)
@@ -5,16 +5,17 @@ using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollectionWrapperGoo>
{
private readonly IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> _instanceObjectsManager;
private readonly IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> _instanceObjectsManager;
// each Build() call gets a fresh scoped IInstanceObjectsManager
public GrasshopperRootObjectBuilder(
IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> instanceObjectsManager
IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> instanceObjectsManager
)
{
_instanceObjectsManager = instanceObjectsManager;
@@ -29,14 +30,11 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<SpeckleCollectionWrapperGoo> input,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
)
{
// TODO: Send info is used in other connectors to get the project ID to populate the SendConversionCache
Console.WriteLine($"Send Info {sendInfo}");
// deep copy input (to not mutate input) and set the input collection name to "Grasshopper Model"
var inputCollectionGoo = (SpeckleCollectionWrapperGoo)input[0].Duplicate();
inputCollectionGoo.Value.Name = "Grasshopper Model";
@@ -90,9 +88,9 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
Unwrap(collWrapper, colorPacker, materialPacker, blockPacker);
break;
case SpeckleObjectWrapper so: // handles both SpeckleObjectWrapper and SpeckleBlockInstanceWrapper (inheritance)
case SpeckleGeometryWrapper so: // handles both SpeckleObjectWrapper and SpeckleBlockInstanceWrapper (inheritance)
// convert wrapper to base and add to collection - common for all object wrappers
Base objectBase = Unwrap(so);
Base objectBase = UnwrapGeometry(so);
string applicationId = objectBase.applicationId!;
currentColl.elements.Add(objectBase);
@@ -106,6 +104,14 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
colorPacker.ProcessColor(applicationId, so.Color);
materialPacker.ProcessMaterial(applicationId, so.Material);
break;
case SpeckleDataObjectWrapper dataObjectWrapper:
// convert wrapper to DataObject and add to collection
// UnwrapDataObject will unwrap underlying geometry and handle color and material
// arguably doing too much, but I'm apprehensive looping twice without good reason
DataObject dataObject = UnwrapDataObject(dataObjectWrapper, colorPacker, materialPacker);
currentColl.elements.Add(dataObject);
break;
}
}
@@ -125,12 +131,12 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
}
/// <summary>
/// Converts a <see cref="SpeckleObjectWrapper"/> to underlying Base object with dynamically attached properties.
/// Converts a <see cref="SpeckleGeometryWrapper"/> to underlying Base object with dynamically attached properties.
/// </summary>
/// <remarks>
/// POC: if we move properties assignment to auto set the wrapped base, we can get rid of this entirely!
/// </remarks>
private Base Unwrap(SpeckleObjectWrapper wrapper)
private Base UnwrapGeometry(SpeckleGeometryWrapper wrapper)
{
Dictionary<string, object?> props = [];
Base baseObject = wrapper.Base;
@@ -164,7 +170,7 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
{
foreach (var definitionObject in definitionObjects)
{
Base defObjectBase = Unwrap(definitionObject);
Base defObjectBase = UnwrapGeometry(definitionObject);
string applicationId = defObjectBase.applicationId!;
// just add to current collection for now
@@ -176,6 +182,40 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
}
}
/// <summary>
/// Converts a <see cref="SpeckleDataObjectWrapper"/> to underlying DataObject with properly configured displayValue.
/// Processes colors and materials for each individual geometry during conversion.
/// </summary>
private DataObject UnwrapDataObject(
SpeckleDataObjectWrapper wrapper,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker
)
{
DataObject dataObject = wrapper.DataObject;
// Convert geometries back to Base objects for displayValue
var displayValue = new List<Base>();
foreach (var geometryWrapper in wrapper.Geometries)
{
Base geometryBase = UnwrapGeometry(geometryWrapper);
displayValue.Add(geometryBase);
// process color and material for each geometry while we're iterating
// this could be in the switch statements (like SpeckleGeometryWrapper) but then we're unnecessarily looping twice
if (geometryWrapper.ApplicationId != null)
{
colorPacker.ProcessColor(geometryWrapper.ApplicationId, geometryWrapper.Color);
materialPacker.ProcessMaterial(geometryWrapper.ApplicationId, geometryWrapper.Material);
}
}
// Update the DataObject's displayValue
dataObject.displayValue = displayValue;
return dataObject;
}
/*
// will cache the object wrappers and group them by similarity.
private void ProcessObjectWrapper(SpeckleObjectWrapper objectWrapper, ref HashSet<string> processedIds)
@@ -1,5 +1,8 @@
using Grasshopper.Documentation;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Sdk;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
@@ -32,9 +35,44 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
{
switch (source)
{
case List<object?> list:
List<object?> castedItems = new();
foreach (var item in list)
{
SpecklePropertyGoo itemGoo = new();
if (itemGoo.CastFrom(item))
{
castedItems.Add(itemGoo.Value);
}
else
{
return false;
}
}
Value = castedItems;
return true;
case SpecklePropertyGoo speckleProperty:
Value = speckleProperty.Value;
return true;
case Base @base: // this would capture cases of planes, vectors, and intervals from GH
try
{
Value = SpeckleConversionContext.Current.ConvertToHost(@base!).First().Item1;
return true;
}
catch (SpeckleException)
{
return false;
}
case GH_Plane plane:
Value = plane.Value;
return true;
case GH_Vector vector:
Value = vector.Value;
return true;
case GH_Interval interval:
Value = interval.Value;
return true;
case double d:
Value = d;
return true;
@@ -89,6 +127,27 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
return true;
}
if (type.IsAssignableFrom(typeof(GH_Plane)))
{
object ptr = new GH_Plane((Rhino.Geometry.Plane)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Vector)))
{
object ptr = new GH_Vector((Rhino.Geometry.Vector3d)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Interval)))
{
object ptr = new GH_Interval((Rhino.Geometry.Interval)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Integer)))
{
object ptr = new GH_Integer((int)Value);
@@ -129,6 +188,12 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
switch (Value)
{
case Rhino.Geometry.Plane plane:
return prop.Value is Rhino.Geometry.Plane otherPlane && plane.Equals(otherPlane);
case Rhino.Geometry.Vector3d vector:
return prop.Value is Rhino.Geometry.Vector3d otherVector && vector.Equals(otherVector);
case Rhino.Geometry.Interval interval:
return prop.Value is Rhino.Geometry.Interval otherInterval && interval.Equals(otherInterval);
case string s:
return s == prop.Value.ToString();
case bool b:
@@ -22,21 +22,51 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
return CastFromModelObject(modelObject.UserText);
case ModelUserText userText:
Dictionary<string, ISpecklePropertyGoo> dictionary = new();
foreach (KeyValuePair<string, string> entry in userText)
{
SpecklePropertyGoo value = new() { Value = entry.Value };
dictionary.Add(entry.Key, value);
}
Value = dictionary;
return true;
var processedDictionary = ConvertToNested(userText.ToDictionary(o => o.Key, o => (object)o.Value));
return CastFrom(processedDictionary);
default:
return false;
}
}
// Property keys may already be concatenated with the `.` char, eg if baked from grasshopper.
public Dictionary<string, object> ConvertToNested(Dictionary<string, object> flatDict)
{
var nestedDict = new Dictionary<string, object>();
foreach (string keyPath in flatDict.Keys)
{
var keys = keyPath.Split('.');
var current = nestedDict;
for (int i = 0; i < keys.Length; i++)
{
var key = keys[i];
if (i == keys.Length - 1)
{
current[key] = flatDict[keyPath];
}
else
{
if (!current.TryGetValue(key, out var next))
{
var newDict = new Dictionary<string, object>();
current[key] = newDict;
current = newDict;
}
else
{
current = (Dictionary<string, object>)next;
}
}
}
}
return nestedDict;
}
private bool CastToModelObject<T>(ref T target)
{
var type = typeof(T);
@@ -1,9 +1,6 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.DocObjects;
using Speckle.Connectors.GrasshopperShared.Components;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
@@ -91,7 +88,7 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
var otherProps = propGroup.Flatten();
foreach (var entry in thisProps)
{
if (!otherProps.TryGetValue(entry.Key, out SpecklePropertyGoo otherValue) || !entry.Value.Equals(otherValue))
if (!otherProps.TryGetValue(entry.Key, out SpecklePropertyGoo? otherValue) || !entry.Value.Equals(otherValue))
{
return false;
}
@@ -189,11 +186,27 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
Dictionary<string, object?> dict = new();
foreach (var kvp in properties)
{
object? val = kvp.Value is SpecklePropertyGroupGoo propertyGroup
? UnwrapWorker(propertyGroup.Value)
: kvp.Value is SpecklePropertyGoo property
? property.Value
: null;
object? val = null;
switch (kvp.Value)
{
case SpecklePropertyGroupGoo propertyGroup:
val = UnwrapWorker(propertyGroup.Value);
break;
case SpecklePropertyGoo property:
switch (property.Value)
{
case Rhino.Geometry.Plane:
case Rhino.Geometry.Vector3d:
case Rhino.Geometry.Interval:
val = SpeckleConversionContext.Current.ConvertToSpeckle(property.Value);
break;
default:
val = property.Value;
break;
}
break;
}
dict.Add(kvp.Key, val);
}
@@ -202,29 +215,3 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
public override int GetHashCode() => base.GetHashCode();
}
public class SpecklePropertyGroupParam : GH_Param<SpecklePropertyGroupGoo>
{
public override Guid ComponentGuid => new("AF4757C3-BA33-4ACD-A92B-C80356043129");
protected override Bitmap Icon => Resources.speckle_param_properties;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public SpecklePropertyGroupParam()
: this(GH_ParamAccess.item) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpecklePropertyGroupParam(GH_ParamAccess access)
: base(
"Speckle Properties",
"SP",
"Represents a set of Speckle Properties",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
}
@@ -0,0 +1,31 @@
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Components;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public class SpecklePropertyGroupParam : GH_Param<SpecklePropertyGroupGoo>
{
public override Guid ComponentGuid => new("AF4757C3-BA33-4ACD-A92B-C80356043129");
protected override Bitmap Icon => Resources.speckle_param_properties;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public SpecklePropertyGroupParam()
: this(GH_ParamAccess.item) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpecklePropertyGroupParam(GH_ParamAccess access)
: base(
"Speckle Properties",
"SP",
"Represents a set of Speckle Properties",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
}
@@ -16,8 +16,8 @@ public class SpeckleUrlModelResourceParam : GH_Param<SpeckleUrlModelResourceGoo>
public SpeckleUrlModelResourceParam(GH_ParamAccess access)
: base(
"Model Link",
"SML",
"Speckle Model",
"SM",
"A resource link to a Speckle Model",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
@@ -1,7 +1,10 @@
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.GrasshopperShared.Components.Objects;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.HostApp.Extras;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
@@ -51,6 +54,59 @@ public class SpeckleVariableParam : Param_GenericObject
public override Guid ComponentGuid => new("A1B2C3D4-E5F6-7890-ABCD-123456789ABC");
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
// Append list access menu item if this is a create properties node
if (Attributes?.Parent.DocObject is CreateSpeckleProperties)
{
Menu_AppendSeparator(menu);
var listAccessToggle = Menu_AppendItem(
menu,
"List Access",
(s, e) => SetAccess(Access == GH_ParamAccess.list ? GH_ParamAccess.item : GH_ParamAccess.list),
true,
Access == GH_ParamAccess.list
);
listAccessToggle.ToolTipText = "Set this parameter as a List. If disabled, defaults to item access.";
listAccessToggle.Image = Resources.speckle_state_access;
}
}
public override GH_StateTagList StateTags
{
get
{
var tags = base.StateTags;
if (Kind == GH_ParamKind.input)
{
if (Access == GH_ParamAccess.list)
{
tags.Add(new ListAccessStateTag());
}
}
return tags;
}
}
protected void SetAccess(GH_ParamAccess accessType)
{
Access = accessType;
HandleParamStateChange();
}
private void HandleParamStateChange()
{
OnObjectChanged(GH_ObjectEventType.DataMapping);
OnDisplayExpired(true);
ExpireSolution(true);
}
public override void AddSource(IGH_Param source, int index)
{
base.AddSource(source, index);
@@ -30,9 +30,9 @@ public class SpeckleBlockDefinitionWrapper : SpeckleWrapper
}
}
private List<SpeckleObjectWrapper> _objects = new();
private List<SpeckleGeometryWrapper> _objects = new();
public List<SpeckleObjectWrapper> Objects
public List<SpeckleGeometryWrapper> Objects
{
get => _objects;
set
@@ -42,10 +42,10 @@ public class SpeckleBlockDefinitionWrapper : SpeckleWrapper
}
}
private static void ValidateObjects(List<SpeckleObjectWrapper> objects)
private static void ValidateObjects(List<SpeckleGeometryWrapper> objects)
{
// SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper, check if it's assignable, not exact type match
var invalidObjects = objects.Where(o => !typeof(SpeckleObjectWrapper).IsAssignableFrom(o.GetType())).ToList();
var invalidObjects = objects.Where(o => !typeof(SpeckleGeometryWrapper).IsAssignableFrom(o.GetType())).ToList();
if (invalidObjects.Count > 0)
{
@@ -64,7 +64,7 @@ public class SpeckleBlockDefinitionWrapper : SpeckleWrapper
/// Creates a preview of the block definition by displaying all contained objects
/// </summary>
/// <remarks>
/// Leveraging already defined preview logic for the objects which make up this block. Refer to <see cref="SpeckleObjectWrapper.DrawPreview"/>.
/// Leveraging already defined preview logic for the objects which make up this block. Refer to <see cref="SpeckleGeometryWrapper.DrawPreview"/>.
/// </remarks>
public void DrawPreview(IGH_PreviewArgs args, bool isSelected = false) =>
DrawDepthLimitedPreview(args, isSelected, 0);
@@ -228,7 +228,7 @@ public class SpeckleBlockDefinitionWrapper : SpeckleWrapper
public SpeckleBlockDefinitionWrapper DeepCopy() =>
new()
{
Base = InstanceDefinitionProxy.ShallowCopy(),
Base = (Base)InstanceDefinitionProxy.ShallowCopy(),
ApplicationId = ApplicationId,
Name = Name,
Objects = Objects.Select(o => o.DeepCopy()).ToList()
@@ -13,10 +13,10 @@ public partial class SpeckleBlockDefinitionWrapperGoo
switch (source)
{
case InstanceDefinition instanceDefinition:
List<SpeckleObjectWrapper> objects = new();
List<SpeckleGeometryWrapper> objects = new();
foreach (var defObj in instanceDefinition.GetObjects())
{
SpeckleObjectWrapperGoo defObjGoo = new();
SpeckleGeometryWrapperGoo defObjGoo = new();
if (defObjGoo.CastFrom(defObj))
{
objects.Add(defObjGoo.Value);
@@ -28,43 +28,51 @@ public partial class SpeckleBlockDefinitionWrapperGoo
return false;
}
Value = new SpeckleBlockDefinitionWrapper()
{
Base = new InstanceDefinitionProxy
{
name = instanceDefinition.Name,
objects = objects.Select(o => o.ApplicationId!).ToList(),
maxDepth = 0 // represent newly created, top-level objects. actual depth calculation happens in GrasshopperBlockPacker
},
Name = instanceDefinition.Name,
Objects = objects,
ApplicationId = instanceDefinition.Id.ToString()
};
SetValueFromDefinitionProps(objects, instanceDefinition.Name, instanceDefinition.Id.ToString());
return true;
case ModelInstanceDefinition modelInstanceDef:
InstanceDefinition? instanceDef = RhinoDoc.ActiveDoc?.InstanceDefinitions.Find(modelInstanceDef.Name);
if (instanceDef == null)
List<SpeckleGeometryWrapper> defObjs = new();
foreach (var defObj in modelInstanceDef.Objects)
{
SpeckleGeometryWrapperGoo geoWrapperGoo = new();
if (geoWrapperGoo.CastFrom(defObj))
{
defObjs.Add(geoWrapperGoo.Value);
}
}
if (defObjs.Count == 0)
{
// Rhino → Model → Model Block Definition passthrough component returns type ModelInstanceDefinition
// .Objects of a ModelInstanceDefinition returns ModelObjects
// ModelObject.Geometry is internal and cannot be accessed directly.
// Only way to get geometry from a ModelObject is through RhinoDoc.Objects.FindId(), which only works for baked objects.
// Unbaked Grasshopper geometry cannot be processed through the ModelObject workflow until we get a public geometry accessor 😓
// ⚠️ So if user defines a Model Block Definition in Grasshopper with Grasshopper (unbaked) geometry, we're stuck.
// That's why we're intercepting this case early → if the instanceDef == null don't go further
throw new InvalidOperationException(
$"Block definition '{modelInstanceDef.Name}' not found in Rhino document. Please bake the definition first or use Speckle Block Definition components instead."
$"Block definition '{modelInstanceDef.Name}' did not have any valid geometry."
);
}
return CastFromModelObject(instanceDef);
SetValueFromDefinitionProps(defObjs, modelInstanceDef.Name, modelInstanceDef.Id.ToString());
return true;
default:
return false;
}
}
private void SetValueFromDefinitionProps(List<SpeckleGeometryWrapper> objs, string name, string id)
{
string validAppId = string.IsNullOrWhiteSpace(id) ? Guid.NewGuid().ToString() : id;
Value = new SpeckleBlockDefinitionWrapper()
{
Base = new InstanceDefinitionProxy
{
name = name,
objects = objs.Select(o => o.ApplicationId!).ToList(),
maxDepth = 0 // represent newly created, top-level objects. actual depth calculation happens in GrasshopperBlockPacker
},
Name = name,
Objects = objs,
ApplicationId = validAppId
};
}
private bool CastToModelObject<T>(ref T target)
{
var type = typeof(T);
@@ -1,4 +1,5 @@
using Grasshopper.Kernel.Types;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
@@ -75,7 +76,7 @@ public partial class SpeckleBlockDefinitionWrapperGoo : GH_Goo<SpeckleBlockDefin
public SpeckleBlockDefinitionWrapper DeepCopy() =>
new()
{
Base = Value.InstanceDefinitionProxy.ShallowCopy(),
Base = (Base)Value.InstanceDefinitionProxy.ShallowCopy(),
Name = Value.Name,
Objects = Value.Objects.Select(o => o.DeepCopy()).ToList(),
ApplicationId = Value.ApplicationId
@@ -35,7 +35,7 @@ public class SpeckleBlockDefinitionWrapperParam
protected override Bitmap Icon => Resources.speckle_param_block_def;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override void RegisterRemoteIDs(GH_GuidTable idList)
{
@@ -11,11 +11,11 @@ using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public class SpeckleBlockInstanceWrapper : SpeckleObjectWrapper
public class SpeckleBlockInstanceWrapper : SpeckleGeometryWrapper
{
private InstanceProxy _instanceProxy;
private Transform _transform = Transform.Identity;
private List<SpeckleObjectWrapper>? _cachedTransformedObjects;
private List<SpeckleGeometryWrapper>? _cachedTransformedObjects;
private Transform _lastCachedTransform = Transform.Unset;
private const int MAX_DISPLAY_DEPTH = 3;
private SpeckleBlockDefinitionWrapper? _definition;
@@ -194,10 +194,10 @@ public class SpeckleBlockInstanceWrapper : SpeckleObjectWrapper
}
}
public override SpeckleObjectWrapper DeepCopy() =>
public override SpeckleGeometryWrapper DeepCopy() =>
new SpeckleBlockInstanceWrapper()
{
Base = InstanceProxy.ShallowCopy(),
Base = (Base)InstanceProxy.ShallowCopy(),
GeometryBase = GeometryBase?.Duplicate(),
Color = Color,
Material = Material,
@@ -224,7 +224,7 @@ public class SpeckleBlockInstanceWrapper : SpeckleObjectWrapper
/// Gets or builds a cached list of transformed objects for displaying.
/// Only rebuilds the cache when the transform changes, dramatically improving performance.
/// </summary>
private List<SpeckleObjectWrapper> GetTransformedObjectsForDisplay()
private List<SpeckleGeometryWrapper> GetTransformedObjectsForDisplay()
{
// Check if cache is valid (transform hasn't changed)
if (_cachedTransformedObjects != null && Transform.Equals(_lastCachedTransform))
@@ -233,7 +233,7 @@ public class SpeckleBlockInstanceWrapper : SpeckleObjectWrapper
}
// Rebuild cache
_cachedTransformedObjects = new List<SpeckleObjectWrapper>();
_cachedTransformedObjects = new List<SpeckleGeometryWrapper>();
_lastCachedTransform = Transform;
if (Definition?.Objects == null)
@@ -14,7 +14,7 @@ public partial class SpeckleBlockInstanceWrapperGoo
switch (source)
{
case InstanceReferenceGeometry instanceRef:
SpeckleObjectWrapperGoo objGoo = new();
SpeckleGeometryWrapperGoo objGoo = new();
objGoo.CastFrom(instanceRef);
if (objGoo.Value is SpeckleBlockInstanceWrapper instanceWrapper)
{
@@ -33,7 +33,7 @@ public partial class SpeckleBlockInstanceWrapperGoo
case ModelObject modelObject:
if (modelObject.ObjectType == ObjectType.InstanceReference)
{
SpeckleObjectWrapperGoo modelObjGoo = new();
SpeckleGeometryWrapperGoo modelObjGoo = new();
modelObjGoo.CastFrom(modelObject); // handles all model object casting like geo conversion, model object name and props and color and mat
if (modelObjGoo.Value is SpeckleBlockInstanceWrapper modelInstanceWrapper)
@@ -47,14 +47,14 @@ public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanc
case GH_Goo<SpeckleBlockInstanceWrapper> goo:
Value = goo.Value;
return true;
case SpeckleObjectWrapperGoo objWrapperGoo:
case SpeckleGeometryWrapperGoo objWrapperGoo:
if (objWrapperGoo.Value is SpeckleBlockInstanceWrapper objWrapper)
{
Value = objWrapper;
return true;
}
break;
case GH_Goo<SpeckleObjectWrapper> goo:
case GH_Goo<SpeckleGeometryWrapper> goo:
if (goo.Value is SpeckleBlockInstanceWrapper wrapper)
{
Value = wrapper;
@@ -75,7 +75,7 @@ public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanc
return false;
}
Base converted = SpeckleConversionContext.ConvertToSpeckle(instance);
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(instance);
Value = new SpeckleBlockInstanceWrapper()
{
GeometryBase = instance,
@@ -93,7 +93,7 @@ public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanc
{
switch (target)
{
case SpeckleObjectWrapperGoo:
case SpeckleGeometryWrapperGoo:
target = (T)(object)Value;
return true;
case Transform:
@@ -34,7 +34,7 @@ public class SpeckleBlockInstanceParam
public override Guid ComponentGuid => new("938CCD6E-B202-4A0C-9D68-ABD7683B0EDE");
protected override Bitmap Icon => Resources.speckle_param_block_instance;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override void RegisterRemoteIDs(GH_GuidTable idList)
{
@@ -92,7 +92,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
{
switch (element)
{
case SpeckleObjectWrapper o:
case SpeckleGeometryWrapper o:
o.Path = newPath;
o.Parent = this;
break;
@@ -122,7 +122,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
{
SpeckleCollectionWrapper c => c.DeepCopy(),
SpeckleBlockInstanceWrapper b => b.DeepCopy(),
SpeckleObjectWrapper o => o.DeepCopy(),
SpeckleGeometryWrapper o => o.DeepCopy(),
_ => e
}
)
@@ -156,7 +156,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
// then bake elements in this collection
foreach (var obj in Elements)
{
if (obj is SpeckleObjectWrapper so)
if (obj is SpeckleGeometryWrapper so)
{
if (bakeObjects)
{
@@ -66,7 +66,7 @@ public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH
public bool IsPreviewCapable => !VolatileData.IsEmpty;
private readonly List<SpeckleObjectWrapper> _previewObjects = new();
private readonly List<SpeckleGeometryWrapper> _previewObjects = new();
public void DrawViewportMeshes(IGH_PreviewArgs args)
{
@@ -118,7 +118,7 @@ public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH
FlattenForPreview(subCollWrapper);
}
if (element is SpeckleObjectWrapper objWrapper)
if (element is SpeckleGeometryWrapper objWrapper)
{
_previewObjects.Add(objWrapper);
var box = objWrapper.GeometryBase is null ? new() : objWrapper.GeometryBase.GetBoundingBox(false);
@@ -0,0 +1,278 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino;
using Rhino.Display;
using Speckle.Sdk;
using Speckle.Sdk.Models;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// <summary>
/// Wrapper around a data object and its converted speckle equivalent.
/// </summary>
public class SpeckleDataObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
{
/// <summary>
/// Name on the wrapper and wrapped Base (DataObject) are kept in sync here.
/// DataObject.name is the single source of truth - all geometry names inherit from it.
/// </summary>
public override string Name
{
get => DataObject.name;
set
{
DataObject.name = value;
SyncGeometryNames(); // Only sync names when name changes
}
}
/// <summary>
/// The wrapped DataObject.
/// </summary>
public DataObject DataObject { get; set; }
/// <summary>
/// Validated gateway to the typed property (validates and delegates to DataObject).
/// </summary>
public override required Base Base
{
get => DataObject;
set
{
if (value is not DataObject dataObject)
{
throw new ArgumentException("Cannot create data object wrapper from a non-DataObject Base");
}
DataObject = dataObject;
}
}
/// <summary>
/// Contains a list of <see cref="SpeckleGeometryWrapper"/>.
/// </summary>
/// <remarks>
/// A list of the wrappers as opposed to the geometry bases allows us to hold on to the color and material information.
/// However, this does make syncing of name, props etc. more challenging.
/// </remarks>
public List<SpeckleGeometryWrapper> Geometries { get; set; } = [];
private List<string> _path = [];
/// <summary>
/// The list of collection names that forms the full path to this object.
/// </summary>
public List<string> Path
{
get => _path;
set
{
_path = value;
SyncGeometryPath();
}
}
private SpeckleCollectionWrapper? _parent;
/// <summary>
/// Reference to the parent collection wrapper.
/// </summary>
public SpeckleCollectionWrapper? Parent
{
get => _parent;
set
{
_parent = value;
SyncGeometryParent();
}
}
/// <summary>
/// Try to keep DataObject.properties as source of truth.
/// </summary>
public SpecklePropertyGroupGoo Properties
{
get => new(DataObject.properties);
set
{
DataObject.properties = value.Unwrap();
SyncGeometryProperties(value); // Pass existing goo, only sync properties
}
}
public override IGH_Goo CreateGoo() => new SpeckleDataObjectWrapperGoo(this);
public override string ToString() =>
$"Speckle Data Object : {(string.IsNullOrWhiteSpace(Name) ? Base.speckle_type : Name)}";
/// <summary>
/// Draws preview for all geometries contained in this data object.
/// </summary>
public virtual void DrawPreview(IGH_PreviewArgs args, bool isSelected = false)
{
// iterate through all geometries and delegate to their existing preview logic
foreach (var geometry in Geometries)
{
geometry.DrawPreview(args, isSelected);
}
}
/// <summary>
/// Draws raw preview for all geometries using a specific material.
/// </summary>
public void DrawPreviewRaw(DisplayPipeline display, DisplayMaterial material)
{
foreach (var geometry in Geometries)
{
geometry.DrawPreviewRaw(display, material);
}
}
/// <summary>
/// Bakes the DataObject as a Rhino group containing all the display geometries.
/// </summary>
/// <param name="doc">Rhino doc to bake into</param>
/// <param name="objIds">Collection to store created objects GUIDs</param>
/// <param name="bakeLayerIndex">Layer index to bake geometries to (-1 for automatic)</param>
/// <param name="layersAlreadyCreated">Indicates whether layers have already been created or not</param>
/// <param name="baseLayerName">Base layer name for group naming</param>
public virtual void Bake(
RhinoDoc doc,
List<Guid> objIds,
int bakeLayerIndex = -1,
bool layersAlreadyCreated = false,
string? baseLayerName = null
)
{
// handles layer creation (if needed)
if (!layersAlreadyCreated && bakeLayerIndex < 0 && Path.Count > 0 && Parent != null)
{
bakeLayerIndex = Parent.Bake(doc, objIds, false);
if (bakeLayerIndex < 0)
{
return; // failed to create layers
}
}
if (Geometries.Count == 0)
{
return; // nothing to bake
}
// bake all display geometries as individual objects
List<Guid> geometryIds = [];
foreach (SpeckleGeometryWrapper geometryWrapper in Geometries)
{
if (geometryWrapper.GeometryBase != null)
{
// geometry wrappers should already be synced via prop setters, assume we're in a consistent state
List<Guid> currentGeometryIds = [];
geometryWrapper.Bake(doc, currentGeometryIds, bakeLayerIndex, true);
geometryIds.AddRange(currentGeometryIds);
}
}
// create a group for all geometries
if (geometryIds.Count > 1)
{
string groupName = CreateGroupName(baseLayerName);
try
{
int groupIndex = doc.Groups.Add(groupName, geometryIds);
if (groupIndex >= 0)
{
var group = doc.Groups.FindIndex(groupIndex);
objIds.Add(group.Id); // add group ID first
}
}
catch (Exception ex) when (!ex.IsFatal())
{
// group creation failed - continue like RhinoGroupBaker pattern
// causes: invalid object IDs? duplicate names? doc state issues?
}
// always add individual geometry IDs (whether group creation succeeded or failed)
objIds.AddRange(geometryIds);
}
}
/// <summary>
/// Creates a deep copy of this wrapper.
/// </summary>
public SpeckleDataObjectWrapper DeepCopy() =>
new()
{
Base = (Base)DataObject.ShallowCopy(),
Geometries = [.. Geometries.Select(g => g.DeepCopy())],
Properties = Properties,
ApplicationId = ApplicationId,
Name = Name,
Path = [.. Path],
Parent = Parent
};
/// <summary>
/// Syncs geometry names to match the DataObject name.
/// </summary>
private void SyncGeometryNames()
{
foreach (var geometry in Geometries)
{
geometry.Name = DataObject.name;
}
}
/// <summary>
/// Syncs geometry properties to match the DataObject properties.
/// </summary>
private void SyncGeometryProperties(SpecklePropertyGroupGoo propertyGoo)
{
foreach (var geometry in Geometries)
{
geometry.Properties = propertyGoo; // Reuse the passed goo
}
}
/// <summary>
/// Syncs geometry paths.
/// </summary>
private void SyncGeometryPath()
{
foreach (var geometry in Geometries)
{
geometry.Path = [.. Path];
}
}
/// <summary>
/// Syncs geometry parents.
/// </summary>
private void SyncGeometryParent()
{
foreach (var geometry in Geometries)
{
geometry.Parent = Parent;
}
}
/// <summary>
/// Creates a descriptive group name
/// </summary>
private string CreateGroupName(string? baseLayerName)
{
// Reference: RhinoGroupBaker.BakeGroups pattern:
// var groupName = (groupProxy.name ?? "No Name Group") + $" ({baseLayerName})";
string groupName = !string.IsNullOrEmpty(Name) ? Name : "No Name DataObject";
if (!string.IsNullOrEmpty(baseLayerName))
{
return $"{groupName} ({baseLayerName})";
}
return groupName;
}
}
@@ -0,0 +1,58 @@
#if RHINO8_OR_GREATER
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Grasshopper.Rhinoceros.Model;
using Rhino.DocObjects;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// <summary>
/// Rhino 8+ ModelObject casting support for SpeckleDataObjectWrapperGoo.
/// </summary>
public partial class SpeckleDataObjectWrapperGoo : GH_Goo<SpeckleDataObjectWrapper>, IGH_PreviewData
{
/// <summary>
/// Handles casting from Rhino 8+ ModelObjects to DataObject.
/// </summary>
private bool CastFromModelObject(object source)
{
switch (source)
{
case ModelObject modelObject:
var geometryWrapperGoo = new SpeckleGeometryWrapperGoo(modelObject);
if (geometryWrapperGoo.IsValid)
{
return CastFromSpeckleGeometryWrapper(geometryWrapperGoo.Value);
}
return false;
case RhinoObject rhinoObject: // can this happen? I'm inclined to say no, but who knows with gh
return CastFromModelObject((ModelObject)rhinoObject);
default:
return false;
}
}
/// <summary>
/// Handles casting from DataObject to Rhino 8+ ModelObjects.
/// </summary>
/// <remarks>
/// Only works if DataObject has exactly one geometry
/// </remarks>
private bool CastToModelObject<T>(ref T target)
{
if (Value.Geometries.Count != 1)
{
return false;
}
// extract first (and only) geometry and delegate to SpeckleGeometryWrapperGoo
var firstGeometry = Value.Geometries[0];
var geometryGoo = new SpeckleGeometryWrapperGoo(firstGeometry);
// using existing ModelObject casting logic from SpeckleGeometryWrapperGoo
return geometryGoo.CastTo(ref target);
}
}
#endif
@@ -0,0 +1,199 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Speckle.Sdk.Models;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// <summary>
/// Goo wrapper for SpeckleDataObjectWrapper.
/// </summary>
public partial class SpeckleDataObjectWrapperGoo : GH_Goo<SpeckleDataObjectWrapper>, IGH_PreviewData
{
/// <summary>
/// Creates goo with a DataObject wrapper.
/// </summary>
public SpeckleDataObjectWrapperGoo(SpeckleDataObjectWrapper value)
{
Value = value;
}
/// <summary>Parameterless constructor</summary>
/// <remarks>Should only be used for casting!</remarks>
public SpeckleDataObjectWrapperGoo()
{
Value = new()
{
Base = new DataObject
{
name = "",
displayValue = [],
properties = new Dictionary<string, object?>()
},
Geometries = []
};
}
public override bool IsValid => Value?.DataObject is not null && Value.ApplicationId is not null;
public override string TypeName => "Speckle Data Object";
public override string TypeDescription => "Represents a Speckle data object";
public override IGH_Goo Duplicate() => new SpeckleDataObjectWrapperGoo(Value.DeepCopy());
public override string ToString() =>
$"Speckle Data Object : {(string.IsNullOrWhiteSpace(Value.Name) ? Value.Base.speckle_type : Value.Name)}";
/// <summary>
/// Handles casting from other types to DataObject wrapper.
/// </summary>
public override bool CastFrom(object source)
{
switch (source)
{
// 1 - data object → data object
case SpeckleDataObjectWrapper wrapper:
Value = wrapper;
return true;
case SpeckleDataObjectWrapperGoo wrapperGoo:
Value = wrapperGoo.Value;
return true;
// 2 - speckle geometry → data object
case SpeckleBlockInstanceWrapper:
case SpeckleBlockInstanceWrapperGoo:
// TODO: We need to have a larger discussion around allowing instances within data objects.
// We don't allow instances within data objects for now
return false;
case SpeckleGeometryWrapper geometryWrapper:
return CastFromSpeckleGeometryWrapper(geometryWrapper);
case SpeckleGeometryWrapperGoo geometryWrapperGoo:
return CastFromSpeckleGeometryWrapper(geometryWrapperGoo.Value);
// 3 - gh geometry → data object
case IGH_GeometricGoo geometricGoo:
return CastFromIghGeometricGoo(geometricGoo);
// 4 - model object → data object (Rhino 8+)
default:
return CastFromModelObject(source); // Try ModelObject casting (will return false on Rhino 7)
}
}
/// <summary>
/// Handles casting to other types from DataObject wrapper.
/// </summary>
/// <remarks>
/// Only allows geometry casting when DataObject has exactly one geometry.
/// </remarks>
public override bool CastTo<T>(ref T target)
{
switch (target)
{
case DataObject:
target = (T)(object)Value.DataObject;
return true;
case SpeckleDataObjectWrapper:
target = (T)(object)Value;
return true;
case SpeckleDataObjectWrapperGoo:
target = (T)(object)this;
return true;
case Base:
target = (T)(object)Value.DataObject;
return true;
// for geometry types, only allow if exactly one geometry
default:
if (Value.Geometries.Count == 1)
{
var singleGeometry = Value.Geometries[0];
var geometryGoo = new SpeckleGeometryWrapperGoo(singleGeometry);
// this should handle all IGH_GeometricGoo types and ModelObjects
return geometryGoo.CastTo(ref target);
}
return CastToModelObject(ref target);
}
}
#if !RHINO8_OR_GREATER
private bool CastFromModelObject(object _) => false;
private bool CastToModelObject<T>(ref T _) => false;
#endif
public void DrawViewportWires(GH_PreviewWireArgs args)
{
// TODO?
}
/// <summary>
/// Draws viewport meshes/surfaces for the data object.
/// </summary>
public void DrawViewportMeshes(GH_PreviewMeshArgs args) => Value.DrawPreviewRaw(args.Pipeline, args.Material);
/// <summary>
/// Calculates the bounding box for all geometries in this data object.
/// </summary>
public BoundingBox ClippingBox
{
get
{
var clippingBox = new BoundingBox();
foreach (var geometry in Value.Geometries)
{
if (geometry.GeometryBase != null)
{
var box = geometry.GeometryBase.GetBoundingBox(false);
clippingBox.Union(box);
}
}
return clippingBox;
}
}
/// <summary>
/// Creates a single-element DataObject from <see cref="SpeckleGeometryWrapper"/> (one geometry → one display value).
/// </summary>
private bool CastFromSpeckleGeometryWrapper(SpeckleGeometryWrapper geometryWrapper)
{
// create DataObject with single displayValue
DataObject dataObject =
new()
{
name = geometryWrapper.Name,
displayValue = [geometryWrapper.Base],
properties = geometryWrapper.Properties.Unwrap(),
applicationId = geometryWrapper.ApplicationId
};
// create wrapper - Name, ApplicationId and Properties kept in sync with wrapped DataObject through getters/setters
// geometry will inherit DataObject properties through the syncing (hopefully)
Value = new SpeckleDataObjectWrapper
{
Base = dataObject,
Geometries = [geometryWrapper],
Path = [.. geometryWrapper.Path],
Parent = geometryWrapper.Parent
};
return true;
}
private bool CastFromIghGeometricGoo(IGH_GeometricGoo geometricGoo)
{
SpeckleGeometryWrapperGoo geoGoo = new();
if (geoGoo.CastFrom(geometricGoo))
{
return CastFromSpeckleGeometryWrapper(geoGoo.Value);
}
return false;
}
}
@@ -0,0 +1,112 @@
using Grasshopper.Kernel;
using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.Components;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public class SpeckleDataObjectParam : GH_Param<SpeckleDataObjectWrapperGoo>, IGH_BakeAwareObject, IGH_PreviewObject
{
public SpeckleDataObjectParam()
: this(GH_ParamAccess.item) { }
public SpeckleDataObjectParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpeckleDataObjectParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpeckleDataObjectParam(GH_ParamAccess access)
: base(
"Speckle Data Object",
"SDO",
"A Speckle data object with structured properties and display geometries",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
public override Guid ComponentGuid => new("47B930F9-587B-4A88-8CEB-19986E60BA61");
protected override Bitmap Icon => Resources.speckle_param_dataobject;
public override GH_Exposure Exposure => GH_Exposure.secondary;
bool IGH_BakeAwareObject.IsBakeCapable => !VolatileData.IsEmpty;
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, List<Guid> objIds)
{
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleDataObjectWrapperGoo goo)
{
goo.Value.Bake(doc, objIds);
}
}
}
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> objIds)
{
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleDataObjectWrapperGoo goo)
{
int layerIndex = goo.Value.Path.Count == 0 ? att.LayerIndex : -1;
bool layerCreated = goo.Value.Path.Count == 0;
goo.Value.Bake(doc, objIds, layerIndex, layerCreated);
}
}
}
/// <summary>
/// Draws viewport wires for all data objects in this parameter.
/// </summary>
public void DrawViewportWires(IGH_PreviewArgs args)
{
// Following the pattern - most wire drawing is handled in DrawViewportMeshes
// Keep this minimal like other parameter types
}
/// <summary>
/// Draws viewport meshes for all data objects in this parameter.
/// </summary>
public void DrawViewportMeshes(IGH_PreviewArgs args)
{
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleDataObjectWrapperGoo goo)
{
goo.Value.DrawPreview(args, isSelected);
}
}
}
public bool Hidden { get; set; }
public bool IsPreviewCapable => !VolatileData.IsEmpty;
/// <summary>
/// Calculates the clipping box for all data objects in this parameter.
/// </summary>
public BoundingBox ClippingBox
{
get
{
var clippingBox = new BoundingBox();
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleDataObjectWrapperGoo goo)
{
clippingBox.Union(goo.ClippingBox);
}
}
return clippingBox;
}
}
private bool OwnerSelected() => Attributes?.Parent?.Selected ?? false;
}
@@ -11,7 +11,7 @@ namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// <summary>
/// Wrapper around a geometry base object and its converted speckle equivalent.
/// </summary>
public class SpeckleObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
public class SpeckleGeometryWrapper : SpeckleWrapper, ISpeckleCollectionObject
{
public override required Base Base { get; set; }
@@ -40,7 +40,7 @@ public class SpeckleObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
public SpeckleMaterialWrapper? Material { get; set; }
public override string ToString() =>
$"Speckle Object : {(string.IsNullOrWhiteSpace(Name) ? Base.speckle_type : Name)}";
$"Speckle Geometry : {(string.IsNullOrWhiteSpace(Name) ? Base.speckle_type : Name)}";
public virtual void DrawPreview(IGH_PreviewArgs args, bool isSelected = false)
{
@@ -145,10 +145,10 @@ public class SpeckleObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
objIds.Add(guid);
}
public virtual SpeckleObjectWrapper DeepCopy() =>
public virtual SpeckleGeometryWrapper DeepCopy() =>
new()
{
Base = Base.ShallowCopy(),
Base = (Base)Base.ShallowCopy(),
GeometryBase = GeometryBase?.Duplicate(),
Color = Color,
Material = Material,
@@ -175,7 +175,7 @@ public class SpeckleObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
return attributes;
}
public override IGH_Goo CreateGoo() => new SpeckleObjectWrapperGoo(this);
public override IGH_Goo CreateGoo() => new SpeckleGeometryWrapperGoo(this);
protected virtual void AddPropertiesToAttributes(ObjectAttributes attributes) =>
Properties?.AssignToObjectAttributes(attributes);
@@ -11,9 +11,9 @@ using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>, IGH_PreviewData
{
public SpeckleObjectWrapperGoo(ModelObject mo)
public SpeckleGeometryWrapperGoo(ModelObject mo)
{
CastFrom(mo);
}
@@ -35,20 +35,20 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
private bool HandleModelObject(ModelObject modelObject)
{
if (RhinoDoc.ActiveDoc.Objects.FindId(modelObject.Id ?? Guid.Empty)?.Geometry is not GeometryBase geometryBase)
modelObject.CastTo<IGH_GeometricGoo>(out IGH_GeometricGoo? geometryGoo);
if (geometryGoo is null)
{
throw new InvalidOperationException(
$"Could not retrieve geometry from Model Object {modelObject.ObjectType}. Did you forget to bake these objects in your document?"
);
throw new InvalidOperationException($"Could not retrieve geometry from model object.");
}
Base converted = SpeckleConversionContext.ConvertToSpeckle(geometryBase);
GeometryBase geometryBase = geometryGoo.ToGeometryBase();
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(geometryBase);
// get layer, props, color, and mat
SpeckleCollectionWrapper? collection = GetLayerCollectionFromModelObject(modelObject);
SpecklePropertyGroupGoo? props = GetPropsFromModelObjectAndAssignToBase(modelObject, converted);
Color? color = GetColorFromModelObject(modelObject);
SpeckleMaterialWrapper? material = GetMaterialFromModelObject(modelObject);
Color? color = GetColorFromModelObject(modelObject, material);
// get the definition if this is an instance
SpeckleBlockDefinitionWrapper? definition = GetBlockDefinition(geometryBase);
@@ -61,7 +61,7 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
collection,
color,
material,
modelObject.Id.ToString(),
modelObject.Id?.ToString(),
definition
);
}
@@ -154,7 +154,7 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
{
// we need to retrieve the actual material by the material source (otherwise will return default material for anything other than by object)
Guid? matId = null;
switch (modelObject.Render.Material?.Source)
switch (modelObject.Render?.Material?.Source)
{
case ObjectMaterialSource.MaterialFromLayer:
matId = modelObject.Layer.Material.Id;
@@ -177,10 +177,11 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
SpeckleCollectionWrapper? parent,
Color? color,
SpeckleMaterialWrapper? mat,
string appId,
string? appId,
SpeckleBlockDefinitionWrapper? definition = null
)
{
string validAppId = string.IsNullOrWhiteSpace(appId) ? Guid.NewGuid().ToString() : appId!;
Value = geometryBase is InstanceReferenceGeometry instance
? new SpeckleBlockInstanceWrapper()
{
@@ -189,22 +190,24 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
Transform = instance.Xform,
Definition = definition, // May be null in pure Grasshopper workflows
Parent = parent,
Path = parent?.Path ?? new(),
Name = name,
Color = color,
Material = mat,
Properties = props,
ApplicationId = appId
ApplicationId = validAppId
}
: new SpeckleObjectWrapper()
: new SpeckleGeometryWrapper()
{
GeometryBase = geometryBase,
Base = @base,
Parent = parent,
Path = parent?.Path ?? new(),
Name = name,
Color = color,
Material = mat,
Properties = props,
ApplicationId = appId
ApplicationId = validAppId
};
return true;
@@ -268,11 +271,11 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
return null;
}
private Color? GetColorFromModelObject(ModelObject modelObject)
private Color? GetColorFromModelObject(ModelObject modelObject, SpeckleMaterialWrapper? material)
{
// we need to retrieve the actual color by the color source (otherwise will return default color for anything other than by object)
int? argb = null;
switch (modelObject.Display.Color?.Source)
switch (modelObject.Display?.Color?.Source)
{
case ObjectColorSource.ColorFromLayer:
argb = modelObject.Layer.DisplayColor?.ToArgb();
@@ -281,8 +284,10 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
argb = modelObject.Display.Color?.Color.ToArgb();
break;
case ObjectColorSource.ColorFromMaterial:
Rhino.Render.RenderMaterial? mat = GetRenderMaterial(modelObject);
argb = mat?.ToMaterial(Rhino.Render.RenderTexture.TextureGeneration.Skip)?.DiffuseColor.ToArgb();
if (material is not null)
{
argb = material.Material.diffuse;
}
break;
default:
break;
@@ -6,20 +6,20 @@ using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>, IGH_PreviewData
{
public override bool IsValid => Value.Base is not null && Value.ApplicationId is not null;
public override string TypeName => "Speckle Object";
public override string TypeName => "Speckle Geometry";
public override string TypeDescription => "Represents a geometry object from Speckle";
public SpeckleObjectWrapperGoo(SpeckleObjectWrapper value)
public SpeckleGeometryWrapperGoo(SpeckleGeometryWrapper value)
{
Value = value;
}
/// <summary>Parameterless constructor</summary>
/// <remarks>Should only be used for casting!</remarks>
public SpeckleObjectWrapperGoo()
public SpeckleGeometryWrapperGoo()
{
Value = new()
{
@@ -30,10 +30,10 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
};
}
public override IGH_Goo Duplicate() => new SpeckleObjectWrapperGoo(Value.DeepCopy());
public override IGH_Goo Duplicate() => new SpeckleGeometryWrapperGoo(Value.DeepCopy());
public override string ToString() =>
$"Speckle Object : {(string.IsNullOrWhiteSpace(Value.Name) ? Value.Base.speckle_type : Value.Name)}";
$"Speckle Geometry : {(string.IsNullOrWhiteSpace(Value.Name) ? Value.Base.speckle_type : Value.Name)}";
/// <summary>
/// Casts from Speckle objects, geometry base, and model objects.
@@ -45,18 +45,22 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
{
switch (source)
{
case SpeckleObjectWrapper wrapper:
case SpeckleGeometryWrapper wrapper:
Value = wrapper;
return true;
case SpeckleObjectWrapperGoo wrapperGoo:
case SpeckleGeometryWrapperGoo wrapperGoo:
Value = wrapperGoo.Value;
return true;
case SpeckleBlockInstanceWrapperGoo instanceWrapperGoo:
Value = instanceWrapperGoo.Value;
return true;
case SpeckleDataObjectWrapperGoo dataObjectGoo:
return CastFromDataObject(dataObjectGoo.Value);
case SpeckleDataObjectWrapper dataObjectWrapper:
return CastFromDataObject(dataObjectWrapper);
case IGH_GeometricGoo geometricGoo:
GeometryBase gb = geometricGoo.ToGeometryBase();
Base converted = SpeckleConversionContext.ConvertToSpeckle(gb);
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(gb);
string appId = Guid.NewGuid().ToString();
Value = gb is InstanceReferenceGeometry instance
? new SpeckleBlockInstanceWrapper()
@@ -66,7 +70,7 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
Transform = instance.Xform,
ApplicationId = appId,
}
: new SpeckleObjectWrapper()
: new SpeckleGeometryWrapper()
{
GeometryBase = gb,
Base = converted,
@@ -211,6 +215,23 @@ public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH
return false;
}
/// <summary>
/// Handles casting from DataObject to SpeckleGeometryWrapper.
/// Only works if DataObject has exactly one geometry.
/// </summary>
private bool CastFromDataObject(SpeckleDataObjectWrapper dataObjectWrapper)
{
// Apply single-geometry constraint
if (dataObjectWrapper.Geometries.Count != 1)
{
return false;
}
// Extract the single geometry
Value = dataObjectWrapper.Geometries[0];
return true;
}
public void DrawViewportWires(GH_PreviewWireArgs args)
{
// TODO ?

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