Compare commits

...

86 Commits

Author SHA1 Message Date
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
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 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 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
Jedd Morgan 50d69a0f0e Bump GrasshopperAsyncComponent (#976)
.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
2025-07-11 09:29:12 +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
Claire Kuang 5e93f23d06 remove new app id on passthrough nodes (#964)
.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
2025-07-03 14:25:40 +01:00
Adam Hathcock 471613a77f Merge pull request #961 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 3.5.0 release
2025-06-30 16:17:10 +01:00
Adam Hathcock 778ddf4b47 Merge pull request #962 from specklesystems/main-dev
Main to dev
2025-06-30 16:08:01 +01:00
Adam Hathcock 180c1d8345 Merge branch 'dev' into main-dev 2025-06-30 16:03:14 +01:00
Claire Kuang ae847d8625 chore(grasshopper): update icon order (#963)
* cleans up params

* Update SpeckleMaterialWrapperParam.cs

* update operations icons
2025-06-30 15:03:01 +00:00
Adam Hathcock a4ba43a632 Merge branch 'dev' into main-dev 2025-06-30 15:58:51 +01:00
Oğuzhan Koral b7d23aea8d Feat(gh): sign in component (#955)
* Sign in component

* Delete debug writeline

* adds icon

* changes some text

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-30 14:52:03 +00:00
Adam Hathcock bb8634f650 Merge branch 'dev' into main-dev 2025-06-30 15:42:48 +01:00
Adam Hathcock 34c56e7c41 Update to SDK 3.4.4 (#960) 2025-06-30 14:40:03 +00:00
Claire Kuang a92986c1ce Update SpecklePropertyGoo.cs (#959) 2025-06-30 15:23:07 +01:00
Björn Steinhagen 831e5c0a82 feat(grasshopper): block support (#949)
* feat(grasshopper): add `SpeckleBlockDefinition` support (#891)

* feat: initial commit `SpeckleBlockDefinitionWrapper`

* feat: `SpeckleBlockDefinitionWrapper``Bake`and `DrawPreview`

* feat: Casting for `SpeckleBlockDefinitionWrapperGoo`

* feat: `SpeckleBlockDefinitionParam` functionality to `BakeGeometry`

* feat: `CreateSpeckleBlockDefinition`

* fix: casting and other things

* fix: defining objects of a definition, not all instance objects (LOL)

* refactor: preview

- still not previewing nicely though

* refactor: transform in helpers class

* chore: `CreateSpeckleBlockDefinition` icon

* docs: pr comment re. api limitation

* feat(grasshopper): add `SpeckleBlockInstance` support (#893)

* feat: add `SpeckleBlockInstanceWrapper` class

empty

* chore: adds `SpeckleBlockInstanceWrapperGoo`

* chore: adds `SpeckleBlockInstanceParameters`

* chore: `SpeckleBlockInstanceWrapper` wrapped `Base` is `InstanceProxy`

* docs: tiny typo

* feat: `SpeckleBlockInstanceWrapper` and parameters

* fix: missing default constructor and naming refactor

* feat: initial commit for `SpeckleBlockDefinitionWrapper`

* Revert "feat: initial commit for `SpeckleBlockDefinitionWrapper`"

This reverts commit a3c1fcf978.

* fix: `SpeckleBlockInstanceWrapper`

- `type` correction for `Properties`
- keeping transforms in sync (kinda, waiting for PR merge)
- `DeepCopy()`

* fix: `SpeckleBlockInstanceWrapperGoo`

- `CastFrom`and `CastTo`methods
- `Duplicate` method

* fix: `SpeckleBlockInstanceParam`

- adds missing implementations

* feat: placeholders for `Bake` and `DrawPreview`

* feat: `CreateSpeckleBlockInstance`

* feat: `ModelObjects`

* fix: Rhino7 preprocessor directives

* fix: casting for GH created / referenced instances

* fix: `Transform` casting

* chore: icon for `CreateSpeckleBlockInstance`

* feat: casting to and from `CreateSpeckleBlockInstance`

* chore: removing redundant code

* feat: validating `SpeckleWrapper`

* chore: updates to `.ToString()`

* fix: recompute when `doc` definition changes

* refactor: `!string.IsNullOrWhiteSpace()`

* feat: deconstruct

* fix: cast for `RhinoObject`

* refactor: consolidate duplicate baking code

* Cleans up casting logic in instance and definitions

* refactor: `RhinoObject` coming from referenced instances

* docs: comments

* refactor: code clean up for `ModelInstanceDefinition`

* refactor: consistency across wrappers

* fix: intercept block definitions from pure gh

* docs: gh defined model block definition

* docs: api limitation on `ModelInstanceDefinition` constructor

* fix: stop instances from overwriting shared block definitions on bake

* feat: disallowing block definitions in collections for now

* feat: baking instances within collection

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* feat (grasshopper): update publish to handle block instances and definitions (#909)

* feat: add `SpeckleBlockInstanceWrapper` class

empty

* chore: adds `SpeckleBlockInstanceWrapperGoo`

* chore: adds `SpeckleBlockInstanceParameters`

* chore: `SpeckleBlockInstanceWrapper` wrapped `Base` is `InstanceProxy`

* docs: tiny typo

* feat: `SpeckleBlockInstanceWrapper` and parameters

* fix: missing default constructor and naming refactor

* feat: initial commit for `SpeckleBlockDefinitionWrapper`

* Revert "feat: initial commit for `SpeckleBlockDefinitionWrapper`"

This reverts commit a3c1fcf978.

* fix: `SpeckleBlockInstanceWrapper`

- `type` correction for `Properties`
- keeping transforms in sync (kinda, waiting for PR merge)
- `DeepCopy()`

* fix: `SpeckleBlockInstanceWrapperGoo`

- `CastFrom`and `CastTo`methods
- `Duplicate` method

* fix: `SpeckleBlockInstanceParam`

- adds missing implementations

* feat: placeholders for `Bake` and `DrawPreview`

* feat: `CreateSpeckleBlockInstance`

* feat: `ModelObjects`

* fix: Rhino7 preprocessor directives

* fix: casting for GH created / referenced instances

* fix: `Transform` casting

* chore: icon for `CreateSpeckleBlockInstance`

* feat: casting to and from `CreateSpeckleBlockInstance`

* chore: removing redundant code

* feat: validating `SpeckleWrapper`

* chore: updates to `.ToString()`

* fix: recompute when `doc` definition changes

* refactor: `!string.IsNullOrWhiteSpace()`

* feat: deconstruct

* fix: cast for `RhinoObject`

* refactor: consolidate duplicate baking code

* Cleans up casting logic in instance and definitions

* refactor: `RhinoObject` coming from referenced instances

* docs: comments

* refactor: code clean up for `ModelInstanceDefinition`

* refactor: consistency across wrappers

* fix: intercept block definitions from pure gh

* docs: gh defined model block definition

* docs: api limitation on `ModelInstanceDefinition` constructor

* fix: stop instances from overwriting shared block definitions on bake

* feat: disallowing block definitions in collections for now

* feat: baking instances within collection

* feat: update publish to handle block instances and definitions

* refactpr: consolidating switch statements

* feat: runtime message on unsupported inputs for creating collections

* docs: `GrasshopperBlockPacker`

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* Update Helpers.cs

* Update Helpers.cs

* fix: removing unresolved references

`ISpeckleGoo` removed on `props` pr

* refactor: `SpeckleBlockInstanceWrapper` to use `BakingHelper`

* fix: compiler errors

* fix: still need explicit cases

* refactor(grasshopper): make `SpeckleBlockInstanceWrapper` inherit from `SpeckleObjectWrapper` (#917)

* feat: first pass

* fix: deconstruct speckle params

* refactor: cleanup

* docs: `GeometryBase`note on `SpeckleBlockInstanceWrapper`

* refactor: CreateGoo() to remove type checking in ExpandCollection

* refactor: incorporating pr comments

* fixes deconstruct component

adds removed support for materials and collections

consolidates logic for name and output creation

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* adds new icons for blocks

* feat(grasshopper): nested block support (#934)

* fix dev merge changes

* feat(grasshopper): adds color and materials to instances (#945)

* adds color and materials to instances

also includes refactoring for passthrough nodes

* some more appid and name changes

* removes unnecessary casting

also fixes some bugs with missing applicationIds on cast

* Update SpeckleBlockInstanceWrapper.ModelObjects.cs

* Update SpeckleBlockInstancePassthrough.cs

* fixes broken url

* feat(grasshopper): update load to handle blocks (#947)

* feat: block recognition in receive pipeline

* feat: `GrasshopperBlockUnpacker`

* feat: working nested blocks and removing `LocalToGlobalUnpacker`

* chore: cleanup

* refactor: remove unnecessary check

* refactor: better naming

* pr review fixes

* adds colors and materials to instance and defs on load

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* adds create goo to wrapper class

also adds missing instance casting to create collection

* moves all param classes to their own file (#950)

* chore: xml comment on the parameterless constructor

* fix: `SpeckleBlockInstanceWrapper` inheritance in `DeepCopy()`

* fix: preserve id in `SpeckleBlockInstanceWrapper` round-trip casting

* fix: url broken

* fix: auto-sync block instance definition references

* fix: throw if id null

review comment

* fix(grasshopper): improve block instance preview for nested instances (#951)

* fix: display block instances

- problem on deeply nested instances
- good poc

* chore: missing method in block definition

* fix: re-instating changes after param classes refactor

* fix: add 3-level depth limit for block instance DrawPreview methods

* fixes collection preview and some isvalid props

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* chore: removing todo comments

* fix(grasshopper): a bunch of instance vs object casting issues (#954)

* adds instance converter and filter objects casting helper

* adds missing casting an fixes casting to speckle object passthrough

* cleans up more casting logic

* more casting fixes

* Update InstanceReferenceGeometryToSpeckleConverter.cs

* fixes goos inputs and outputs for expand collection, query, filter, etc

* removes deep copying from casting

* more mutations on object passthrough

* fixes missing model object instance  casting properties

* fixes build

* model instance and def casting issues for nesting

* im literally crying

* Update InstanceReferenceGeometryToSpeckleConverter.cs

* fix: POC disabling cyclomatic complexity

* refactors speckle object code to reduce complexity

* further strips model object casting since rhino objects can be passed as model objects

* light at the end of the tunnel

* last commit i swear

fixes model insstance casting since this actually registers as IGH_GeometricGoo of type reference

* last LAST commit on god

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>

* fix(grasshopper): receive pipeline polish (#957)

* fix: loading, publish and query

* docs: explanations

* fix: format

* docs: remove old comment

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* fix(grasshopper): prevent duplicate nested block instances in receive (#958)

* fix: `consumedObjectIds` tracking preventing duplicate nested block instances

* refactor: remove redundant `isDefinitionObject` logic

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-30 15:36:02 +02:00
Oğuzhan Koral 2adfee6f49 Reset menu list on closed according to search text (#956) 2025-06-27 19:14:23 +01:00
kekesidavid 4bb67318a8 feat(revit): Reference Point Setting on Receive (#948)
* Receive settings

* wip

* wip

* resolved warnings

* cleanup

* cleanup

* netlify url restored

* review comments fixed

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
2025-06-27 13:53:30 +02:00
Adam Hathcock dadf07a3c3 Detect by OS, Windows only (#943) 2025-06-25 10:51:13 +01:00
Claire Kuang 7f6c8bb1a3 fix(grasshopper): deconstructing collections now has elements (#946)
* Update DeconstructSpeckleParam.cs

* Update DeconstructSpeckleParam.cs
2025-06-25 09:24:52 +00:00
Adam Hathcock eb8db87d9f feat (Grasshopper) dev enable account auth by dev token (#937)
* adds url by token component

* Add gubbins for passing Account objects and AccountResource objects to include token usage

* format

* add bits to make things work?

* revert usage of SpeckleApplication

* review fixes

* more reverts

* Fix tests

* token is correct now

* fix build

* fixes url resource exception and adds new icon

* Made model cards dumb and moved conversions

* can build NW

* actually, remove dead code

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-25 08:42:37 +00:00
Claire Kuang bf91a8d6a3 chore(grasshopper): various touchups (#942)
* adds view in browser and exposures

* updates categories

* removes obscure
2025-06-24 11:35:40 +01:00
Oğuzhan Koral bce949951c Scale elevation with internal (#941) 2025-06-23 18:58:04 +03:00
Oğuzhan Koral 744b185cfe Feat(revit): proxify levels with their all props (#940)
* Unpack levels as proxy

* Dynamically attach elevation and units to data object

* Do not add level properties to collection

* Bump SDK to 3.4.3
2025-06-23 17:48:18 +03:00
Adam Hathcock 8919ba2491 Merge pull request #939 from specklesystems/adam/revert-non-windows-build
Revert "(feat) non windows building (#935)"
2025-06-23 15:48:25 +03:00
Claire Kuang fc82dda558 fix(rhino): toolbar now loads in rhino 7 (#932)
* Update Speckle.Connectors.Rhino.rui

* Update Speckle.Connectors.Rhino.rui

* Update Speckle.Connectors.Rhino.rui
2025-06-23 11:44:33 +00:00
Claire Kuang d70fe9df73 adds latest version handlers (#938)
and reformats dividers and toolstrips
2025-06-23 09:56:43 +01:00
Claire Kuang d2c78695fe adds owner selected to collection and object param (#933)
also changes the output type of create collection to be a collection param, to enable previewing

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-06-20 14:29:28 +01:00
Adam Hathcock 4aa087e38d (feat) non windows building (#935)
* add check for non-windows building

* Remove mac solution
2025-06-20 12:49:23 +01:00
Adam Hathcock a3cbb1bcb6 Merge pull request #936 from specklesystems/adam/recieve-spam
Don't mark cancelled or conversion exceptions as errors
2025-06-20 12:48:43 +01:00
Adam Hathcock 60823dda97 Remove error from test 2025-06-20 10:59:08 +01:00
Adam Hathcock 40650bfed2 Don't mark cancelled or conversion exceptions as errors 2025-06-20 10:38:48 +01:00
Claire Kuang b0423af14b feat(grasshopper): adds create properties by keyvalue (#930)
* adds keyvalue component

* fixes some bugs

* more bugs

* adds modes to speckle props passthrough node

* Update Speckle.Connectors.GrasshopperShared.projitems

* adds new icon

* removes unused method
2025-06-20 08:14:14 +00:00
kekesidavid 636af5c7c2 fix (civil3d) extract properties from c3d block references (#931)
* extract properties from c3d block references

* cleans up property set structure and adds properties to autocad root to speckle

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-19 14:19:39 +01:00
Jedd Morgan efc532fce0 refactor(grsashopper): Update grasshopper async to 2.0.1 (#919)
* easy changes

* generics are fun

* second pass

* reverted cancellation changes

* bring back receive

* ConfigureAwait(false);

* worker count

* lock files, that will need to be changed before pr

* updates

* notnull

* minor tweaks

* made stable tag

* locks

* locks

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-19 06:24:26 +00:00
Claire Kuang 4bf54550aa Update EllipseToHostConverter.cs (#929) 2025-06-17 15:06:31 +00:00
Claire Kuang 43773c63eb feat(grasshopper): adds variable type outputs to the query objects node (#926)
* adds type outputs to query node

* Update Speckle.Connectors.GrasshopperShared.projitems

* fixes output param creation bug

* fixes last index bug

* removes speckle exception from query node

* adds better error
2025-06-17 13:57:31 +01:00
340 changed files with 13515 additions and 4612 deletions
-20
View File
@@ -81,7 +81,6 @@ public static class Solutions
var revit = Consts.ProjectGroups.Single(x => x.HostAppSlug.Equals("revit"));
await GenerateConnector(connectors, revit, "Revit.Local");
await GenerateMacSolutions();
}
public static async Task GenerateConnector(SolutionModel connectors, ProjectGroup group, string? name)
@@ -115,23 +114,4 @@ public static class Solutions
var connectorsSln = Path.Combine(DIRECTORY, solutionName);
return await SolutionSerializers.SlnFileV12.OpenAsync(connectorsSln, default);
}
public static async Task GenerateMacSolutions()
{
var connectors = await GetFullSlnx();
var foldersToRemove = connectors
.SolutionFolders.Where(x =>
//need base folder
!x.Path.Equals("/Connectors/")
//don't grab all
&& (x.Path.StartsWith("/Connectors/"))
)
.ToList();
foreach (var folderToRemove in foldersToRemove)
{
connectors.RemoveFolder(folderToRemove);
}
var sln = Path.Combine(DIRECTORY, $"Speckle.Connectors.Mac.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
}
}
+6 -6
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<AutoCADDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
@@ -12,11 +12,11 @@
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion)\%(RecursiveDir)" SourceFiles="@(AutoCADDLLs)" />
</Target>
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<Civil3DDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
@@ -24,11 +24,11 @@
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion)\%(RecursiveDir)" SourceFiles="@(Civil3DDLLs)" />
</Target>
<PropertyGroup Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<PropertyGroup Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<PropertyGroup Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<PropertyGroup Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
<StartArguments>/product C3D</StartArguments>
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -2,6 +2,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -17,7 +18,7 @@ public abstract class AutocadReceiveBaseBinding(
private ReceiveBindingUICommands Commands { get; } = new(parent);
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
protected abstract void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc);
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
@@ -3,6 +3,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
@@ -24,7 +25,7 @@ public sealed class AutocadReceiveBinding : AutocadReceiveBaseBinding
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
protected override void InitializeSettings(IServiceProvider serviceProvider)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Instances;
using Speckle.Converters.AutocadShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.DoubleNumerics;
using Speckle.Sdk;
@@ -17,16 +18,19 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
{
private readonly IHostToSpeckleUnitConverter<UnitsValue> _unitsConverter;
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
private readonly IPropertiesExtractor _propertiesExtractor;
private readonly ILogger<AutocadInstanceUnpacker> _logger;
public AutocadInstanceUnpacker(
IHostToSpeckleUnitConverter<UnitsValue> unitsConverter,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectsManager,
IPropertiesExtractor propertiesExtractor,
ILogger<AutocadInstanceUnpacker> logger
)
{
_unitsConverter = unitsConverter;
_instanceObjectsManager = instanceObjectsManager;
_propertiesExtractor = propertiesExtractor;
_logger = logger;
}
@@ -71,6 +75,13 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
transform = GetMatrix(instance.BlockTransform.ToArray()),
units = _unitsConverter.ConvertOrThrow(Application.DocumentManager.CurrentDocument.Database.Insunits)
};
var properties = _propertiesExtractor.GetProperties(instance);
if (properties?.Count > 0)
{
instanceProxy["properties"] = properties;
}
_instanceObjectsManager.AddInstanceProxy(instanceId, instanceProxy);
// For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found.
@@ -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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -4,6 +4,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
@@ -31,7 +32,7 @@ public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding
// POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters.
// POC: We need a separate receive binding for civil3d due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Civil3dConversionSettings>>()
@@ -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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -21,7 +21,7 @@
</ItemGroup>
<Target Name="PostBuild" AfterTargets="Build">
<Target Name="PostBuild" AfterTargets="Build" Condition="'$(NavisworksVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Message Text="Navisworks Version $(NavisworksVersion)" Importance="high"/>
<RemoveDir Directories="$(PluginVersionContentTarget)" Condition="Exists('$(PluginVersionContentTarget)')"/>
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -14,7 +14,6 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connector.Navisworks.Bindings;
@@ -28,7 +27,6 @@ public class NavisworksSendBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly ISpeckleApplication _speckleApplication;
private readonly INavisworksConversionSettingsFactory _conversionSettingsFactory;
private readonly ToSpeckleSettingsManagerNavisworks _toSpeckleSettingsManagerNavisworks;
private readonly IElementSelectionService _selectionService;
@@ -39,7 +37,6 @@ public class NavisworksSendBinding : ISendBinding
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
ISpeckleApplication speckleApplication,
INavisworksConversionSettingsFactory conversionSettingsFactory,
ToSpeckleSettingsManagerNavisworks toSpeckleSettingsManagerNavisworks,
IElementSelectionService selectionService,
@@ -51,7 +48,6 @@ public class NavisworksSendBinding : ISendBinding
Commands = new SendBindingUICommands(parent);
_store = store;
_cancellationManager = cancellationManager;
_speckleApplication = speckleApplication;
_conversionSettingsFactory = conversionSettingsFactory;
_toSpeckleSettingsManagerNavisworks = toSpeckleSettingsManagerNavisworks;
_selectionService = selectionService;
@@ -77,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) =>
@@ -97,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)
)
);
@@ -133,22 +131,6 @@ public class NavisworksSendBinding : ISendBinding
return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems;
}
private async Task<SendOperationResult> ExecuteSendOperation(
IServiceScope scope,
SenderModelCard modelCard,
List<NAV.ModelItem> navisworksModelItems,
IProgress<CardProgress> onOperationProgressed,
CancellationToken token
) =>
await scope
.ServiceProvider.GetRequiredService<SendOperation<NAV.ModelItem>>()
.Execute(
navisworksModelItems,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
onOperationProgressed,
token
);
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
/// <summary>
@@ -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"/>
+2 -2
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Target AfterTargets="Clean" Name="CleanAddinsRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinsRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(TargetDir);$(ProjectDir)\..\Release\Release$(RevitVersion);$(AppData)\Autodesk\Revit\Addins\$(RevitVersion)\Speckle.Connectors.Revit$(RevitVersion);" />
<Delete Files="$(AppData)\Autodesk\Revit\Addins\$(RevitVersion)\Speckle.Connectors.Revit$(RevitVersion).addin" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<RevitDLLs Include="$(TargetDir)\**\*.*" Exclude="$(TargetDir)*.addin" />
<SourceManifest Include="$(TargetDir)\Plugin\Speckle.Connectors.Revit$(RevitVersion).addin" />
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"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.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"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.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -3,16 +3,18 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Bindings;
internal sealed class RevitReceiveBinding(
public sealed class RevitReceiveBinding(
ICancellationManager cancellationManager,
IBrowserBridge parent,
ILogger<RevitReceiveBinding> logger,
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
@@ -21,6 +23,11 @@ internal sealed class RevitReceiveBinding(
public IBrowserBridge Parent { get; } = parent;
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
#pragma warning disable CA1024
public List<ICardSetting> GetReceiveSettings() =>
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
#pragma warning restore CA1024
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
@@ -29,13 +36,13 @@ internal sealed class RevitReceiveBinding(
await manager.Process(
Commands,
modelCardId,
(sp) =>
(sp, card) =>
{
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
revitConversionSettingsFactory.Create(
DetailLevelType.Coarse, // TODO figure out
null,
toHostSettingsManager.GetReferencePointSetting(card),
false,
true,
false
@@ -11,6 +11,7 @@ using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.Revit.Bindings;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Operations.Receive;
using Speckle.Connectors.Revit.Operations.Receive.Settings;
using Speckle.Connectors.Revit.Operations.Send;
using Speckle.Connectors.Revit.Operations.Send.Settings;
using Speckle.Connectors.Revit.Plugin;
@@ -57,10 +58,12 @@ public static class ServiceRegistration
// send operation and dependencies
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
serviceCollection.AddScoped<ElementUnpacker>();
serviceCollection.AddScoped<LevelUnpacker>();
serviceCollection.AddScoped<SendCollectionManager>();
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
serviceCollection.AddSingleton<ToSpeckleSettingsManager>();
serviceCollection.AddSingleton<ToHostSettingsManager>();
serviceCollection.AddSingleton<LinkedModelHandler>();
// receive operation and dependencies
@@ -0,0 +1,71 @@
using Autodesk.Revit.DB;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Converters.RevitShared.ToSpeckle.Properties;
using Speckle.Objects.Data;
using Speckle.Objects.Other;
namespace Speckle.Connectors.Revit.HostApp;
/// <summary>
/// Helper class to proxify the levels. Runs over for every element with their LevelId prop.
/// We can handle bottom-top levels for elements later only if it is asked.
/// </summary>
public class LevelUnpacker
{
private readonly LevelExtractor _levelExtractor;
private readonly PropertiesExtractor _propertiesExtractor;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
public LevelUnpacker(
LevelExtractor levelExtractor,
PropertiesExtractor propertiesExtractor,
IConverterSettingsStore<RevitConversionSettings> converterSettings
)
{
_levelExtractor = levelExtractor;
_propertiesExtractor = propertiesExtractor;
_converterSettings = converterSettings;
}
public List<LevelProxy> Unpack(List<Element> elements)
{
Dictionary<string, LevelProxy> levelProxies = new();
foreach (var element in elements)
{
if (levelProxies.TryGetValue(element.LevelId.ToString(), out LevelProxy? levelProxy))
{
levelProxy.objects.Add(element.UniqueId);
}
else
{
var level = _levelExtractor.GetLevel(element);
if (level is null)
{
continue;
}
var levelDataObject = new DataObject()
{
name = level.Name,
displayValue = [],
properties = _propertiesExtractor.GetProperties(level)
};
var unitSettings = _converterSettings.Current.Document.GetUnits();
var lengthUnitType = unitSettings.GetFormatOptions(Autodesk.Revit.DB.SpecTypeId.Length).GetUnitTypeId();
levelDataObject["elevation"] = UnitUtils.ConvertFromInternalUnits(level.Elevation, lengthUnitType);
levelDataObject["units"] = _converterSettings.Current.SpeckleUnits;
levelProxies[element.LevelId.ToString()] = new LevelProxy()
{
applicationId = level.UniqueId,
objects = [element.UniqueId],
value = levelDataObject
};
}
}
return levelProxies.Values.ToList();
}
}
@@ -14,7 +14,7 @@ public class SendCollectionManager
{
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
private readonly Dictionary<string, Collection> _collectionCache = new();
private readonly Dictionary<ElementId, (string name, Dictionary<string, object?> props)> _levelCache = new(); // stores level id and its properties
private readonly Dictionary<ElementId, string> _levelCache = new(); // stores level id and its properties
private readonly Dictionary<string, Collection> _linkedModelCollections = new(); // cache for linked model collections
private Collection? _mainModelCollection; // collection for main model elements
@@ -91,13 +91,11 @@ public class SendCollectionManager
// get the level and its properties
string levelName = "No Level";
Dictionary<string, object?> levelProperties = new();
if (element.LevelId != ElementId.InvalidElementId)
{
if (_levelCache.TryGetValue(element.LevelId, out var cachedLevel))
{
levelName = cachedLevel.name;
levelProperties = cachedLevel.props;
levelName = cachedLevel;
}
else
{
@@ -105,9 +103,7 @@ public class SendCollectionManager
{
var level = (Level)doc.GetElement(element.LevelId);
levelName = level.Name;
levelProperties.Add("elevation", level.Elevation);
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
_levelCache.Add(element.LevelId, (levelName, levelProperties));
_levelCache.Add(element.LevelId, levelName);
}
// the exception is swallowed since if an exception occurs, we fall back to "No Level" for the element
catch (Exception e) when (!e.IsFatal()) { }
@@ -157,12 +153,6 @@ public class SendCollectionManager
else
{
childCollection = new Collection(pathItem);
// add properties to level collection
if (i == 0 && levelProperties.Count > 0)
{
childCollection["properties"] = levelProperties;
}
previousCollection.elements.Add(childCollection);
_collectionCache[flatPathName] = childCollection;
}
@@ -0,0 +1,18 @@
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ReferencePointSetting(ReceiveReferencePointType value) : ICardSetting
{
public string? Id { get; set; } = "referencePoint";
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReceiveReferencePointType)).ToList();
public object? Value { get; set; } = value.ToString();
public static readonly Dictionary<string, ReceiveReferencePointType> ReferencePointMap = System
.Enum.GetValues(typeof(ReceiveReferencePointType))
.Cast<ReceiveReferencePointType>()
.ToDictionary(v => v.ToString(), v => v);
}
@@ -64,14 +64,14 @@ public sealed class RevitHostObjectBuilder(
)
{
// TODO: formalise getting transform info from rootObject. this dict access is gross.
Autodesk.Revit.DB.Transform? referencePointTransform = null;
Autodesk.Revit.DB.Transform? referencePointTransformFromRootObject = null;
if (
rootObject.DynamicPropertyKeys.Contains(ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY)
&& rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] is Dictionary<string, object> transformDict
&& transformDict.TryGetValue("transform", out var transformValue)
)
{
referencePointTransform = ReferencePointHelper.GetTransformFromRootObject(transformValue);
referencePointTransformFromRootObject = ReferencePointHelper.GetTransformFromRootObject(transformValue);
}
var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors!
@@ -184,11 +184,15 @@ public sealed class RevitHostObjectBuilder(
{
using var _ = activityFactory.Start("Baking objects");
transactionManager.StartTransaction(true, "Baking objects");
using (
converterSettings.Push(currentSettings =>
currentSettings with
{
ReferencePointTransform = referencePointTransform
ReferencePointTransform = CalculateNewTransform(
currentSettings.ReferencePointTransform,
referencePointTransformFromRootObject
)
}
)
)
@@ -217,6 +221,24 @@ public sealed class RevitHostObjectBuilder(
return conversionResults.builderResult;
}
private Autodesk.Revit.DB.Transform? CalculateNewTransform(
Autodesk.Revit.DB.Transform? receiveTransform,
Autodesk.Revit.DB.Transform? rootTransform
)
{
if (receiveTransform == null)
{
return rootTransform;
}
if (rootTransform == null)
{
return receiveTransform;
}
return rootTransform.Multiply(receiveTransform);
}
private (
HostObjectBuilderResult builderResult,
List<(DirectShape res, string applicationId)> postBakePaintTargets
@@ -0,0 +1,97 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.InterfaceGenerator;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
[GenerateAutoInterface]
public class ToHostSettingsManager : IToHostSettingsManager
{
private readonly RevitContext _revitContext;
public ToHostSettingsManager(RevitContext revitContext)
{
_revitContext = revitContext;
}
public Transform? GetReferencePointSetting(ModelCard modelCard)
{
var referencePointString = modelCard.Settings?.First(s => s.Id == "referencePoint").Value as string;
if (
referencePointString is not null
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
referencePointString,
out ReceiveReferencePointType referencePoint
)
)
{
// get the current transform from setting first
// we are doing this because we can't track if reference points were changed between send operations.
Transform? currentTransform = GetTransform(referencePoint);
return currentTransform;
}
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
}
private Transform? GetTransform(ReceiveReferencePointType referencePointType)
{
Transform? referencePointTransform = null;
if (_revitContext.UIApplication is UIApplication uiApplication)
{
// first get the main doc base points and reference setting transform
using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document);
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
BasePoint? surveyPoint = points.FirstOrDefault(o => o.IsShared);
switch (referencePointType)
{
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReceiveReferencePointType.ProjectBase:
if (projectPoint is not null)
{
referencePointTransform = Transform.CreateTranslation(projectPoint.Position);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Project Point from document");
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReceiveReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
}
break;
case ReceiveReferencePointType.Source:
break;
case ReceiveReferencePointType.InternalOrigin:
break;
}
return referencePointTransform;
}
throw new InvalidOperationException(
"Revit Context UI Application was null when retrieving reference point transform."
);
}
}
@@ -23,6 +23,7 @@ public class RevitRootObjectBuilder(
IConverterSettingsStore<RevitConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
LevelUnpacker levelUnpacker,
IThreadContext threadContext,
SendCollectionManager sendCollectionManager,
ILogger<RevitRootObjectBuilder> logger,
@@ -32,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
)
@@ -182,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++;
@@ -232,12 +230,15 @@ public class RevitRootObjectBuilder(
throw new SpeckleException("Failed to convert all objects.");
}
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(
atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList()
);
var flatElements = atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList();
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(flatElements);
var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds);
rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
var levelProxies = levelUnpacker.Unpack(flatElements);
rootObject[ProxyKeys.LEVEL] = levelProxies;
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
@@ -57,7 +57,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
throw new ArgumentException($"Invalid geometry fidelity value: {fidelityString}");
}
public Transform? GetReferencePointSetting(SenderModelCard modelCard)
public Transform? GetReferencePointSetting(ModelCard modelCard)
{
var referencePointString = modelCard.Settings?.First(s => s.Id == "referencePoint").Value as string;
if (
@@ -75,9 +75,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
if (_referencePointCache.TryGetValue(modelCard.ModelCardId.NotNull(), out Transform? previousTransform))
{
// invalidate conversion cache if the transform has changed
if (previousTransform != currentTransform)
if (modelCard is SenderModelCard senderModelCard && previousTransform != currentTransform)
{
EvictCacheForModelCard(modelCard);
EvictCacheForModelCard(senderModelCard);
}
}
@@ -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);
@@ -22,6 +22,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
@@ -36,7 +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\RevitHostObjectBuilder.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" />
@@ -58,4 +61,4 @@
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
</ItemGroup>
</Project>
</Project>
+3 -3
View File
@@ -3,13 +3,13 @@
<ItemGroup>
<PublicReleasePath Include="$(AppData)\McNeel\Rhinoceros\$(RhinoVersion).0\Plug-ins\Speckle.Connectors.Rhino$(RhinoVersion) (2153799A-0CEC-40DE-BC3A-01E5055222FF)" />
</ItemGroup>
<Target AfterTargets="Build" Name="WarnIfPublicReleaseVersionInstalled" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<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>
<Target AfterTargets="Build" Name="AfterBuildRhino" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildRhino" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Message Text="Rhino Version $(RhinoVersion)" Importance="high"/>
</Target>
</Project>
@@ -13,9 +13,12 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"dependencies": {
"PolySharp": "1.14.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -322,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.logging": {
@@ -334,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.rhino7": {
@@ -379,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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",
@@ -400,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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -13,9 +13,12 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"dependencies": {
"PolySharp": "1.14.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -322,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
}
},
"speckle.connectors.logging": {
@@ -334,13 +337,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.1, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -379,18 +381,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.1"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"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",
@@ -400,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.2"
"Speckle.Sdk.Dependencies": "3.5.1"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
}
}
}
@@ -36,7 +36,13 @@ public class CreateCollection : VariableParameterComponentBase
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Collection", "C", "Created parent collection", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleCollectionParam(),
"Collection",
"C",
"Created parent collection",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess dataAccess)
@@ -152,12 +158,26 @@ public class CreateCollection : VariableParameterComponentBase
{
foreach (var obj in objects)
{
var wrapperGoo = new SpeckleObjectWrapperGoo();
if (wrapperGoo.CastFrom(obj))
// 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)
{
wrapperGoo.Value.Path = childPath;
wrapperGoo.Value.Parent = parentCollection;
parentCollection.Elements.Add(wrapperGoo.Value);
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);
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"{obj?.GetType().Name} type cannot be added to collections.");
}
}
}
@@ -2,6 +2,7 @@ using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
@@ -30,9 +31,9 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
{
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Data",
"D",
"The data you want to expand",
"Collection",
"C",
"The Collection you want to expand",
GH_ParamAccess.item
);
}
@@ -51,15 +52,11 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
Name = wrapper.Name;
NickName = wrapper.Name;
var objects = wrapper
.Elements.Where(el => el is not SpeckleCollectionWrapper)
.OfType<SpeckleObjectWrapper>()
.Select(o => new SpeckleObjectWrapperGoo(o))
.ToList();
var collections = wrapper
.Elements.Where(el => el is SpeckleCollectionWrapper)
.OfType<SpeckleCollectionWrapper>()
.ToList();
// Separate objects and collections
// Note: SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper,
// so it will be included in objects
List<SpeckleWrapper> objects = wrapper.GetAtomicObjects().ToList();
List<SpeckleCollectionWrapper> collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
@@ -69,11 +66,14 @@ 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
};
outputParams.Add(new OutputParamWrapper(param, objects, null));
// Create appropriate Goo types for each object (downside of the inheritance refactor)
List<IGH_Goo> atomicObjectGoos = objects.Select(obj => obj.CreateGoo()).ToList();
outputParams.Add(new OutputParamWrapper(param, atomicObjectGoos, null));
}
foreach (SpeckleCollectionWrapper childWrapper in collections)
@@ -86,7 +86,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
*/
var hasInnerCollections = childWrapper.Elements.Any(el => el is SpeckleCollectionWrapper);
var topology = childWrapper.Topology; // Note: this is a reminder for the future
var topology = childWrapper.Topology;
var nickName = childWrapper.Name;
if (nickName.Length > 16)
{
@@ -102,18 +102,22 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
? GH_ParamAccess.item
: topology is null
? GH_ParamAccess.list
: GH_ParamAccess.tree // we will directly set objects out; note access can be list or tree based on whether it will be a path based collection
: GH_ParamAccess.tree
};
outputParams.Add(
new OutputParamWrapper(
param,
hasInnerCollections
? new SpeckleCollectionWrapperGoo(childWrapper)
: childWrapper.Elements.OfType<SpeckleObjectWrapper>().Select(o => new SpeckleObjectWrapperGoo(o)).ToList(),
topology
)
);
object outputValue;
if (hasInnerCollections)
{
outputValue = new SpeckleCollectionWrapperGoo(childWrapper);
}
else
{
// Create appropriate Goo types for child objects
List<IGH_Goo> childObjectGoos = childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
outputValue = childObjectGoos;
}
outputParams.Add(new OutputParamWrapper(param, outputValue, topology));
}
if (da.Iteration == 0 && OutputMismatch(outputParams))
@@ -142,7 +146,6 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
da.SetDataList(i, outParamWrapper.Values as IList);
break;
case GH_ParamAccess.tree:
//TODO: means we need to convert the collection to a tree
var topo = outParamWrapper.Topology.NotNull();
var values = outParamWrapper.Values as IList;
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topo, values.NotNull());
@@ -3,17 +3,19 @@ namespace Speckle.Connectors.GrasshopperShared.Components;
// NOTE: The number of spaces determines the order in which they display in the ribbon (nice hack)
public static class ComponentCategories
{
// ribbon
public const string PRIMARY_RIBBON = "Speckle";
public const string OPERATIONS = " Ops";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
// categories
public const string OPERATIONS = " Models";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
public const string PARAMETERS = " Params";
public const string DEVELOPER = "Dev";
}
public enum ComponentState
{
Cancelled,
Expired,
NeedsInput,
Receiving,
@@ -2,12 +2,14 @@ using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.GrasshopperShared.Components.Dev;
@@ -28,12 +30,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter(
"Speckle Param",
"SP",
"Speckle param to deconstruct. Expects Collections, Objects, Materials, or Properties",
GH_ParamAccess.item
);
pManager.AddGenericParameter("Speckle Param", "SP", "Speckle param to deconstruct", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
@@ -47,20 +44,29 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
switch (data)
{
case SpeckleObjectWrapperGoo obj:
Name = string.IsNullOrEmpty(obj.Value.Name) ? obj.Value.Base.speckle_type : obj.Value.Name;
outputParams = CreateOutputParamsFromBase(obj.Value.Base);
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 SpeckleCollectionWrapperGoo coll:
Name = string.IsNullOrEmpty(coll.Value.Collection.name)
? coll.Value.Collection.speckle_type
: coll.Value.Collection.name;
outputParams = CreateOutputParamsFromBase(coll.Value.Collection);
case SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null:
// get geometries from the wrapper to override the displayvalue prop while parsing
List<IGH_Goo> display = dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList();
outputParams = ParseSpeckleWrapper(dataObjectGoo.Value, null, display);
break;
case SpeckleMaterialWrapperGoo matGoo:
Name = string.IsNullOrEmpty(matGoo.Value.Name) ? matGoo.Value.Material.speckle_type : matGoo.Value.Name;
outputParams = CreateOutputParamsFromBase(matGoo.Value.Base);
case SpeckleGeometryWrapperGoo 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;
case SpecklePropertyGroupGoo propGoo:
Name = $"properties ({propGoo.Value.Count})";
outputParams = new();
@@ -72,10 +78,17 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
outputParams.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
OutputParamWrapper output =
outputValue is IList
? CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.list)
: CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item);
outputParams.Add(output);
}
break;
default:
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return;
}
@@ -111,7 +124,21 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
}
}
private List<OutputParamWrapper> CreateOutputParamsFromBase(Base @base)
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, elements, displayValue);
}
private List<OutputParamWrapper> CreateOutputParamsFromBase(
Base @base,
List<IGH_Goo>? elements = null,
List<IGH_Goo>? displayValue = null
)
{
List<OutputParamWrapper> result = new();
if (@base == null)
@@ -132,16 +159,25 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
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" && elements != null)
{
list = elements;
}
// override list value if base is a dataobject and this is the displayvalue prop, since this is empty if coming from a dataobject wrapper
if (@base is Speckle.Objects.Data.DataObject && prop.Key == "displayValue" && displayValue != null)
{
list = displayValue;
}
foreach (var x in list)
{
switch (x)
{
case SpeckleCollectionWrapper collWrapper:
nativeObjects.Add(new SpeckleCollectionWrapperGoo(collWrapper));
break;
case SpeckleObjectWrapper objWrapper:
nativeObjects.Add(new SpeckleObjectWrapperGoo(objWrapper));
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base xBase:
@@ -163,16 +199,8 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
break;
case SpeckleCollectionWrapper collWrapper:
result.Add(
CreateOutputParamByKeyValue(prop.Key, new SpeckleCollectionWrapperGoo(collWrapper), GH_ParamAccess.item)
);
break;
case SpeckleObjectWrapper objWrapper:
result.Add(
CreateOutputParamByKeyValue(prop.Key, new SpeckleObjectWrapperGoo(objWrapper), GH_ParamAccess.item)
);
case SpeckleWrapper wrapper:
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
break;
case Base baseValue:
@@ -194,24 +222,24 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return result;
}
private List<SpeckleObjectWrapperGoo> ConvertOrCreateWrapper(Base @base)
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)
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
List<SpeckleGeometryWrapperGoo> convertedWrappers = new();
foreach ((object o, Base b) in convertedBase)
{
SpeckleObjectWrapper convertedWrapper =
GeometryBase? g = o as GeometryBase;
SpeckleGeometryWrapper convertedWrapper =
new()
{
Base = b,
GeometryBase = g,
Name = b["name"] as string ?? "",
Color = null,
Material = null,
WrapperGuid = null
Material = null
};
convertedWrappers.Add(new(convertedWrapper));
@@ -223,17 +251,17 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
{
// 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 =
SpeckleGeometryWrapper convertedWrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null,
WrapperGuid = null,
Material = null
};
return new() { new SpeckleObjectWrapperGoo(convertedWrapper) };
return new() { new SpeckleGeometryWrapperGoo(convertedWrapper) };
}
}
@@ -0,0 +1,129 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
namespace Speckle.Connectors.GrasshopperShared.Components.Dev;
[Guid("18152AE4-4BE7-46F0-9826-09061897A5CC")]
public class TokenUrlComponent : GH_Component
{
public TokenUrlComponent()
: base(
"Speckle Model URL",
"URL",
"Create a Speckle model link using URL and developer token",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.DEVELOPER
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_inputs_token;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
pManager.AddTextParameter("Speckle Token", "Token", "Speckle Authorization Token", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
protected override void SolveInstance(IGH_DataAccess da)
{
// get inputs
string urlInput = "";
if (!da.GetData(0, ref urlInput))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Speckle Url is missing");
return;
}
string tokenInput = "";
if (!da.GetData(1, ref tokenInput))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Speckle token is missing");
return;
}
try
{
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
var (resource, hasPermission) = SolveInstanceWithUrAndToken(urlInput, tokenInput, true);
if (!hasPermission)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You do not have enough permission for this project.");
}
da.SetData(0, resource);
}
catch (SpeckleException e)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
da.AbortComponentSolution();
}
}
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrAndToken(
string input,
string token,
bool isSender
)
{
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
// Should perform validation, fill in all internal data of the component (project, model, version, account)
// Should notify user if any of this goes wrong.
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
if (resources.Length != 1)
{
// POC: this shouldn't ever hit since exceptions are thrown in the FromUrlString method
throw new SpeckleException($"FromUrlString parser returned an invalid resource");
}
var resource = resources.First();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var account = resource.Account.GetAccount(scope);
if (account != null)
{
scope.Get<IAccountService>().SetUserSelectedAccountId(account.id);
}
else
{
throw new SpeckleException("No account found for server URL");
}
IClient client = scope.Get<IClientFactory>().Create(account);
var project = client.Project.Get(resource.ProjectId).Result;
var projectPermissions = client.Project.GetPermissions(resource.ProjectId).Result;
if (project != null && project.workspaceId != null)
{
var workspace = client.Workspace.Get(project.workspaceId).Result;
}
switch (resource)
{
case SpeckleUrlLatestModelVersionResource latestVersionResource:
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
break;
case SpeckleUrlModelVersionResource versionResource:
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
// TODO: this wont be the case when we have separation between send and receive components
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
break;
case SpeckleUrlModelObjectResource:
throw new SpeckleException("Object URLs are not supported");
default:
throw new SpeckleException("Unknown Speckle resource type");
}
return (resource, isSender ? projectPermissions.canPublish.authorized : projectPermissions.canLoad.authorized);
}
}
@@ -1,241 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class CreateSpeckleObject : GH_Component
{
public CreateSpeckleObject()
: 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;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter(
"Object",
"O",
"Input Object. Speckle Objects, Model Objects, and geometry are accepted.",
GH_ParamAccess.item
);
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
Params.Input[1].Optional = true;
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
Params.Input[2].Optional = true;
pManager.AddGenericParameter(
"Properties",
"P",
"The properties of the Speckle Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[3].Optional = true;
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
Params.Input[4].Optional = true;
pManager.AddGenericParameter(
"Material",
"m",
"The material of the Speckle Object. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[5].Optional = true;
/* POC: disable for now as we are doing anything with this
pManager.AddTextParameter(
"Path",
"p",
"The Collection Path of the Speckle Object. Should be delimited with `:`",
GH_ParamAccess.item
);
Params.Input[6].Optional = true;
*/
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleObjectParam(), "Object", "O", "Speckle Object", GH_ParamAccess.item);
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Object",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Speckle Object.",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Object, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
IGH_Goo? inputObject = null;
da.GetData(0, ref inputObject);
IGH_GeometricGoo? inputGeometry = null;
da.GetData(1, ref inputGeometry);
string? inputName = null;
da.GetData(2, ref inputName);
IGH_Goo? inputProperties = null;
da.GetData(3, ref inputProperties);
Color? inputColor = null;
da.GetData(4, ref inputColor);
IGH_Goo? inputMaterial = null;
da.GetData(5, ref inputMaterial);
//string? inputPath = null;
//da.GetData(6, ref inputPath);
// keep track of mutation
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
bool mutated = false;
// process the object
SpeckleObjectWrapperGoo result = new();
if (inputObject != null)
{
if (!result.CastFrom(inputObject))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Object input is not valid. Only Speckle Objects, Baked Model Objects, and Geometry are accepted."
);
return;
}
}
// process geometry
// at this point, we can ensure that the Base in the wrapper is a DataObject.
if (inputObject == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Object or Geometry.");
return;
}
if (inputGeometry != null)
{
result.Value.GeometryBase = inputGeometry.GeometricGooToGeometryBase();
Base converted = SpeckleConversionContext.ConvertToSpeckle(result.Value.GeometryBase);
converted[Constants.NAME_PROP] = result.Value.Name;
converted.applicationId = result.Value.ApplicationId;
result.Value.Base = converted;
mutated = true;
}
// process name
if (inputName != null)
{
result.Value.Name = inputName;
mutated = true;
}
// process properties
if (inputProperties != null)
{
SpecklePropertyGroupGoo propGoo = new();
if (!propGoo.CastFrom(inputProperties))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Properties input is not valid. Only Speckle Properties and User Content are accepted."
);
return;
}
result.Value.Properties = propGoo;
mutated = true;
}
// process color (no mutation)
if (inputColor != null)
{
result.Value.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
SpeckleMaterialWrapperGoo matWrapperGoo = new();
if (!matWrapperGoo.CastFrom(inputMaterial))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Material input is not valid. Only Display Materials, Baked Model Materials, and Speckle Materials are accepted."
);
return;
}
result.Value.Material = matWrapperGoo.Value;
}
// process application Id. Use a new appId if mutated, or if this is a new object
result.Value.ApplicationId = mutated
? Guid.NewGuid().ToString()
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
// get the path
string path =
result.Value.Path.Count > 1
? string.Join(Constants.LAYER_PATH_DELIMITER, result.Value.Path)
: result.Value.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.Value);
da.SetData(1, result.Value.GeometryBase);
da.SetData(2, result.Value.Name);
da.SetData(3, result.Value.Properties);
da.SetData(4, result.Value.Color);
da.SetData(5, result.Value.Material);
da.SetData(6, path);
}
}
@@ -17,6 +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.quarternary;
public CreateSpeckleProperties()
: base(
@@ -35,7 +36,13 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Properties", "P", "Properties for Speckle Objects", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Properties for Speckle Objects",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
@@ -59,7 +66,17 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
for (int i = 0; i < Params.Input.Count; i++)
{
var paramName = Params.Input[i].NickName;
var propertyValue = ExtractPropertyValue(da, i, paramName);
var data = Params.Input[i].VolatileData.AllData(true).ToList();
if (data.Count == 0)
{
continue;
}
ISpecklePropertyGoo? propertyValue =
Params.Input[i].Access == GH_ParamAccess.item
? ExtractPropertyValue(da, i, paramName)
: ExtractPropertyListValue(da, i, paramName);
if (propertyValue != null)
{
@@ -100,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);
@@ -1,6 +1,8 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
@@ -14,6 +16,7 @@ public class FilterSpeckleObjects : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_filter;
public override GH_Exposure Exposure => GH_Exposure.primary;
public FilterSpeckleObjects()
: base(
@@ -26,7 +29,7 @@ public class FilterSpeckleObjects : GH_Component
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleObjectParam(), "Objects", "O", "Speckle Objects to filter", GH_ParamAccess.list);
pManager.AddGenericParameter("Objects", "O", "Speckle Objects to filter", GH_ParamAccess.list);
pManager.AddTextParameter("Name", "N", "Find objects with a matching name", GH_ParamAccess.item);
Params.Input[1].Optional = true;
@@ -61,16 +64,9 @@ public class FilterSpeckleObjects : GH_Component
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"The objects that match the queries",
GH_ParamAccess.tree
);
pManager.AddGenericParameter("Objects", "O", "The objects that match the queries", GH_ParamAccess.tree);
pManager.AddParameter(
new SpeckleObjectParam(),
pManager.AddGenericParameter(
"Culled Objects",
"co",
"The objects that did not match the queries",
@@ -80,11 +76,24 @@ public class FilterSpeckleObjects : GH_Component
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
List<SpeckleObjectWrapperGoo?> inputObjects = new();
List<IGH_Goo> inputObjects = new();
dataAccess.GetDataList(0, inputObjects);
if (inputObjects.Count == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Add objects to filter");
return;
}
List<SpeckleWrapper?> objects = inputObjects
.Select(o => o.ToSpeckleObjectWrapper())
.Where(o => o is not null)
.ToList();
int unsupported = inputObjects.Count - objects.Count;
if (unsupported > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Input contained {unsupported} unsupported objects.");
return;
}
@@ -99,21 +108,16 @@ public class FilterSpeckleObjects : GH_Component
string speckleId = "";
dataAccess.GetData(5, ref speckleId);
List<SpeckleObjectWrapper> matchedObjects = new();
List<SpeckleObjectWrapper> removedObjects = new();
for (int i = 0; i < inputObjects.Count; i++)
List<SpeckleWrapper> matchedObjects = new();
List<SpeckleWrapper> removedObjects = new();
for (int i = 0; i < objects.Count; i++)
{
SpeckleObjectWrapperGoo? inputObject = inputObjects[i];
if (inputObject is null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"A null input object was detected.");
return;
}
SpeckleWrapper wrapper = objects[i]!;
// filter by name
if (!MatchesSearchPattern(name, inputObject.Value.Name))
if (!MatchesSearchPattern(name, wrapper.Name))
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
@@ -125,49 +129,61 @@ public class FilterSpeckleObjects : GH_Component
}
else
{
foreach (string key in inputObject.Value.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;
}
}
}
}
if (!foundProperty)
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
// filter by material name
if (!MatchesSearchPattern(material, inputObject.Value.Material?.Name ?? ""))
if (wrapper is SpeckleGeometryWrapper geoWrapper)
{
removedObjects.Add(inputObject.Value);
continue;
if (!MatchesSearchPattern(material, geoWrapper.Material?.Name ?? ""))
{
removedObjects.Add(wrapper);
continue;
}
}
// filter by application id
if (!MatchesSearchPattern(appId, inputObject.Value.Base.applicationId ?? ""))
if (!MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
// filter by speckle id
if (!MatchesSearchPattern(speckleId, inputObject.Value.Base.id ?? ""))
if (!MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
matchedObjects.Add(inputObject.Value);
matchedObjects.Add(wrapper);
}
// Set output objects
dataAccess.SetDataList(0, matchedObjects);
dataAccess.SetDataList(1, removedObjects);
dataAccess.SetDataList(0, matchedObjects.Select(o => o.CreateGoo()));
dataAccess.SetDataList(1, removedObjects.Select(o => o.CreateGoo()));
}
private bool MatchesSearchPattern(string searchPattern, string target)
@@ -1,141 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// <summary>
/// Given a collection, this component will output the objects in the subcollection corresponding to the input path
/// </summary>
[Guid("77CAEE94-F0B9-4611-897C-71F2A22BA311")]
public class GetCollectionObjects : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_query;
public GetCollectionObjects()
: base(
"Query Objects",
"qO",
"Retrieves the objects inside a Speckle collection at the specified path",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(),
"Collection",
"C",
"Collection to retrieve objects from",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"C",
"Get the Speckle objects in the subcollection indicated by this path",
GH_ParamAccess.item
);
Params.Input[1].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"The objects in the input collection that match the queries",
GH_ParamAccess.tree
);
}
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
SpeckleCollectionWrapperGoo collectionWrapperGoo = new();
dataAccess.GetData(0, ref collectionWrapperGoo);
if (collectionWrapperGoo.Value == null)
{
return;
}
string path = "";
dataAccess.GetData(1, ref path);
// 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 = new();
SpeckleCollectionWrapper? targetCollectionWrapper = null;
if (!string.IsNullOrEmpty(path))
{
targetCollectionWrapper =
path == "_objects" ? collectionWrapperGoo.Value : FindCollection(collectionWrapperGoo.Value, path);
filteredObjects = targetCollectionWrapper
.Elements.Where(e => e is SpeckleObjectWrapper)
.Select(e => (SpeckleObjectWrapper)e)
.ToList();
}
else
{
filteredObjects = GetAllObjectsFromCollection(collectionWrapperGoo.Value).ToList();
}
// Set output objects
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
{
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, filteredObjects);
dataAccess.SetDataTree(0, tree);
}
else
{
dataAccess.SetDataList(0, filteredObjects);
}
}
private IEnumerable<SpeckleObjectWrapper> GetAllObjectsFromCollection(SpeckleCollectionWrapper collectionWrapper)
{
foreach (SpeckleWrapper 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 FindCollection(SpeckleCollectionWrapper root, string unifiedPath)
{
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
List<string> paths = unifiedPath.Split([Constants.LAYER_PATH_DELIMITER], StringSplitOptions.None).ToList();
SpeckleCollectionWrapper currentCollectionWrapper = root;
while (paths.Count != 0)
{
currentCollectionWrapper = currentCollectionWrapper
.Elements.OfType<SpeckleCollectionWrapper>()
.First(wrapper => wrapper.Name == paths.First());
paths.RemoveAt(0);
if (paths.Count == 0)
{
return currentCollectionWrapper;
}
}
throw new SpeckleException("Did not find collection");
}
}
@@ -1,178 +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 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() { }
}
@@ -1,11 +1,9 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
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;
@@ -21,36 +19,29 @@ 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.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;
}
}
@@ -0,0 +1,282 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.DocObjects;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// <summary>
/// Given a collection, this component will output the objects in the collection that satisfy the input parameters
/// </summary>
[Guid("77CAEE94-F0B9-4611-897C-71F2A22BA311")]
public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_query;
public override GH_Exposure Exposure => GH_Exposure.primary;
public QuerySpeckleObjects()
: base(
"Query Speckle Objects",
"qO",
"Retrieves the Speckle objects inside a Speckle collection satisfying the input conditions",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(),
"Collection",
"C",
"Collection to retrieve objects from",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"C",
"Get the Speckle objects in the subcollection indicated by this path",
GH_ParamAccess.item
);
Params.Input[1].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter(
"Objects",
"O",
"The objects in the input collection that match the queries",
GH_ParamAccess.tree
);
}
// The list of filters that can be added by the user as a dynamic output
// The order of this array will determine the order of outputs in this component
private List<ObjectType> Filters =>
[
ObjectType.InstanceReference,
ObjectType.Point,
ObjectType.PointSet,
ObjectType.Curve,
ObjectType.Extrusion,
ObjectType.Brep,
ObjectType.SubD,
ObjectType.Mesh,
ObjectType.Hatch
];
private string GetFilterNickName(ObjectType type) =>
type switch
{
ObjectType.InstanceReference => "Block Instances",
ObjectType.Point => "Points",
ObjectType.PointSet => "Point Clouds",
ObjectType.Curve => "Curves",
ObjectType.Extrusion => "Extrusions",
ObjectType.Brep => "Breps",
ObjectType.SubD => "SubDs",
ObjectType.Mesh => "Meshes",
ObjectType.Hatch => "Hatches",
_ => ""
};
private List<int>? _outputFilterIndices;
// Caches the list of all objects by geometrybase type
private readonly Dictionary<ObjectType, List<SpeckleGeometryWrapper>> _filterDict = new();
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
SpeckleCollectionWrapperGoo collectionWrapperGoo = new();
dataAccess.GetData(0, ref collectionWrapperGoo);
if (collectionWrapperGoo.Value == null)
{
return;
}
string path = "";
dataAccess.GetData(1, ref path);
// 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<SpeckleWrapper> filteredObjects;
SpeckleCollectionWrapper? targetCollectionWrapper = null;
if (!string.IsNullOrEmpty(path))
{
targetCollectionWrapper =
path == "_objects" ? collectionWrapperGoo.Value : FindCollectionAtPath(collectionWrapperGoo.Value, path);
if (targetCollectionWrapper is null)
{
return;
}
filteredObjects = targetCollectionWrapper.GetAtomicObjects(true).ToList();
}
else
{
filteredObjects = collectionWrapperGoo.Value.GetAtomicObjects(true).ToList();
}
// sort geometry objects by filters
var geometryObjects = filteredObjects.OfType<SpeckleGeometryWrapper>().ToList();
if (_filterDict.Count == 0)
{
SortObjectsByGeometryBaseType(geometryObjects);
}
// Set output objects
for (int i = 0; i < Params.Output.Count; i++)
{
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))
{
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoos);
dataAccess.SetDataTree(i, tree);
}
else
{
dataAccess.SetDataList(i, outputGoos);
}
}
}
// Sort the input objects by the FilterType enums, based on the type of their geometryBase
private void SortObjectsByGeometryBaseType(List<SpeckleGeometryWrapper> objs)
{
if (_filterDict.Count > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Stored input objects are in an invalid state.");
return;
}
foreach (ObjectType filter in Filters)
{
_filterDict.Add(filter, new());
}
foreach (var wrapper in objs)
{
if (
wrapper.GeometryBase?.ObjectType is ObjectType objType
&& _filterDict.TryGetValue(objType, out List<SpeckleGeometryWrapper>? value)
)
{
value.Add(wrapper);
}
}
}
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?
List<string> paths = unifiedPath.Split([Constants.LAYER_PATH_DELIMITER], StringSplitOptions.None).ToList();
SpeckleCollectionWrapper currentCollectionWrapper = root;
while (paths.Count != 0)
{
try
{
currentCollectionWrapper = currentCollectionWrapper
.Elements.OfType<SpeckleCollectionWrapper>()
.First(wrapper => wrapper.Name == paths.First());
paths.RemoveAt(0);
if (paths.Count == 0)
{
return currentCollectionWrapper;
}
}
catch (InvalidOperationException) // when no wrappers match the current path
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"[{unifiedPath}] is an invalid path for this collection");
return null;
}
}
return null;
}
public bool CanInsertParameter(GH_ParameterSide side, int index)
{
if (side == GH_ParameterSide.Input || index == 0 || index > Filters.Count)
{
return false;
}
// repopulate current output params if needed
if (_outputFilterIndices is null)
{
_outputFilterIndices = new();
foreach (var output in Params.Output)
{
if (Enum.TryParse(output.Name, out ObjectType filter))
{
_outputFilterIndices.Add(Filters.IndexOf(filter));
}
}
}
int? previousFilterIndex = index == 1 ? null : _outputFilterIndices[index - 2];
int? nextFilterIndex = index > _outputFilterIndices.Count ? null : _outputFilterIndices[index - 1];
return (previousFilterIndex is null && nextFilterIndex != 0)
|| (nextFilterIndex is null && previousFilterIndex != Filters.Count - 1)
|| nextFilterIndex - previousFilterIndex > 1;
}
public bool CanRemoveParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output && index != 0;
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
// get the next filter name based on what the previous output filter at this index is
// index should account for the first output which is always all objects
int? previousFilterIndex = _outputFilterIndices is null || index == 1 ? null : _outputFilterIndices[index - 2];
_outputFilterIndices = null;
ObjectType filter = previousFilterIndex is null ? Filters.First() : Filters[(int)previousFilterIndex + 1];
return new Param_GenericObject
{
Name = filter.ToString(),
NickName = GetFilterNickName(filter),
MutableNickName = false,
Optional = true
};
}
public bool DestroyParameter(GH_ParameterSide side, int index)
{
_outputFilterIndices = null;
return side == GH_ParameterSide.Output;
}
public void VariableParameterMaintenance() { }
public override void AddedToDocument(GH_Document document)
{
base.AddedToDocument(document);
Params.ParameterSourcesChanged += OnParameterSourceChanged;
}
public override void RemovedFromDocument(GH_Document document)
{
Params.ParameterSourcesChanged -= OnParameterSourceChanged;
base.RemovedFromDocument(document);
}
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args)
{
// an empty filter dict will trigger the SortObjectsByGeometryBaseType method.
// we only want to re-sort objects if an input has changed, not on every trigger of solve instance.
_filterDict.Clear();
}
}
@@ -0,0 +1,129 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
public class SpeckleBlockDefinitionPassthrough()
: SpeckleSolveInstance(
"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.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Block Definition",
"BD",
"Input Block Definition. Speckle definitions and Model definitions are accepted.",
GH_ParamAccess.item
);
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"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;
pManager.AddTextParameter("Name", "N", "Name of the Speckle Definition", GH_ParamAccess.item);
Params.Input[2].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Block Definition",
"BD",
"Speckle Block Definition",
GH_ParamAccess.item
);
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);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
da.GetData(0, ref inputDefinition);
List<IGH_Goo> inputObjects = new();
da.GetDataList(1, inputObjects);
if (inputDefinition == null && inputObjects.Count == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Geometry.");
return;
}
string? inputName = null;
da.GetData(2, ref inputName);
if (inputDefinition == null && string.IsNullOrWhiteSpace(inputName))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Name for the definition.");
return;
}
// process the definition
// deep copy so we don't mutate the object
SpeckleBlockDefinitionWrapperGoo result = inputDefinition != null ? new(inputDefinition.Value.DeepCopy()) : new();
// process geometry
if (inputObjects.Count > 0)
{
List<SpeckleGeometryWrapper> processedObjects = new();
foreach (IGH_Goo goo in inputObjects)
{
if (goo.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
{
processedObjects.Add(gooWrapper);
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"Unsupported type {goo.TypeName} not added to definition");
}
}
result.Value.Objects = processedObjects;
result.Value.InstanceDefinitionProxy.objects = processedObjects.Select(o => o.ApplicationId!).ToList(); // TODO: this could also be set at the same time as `Objects` on the definition wrapper.
}
// process name
if (inputName != null)
{
if (string.IsNullOrWhiteSpace(inputName))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Pass in a non-empty name for the definition.");
return;
}
result.Value.Name = inputName;
}
// 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.
// set outputs
da.SetData(0, result);
da.SetDataList(1, result.Value.Objects.Select(o => o.CreateGoo()));
da.SetData(2, result.Value.Name);
}
}
@@ -0,0 +1,225 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
public class SpeckleBlockInstancePassthrough()
: SpeckleSolveInstance(
"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.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int instanceIndex = pManager.AddParameter(
new SpeckleBlockInstanceParam(),
"Block Instance",
"BI",
"Input Block Instance. Speckle instances and Grasshopper instances are accepted.",
GH_ParamAccess.item
);
Params.Input[instanceIndex].Optional = true;
int definitionIndex = pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Definition",
"D",
"Block Instance Definition. Speckle definitions and Grasshopper definitions are accepted.",
GH_ParamAccess.item
);
Params.Input[definitionIndex].Optional = true;
int transformIndex = pManager.AddGenericParameter(
"Transform",
"T",
"Transform of the Speckle instance. Transforms and Planes are accepted.",
GH_ParamAccess.item
);
Params.Input[transformIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Instance", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Instance. 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 Instance",
GH_ParamAccess.item
);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Instance. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockInstanceParam(),
"Block Instance",
"BI",
"Speckle Block Instance",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Definition",
"D",
"Block Definition of the instance",
GH_ParamAccess.item
);
pManager.AddGenericParameter("Transform", "T", "Transform of the Block Instance", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Block Instance", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Properties of the Block Instance",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Block Instance.",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpeckleBlockInstanceWrapperGoo? inputInstance = null;
da.GetData(0, ref inputInstance);
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
da.GetData(1, ref inputDefinition);
if (inputInstance == null && inputDefinition == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Instance or Definition.");
return;
}
IGH_Goo? inputTransform = null;
da.GetData(2, ref inputTransform);
string? inputName = null;
da.GetData(3, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(4, ref inputProperties);
Color? inputColor = null;
da.GetData(5, ref inputColor);
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(6, ref inputMaterial);
// process the instance
// deep copy so we don't mutate the incoming object
SpeckleBlockInstanceWrapperGoo result =
inputInstance != null ? new((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy()) : new();
// process definition
if (inputDefinition != null)
{
result.Value.Definition = inputDefinition.Value;
}
// Process transform
if (inputTransform != null)
{
Transform? extractedTransform = ExtractTransform(inputTransform);
if (extractedTransform.HasValue)
{
result.Value.Transform = extractedTransform.Value;
}
else
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
"Transform input is not valid. Only Transforms and Planes are accepted."
);
return;
}
}
// Process name
if (inputName != null)
{
result.Value.Name = inputName;
}
// Process properties
if (inputProperties != null)
{
result.Value.Properties = inputProperties;
}
// process color (no mutation)
if (inputColor != null)
{
result.Value.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
result.Value.Material = inputMaterial.Value;
}
// no need to process application id.
// new appids are generated if this is a new object, otherwise the input object appID should be preserved for change tracking.
// Set outputs
da.SetData(0, result);
da.SetData(1, result.Value.Definition);
da.SetData(2, new GH_Transform(result.Value.Transform));
da.SetData(3, result.Value.Name);
da.SetData(4, result.Value.Properties);
da.SetData(5, result.Value.Color);
da.SetData(6, result.Value.Material);
}
private Transform? ExtractTransform(IGH_Goo input) =>
input switch
{
GH_Transform ghTransform => ghTransform.Value,
GH_Plane ghPlane => Transform.PlaneToPlane(Plane.WorldXY, ghPlane.Value),
_ => null
};
}
@@ -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);
}
}
@@ -0,0 +1,251 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
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 SpeckleGeometryPassthrough()
: SpeckleSolveInstance(
"Speckle Geometry",
"SG",
"Create or modify a Speckle Geometry",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
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(
"Speckle Geometry",
"SG",
"Input Speckle Geometry. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
int geoIndex = pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Geometry.",
GH_ParamAccess.item
);
Params.Input[geoIndex].Optional = true;
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 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 Geometry",
GH_ParamAccess.item
);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Geometry. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
/* POC: disable for now as we are doing anything with this
pManager.AddTextParameter(
"Path",
"p",
"The Collection Path of the Speckle Object. Should be delimited with `:`",
GH_ParamAccess.item
);
Params.Input[6].Optional = true;
*/
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Speckle Geometry", "SG", "Speckle Geometry", GH_ParamAccess.item);
pManager.AddGeometryParameter("Geometry", "G", "Geometry of the Speckle Geometry.", 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 Geometry",
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 Geometry.",
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
IGH_Goo? inputObject = null;
SpeckleGeometryWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
if (inputObject?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
{
result = gooWrapper.DeepCopy();
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Unsupported object type: {inputObject?.TypeName}");
return;
}
}
IGH_GeometricGoo? inputGeometry = null;
da.GetData(1, ref inputGeometry);
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
return;
}
string? inputName = null;
da.GetData(2, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(3, ref inputProperties);
Color? inputColor = null;
da.GetData(4, ref inputColor);
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(5, ref inputMaterial);
// process geometry
// deep copy so we don't mutate the input geo which may be speckle objects
if (inputGeometry != null)
{
if (inputGeometry.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper geoWrapper)
{
SpeckleGeometryWrapper mutatingGeo = geoWrapper.DeepCopy();
if (result is null)
{
result = mutatingGeo;
}
else
{
// we need to switch to the actual object wrapper type of the incoming geo if this is a mutation on the object
if (mutatingGeo is SpeckleBlockInstanceWrapper mutatingInstance && result is not SpeckleBlockInstanceWrapper)
{
MatchNonGeometryProps(mutatingInstance, result);
result = mutatingInstance;
}
else if (mutatingGeo is not SpeckleBlockInstanceWrapper && result is SpeckleBlockInstanceWrapper)
{
MatchNonGeometryProps(mutatingGeo, result);
result = mutatingGeo;
}
mutatingGeo.Base[Constants.NAME_PROP] = result.Name; // assign these before assigning base since otherwise wrapper name and app will reset
mutatingGeo.Base.applicationId = result.ApplicationId; // assign these before assigning base since otherwise wrapper name and app will reset
result.Base = mutatingGeo.Base;
result.GeometryBase = mutatingGeo.GeometryBase;
}
}
else
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"{inputGeometry.TypeName} is not a valid type for Speckle Geometry."
);
return;
}
}
result.NotNull();
// process name
if (inputName != null)
{
result.Name = inputName;
}
// process properties
if (inputProperties != null)
{
result.Properties = inputProperties;
}
// process color (no mutation)
if (inputColor != null)
{
result.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
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();
// set all the data
da.SetData(0, result.CreateGoo());
da.SetData(1, result.GeometryBase);
da.SetData(2, result.Name);
da.SetData(3, result.Properties);
da.SetData(4, result.Color);
da.SetData(5, result.Material);
da.SetData(6, path);
}
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
private void MatchNonGeometryProps(SpeckleGeometryWrapper wrapper, SpeckleGeometryWrapper wrapperToMatch)
{
wrapper.Name = wrapperToMatch.Name;
wrapper.ApplicationId = wrapperToMatch.ApplicationId;
wrapper.Properties = wrapperToMatch.Properties;
wrapper.Parent = wrapperToMatch.Parent;
wrapper.Path = wrapperToMatch.Path;
wrapper.Color = wrapperToMatch.Color;
wrapper.Material = wrapperToMatch.Material;
}
}
@@ -0,0 +1,235 @@
using System.Runtime.InteropServices;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// <summary>
/// CreateSpeckleProperties passthrough component by key value pairs
/// </summary>
[Guid("FED2298C-0D2B-4868-94B5-B8D17F9385A5")]
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.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
{
Merge, // this should be default mode
Replace,
Remove
}
private PropertyMode _mode = PropertyMode.Merge;
private PropertyMode Mode
{
get => _mode;
set
{
if (_mode != value)
{
_mode = value;
Message = Mode.ToString();
ExpireSolution(true);
}
}
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Input properties", GH_ParamAccess.item);
Params.Input[0].Optional = true;
pManager.AddTextParameter("Keys", "k", "Keys of all properties", GH_ParamAccess.list);
Params.Input[1].Optional = true;
pManager.AddGenericParameter(
"Values",
"v",
"Values of all properties. Accepts text, number, bool, null, or other properties as inputs.",
GH_ParamAccess.list
);
Params.Input[2].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Properties", GH_ParamAccess.item);
pManager.AddTextParameter("Keys", "k", "Keys of all properties", GH_ParamAccess.list);
pManager.AddGenericParameter("Values", "v", "Values of all properties", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(0, ref inputProperties);
List<string> inputKeys = new();
bool hasKeys = da.GetDataList(1, inputKeys);
List<object?> inputValues = new();
bool hasValues = da.GetDataList(2, inputValues);
// if no props or keyvalues were input, create empty props and return
if (inputProperties == null && !hasKeys)
{
SpecklePropertyGroupGoo emptyProps = new();
da.SetData(0, emptyProps);
da.SetDataList(1, emptyProps.Value.Keys);
da.SetDataList(2, emptyProps.Value.Values);
return;
}
// 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
? new()
: inputProperties.Value.ToDictionary(entry => entry.Key, entry => entry.Value);
// process keys and values
if (hasKeys)
{
// check for duplicate keys
var duplicates = inputKeys.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key);
if (duplicates.Any())
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Duplicate property keys found: {string.Join(", ", duplicates)}"
);
return;
}
// set keyvalue pairs
for (int i = 0; i < inputKeys.Count; i++)
{
string key = inputKeys[i];
object? value = Mode == PropertyMode.Remove ? null : inputValues[i];
ISpecklePropertyGoo? convertedValue = null;
switch (value)
{
case SpecklePropertyGroupGoo propGoo:
convertedValue = propGoo;
break;
case null:
convertedValue = new SpecklePropertyGoo();
break;
default:
SpecklePropertyGoo propValue = new();
if (!propValue.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
);
return;
}
convertedValue = propValue;
break;
}
switch (Mode)
{
case PropertyMode.Merge:
if (result.ContainsKey(key))
{
result[key] = convertedValue;
}
else
{
result.Add(key, convertedValue);
}
break;
case PropertyMode.Replace:
result.Add(key, convertedValue);
break;
case PropertyMode.Remove:
result.Remove(key);
break;
}
}
}
var groupGoo = new SpecklePropertyGroupGoo(result);
da.SetData(0, groupGoo);
da.SetDataList(1, result.Keys);
da.SetDataList(2, result.Values);
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
Menu_AppendSeparator(menu); // modes section
foreach (PropertyMode mode in Enum.GetValues(typeof(PropertyMode)))
{
var modeItem = Menu_AppendItem(menu, mode.ToString(), (_, _) => Mode = mode, true, mode == Mode);
switch (mode)
{
case PropertyMode.Merge:
modeItem.ToolTipText =
"Input keyvalue pairs will be merged with existing properties. Any existing keys will be updated with new values.";
break;
case PropertyMode.Replace:
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 keys will be removed from properties.";
break;
}
}
Menu_AppendSeparator(menu);
}
public override bool Write(GH_IWriter writer)
{
var result = base.Write(writer);
writer.SetString("Mode", Mode.ToString());
return result;
}
public override bool Read(GH_IReader reader)
{
var result = base.Read(reader);
string mode = "";
if (reader.TryGetString("Mode", ref mode))
{
if (Enum.TryParse(mode, out PropertyMode modeEnum))
{
Mode = modeEnum;
}
}
return result;
}
}
@@ -0,0 +1,188 @@
using System.Diagnostics;
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Credentials;
using Timer = System.Timers.Timer;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations;
public class AccountManagerComponent : GH_Component, IDisposable
{
private bool _disposed;
private bool _isAddingAccount;
private Timer _timeoutTimer;
private Timer? _accountCheckerTimer;
private List<Account>? Accounts { get; set; }
private string? CustomUrlInput { get; set; }
private readonly IAccountManager _accountManager;
public override Guid ComponentGuid => new("c8ede281-acdf-49bf-8611-e9579be1bd41");
protected override Bitmap Icon => Resources.speckle_operations_account;
public override GH_Exposure Exposure => GH_Exposure.primary;
public GhContextMenuButton SignInButton { get; }
public AccountManagerComponent()
: base(
"Sign In",
"SI",
"Sign in to a Speckle Account",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
)
{
Attributes = new AccountManagerComponentAttributes(this);
_accountManager = PriorityLoader.Container.GetRequiredService<IAccountManager>();
Accounts = _accountManager.GetAccounts().ToList();
SignInButton = new GhContextMenuButton("Sign In", "Sign In", "Click to sign into Speckle account.", AuthFlow);
}
private bool AuthFlow(ToolStripDropDown menu)
{
_isAddingAccount = true;
ResumeAccountChecker();
string url = string.IsNullOrEmpty(CustomUrlInput)
? "http://localhost:29364/auth/add-account"
: $"http://localhost:29364/auth/add-account?serverUrl={new Uri(CustomUrlInput).GetLeftPart(UriPartial.Authority)}";
// Open the auth URL in the default browser
try
{
Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
}
catch (Exception ex) when (!ex.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Sign in failed: {ex.Message}");
_isAddingAccount = false;
return false;
}
_timeoutTimer = new Timer(30_000);
_timeoutTimer.Elapsed += (s, e) =>
{
_timeoutTimer.Stop();
if (_isAddingAccount)
{
_isAddingAccount = false;
PauseAccountChecker();
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
"Sign in timed out. This may have happened because you tried adding an existing account."
);
}
};
_timeoutTimer.Start();
return true;
}
private bool CheckIfAccountAdded()
{
var previousAccountCount = Accounts?.Count ?? 0;
Accounts = _accountManager.GetAccounts().ToList();
return previousAccountCount < Accounts.Count;
}
private void PauseAccountChecker()
{
_accountCheckerTimer?.Stop();
_accountCheckerTimer?.Dispose();
_accountCheckerTimer = null;
}
private void ResumeAccountChecker()
{
_accountCheckerTimer?.Dispose();
_accountCheckerTimer = new Timer(1000); // check every 1 second
_accountCheckerTimer.Elapsed += (s, e) =>
{
bool accountAdded = CheckIfAccountAdded();
if (accountAdded)
{
_accountCheckerTimer.Stop();
_isAddingAccount = false;
// Optionally cancel timeout timer
_timeoutTimer?.Stop();
OnPingDocument()
?.ScheduleSolution(
100,
doc =>
{
ExpireSolution(true);
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Account added successfully!");
}
);
}
};
_accountCheckerTimer.Start();
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
var urlIndex = pManager.AddTextParameter(
"Server Url",
"Url",
"Optional URL for signing into a self deployed Speckle server.",
GH_ParamAccess.item
);
pManager[urlIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddTextParameter($"Accounts", "Accounts", "List of available accounts", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
string? urlInput = null;
if (da.GetData(0, ref urlInput))
{
CustomUrlInput = urlInput;
}
if (Accounts != null)
{
da.SetDataList(0, Accounts);
}
else
{
da.SetDataList(0, new List<Account>());
}
}
public override void ExpirePreview(bool redraw)
{
SignInButton.ExpirePreview(redraw);
base.ExpirePreview(redraw);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_timeoutTimer?.Dispose();
_accountManager.Dispose();
_accountCheckerTimer?.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
@@ -0,0 +1,54 @@
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations;
public class AccountManagerComponentAttributes : GH_ComponentAttributes
{
private readonly AccountManagerComponent _typedOwner;
public AccountManagerComponentAttributes(IGH_Component component)
: base(component)
{
_typedOwner = (AccountManagerComponent)component;
}
public override void AppendToAttributeTree(List<IGH_Attributes> attributes)
{
base.AppendToAttributeTree(attributes);
_typedOwner.SignInButton.Attributes?.AppendToAttributeTree(attributes);
}
private void InitialiseAttributes()
{
_typedOwner.SignInButton.Attributes ??= new GhContextMenuButtonAttributes(_typedOwner.SignInButton)
{
Parent = this,
};
}
protected override void Layout()
{
base.Layout();
var baseRec = GH_Convert.ToRectangle(Bounds);
baseRec.Height += 26;
var btnRec = baseRec;
btnRec.Y = baseRec.Bottom - 26;
btnRec.Height = 26;
btnRec.Inflate(-2, -2);
Bounds = baseRec;
InitialiseAttributes();
_typedOwner.SignInButton.Attributes.Pivot = btnRec.Location;
_typedOwner.SignInButton.Attributes.Bounds = btnRec;
}
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
_typedOwner.SignInButton.Attributes.RenderToCanvas(canvas, channel);
}
}
@@ -1,10 +1,10 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
public record GrasshopperReceiveInfo(
string AccountId,
Uri ServerUrl,
Account Account,
string? WorkspaceId,
string ProjectId,
string ProjectName,
@@ -13,4 +13,4 @@ public record GrasshopperReceiveInfo(
string SelectedVersionId,
string SourceApplication,
string? SelectedVersionUserId
) : ReceiveInfo(AccountId, ServerUrl, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, SourceApplication);
) : ReceiveInfo(Account, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, SourceApplication);
@@ -7,7 +7,6 @@ using GrasshopperAsyncComponent;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -26,7 +25,7 @@ using Speckle.Sdk.Models.Extensions;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
[Guid("1587DF34-83E5-4AFE-B42E-F7C5C37ECD68")]
public class ReceiveAsyncComponent : GH_AsyncComponent
public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
{
public ReceiveAsyncComponent()
: base("Load", "L", "Load a model from Speckle", ComponentCategories.PRIMARY_RIBBON, ComponentCategories.OPERATIONS)
@@ -38,6 +37,8 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_operations_load;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public string InputType { get; set; }
public bool AutoReceive { get; set; }
public string ReceivedVersionId { get; set; }
@@ -270,7 +271,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
ResetApiClient(UrlModelResource);
}
private void ApiClient_OnVersionCreated(object sender, ProjectVersionsUpdatedMessage e)
private void ApiClient_OnVersionCreated(object? sender, ProjectVersionsUpdatedMessage e)
{
HandleNewCommit();
}
@@ -280,11 +281,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
try
{
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Account? account =
urlResource.AccountId != null
? scope.Get<IAccountManager>().GetAccount(urlResource.AccountId)
: scope.Get<IAccountService>().GetAccountWithServerUrlFallback("", new Uri(urlResource.Server)); // fallback the account that matches with URL if any
Account? account = urlResource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
@@ -302,24 +299,28 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
}
}
public class ReceiveComponentWorker : WorkerInstance
public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponent>
{
public ReceiveComponentWorker(GH_Component p)
: base(p) { }
public ReceiveComponentWorker(
ReceiveAsyncComponent p,
string id = "baseWorker",
CancellationToken cancellationToken = default
)
: base(p, id, cancellationToken) { }
public Base Root { get; set; }
public SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo Result { get; set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance Duplicate()
public override WorkerInstance<ReceiveAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
{
return new ReceiveComponentWorker(Parent);
return new ReceiveComponentWorker(Parent, id, cancellationToken);
}
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
{
UrlModelResource = ((ReceiveAsyncComponent)Parent).UrlModelResource;
UrlModelResource = Parent.UrlModelResource;
}
public override void SetData(IGH_DataAccess da)
@@ -334,7 +335,7 @@ public class ReceiveComponentWorker : WorkerInstance
Parent.AddRuntimeMessage(level, message);
}
var parent = (ReceiveAsyncComponent)Parent;
var parent = Parent;
parent.CurrentComponentState = ComponentState.UpToDate;
@@ -348,130 +349,19 @@ public class ReceiveComponentWorker : WorkerInstance
da.SetData(0, Result);
}
#pragma warning disable CA1506
public override void DoWork(Action<string, double> reportProgress, Action done)
#pragma warning restore CA1506
public override async Task DoWork(Action<string, double> reportProgress, Action done)
{
var receiveComponent = (ReceiveAsyncComponent)Parent;
try
{
if (UrlModelResource is null)
{
throw new InvalidOperationException("Model Resource was null");
}
// Means it's a copy paste of an empty non-init component; set the record and exit fast.
if (receiveComponent.JustPastedIn && !receiveComponent.AutoReceive)
{
receiveComponent.JustPastedIn = false;
return;
}
receiveComponent.CurrentComponentState = ComponentState.Receiving;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
receiveComponent.OnDisplayExpired(true);
}
);
var t = Task.Run(async () =>
{
// Step 1 - RECEIVE FROM SERVER
var receiveInfo = await UrlModelResource
.GetReceiveInfo(receiveComponent.ApiClient, CancellationToken)
.ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//eceiveComponent.Message = $"{p.Status}";
});
if (CancellationToken.IsCancellationRequested)
{
return;
}
if (receiveInfo == null)
{
done();
return;
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
if (CancellationToken.IsCancellationRequested)
{
return;
}
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
LocalToGlobalUnpacker localToGlobalUnpacker = new();
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
);
// TODO: unpack colors and render materials
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
GrasshopperCollectionRebuilder collectionRebuilder =
new((Root as Collection) ?? new Collection() { name = "unnamed" });
LocalToGlobalMapHandler mapHandler =
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
int count = 0;
int total = localToGlobalMaps.Count;
foreach (var map in localToGlobalMaps)
{
mapHandler.CreateGrasshopperObjectFromMap(map);
count++;
}
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", receiveComponent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add(
"isMultiplayer",
receiveInfo.SelectedVersionUserId != receiveComponent.ApiClient.Account.userInfo.id
);
}
await scope
.Get<IMixPanelManager>()
.TrackEvent(MixPanelEvents.Receive, receiveComponent.ApiClient.Account, customProperties);
// DONE
done();
});
t.Wait();
await Receive(reportProgress);
done();
}
catch (OperationCanceledException) when (CancellationToken.IsCancellationRequested)
{
RuntimeMessages.Add((GH_RuntimeMessageLevel.Remark, "Operation cancelled"));
Parent.CurrentComponentState = ComponentState.Expired;
//No need to call `done()` - GrasshopperAsyncComponent assumes immediate cancel,
//thus it has already performed clean-up actions that would normally be done on `done()`
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -479,6 +369,110 @@ public class ReceiveComponentWorker : WorkerInstance
done();
}
}
#pragma warning disable CA1506
private async Task Receive(Action<string, double> reportProgress)
#pragma warning restore CA1506
{
if (UrlModelResource is null)
{
throw new InvalidOperationException("Model Resource was null");
}
// Means it's a copy paste of an empty non-init component; set the record and exit fast.
if (Parent.JustPastedIn && !Parent.AutoReceive)
{
Parent.JustPastedIn = false;
return;
}
Parent.CurrentComponentState = ComponentState.Receiving;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
Parent.OnDisplayExpired(true);
}
);
// Step 1 - RECEIVE FROM SERVER
var receiveInfo = await UrlModelResource.GetReceiveInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//eceiveComponent.Message = $"{p.Status}";
});
CancellationToken.ThrowIfCancellationRequested();
if (receiveInfo == null)
{
return;
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
CancellationToken.ThrowIfCancellationRequested();
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// separate atomic objects from block instances
var (atomicObjects, blockInstances) = scope
.Get<RootObjectUnpacker>()
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
// initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(Root as Collection) ?? new Collection { name = "unnamed" }
);
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
foreach (var atomicContext in atomicObjects)
{
mapHandler.ConvertAtomicObject(atomicContext);
}
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", Parent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id);
}
await scope.Get<IMixPanelManager>().TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
}
}
public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
@@ -537,7 +531,7 @@ public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
else
{
var palette =
state == ComponentState.Expired || state == ComponentState.UpToDate || state == ComponentState.Cancelled
state == ComponentState.Expired || state == ComponentState.UpToDate
? GH_Palette.Black
: GH_Palette.Transparent;
var text = state != ComponentState.Receiving ? "Load" : "Loading...";
@@ -2,7 +2,6 @@ using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
@@ -116,18 +115,12 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
// Do the thing 👇🏼
Account? account =
input.Resource.AccountId != null
? accountManager.GetAccount(input.Resource.AccountId)
: accountService.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server)); // fallback the account that matches with URL if any
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
@@ -165,32 +158,39 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
// We need to rethink these lovely unpackers, there's a bit too many of 'em
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
var traversalContextUnpacker = new TraversalContextUnpacker();
var unpackedRoot = rootObjectUnpacker.Unpack(root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
// split atomic objects from block components before conversion
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
// unpack colors and render materials
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
// Initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(root as Collection) ?? new Collection { name = "unnamed" }
);
GrasshopperCollectionRebuilder collectionRebuilder =
new((root as Collection) ?? new Collection() { name = "unnamed" });
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
LocalToGlobalMapHandler mapHandler =
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
foreach (var map in localToGlobalMaps)
foreach (var atomicContext in atomicObjects)
{
mapHandler.CreateGrasshopperObjectFromMap(map);
mapHandler.ConvertAtomicObject(atomicContext);
}
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo };
@@ -1,12 +1,12 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
public record GrasshopperSendInfo(
string AccountId,
Uri ServerUrl,
Account Account,
string? WorkspaceId,
string ProjectId,
string ModelId,
string SourceApplication
) : SendInfo(AccountId, ServerUrl, ProjectId, ModelId, SourceApplication);
) : SendInfo(Account, ProjectId, ModelId, SourceApplication);
@@ -16,17 +16,15 @@ using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Extensions;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
[Guid("52481972-7867-404F-8D9F-E1481183F355")]
public class SendAsyncComponent : GH_AsyncComponent
public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
{
public GhContextMenuButton ProjectContextMenuButton { get; set; }
public GhContextMenuButton ModelContextMenuButton { get; set; }
public SendAsyncComponent()
: base(
"Publish",
@@ -43,6 +41,7 @@ public class SendAsyncComponent : GH_AsyncComponent
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_operations_publish;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public ComponentState CurrentComponentState { get; set; } = ComponentState.NeedsInput;
public bool AutoSend { get; set; }
@@ -55,6 +54,8 @@ public class SendAsyncComponent : GH_AsyncComponent
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());
@@ -65,6 +66,8 @@ public class SendAsyncComponent : GH_AsyncComponent
"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)
@@ -156,12 +159,8 @@ public class SendAsyncComponent : GH_AsyncComponent
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
// We need to call this always in here to be able to react and set events :/
ParseInput(da, accountService, accountManager, clientFactory);
ParseInput(da, scope);
if (
(AutoSend || CurrentComponentState == ComponentState.Ready || CurrentComponentState == ComponentState.Sending)
@@ -198,7 +197,7 @@ public class SendAsyncComponent : GH_AsyncComponent
public override void DisplayProgress(object sender, ElapsedEventArgs e)
{
if (Workers.Count == 0)
if (WorkerCount == 0)
{
return;
}
@@ -240,12 +239,7 @@ public class SendAsyncComponent : GH_AsyncComponent
base.DocumentContextChanged(document, context);
}
private void ParseInput(
IGH_DataAccess da,
IAccountService accountService,
IAccountManager accountManager,
IClientFactory clientFactory
)
private void ParseInput(IGH_DataAccess da, IServiceScope scope)
{
HostApp.SpeckleUrlModelResource? dataInput = null;
da.GetData(0, ref dataInput);
@@ -259,17 +253,14 @@ public class SendAsyncComponent : GH_AsyncComponent
UrlModelResource = dataInput;
try
{
Account? account =
dataInput.AccountId != null
? accountManager.GetAccount(dataInput.AccountId)
: accountService.GetAccountWithServerUrlFallback("", new Uri(dataInput.Server)); // fallback the account that matches with URL if any
Account? account = dataInput.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
}
ApiClient?.Dispose();
ApiClient = clientFactory.Create(account);
ApiClient = scope.Get<IClientFactory>().Create(account);
}
catch (Exception e) when (!e.IsFatal())
{
@@ -285,21 +276,29 @@ public class SendAsyncComponent : GH_AsyncComponent
return;
}
RootCollectionWrapper = rootCollectionWrapper;
string? versionMessage = null;
da.GetData(2, ref versionMessage);
VersionMessage = versionMessage;
}
}
public class SendComponentWorker : WorkerInstance
public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
{
public SendComponentWorker(GH_Component p)
: base(p) { }
public SendComponentWorker(
SendAsyncComponent p,
string id = "baseWorker",
CancellationToken cancellationToken = default
)
: base(p, id, cancellationToken) { }
private Stopwatch _stopwatch;
private Stopwatch? _stopwatch;
public SpeckleUrlModelResource? OutputParam { get; set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance Duplicate()
public override WorkerInstance<SendAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
{
return new SendComponentWorker(Parent);
return new SendComponentWorker(Parent, id, cancellationToken);
}
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
@@ -310,18 +309,18 @@ public class SendComponentWorker : WorkerInstance
public override void SetData(IGH_DataAccess da)
{
_stopwatch.Stop();
_stopwatch.NotNull("GetData must be called before SetData").Stop();
if (((SendAsyncComponent)Parent).JustPastedIn)
if (Parent.JustPastedIn)
{
((SendAsyncComponent)Parent).JustPastedIn = false;
da.SetData(0, ((SendAsyncComponent)Parent).OutputParam);
Parent.JustPastedIn = false;
da.SetData(0, Parent.OutputParam);
return;
}
if (CancellationToken.IsCancellationRequested)
{
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.Expired;
Parent.CurrentComponentState = ComponentState.Expired;
return;
}
@@ -332,9 +331,9 @@ public class SendComponentWorker : WorkerInstance
da.SetData(0, OutputParam);
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.UpToDate;
((SendAsyncComponent)Parent).OutputParam = OutputParam; // ref the outputs in the parent too, so we can serialise them on write/read
((SendAsyncComponent)Parent).OverallProgress = 0;
Parent.CurrentComponentState = ComponentState.UpToDate;
Parent.OutputParam = OutputParam; // ref the outputs in the parent too, so we can serialise them on write/read
Parent.OverallProgress = 0;
var hasWarnings = RuntimeMessages.Count > 0;
if (!hasWarnings)
@@ -356,96 +355,24 @@ public class SendComponentWorker : WorkerInstance
}
}
public override void DoWork(Action<string, double> reportProgress, Action done)
public override async Task DoWork(Action<string, double> reportProgress, Action done)
{
var sendComponent = (SendAsyncComponent)Parent;
if (sendComponent.JustPastedIn || sendComponent.HasMultipleInputs)
if (Parent.JustPastedIn || Parent.HasMultipleInputs)
{
done();
return;
}
if (CancellationToken.IsCancellationRequested)
{
sendComponent.CurrentComponentState = ComponentState.Expired;
return;
}
try
{
SpeckleUrlModelResource? urlModelResource = sendComponent.UrlModelResource;
if (urlModelResource is null)
{
throw new InvalidOperationException("Url Resource was null");
}
SpeckleCollectionWrapperGoo? rootCollectionWrapper = sendComponent.RootCollectionWrapper;
if (rootCollectionWrapper is null)
{
throw new InvalidOperationException("Root Collection was null");
}
var t = Task.Run(async () =>
{
if (CancellationToken.IsCancellationRequested)
{
sendComponent.CurrentComponentState = ComponentState.Expired;
return;
}
// Step 1 - SEND TO SERVER
var sendInfo = await urlModelResource
.GetSendInfo(sendComponent.ApiClient, CancellationToken)
.ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//sendComponent.Message = $"{p.Status}";
});
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
SendOperationResult? result = await sendOperation
.Execute(
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
sendInfo,
progress,
CancellationToken
)
.ConfigureAwait(false);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "auto", sendComponent.AutoSend }
};
if (sendInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", sendInfo.WorkspaceId);
}
var mixPanelManager = scope.ServiceProvider.GetRequiredService<IMixPanelManager>();
await mixPanelManager.TrackEvent(MixPanelEvents.Send, sendComponent.ApiClient.Account, customProperties);
SpeckleUrlModelVersionResource? createdVersion =
new(
sendInfo.AccountId,
sendInfo.ServerUrl.ToString(),
sendInfo.WorkspaceId,
sendInfo.ProjectId,
sendInfo.ModelId,
result.VersionId
);
OutputParam = createdVersion;
sendComponent.Url = $"{createdVersion.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
// DONE
done();
});
t.Wait();
await Send(reportProgress);
done();
}
catch (OperationCanceledException) when (CancellationToken.IsCancellationRequested)
{
RuntimeMessages.Add((GH_RuntimeMessageLevel.Remark, "Operation cancelled"));
Parent.CurrentComponentState = ComponentState.Expired;
//No need to call `done()` - GrasshopperAsyncComponent assumes immediate cancel,
//thus it has already performed clean-up actions that would normally be done on `done()`
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -453,6 +380,65 @@ public class SendComponentWorker : WorkerInstance
done();
}
}
private async Task Send(Action<string, double> reportProgress)
{
SpeckleUrlModelResource? urlModelResource = Parent.UrlModelResource;
if (urlModelResource is null)
{
throw new InvalidOperationException("Url Resource was null");
}
SpeckleCollectionWrapperGoo? rootCollectionWrapper = Parent.RootCollectionWrapper;
if (rootCollectionWrapper is null)
{
throw new InvalidOperationException("Root Collection was null");
}
// Step 1 - SEND TO SERVER
var sendInfo = await urlModelResource.GetSendInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//sendComponent.Message = $"{p.Status}";
});
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
SendOperationResult? result = await sendOperation
.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
var customProperties = new Dictionary<string, object>() { { "isAsync", true }, { "auto", Parent.AutoSend } };
if (sendInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", sendInfo.WorkspaceId);
}
var mixPanelManager = scope.ServiceProvider.GetRequiredService<IMixPanelManager>();
await mixPanelManager
.TrackEvent(MixPanelEvents.Send, Parent.ApiClient.Account, customProperties)
.ConfigureAwait(false);
SpeckleUrlModelVersionResource createdVersion =
new(
new(sendInfo.Account.id, null, sendInfo.Account.serverInfo.url),
sendInfo.WorkspaceId,
sendInfo.ProjectId,
sendInfo.ModelId,
result.VersionId
);
OutputParam = createdVersion;
Parent.Url = $"{createdVersion.Account.Server}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
}
}
public class SendAsyncComponentAttributes : GH_ComponentAttributes
@@ -496,7 +482,7 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
{
if (((SendAsyncComponent)Owner).AutoSend)
{
var autoSendButton = GH_Capsule.CreateTextCapsule(
using var autoSendButton = GH_Capsule.CreateTextCapsule(
ButtonBounds,
ButtonBounds,
GH_Palette.Blue,
@@ -506,7 +492,6 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
);
autoSendButton.Render(graphics, Selected, Owner.Locked, false);
autoSendButton.Dispose();
}
else
{
@@ -517,7 +502,7 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
var text = state == ComponentState.Sending ? "Publishing..." : "Publish";
var button = GH_Capsule.CreateTextCapsule(
using var button = GH_Capsule.CreateTextCapsule(
ButtonBounds,
ButtonBounds,
palette,
@@ -526,7 +511,6 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
state == ComponentState.Expired ? 10 : 0
);
button.Render(graphics, Selected, Owner.Locked, false);
button.Dispose();
}
}
}
@@ -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);
}
@@ -160,16 +167,10 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
Account? account =
input.Resource.AccountId != null
? accountManager.GetAccount(input.Resource.AccountId)
: accountService.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server)); // fallback the account that matches with URL if any
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
@@ -183,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
@@ -199,14 +206,12 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
SpeckleUrlLatestModelVersionResource createdVersionResource =
new(
sendInfo.AccountId,
sendInfo.ServerUrl.ToString(),
new(sendInfo.Account.id, null, sendInfo.Account.serverInfo.url),
sendInfo.WorkspaceId,
sendInfo.ProjectId,
sendInfo.ModelId
);
Url = $"{createdVersionResource.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
Url = $"{sendInfo.Account.serverInfo.url}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
return new SendComponentOutput(createdVersionResource);
}
}
@@ -1,3 +1,4 @@
using System.Diagnostics;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Components.Operations.Wizard;
@@ -31,10 +32,12 @@ public class SpeckleSelectModelComponent : GH_Component
protected override Bitmap Icon => Resources.speckle_inputs_model;
public override GH_Exposure Exposure => GH_Exposure.primary;
public SpeckleSelectModelComponent()
: base(
"Speckle Model URL",
"URL",
"Speckle Model",
"SM",
"User selectable model from Speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
@@ -99,13 +102,13 @@ public class SpeckleSelectModelComponent : GH_Component
try
{
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
var (resource, hasPermission) = SpeckleOperationWizard.SolveInstanceWithUrlInput(urlInput, true);
var (resource, hasPermission) = SpeckleOperationWizard.SolveInstanceWithUrlInput(urlInput, true, null);
if (!hasPermission)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You do not have enough permission for this project.");
}
_storedUserId = SpeckleOperationWizard.SelectedAccount?.id;
_storedServer = resource.Server;
_storedServer = resource.Account.Server;
da.SetData(0, resource);
}
catch (SpeckleException e)
@@ -257,8 +260,11 @@ public class SpeckleSelectModelComponent : GH_Component
da.SetData(
0,
new SpeckleUrlLatestModelVersionResource(
SpeckleOperationWizard.SelectedAccount.id,
SpeckleOperationWizard.SelectedAccount.serverInfo.url,
new AccountResource(
SpeckleOperationWizard.SelectedAccount.id,
null,
SpeckleOperationWizard.SelectedAccount.serverInfo.url
),
SpeckleOperationWizard.SelectedWorkspace?.id,
SpeckleOperationWizard.SelectedProject.id,
SpeckleOperationWizard.SelectedModel.id
@@ -271,8 +277,11 @@ public class SpeckleSelectModelComponent : GH_Component
da.SetData(
0,
new SpeckleUrlModelVersionResource(
SpeckleOperationWizard.SelectedAccount.id,
SpeckleOperationWizard.SelectedAccount.serverInfo.url,
new AccountResource(
SpeckleOperationWizard.SelectedAccount.id,
null,
SpeckleOperationWizard.SelectedAccount.serverInfo.url
),
SpeckleOperationWizard.SelectedWorkspace?.id,
SpeckleOperationWizard.SelectedProject.id,
SpeckleOperationWizard.SelectedModel.id,
@@ -325,6 +334,30 @@ public class SpeckleSelectModelComponent : GH_Component
SpeckleOperationWizard.SelectedAccount?.id == account.id
);
}
if (SpeckleOperationWizard.SelectedAccount != null && SpeckleOperationWizard.SelectedProject != null)
{
Menu_AppendSeparator(menu);
Menu_AppendItem(
menu,
$"View model online ↗",
(s, e) =>
Open(
SpeckleOperationWizard.SelectedAccount.serverInfo.url,
SpeckleOperationWizard.SelectedProject.id,
SpeckleOperationWizard.SelectedModel?.id,
SpeckleOperationWizard.SelectedVersion?.id
)
);
}
static void Open(string server, string projectId, string? modelId, string? versionId)
{
string url =
$"{server}/projects/{projectId}/models/{(modelId is null ? "" : modelId)}{(versionId is null ? "" : $"@{versionId}")}";
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
Process.Start(psi);
}
}
public override bool Write(GH_IWriter writer)
@@ -71,6 +71,7 @@ public class ModelMenuHandler
private bool PopulateMenu(ToolStripDropDown menu)
{
menu.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
_menu = menu;
_searchItem = new SearchToolStripMenuItem(menu, Refetch);
@@ -71,6 +71,7 @@ public class ProjectMenuHandler
private bool PopulateMenu(ToolStripDropDown menu)
{
menu.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
_menu = menu;
_searchItem = new SearchToolStripMenuItem(menu, Refetch);
@@ -1,3 +1,5 @@
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Wizard;
public class SearchToolStripMenuItem
@@ -16,6 +18,7 @@ public class SearchToolStripMenuItem
public SearchToolStripMenuItem(ToolStripDropDown parent, Func<string, Task> onSearchTextChanged)
{
parent.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
ParentDropDown = parent;
ParentDropDown.Opacity = 0.95;
ParentDropDown.TopLevel = true;
@@ -38,9 +41,11 @@ public class SearchToolStripMenuItem
{
var item = new ToolStripMenuItem(text)
{
TextAlign = ContentAlignment.MiddleLeft,
Checked = isChecked ?? false,
Image = image,
ImageScaling = ToolStripItemImageScaling.SizeToFit
ImageScaling = ToolStripItemImageScaling.SizeToFit,
ImageAlign = ContentAlignment.MiddleLeft
};
item.Click += click;
if (visible == false)
@@ -60,10 +65,10 @@ public class SearchToolStripMenuItem
{
var textBox = new TextBox
{
BorderStyle = BorderStyle.None,
Width = 600,
Font = new Font("Segoe UI", 9),
TextAlign = HorizontalAlignment.Left,
BorderStyle = BorderStyle.None,
Width = ParentDropDown.Width,
Font = new Font("Segoe UI", 9),
Text = SEARCH_PLACEHOLDER_TEXT,
BackColor = Color.White,
};
@@ -105,10 +110,10 @@ public class SearchToolStripMenuItem
SearchHost = new ToolStripControlHost(textBox)
{
Alignment = ToolStripItemAlignment.Left,
ControlAlign = ContentAlignment.MiddleLeft,
Name = SearchItemId,
AutoSize = false,
Size = new Size(170, 24),
Margin = new Padding(4),
Margin = new Padding(2),
Padding = new Padding(2)
};
@@ -118,16 +123,10 @@ public class SearchToolStripMenuItem
private void RegisterEvents()
{
// Resets the search filter
// ParentDropDown.Opening += async (sender, args) =>
// {
// await _onSearchTextChanged.Invoke("");
// };
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;
}
@@ -135,8 +134,14 @@ public class SearchToolStripMenuItem
ParentDropDown.Close();
};
ParentDropDown.Closed += (sender, args) =>
// Resets the list with empty search texts, otherwise on next menu pop up we end up with latest state
ParentDropDown.Closed += async (sender, args) =>
{
// clear list only if search text is not null
if (SearchText != null)
{
await _onSearchTextChanged.Invoke("");
}
SearchText = null;
};
}
@@ -22,10 +22,11 @@ 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; }
public bool IsLatestVersion { get; private set; }
public WorkspaceMenuHandler WorkspaceMenuHandler { get; }
public ProjectMenuHandler ProjectMenuHandler { get; }
@@ -65,13 +66,17 @@ public class SpeckleOperationWizard
ModelMenuHandler.ModelSelected += OnModelSelected;
}
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrlInput(string input, bool isSender)
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrlInput(
string input,
bool isSender,
string? token
)
{
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
// Should perform validation, fill in all internal data of the component (project, model, version, account)
// Should notify user if any of this goes wrong.
var resources = SpeckleResourceBuilder.FromUrlString(input);
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
if (resources.Length == 0)
{
throw new SpeckleException($"Input url string was empty");
@@ -83,8 +88,8 @@ public class SpeckleOperationWizard
}
var resource = resources.First();
var account = _accountService.GetAccountWithServerUrlFallback(string.Empty, new Uri(resource.Server));
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var account = resource.Account.GetAccount(scope);
SetAccount(account, false);
if (SelectedAccount == null)
@@ -116,7 +121,7 @@ public class SpeckleOperationWizard
// TODO: this wont be the case when we have separation between send and receive components
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
VersionMenuHandler?.RedrawMenuButton(v);
VersionMenuHandler?.RedrawMenuButton(v, false);
break;
case SpeckleUrlModelObjectResource:
throw new SpeckleException("Object URLs are not supported");
@@ -238,7 +243,7 @@ public class SpeckleOperationWizard
using IClient client = _clientFactory.Create(SelectedAccount);
var version = client.Version.Get(versionId, SelectedProject.id).Result;
SelectedVersion = version;
VersionMenuHandler?.RedrawMenuButton(SelectedVersion);
VersionMenuHandler?.RedrawMenuButton(SelectedVersion, IsLatestVersion);
}
/// <summary>
@@ -411,40 +416,45 @@ 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();
ResetVersions(true);
_refreshComponent.Invoke();
}
private void OnVersionSelected(object sender, VersionSelectedEventArgs e)
private void OnVersionSelected(object? sender, VersionSelectedEventArgs e)
{
SelectedVersion = e.SelectedVersion;
IsLatestVersion = e.IsLatest;
_refreshComponent.Invoke();
}
@@ -469,10 +479,10 @@ public class SpeckleOperationWizard
ResetVersions();
}
private void ResetVersions()
private void ResetVersions(bool defaultToLatest = false)
{
SelectedVersion = null;
VersionMenuHandler?.Reset();
VersionMenuHandler?.Reset(defaultToLatest);
}
private Task CreateNewWorkspace()
@@ -1,11 +1,12 @@
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Api.GraphQL.Models;
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Wizard;
public class VersionSelectedEventArgs(Version? version) : EventArgs
public class VersionSelectedEventArgs(Version? version, bool isLatest) : EventArgs
{
public Version? SelectedVersion { get; } = version;
public bool IsLatest { get; } = isLatest;
}
public class VersionMenuHandler
@@ -14,6 +15,7 @@ public class VersionMenuHandler
private readonly Func<int, Task<ResourceCollection<Version>>> _fetchVersions;
private ToolStripDropDown? _menu;
private Version? SelectedVersion { get; set; }
public bool IsLatest { get; set; }
public ResourceCollection<Version>? Versions { get; set; }
@@ -32,14 +34,15 @@ public class VersionMenuHandler
);
}
public void Reset()
public void Reset(bool isLatest = false)
{
_menu?.Items.Clear();
_menu?.Close();
IsLatest = isLatest;
SelectedVersion = null;
Versions = null;
FetchedVersionCount = 10;
RedrawMenuButton(null);
RedrawMenuButton(null, isLatest);
}
private async Task Refetch(int versionCount)
@@ -50,6 +53,7 @@ public class VersionMenuHandler
private bool PopulateMenu(ToolStripDropDown menu)
{
menu.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
_menu = menu;
if (Versions is null)
@@ -78,7 +82,7 @@ public class VersionMenuHandler
return;
}
AddMenuItem("Latest Version", (_, _) => OnVersionSelected(null), true, SelectedVersion == null);
AddMenuItem("Latest Version", (_, _) => OnVersionSelected(null, true), true, SelectedVersion == null);
AddMenuSeparator();
foreach (var version in Versions.items)
@@ -87,7 +91,7 @@ public class VersionMenuHandler
var versionItem = AddMenuItem(
$"{version.id} - {desc}",
(_, _) => OnVersionSelected(version),
(_, _) => OnVersionSelected(version, false),
true,
SelectedVersion?.id == version.id
);
@@ -122,8 +126,9 @@ public class VersionMenuHandler
}
}
public void RedrawMenuButton(Version? version)
public void RedrawMenuButton(Version? version, bool isLatest)
{
IsLatest = isLatest;
var suffix = VersionContextMenuButton.Enabled
? "Left-click to select another version."
: "Selection is disabled due to component input.";
@@ -133,26 +138,21 @@ public class VersionMenuHandler
VersionContextMenuButton.NickName = version.id;
VersionContextMenuButton.Description = $"{version.message ?? "No message"}\n\n{suffix}";
}
// else if (_model != null)
// {
// VersionContextMenuButton.NickName = "Latest Version";
// VersionContextMenuButton.Name = "Latest Version";
// VersionContextMenuButton.Description = "Gets the latest version from the selected model";
// }
else
{
VersionContextMenuButton.Name = "Select Version";
VersionContextMenuButton.Name = IsLatest ? "Latest Version" : "Select Version";
VersionContextMenuButton.NickName = "Version";
VersionContextMenuButton.Description = "Left-click to select version";
VersionContextMenuButton.Description = "Left-click to select a specific version";
}
}
private void OnVersionSelected(Version? version)
private void OnVersionSelected(Version? version, bool isLatest)
{
_menu?.Close();
SelectedVersion = version;
RedrawMenuButton(SelectedVersion);
VersionSelected?.Invoke(this, new VersionSelectedEventArgs(version));
IsLatest = isLatest;
RedrawMenuButton(SelectedVersion, isLatest);
VersionSelected?.Invoke(this, new VersionSelectedEventArgs(version, isLatest));
}
private ToolStripMenuItem AddMenuItem(
@@ -162,7 +162,7 @@ public class VersionMenuHandler
bool? isChecked = null
)
{
var item = new ToolStripMenuItem(text) { Checked = isChecked ?? false };
var item = new ToolStripMenuItem(text) { Checked = isChecked ?? false, TextAlign = ContentAlignment.MiddleLeft };
item.Click += click;
if (visible == false)
{
@@ -173,5 +173,8 @@ public class VersionMenuHandler
return item;
}
private void AddMenuSeparator() => _menu?.Items.Add(new ToolStripSeparator());
private void AddMenuSeparator()
{
_menu?.Items.Add(new ToolStripSeparator());
}
}
@@ -1,11 +1,11 @@
using System.Drawing.Drawing2D;
using System.Drawing.Drawing2D;
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; }
@@ -56,6 +56,7 @@ public class WorkspaceMenuHandler
private bool PopulateMenu(ToolStripDropDown menu)
{
menu.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;
_menu = menu;
_searchItem = new SearchToolStripMenuItem(menu, Refetch);
@@ -102,7 +103,7 @@ public class WorkspaceMenuHandler
);
}
private void OnWorkspaceSelected(Workspace? workspace)
private void OnWorkspaceSelected(LimitedWorkspace? workspace)
{
IsPersonalProjects = workspace == null;
_menu?.Close();
@@ -111,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;
}
@@ -96,16 +96,128 @@ public static class GrasshopperHelpers
return t;
}
public static Matrix4x4 TransformToMatrix(Transform rhinoTransform, string? units)
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
var conversionFactor = Units.GetConversionFactor(currentDoc.ModelUnitSystem.ToSpeckleString(), units);
var m = new Matrix4x4
{
M11 = rhinoTransform.M00,
M12 = rhinoTransform.M01,
M13 = rhinoTransform.M02,
M14 = rhinoTransform.M03 * conversionFactor,
M21 = rhinoTransform.M10,
M22 = rhinoTransform.M11,
M23 = rhinoTransform.M12,
M24 = rhinoTransform.M13 * conversionFactor,
M31 = rhinoTransform.M20,
M32 = rhinoTransform.M21,
M33 = rhinoTransform.M22,
M34 = rhinoTransform.M23 * conversionFactor,
M41 = rhinoTransform.M30,
M42 = rhinoTransform.M31,
M43 = rhinoTransform.M32,
M44 = rhinoTransform.M33
};
return m;
}
/// <summary>
/// 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 Geometry Wrapper from the goo, if any</returns>
/// <remarks>This method **does not** deep copy the return value</remarks>
public static SpeckleGeometryWrapper? ToSpeckleGeometryWrapper(this IGH_Goo goo)
{
SpeckleBlockInstanceWrapperGoo instanceGoo = new();
if (instanceGoo.CastFrom(goo))
{
return instanceGoo.Value;
}
else
{
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;
}
}
/// <summary>
/// Attempts to cast the goo to a geometry base object.
/// </summary>
/// <param name="geoGeo"></param>
/// <param name="geoGoo"></param>
/// <returns></returns>
/// <exception cref="SpeckleException">If it fails to cast</exception>
public static GeometryBase GeometricGooToGeometryBase(this IGH_GeometricGoo geoGeo)
public static GeometryBase ToGeometryBase(this IGH_GeometricGoo geoGoo)
{
// note: some objects (like text entities) can have multiple properties of name "Value"
var value = geoGeo.GetType().GetProperties().FirstOrDefault(x => x.Name == "Value")?.GetValue(geoGeo);
var value = geoGoo.GetType().GetProperties().FirstOrDefault(x => x.Name == "Value")?.GetValue(geoGoo);
switch (value)
{
case GeometryBase gb:
@@ -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")
};
}
}
@@ -1,15 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Connectors.GrasshopperShared.HostApp;
// noting that if the user inputs a model url string, this will not contain account info
// (and that's why the accountID is nullable in the record resource)
public abstract record SpeckleUrlModelResource(string? AccountId, string Server, string? WorkspaceId, string ProjectId)
public abstract record SpeckleUrlModelResource(AccountResource Account, string? WorkspaceId, string ProjectId)
{
public abstract Task<GrasshopperReceiveInfo> GetReceiveInfo(
IClient client,
@@ -20,12 +24,11 @@ public abstract record SpeckleUrlModelResource(string? AccountId, string Server,
}
public record SpeckleUrlLatestModelVersionResource(
string? AccountId,
string Server,
AccountResource Account,
string? WorkspaceId,
string ProjectId,
string ModelId
) : SpeckleUrlModelResource(AccountId, Server, WorkspaceId, ProjectId)
) : SpeckleUrlModelResource(Account, WorkspaceId, ProjectId)
{
public override async Task<GrasshopperReceiveInfo> GetReceiveInfo(
IClient client,
@@ -39,8 +42,7 @@ public record SpeckleUrlLatestModelVersionResource(
Version version = model.versions.items[0];
var info = new GrasshopperReceiveInfo(
client.Account.id,
new Uri(Server),
client.Account,
project.workspaceId,
ProjectId,
project.name,
@@ -64,8 +66,7 @@ public record SpeckleUrlLatestModelVersionResource(
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
return new GrasshopperSendInfo(
client.Account.id,
new Uri(Server),
client.Account,
WorkspaceId,
ProjectId,
ModelId,
@@ -75,13 +76,12 @@ public record SpeckleUrlLatestModelVersionResource(
}
public record SpeckleUrlModelVersionResource(
string? AccountId,
string Server,
AccountResource Account,
string? WorkspaceId,
string ProjectId,
string ModelId,
string VersionId
) : SpeckleUrlModelResource(AccountId, Server, WorkspaceId, ProjectId)
) : SpeckleUrlModelResource(Account, WorkspaceId, ProjectId)
{
public override async Task<GrasshopperReceiveInfo> GetReceiveInfo(
IClient client,
@@ -93,8 +93,7 @@ public record SpeckleUrlModelVersionResource(
Version version = await client.Version.Get(VersionId, ProjectId, cancellationToken).ConfigureAwait(false);
var info = new GrasshopperReceiveInfo(
client.Account.id,
new Uri(Server),
client.Account,
project.workspaceId,
ProjectId,
project.name,
@@ -118,8 +117,7 @@ public record SpeckleUrlModelVersionResource(
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
return new GrasshopperSendInfo(
client.Account.id,
new Uri(Server),
client.Account,
WorkspaceId,
ProjectId,
ModelId,
@@ -129,12 +127,11 @@ public record SpeckleUrlModelVersionResource(
}
public record SpeckleUrlModelObjectResource(
string? AccountId,
string Server,
AccountResource Account,
string? WorkspaceId,
string ProjectId,
string ObjectId
) : SpeckleUrlModelResource(AccountId, Server, WorkspaceId, ProjectId)
) : SpeckleUrlModelResource(Account, WorkspaceId, ProjectId)
{
public override Task<GrasshopperReceiveInfo> GetReceiveInfo(
IClient client,
@@ -146,3 +143,17 @@ public record SpeckleUrlModelObjectResource(
CancellationToken cancellationToken = default
) => throw new NotImplementedException("Object Resources are not supported yet");
}
public record AccountResource(string? AccountId, string? Token, string Server)
{
public Account? GetAccount(IServiceScope scope)
{
if (Token is not null)
{
return scope.Get<IAccountFactory>().CreateAccount(new Uri(Server), Token).GetAwaiter().GetResult();
}
return AccountId != null
? scope.Get<IAccountManager>().GetAccount(AccountId)
: scope.Get<IAccountService>().GetAccountWithServerUrlFallback("", new Uri(Server)); // fallback the account that matches with URL if a
}
}
@@ -13,16 +13,16 @@ public record SpeckleResourceBuilder
@"/projects/(?<projectId>[\w\d]+)(?:/models/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?"
);
public static SpeckleUrlModelResource[] FromUrlString(string speckleModel)
public static SpeckleUrlModelResource[] FromUrlString(string speckleModel, string? token)
{
var uri = new Uri(speckleModel);
var serverUrl = uri.GetLeftPart(UriPartial.Authority);
var match = s_fe2UrlRegex.Match(speckleModel);
var result = ParseFe2RegexMatch(serverUrl, match);
var result = ParseFe2RegexMatch(serverUrl, match, token);
return result;
}
private static SpeckleUrlModelResource[] ParseFe2RegexMatch(string serverUrl, Match match)
private static SpeckleUrlModelResource[] ParseFe2RegexMatch(string serverUrl, Match match, string? token)
{
var projectId = match.Groups["projectId"];
var model = match.Groups["model"];
@@ -48,7 +48,7 @@ public record SpeckleResourceBuilder
throw new NotSupportedException("Federation model urls are not supported");
}
var modelRes = GetUrlModelResource(null, serverUrl, null, projectId.Value, model.Value);
var modelRes = GetUrlModelResource(null, token, serverUrl, null, projectId.Value, model.Value);
var result = new List<SpeckleUrlModelResource> { modelRes };
@@ -56,7 +56,14 @@ public record SpeckleResourceBuilder
{
foreach (Capture additionalModelsCapture in additionalModels.Captures)
{
var extraModel = GetUrlModelResource(null, serverUrl, null, projectId.Value, additionalModelsCapture.Value);
var extraModel = GetUrlModelResource(
null,
token,
serverUrl,
null,
projectId.Value,
additionalModelsCapture.Value
);
result.Add(extraModel);
}
}
@@ -66,6 +73,7 @@ public record SpeckleResourceBuilder
private static SpeckleUrlModelResource GetUrlModelResource(
string? accountId,
string? token,
string serverUrl,
string? workspaceId,
string projectId,
@@ -74,15 +82,20 @@ public record SpeckleResourceBuilder
{
if (modelValue.Length == 32)
{
return new SpeckleUrlModelObjectResource(accountId, serverUrl, workspaceId, projectId, modelValue); // Model value is an ObjectID
return new SpeckleUrlModelObjectResource(new(accountId, token, serverUrl), workspaceId, projectId, modelValue); // Model value is an ObjectID
}
if (!modelValue.Contains('@'))
{
return new SpeckleUrlLatestModelVersionResource(accountId, serverUrl, workspaceId, projectId, modelValue); // Model has no version attached
return new SpeckleUrlLatestModelVersionResource(
new(accountId, token, serverUrl),
workspaceId,
projectId,
modelValue
); // Model has no version attached
}
var res = modelValue.Split('@');
return new SpeckleUrlModelVersionResource(accountId, serverUrl, workspaceId, projectId, res[0], res[1]);
return new SpeckleUrlModelVersionResource(new(accountId, token, serverUrl), workspaceId, projectId, res[0], res[1]);
}
}
@@ -0,0 +1,260 @@
using Rhino.Geometry;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
/// <summary>
/// Reconstructs block instances and definitions from received proxies back into Grasshopper wrapper objects.
/// Tracks and returns object IDs consumed by block definitions to prevent duplication in collection hierarchy.
/// </summary>
/// <remarks>
/// Geometry objects that define blocks must already be converted and present in convertedObjectsMap at this stage.
/// Follows Rhino's pattern where objects consumed by block definitions should not appear as standalone objects.
/// </remarks>
internal sealed class GrasshopperBlockUnpacker
{
private readonly TraversalContextUnpacker _traversalContextUnpacker;
private readonly GrasshopperColorUnpacker _colorUnpacker;
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
public GrasshopperBlockUnpacker(
TraversalContextUnpacker traversalContextUnpacker,
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker
)
{
_traversalContextUnpacker = traversalContextUnpacker;
_colorUnpacker = colorUnpacker;
_materialUnpacker = materialUnpacker;
}
/// <summary>
/// Creates block definitions and instances from receive pipeline, returning consumed object IDs.
/// </summary>
/// <returns>Set of object IDs that have been consumed by block definitions and should not appear standalone</returns>
public HashSet<string> UnpackBlocks(
IReadOnlyCollection<TraversalContext> blockComponents,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
GrasshopperCollectionRebuilder collectionRebuilder
)
{
var consumedObjectIds = new HashSet<string>();
var sortedComponents = ExtractAndSortBlocks(blockComponents, definitionProxies);
CreateBlocksInDependencyOrder(sortedComponents, convertedObjectsMap, collectionRebuilder, consumedObjectIds);
return consumedObjectIds;
}
/// <summary>
/// Extracts blocks from TraversalContext and adds metadata definitions, then sorts by depth.
/// Deepest definitions first, then instances, to handle nested hierarchies correctly.
/// </summary>
private List<(Collection[] path, IInstanceComponent component)> ExtractAndSortBlocks(
IReadOnlyCollection<TraversalContext> blockComponents,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
)
{
var allComponents = new List<(Collection[] path, IInstanceComponent component)>();
// Extract instances from traversal contexts
foreach (var traversalContext in blockComponents)
{
if (traversalContext.Current is IInstanceComponent instanceComponent)
{
var collectionPath = _traversalContextUnpacker.GetCollectionPath(traversalContext).ToArray();
allComponents.Add((collectionPath, instanceComponent));
}
}
// Add definition proxies from metadata (these don't have collection paths)
if (definitionProxies != null)
{
foreach (var definitionProxy in definitionProxies)
{
allComponents.Add((Array.Empty<Collection>(), definitionProxy));
}
}
// Sort by depth (deepest first) then by type (definitions before instances)
return allComponents
.OrderByDescending(x => x.component.maxDepth)
.ThenBy(x => x.component is InstanceDefinitionProxy ? 0 : 1)
.ToList();
}
/// <summary>
/// Creates definitions and instances in dependency order, populating convertedObjectsMap
/// with instances as they're created (following Rhino's applicationIdMap pattern).
/// </summary>
private void CreateBlocksInDependencyOrder(
List<(Collection[] path, IInstanceComponent component)> sortedComponents,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
GrasshopperCollectionRebuilder collectionRebuilder,
HashSet<string> consumedObjectIds
)
{
var definitions = new Dictionary<string, SpeckleBlockDefinitionWrapper>();
// NOTE: This relies on ExtractAndSortBlocks to have done its job correctly!
foreach (var (collectionPath, component) in sortedComponents)
{
if (component is InstanceDefinitionProxy definitionProxy)
{
// Create definition using current state of convertedObjectsMap
var definitionId = definitionProxy.applicationId ?? definitionProxy.id ?? Guid.NewGuid().ToString();
var definition = CreateBlockDefinitionWrapper(
definitionProxy,
definitionId,
convertedObjectsMap,
consumedObjectIds
);
if (definition != null)
{
definitions[definitionId] = definition;
}
else
{
// TODO: throw?
}
}
else if (component is InstanceProxy instanceProxy)
{
// Create instance using available definitions
string instanceId = instanceProxy.applicationId ?? instanceProxy.id ?? Guid.NewGuid().ToString();
SpeckleBlockInstanceWrapper? instance = CreateBlockInstanceWrapper(
instanceProxy,
instanceId,
definitions,
_colorUnpacker,
_materialUnpacker
);
if (instance != null)
{
AddInstanceToCollection(instance, collectionPath, collectionRebuilder);
convertedObjectsMap[instanceId] = instance;
}
else
{
// TODO: throw?
}
}
}
}
/// <summary>
/// Creates a <see cref="SpeckleBlockDefinitionWrapper"/> from its proxy using pre-converted defining objects.
/// Tracks consumed object IDs to prevent duplication in collection hierarchy.
/// </summary>
/// <remarks>
/// Objects used in block definitions are considered "consumed" and should not appear as standalone objects,
/// matching Rhino's behavior where doc.Objects.Delete() removes consumed objects after block creation.
/// </remarks>
private SpeckleBlockDefinitionWrapper? CreateBlockDefinitionWrapper(
InstanceDefinitionProxy definitionProxy,
string definitionId,
Dictionary<string, SpeckleGeometryWrapper> convertedObjectsMap,
HashSet<string> consumedObjectIds
)
{
var definitionObjects = new List<SpeckleGeometryWrapper>();
var currentDefinitionObjectIds = new HashSet<string>();
foreach (var objectId in definitionProxy.objects)
{
if (convertedObjectsMap.TryGetValue(objectId, out var convertedObject))
{
definitionObjects.Add(convertedObject);
currentDefinitionObjectIds.Add(objectId);
}
else
{
// TODO: throw?
}
}
// Only create definition if we have objects
if (definitionObjects.Count == 0)
{
return null;
}
// Track consumed objects (matches Rhino's consumedObjectIds.UnionWith pattern)
consumedObjectIds.UnionWith(currentDefinitionObjectIds);
return new SpeckleBlockDefinitionWrapper
{
Base = definitionProxy,
Name = definitionProxy.name,
Objects = definitionObjects,
ApplicationId = definitionId
};
}
/// <summary>
/// Creates a <see cref="SpeckleBlockInstanceWrapper"/> from its proxy using.
/// </summary>
private SpeckleBlockInstanceWrapper? CreateBlockInstanceWrapper(
InstanceProxy instanceProxy,
string instanceId,
Dictionary<string, SpeckleBlockDefinitionWrapper> definitions,
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker
)
{
// Find the referenced definition
if (!definitions.TryGetValue(instanceProxy.definitionId, out var definition))
{
return null; // Definition not found or failed to build
}
Transform transform = GrasshopperHelpers.MatrixToTransform(instanceProxy.transform, instanceProxy.units);
return new SpeckleBlockInstanceWrapper
{
Base = instanceProxy,
Name = instanceProxy["name"] as string ?? "",
ApplicationId = instanceId,
Transform = transform,
Definition = definition,
GeometryBase = new InstanceReferenceGeometry(Guid.Empty, transform), //Instances shouldn't be using this except for the filter objects node,
Color = colorUnpacker.Cache.TryGetValue(instanceProxy.applicationId ?? "", out var cachedInstanceColor)
? cachedInstanceColor
: null,
Material = materialUnpacker.Cache.TryGetValue(instanceProxy.applicationId ?? "", out var cachedInstanceMaterial)
? cachedInstanceMaterial
: null,
};
}
/// <summary>
/// Adds an instance to the collection and sets up hierarchy relationships.
/// </summary>
private void AddInstanceToCollection(
SpeckleBlockInstanceWrapper instance,
Collection[] collectionPath,
GrasshopperCollectionRebuilder collectionRebuilder
)
{
var pathList = collectionPath.ToList();
// Get or create the target collection
var targetCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
pathList,
_colorUnpacker,
_materialUnpacker
);
// Set up instance hierarchy properties
instance.Path = pathList.Select(c => c.name).ToList();
instance.Parent = targetCollection;
// Add to collection
targetCollection.Elements.Add(instance);
}
}
@@ -25,27 +25,12 @@ internal sealed class GrasshopperCollectionRebuilder
}
public void AppendSpeckleGrasshopperObject(
SpeckleObjectWrapper speckleGrasshopperObjectWrapper,
ISpeckleCollectionObject speckleGrasshopperObjectWrapper,
List<Collection> collectionPath,
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker
)
{
// add the object color and material
speckleGrasshopperObjectWrapper.Color = colorUnpacker.Cache.TryGetValue(
speckleGrasshopperObjectWrapper.Base.applicationId ?? "",
out var cachedColor
)
? cachedColor
: null;
speckleGrasshopperObjectWrapper.Material = materialUnpacker.Cache.TryGetValue(
speckleGrasshopperObjectWrapper.Base.applicationId ?? "",
out var cachedMaterial
)
? cachedMaterial
: null;
var collWrapper = GetOrCreateSpeckleCollectionFromPath(collectionPath, colorUnpacker, materialUnpacker);
collWrapper.Elements.Add(speckleGrasshopperObjectWrapper);
}
@@ -58,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;
}
@@ -73,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;
@@ -110,4 +95,38 @@ internal sealed class GrasshopperCollectionRebuilder
return previousCollectionWrapper;
}
/// <summary>
/// Removes consumed objects from the collection hierarchy.
/// Matches Rhino's pattern: createdObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id))
/// </summary>
/// <param name="consumedObjectIds">Set of object IDs that have been consumed by block definitions</param>
public void RemoveConsumedObjects(HashSet<string> consumedObjectIds)
{
if (consumedObjectIds.Count == 0)
{
return;
}
RemoveConsumedObjectsFromCollection(RootCollectionWrapper, consumedObjectIds);
}
private static void RemoveConsumedObjectsFromCollection(
SpeckleCollectionWrapper collection,
HashSet<string> consumedObjectIds
)
{
// Remove consumed objects from this level
collection.Elements.RemoveAll(element =>
element is SpeckleGeometryWrapper obj
&& obj.ApplicationId != null
&& consumedObjectIds.Contains(obj.ApplicationId)
);
// Recurse into child collections
foreach (var childCollection in collection.Elements.OfType<SpeckleCollectionWrapper>())
{
RemoveConsumedObjectsFromCollection(childCollection, consumedObjectIds);
}
}
}
@@ -1,7 +1,6 @@
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Logging;
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Transports;
@@ -10,7 +9,6 @@ namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
public class GrasshopperReceiveOperation
{
private readonly IAccountService _accountService;
private readonly IServerTransportFactory _serverTransportFactory;
private readonly IProgressDisplayManager _progressDisplayManager;
private readonly ISdkActivityFactory _activityFactory;
@@ -18,7 +16,6 @@ public class GrasshopperReceiveOperation
private readonly IClientFactory _clientFactory;
public GrasshopperReceiveOperation(
IAccountService accountService,
IServerTransportFactory serverTransportFactory,
IProgressDisplayManager progressDisplayManager,
ISdkActivityFactory activityFactory,
@@ -26,7 +23,6 @@ public class GrasshopperReceiveOperation
IClientFactory clientFactory
)
{
_accountService = accountService;
_serverTransportFactory = serverTransportFactory;
_progressDisplayManager = progressDisplayManager;
_activityFactory = activityFactory;
@@ -43,9 +39,10 @@ public class GrasshopperReceiveOperation
using var execute = _activityFactory.Start("Receive Operation");
execute?.SetTag("receiveInfo", receiveInfo);
// 2 - Check account exist
Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl);
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)
@@ -1,17 +1,27 @@
using Rhino.Geometry;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
/// <summary>
/// Handles conversion of atomic objects from TraversalContexts into Grasshopper wrapper objects.
/// </summary>
/// <remarks>
/// Follows Rhino's approach: atomic objects are converted directly without pre-transformation,
/// with instance transformations handled separately during block reconstruction. Implements consumedObjectIds
/// tracking to prevent objects consumed by block definitions from appearing as standalone objects.
/// </remarks>
internal sealed class LocalToGlobalMapHandler
{
private readonly TraversalContextUnpacker _traversalContextUnpacker;
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
private readonly TraversalContextUnpacker _traversalContextUnpacker;
private readonly GrasshopperColorUnpacker _colorUnpacker;
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
@@ -29,91 +39,164 @@ internal sealed class LocalToGlobalMapHandler
}
/// <summary>
/// Creates a grasshopper speckle object from a local to global map, and appends it to the collection rebuilder.
/// POC: TODO: this should decimate dataobjects into their display values, while storing the same properties etc
/// This is because we don't want to be storing one-to-many maps in the object wrapper, this will complicate mutations
/// Converts atomic object from TraversalContext to SpeckleObjectWrapper.
/// </summary>
/// <param name="map"></param>
///
public void CreateGrasshopperObjectFromMap(LocalToGlobalMap map)
public void ConvertAtomicObject(TraversalContext atomicContext)
{
var obj = atomicContext.Current;
var objId = obj.applicationId ?? obj.id;
if (objId == null || ConvertedObjectsMap.ContainsKey(objId))
{
return;
}
try
{
List<(GeometryBase, Base)> converted = SpeckleConversionContext.ConvertToHost(map.AtomicObject);
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
if (converted.Count == 0)
{
return; // TODO: throw?
return;
}
// get the units and transform by matrices in the map
string units = map.AtomicObject["units"] is string u
? u
: converted.First().Item2["units"] is string convertedU
? convertedU
: "none";
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
foreach (var matrix in map.Matrix)
{
var mat = GrasshopperHelpers.MatrixToTransform(matrix, units);
converted.ForEach(res => res.Item1.Transform(mat));
}
// get the collection
var path = _traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
SpeckleCollectionWrapper objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
// Always create collection - consumed objects will be cleaned up later
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
path,
_colorUnpacker,
_materialUnpacker
);
// get the name and properties
SpecklePropertyGroupGoo propertyGroup = new();
string name = "";
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
if (obj is Speckle.Objects.Data.DataObject dataObject)
{
propertyGroup.CastFrom(da.properties);
name = da.name;
// 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);
// 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
{
if (map.AtomicObject[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
SpecklePropertyGroupGoo propertyGroup = new();
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
if (map.AtomicObject[Constants.NAME_PROP] is string n)
foreach ((object convertedObj, Base original) in converted)
{
name = n;
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);
}
}
}
// create objects for every value in converted. This is where one to many is not handled very nicely.
// we will decimate dataobjects and multi-object display values here
// meaning for every value in the display value, we will create a grasshopper wrapper, while preserving app id, name, props, etc
// similar objects will be re-packaged on send
foreach ((GeometryBase geometryBase, Base original) in converted)
{
var gh = new SpeckleObjectWrapper()
{
Base = original,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = name,
Color = null,
Material = null,
WrapperGuid = map.AtomicObject.applicationId,
ApplicationId = original.applicationId ?? Guid.NewGuid().ToString() // create if none
};
CollectionRebuilder.AppendSpeckleGrasshopperObject(gh, path, _colorUnpacker, _materialUnpacker);
}
}
catch (ConversionException)
catch (Exception ex) when (!ex.IsFatal())
{
// TODO
// TODO: throw?
}
}
/// <summary>
/// Converts block instances and definitions from traversal contexts into Grasshopper wrapper objects.
/// Automatically handles cleanup of consumed objects from the collection hierarchy.
/// </summary>
/// <remarks>
/// Deliberately handles both block conversion AND consumed object cleanup in a single operation.
/// Too much, I know, BUT it ensures the cleanup always occurs immediately after block processing without
/// requiring receive components to call a separate cleanup method in the correct order.
/// </remarks>
public void ConvertBlockInstances(
IReadOnlyCollection<TraversalContext> blocks,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
)
{
var blockUnpacker = new GrasshopperBlockUnpacker(_traversalContextUnpacker, _colorUnpacker, _materialUnpacker);
// Get consumed object IDs from unpacker
var consumedObjectIds = blockUnpacker.UnpackBlocks(
blocks,
definitionProxies,
ConvertedObjectsMap,
CollectionRebuilder
);
// Clean up consumed objects from collections
CollectionRebuilder.RemoveConsumedObjects(consumedObjectIds);
}
}
@@ -0,0 +1,110 @@
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
/// <summary>
/// Processes block instances and their nested definitions for publish.
/// Handles nested definitions and depth tracking (injected InstanceObjectsManager).
/// </summary>
internal sealed class GrasshopperBlockPacker
{
private readonly IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> _instanceObjectsManager;
public GrasshopperBlockPacker(IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> instanceObjectsManager)
{
_instanceObjectsManager = instanceObjectsManager;
}
/// <summary>
/// Stores a map of instance definition id to instance definition proxy
/// </summary>
/// <remarks>
/// Storing <see cref="InstanceDefinitionProxy"/> directly and not the wrapper (matching Rhino).
/// </remarks>
public Dictionary<string, InstanceDefinitionProxy> InstanceDefinitionProxies { get; } = [];
/// <summary>
/// Processes a <see cref="SpeckleBlockInstanceWrapper"/> by tracking it in InstanceObjectsManager and recursively
/// processing its definition. Handles depth calculation for nested block hierarchies.
/// </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<SpeckleGeometryWrapper>? ProcessInstance(SpeckleBlockInstanceWrapper? blockInstance, int depth = 0)
{
if (blockInstance?.Definition == null)
{
return null;
}
blockInstance.ApplicationId ??= Guid.NewGuid().ToString();
var instanceId = blockInstance.ApplicationId;
blockInstance.InstanceProxy.maxDepth = depth;
_instanceObjectsManager.AddInstanceProxy(instanceId, blockInstance.InstanceProxy);
return ProcessDefinition(blockInstance.Definition, depth);
}
/// <summary>
/// Processes a block definition, adding it and its objects to InstanceObjectsManager.
/// Updates maxDepth for existing definitions when encountered at greater depths.
/// </summary>
private List<SpeckleGeometryWrapper>? ProcessDefinition(SpeckleBlockDefinitionWrapper definition, int depth = 0)
{
// Use wrapper's id as definitive identifier. Create if empty.
definition.ApplicationId ??= Guid.NewGuid().ToString();
string definitionId = definition.ApplicationId;
// Check if already processed using InstanceObjectsManager
if (
_instanceObjectsManager.TryGetInstanceDefinitionProxy(definitionId, out InstanceDefinitionProxy? definitionProxy)
)
{
int depthDifference = depth - definitionProxy.maxDepth;
if (depthDifference > 0)
{
// Use InstanceObjectsManager to update max depth
_instanceObjectsManager.UpdateChildrenMaxDepth(definitionProxy, depthDifference);
}
return null; // this prevents infinite recursion
}
// Process objects recursively
var objectsToAdd = new List<SpeckleGeometryWrapper>();
var currentObjectIds = new List<string>(); // Track current object IDs for proxy update
foreach (var obj in definition.Objects)
{
if (obj.ApplicationId == null) // we should be loud about this. If gone through all casting etc. this should be complete!
{
throw new InvalidOperationException(
$"Object in block definition '{definition.Name}' missing ApplicationId during send operation. This indicates a processing pipeline error."
);
}
objectsToAdd.Add(obj);
currentObjectIds.Add(obj.ApplicationId); // Collect current ID
_instanceObjectsManager.AddAtomicObject(obj.ApplicationId, obj);
if (obj is SpeckleBlockInstanceWrapper nestedInstance)
{
var nestedObjects = ProcessInstance(nestedInstance, depth + 1);
if (nestedObjects != null)
{
objectsToAdd.AddRange(nestedObjects);
}
}
}
// Add definition to InstanceObjectsManager
definition.InstanceDefinitionProxy.objects = currentObjectIds;
definition.InstanceDefinitionProxy.maxDepth = depth;
_instanceObjectsManager.AddDefinitionProxy(definitionId, definition.InstanceDefinitionProxy);
InstanceDefinitionProxies[definitionId] = definition.InstanceDefinitionProxy;
return objectsToAdd;
}
}

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