Compare commits

..

97 Commits

Author SHA1 Message Date
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
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
Oğuzhan Koral d34d615c3b Merge pull request #928 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 into main
2025-06-17 12:25:24 +03:00
Jonathon Broughton 9be0cd8c6e feat(Navisworks): CNX-2009 - Adds saved views filter to Navisworks send operations (#927)
* Refactors hierarchical name building for saved items

Moves hierarchical name construction to a new helper class.

Modifies the selection set filtering logic to enhance clarity.
Updates the root item handling to streamline the recursive logic.

Improves code maintainability and readability.

* Adds saved views filter for object selection

Implements a new filter to manage saved views in the application.

Enables users to select and manage saved viewpoints effectively.
Implements logic to resolve saved views and collect visible object IDs based on current selections, enhancing the user experience in managing views.

Improves selection efficiency and overall usability.

* Adds new view filter for sending operations

Updates service registration to include the new filter for improved data handling.
2025-06-17 08:23:05 +01:00
Jedd Morgan a7e323b026 Add using statements (#924) 2025-06-16 12:38:57 +00:00
Dimitrie Stefanescu 42529443bd Merge pull request #921 from specklesystems/dim/gh-multisend-input-fix
Dim/gh multisend input fix
2025-06-16 12:59:46 +01:00
Dimitrie Stefanescu 16b1f8317c Merge branch 'dev' into dim/gh-multisend-input-fix 2025-06-16 12:53:58 +01:00
Jedd Morgan abe0105095 greater than 1 2025-06-16 12:48:09 +01:00
Dimitrie Stefanescu 1b2b3e6718 Merge remote-tracking branch 'origin/dim/gh-multisend-input-fix' into dim/gh-multisend-input-fix 2025-06-16 12:44:38 +01:00
Dimitrie Stefanescu cb2916ae39 feat: makes the model url component output nothing vs. nulls on incomplete resources 2025-06-16 12:44:33 +01:00
KatKatKateryna 993555b72c Merge pull request #825 from specklesystems/kateryna/cnx-1687-modifying-layer-material-properties-doesnt-trigger-object
Kateryna/cnx 1687 modifying layer material properties doesnt trigger object
2025-06-16 09:22:34 +00:00
Adam Hathcock ddac586795 add more null protection (#923)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-16 08:36:58 +00:00
Björn Steinhagen f92c01e34c fix (grasshopper): InheritNames to resolve and reflect on OutputParams (#922)
* fix: first pass

* refactor: simplified `null` check
2025-06-16 09:30:44 +01:00
Claire Kuang 69e63cc815 Merge branch 'dev' into dim/gh-multisend-input-fix 2025-06-15 03:05:12 +01:00
Dimitrie Stefanescu 81ad00c4d6 feat: fixes sync load component 2025-06-14 19:48:00 +01:00
Dimitrie Stefanescu ca498889dc feat: fixes load async component 2025-06-14 19:43:54 +01:00
Dimitrie Stefanescu 6ad3606091 chore: copy 2025-06-14 19:42:00 +01:00
Dimitrie Stefanescu aae52bdddd fix: sync publish component 2025-06-14 19:32:02 +01:00
Dimitrie Stefanescu 5e6d1ce5bc fix: prevents async publish node from doing multiple sends 2025-06-14 13:34:33 +01:00
Jedd Morgan 9fe28fccd2 Add boxes (#916) 2025-06-13 14:43:40 +00:00
Adam Hathcock e7e148497d Merge pull request #918 from specklesystems/main-dev
Main to dev
2025-06-13 12:16:19 +01:00
Adam Hathcock 2d2fd086d5 Merge branch 'main' into main-dev
# Conflicts:
#	DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json
#	DUI3/Speckle.Connectors.DUI/packages.lock.json
#	Sdk/Speckle.Connectors.Common/packages.lock.json
#	Sdk/Speckle.Converters.Common/packages.lock.json
2025-06-13 10:27:44 +01:00
Adam Hathcock 067c1440d1 Adds Mac slnx which excludes Connectors for now (#913)
* Adds Mac slnx which excludes Connectors for now

* formatting

* change validation of solutions
2025-06-13 10:14:53 +01:00
Adam Hathcock 70e189fa1f Merge pull request #914 from specklesystems/adam/hotfix-gh-scopes
.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
(hotfix) Grasshopper scopes aren't reused and SDK 3.4.2
2025-06-12 13:19:13 +01:00
Adam Hathcock d91b24d645 Another build fix 2025-06-12 13:12:14 +01:00
Adam Hathcock 538abbcb3c Fix usage of MD5 that was introduced with 3.4.0 SDK 2025-06-12 12:44:52 +01:00
Adam Hathcock 6a99a38b2a Update to SDK 3.4.2 (#911)
* Update to SDK 3.4.1

* fix build issues

* Update to 3.4.2

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
# Conflicts:
#	Connectors/Autocad/Speckle.Connectors.Autocad2022/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Autocad2023/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Autocad2024/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Autocad2025/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Autocad2026/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Civil3d2022/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Civil3d2023/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Civil3d2024/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Civil3d2025/packages.lock.json
#	Connectors/Autocad/Speckle.Connectors.Civil3d2026/packages.lock.json
#	Connectors/CSi/Speckle.Connectors.ETABS21/packages.lock.json
#	Connectors/CSi/Speckle.Connectors.ETABS22/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2020/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2021/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2022/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2023/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2024/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2025/packages.lock.json
#	Connectors/Navisworks/Speckle.Connectors.Navisworks2026/packages.lock.json
#	Connectors/Revit/Speckle.Connectors.Revit2022/packages.lock.json
#	Connectors/Revit/Speckle.Connectors.Revit2023/packages.lock.json
#	Connectors/Revit/Speckle.Connectors.Revit2024/packages.lock.json
#	Connectors/Revit/Speckle.Connectors.Revit2025/packages.lock.json
#	Connectors/Revit/Speckle.Connectors.Revit2026/packages.lock.json
#	Connectors/Rhino/Speckle.Connectors.Grasshopper7/packages.lock.json
#	Connectors/Rhino/Speckle.Connectors.Grasshopper8/packages.lock.json
#	Connectors/Rhino/Speckle.Connectors.Rhino7/packages.lock.json
#	Connectors/Rhino/Speckle.Connectors.Rhino8/packages.lock.json
#	Connectors/Tekla/Speckle.Connector.Tekla2023/packages.lock.json
#	Connectors/Tekla/Speckle.Connector.Tekla2024/packages.lock.json
#	Connectors/Tekla/Speckle.Connector.Tekla2025/packages.lock.json
#	Converters/Autocad/Speckle.Converters.Autocad2022/packages.lock.json
#	Converters/Autocad/Speckle.Converters.Autocad2023/packages.lock.json
#	Converters/Autocad/Speckle.Converters.Autocad2024/packages.lock.json
#	Converters/Autocad/Speckle.Converters.Autocad2025/packages.lock.json
#	Converters/Autocad/Speckle.Converters.Autocad2026/packages.lock.json
#	Converters/CSi/Speckle.Converters.ETABS21/packages.lock.json
#	Converters/CSi/Speckle.Converters.ETABS22/packages.lock.json
#	Converters/Civil3d/Speckle.Converters.Civil3d2022/packages.lock.json
#	Converters/Civil3d/Speckle.Converters.Civil3d2023/packages.lock.json
#	Converters/Civil3d/Speckle.Converters.Civil3d2024/packages.lock.json
#	Converters/Civil3d/Speckle.Converters.Civil3d2025/packages.lock.json
#	Converters/Civil3d/Speckle.Converters.Civil3d2026/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2020/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2021/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2022/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2023/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2024/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2025/packages.lock.json
#	Converters/Navisworks/Speckle.Converters.Navisworks2026/packages.lock.json
#	Converters/Revit/Speckle.Converters.Revit2022/packages.lock.json
#	Converters/Revit/Speckle.Converters.Revit2023/packages.lock.json
#	Converters/Revit/Speckle.Converters.Revit2024/packages.lock.json
#	Converters/Revit/Speckle.Converters.Revit2025/packages.lock.json
#	Converters/Revit/Speckle.Converters.Revit2026/packages.lock.json
#	Converters/Rhino/Speckle.Converters.Rhino7/packages.lock.json
#	Converters/Rhino/Speckle.Converters.Rhino8/packages.lock.json
#	Converters/Tekla/Speckle.Converter.Tekla2023/packages.lock.json
#	Converters/Tekla/Speckle.Converter.Tekla2024/packages.lock.json
#	Converters/Tekla/Speckle.Converter.Tekla2025/packages.lock.json
#	DUI3/Speckle.Connectors.DUI.Tests/packages.lock.json
#	DUI3/Speckle.Connectors.DUI.WebView/packages.lock.json
#	DUI3/Speckle.Connectors.DUI/packages.lock.json
#	Directory.Packages.props
#	Importers/Ifc/Speckle.Importers.Ifc.Tester/packages.lock.json
#	Importers/Ifc/Speckle.Importers.Ifc.Tester2/packages.lock.json
#	Importers/Ifc/Speckle.Importers.Ifc/packages.lock.json
#	Sdk/Speckle.Connectors.Common.Tests/packages.lock.json
#	Sdk/Speckle.Connectors.Common/packages.lock.json
#	Sdk/Speckle.Converters.Common.Tests/packages.lock.json
#	Sdk/Speckle.Converters.Common/packages.lock.json
#	Sdk/Speckle.Testing/packages.lock.json
2025-06-12 12:29:12 +01:00
Adam Hathcock afbeeef32f Update to SDK 3.4.2 (#911)
* Update to SDK 3.4.1

* fix build issues

* Update to 3.4.2

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-06-12 12:14:40 +01:00
Adam Hathcock 315db9e19c Don't use static scopes for Grasshopper. Use when necessary (#907)
* Don't use static scopes for Grasshopper.  Use when necessary

* formatting

* Scope correctly
2025-06-12 10:27:39 +01:00
Claire Kuang 183e150466 feat(grasshopper): allows nested properties (#908)
* refactors property group to support nesting

* adds support for nesting speckle props

* build fixes

* removes unused fields

* refactors for simplicity on adding props to atts

* formatting fix

* updates speckle props error message
2025-06-11 20:19:13 +02:00
kekesidavid 39735ca0cb fix (civil3d) extract property sets for solids (#896)
* get properties for ADB entities is back

* removed comment

* added comment, changed condition to filter CDB entities

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-11 18:15:15 +02:00
Adam Hathcock 9d6d56ca2a Rhino API can return null when not expected (#912) 2025-06-11 16:01:09 +01:00
Adam Hathcock 22c6b23d7a Don't use static scopes for Grasshopper. Use when necessary (#907)
* Don't use static scopes for Grasshopper.  Use when necessary

* formatting

* Scope correctly
2025-06-11 11:21:50 +01:00
Adam Hathcock 21b70ec241 fix (Rhino) Missing group from applicationIdMap fix (#903)
* fix (Rhino) Missing group from applicationIdMap fix

* Throw a better exception when an application id is missed

* back to collection and added log statement
2025-06-09 17:30:05 +01:00
Adam Hathcock 72cfc8289a adds a Revit and Local slnx (#905) 2025-06-09 14:26:09 +00:00
Adam Hathcock 75117aa8d3 fix(logs) Only log errors and observe them if they're speckle related. (#887)
* Only log errors and observe them if they're speckle related.  Others log if possible and let bomb out the app

* stacktrace is nullable

* Process the exceptions as inner exceptions

* Null handling

* Change how it's fixed

* ToString

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-06-09 14:12:34 +00:00
Adam Hathcock 0361e5ea10 Introduce receive manager (#895)
* Don't log conversion errors to seq?

* add cancellation

* Make a generic handler for receive conversions

* Use for all receives?

* for cancellations, rethrow

* add vibe tests

* fix up receive cancellation

* fmt

* made ReceiveOperationManager with revit

* everything compiles

* fmt

* add tests

* This check shouldn't report to seq
2025-06-09 12:21:52 +00:00
Adam Hathcock cfac52801f React to using SDK 3.4 (#894)
* use alpha to test

* Change MD5 usage

* update alpha

* update upload progress

* update sending progress

* update to SDK 3.4.0
2025-06-09 12:12:00 +00:00
Adam Hathcock 39a6fb3c50 For Receive conversions, have common path for logging errors (#892)
* Don't log conversion errors to seq?

* add cancellation

* Make a generic handler for receive conversions

* Use for all receives?

* for cancellations, rethrow

* add vibe tests

* fix up receive cancellation

* fmt
2025-06-09 10:26:10 +00:00
Adam Hathcock 6a39635162 Merge pull request #902 from specklesystems/main-dev
Main to dev (do not squash)
2025-06-09 10:07:54 +01:00
Claire Kuang c671b151b4 updates account exception on constructor of speckle operation wizard (#904)
.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-06-06 16:20:55 +01:00
Adam Hathcock 44d2419042 Merge fixes 2025-06-06 11:21:18 +01:00
Adam Hathcock 3cb50a7187 Merge remote-tracking branch 'origin/dev' into main-dev
# Conflicts:
#	Connectors/Rhino/Speckle.Connectors.RhinoShared/HostApp/RhinoLayerBaker.cs
2025-06-06 11:18:10 +01:00
Adam Hathcock 90d83eee41 Change exceptions for not found layers on receive, they should be conversion errors (#900)
* Change exceptions for not found layers on receive, they should be conversion errors

* fmt
2025-06-06 11:05:31 +01:00
Adam Hathcock 8f51f4832d (fix) Avoid CEF browser exception by checking availabity before executing command (#889)
* Avoid CEF browser exception by checking availabity before executing command

* fmt
2025-06-04 10:32:47 +00:00
Jedd Morgan c496bbe817 Small tweak to IFC tester (#890) 2025-06-04 10:23:56 +00:00
Adam Hathcock 6584163911 Introduces a Send operation manager to make Send testable (#871)
* Rhino uses SendOperationManagerFactory

* revit now uses it

* autocad and civil use it

* csi, tekla and NW

* add test

* formatting

* tested with rhino and revit

* formatting

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
2025-06-04 13:12:24 +03:00
kekesidavid b670510da4 new paletteset guid (#886) 2025-06-03 15:33:56 +02:00
Adam Hathcock 73927de454 Remove the NET6 targets now that we don't build ArcGIS (#888) 2025-06-03 13:56:13 +01:00
kekesidavid a65cf42ccf replaced svg image for tekla (#885) 2025-06-02 13:56:39 +02:00
321 changed files with 12045 additions and 7361 deletions
-424
View File
@@ -1,424 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35027.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{85A13E25-EB29-4F31-8853-B9EE83275B3D}"
ProjectSection(SolutionItems) = preProject
.csharpierrc.yaml = .csharpierrc.yaml
.editorconfig = .editorconfig
codecov.yml = codecov.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Directory.Packages.props = Directory.Packages.props
global.json = global.json
README.md = README.md
CodeMetricsConfig.txt = CodeMetricsConfig.txt
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DUI3", "DUI3", "{FD4D6594-D81E-456F-8F2E-35B09E04A755}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D92751C8-1039-4005-90B2-913E55E0B8BD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{2E00592E-558D-492D-88F9-3ECEE4C0C7DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2023", "Connectors\Revit\Speckle.Connectors.Revit2023\Speckle.Connectors.Revit2023.csproj", "{01F98733-7352-47AD-A594-537D979DE3DE}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.RevitShared", "Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.shproj", "{DC570FFF-6FE5-47BD-8BC1-B471A6067786}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared", "Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.shproj", "{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2023.DependencyInjection\Speckle.Converters.Revit2023.DependencyInjection.csproj", "{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023", "Converters\Revit\Speckle.Converters.Revit2023\Speckle.Converters.Revit2023.csproj", "{26391930-F86F-47E0-A5F6-B89919E38CE1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI", "DUI3\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj", "{D81C0B87-F0C1-4297-A147-02F001FB7E1E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Autofac", "Sdk\Speckle.Autofac\Speckle.Autofac.csproj", "{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Utils", "Sdk\Speckle.Connectors.Utils\Speckle.Connectors.Utils.csproj", "{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common", "Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj", "{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{9584AEE5-CD59-46E6-93E6-2DC2B5285B75}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Rhino7", "Connectors\Rhino\Speckle.Connectors.Rhino7\Speckle.Connectors.Rhino7.csproj", "{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7", "Converters\Rhino\Speckle.Converters.Rhino7\Speckle.Converters.Rhino7.csproj", "{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7.DependencyInjection", "Converters\Rhino\Speckle.Converters.Rhino7.DependencyInjection\Speckle.Converters.Rhino7.DependencyInjection.csproj", "{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ArcGIS3", "Connectors\ArcGIS\Speckle.Connectors.ArcGIS3\Speckle.Connectors.ArcGIS3.csproj", "{A97F7177-86C7-4B38-A6ED-DA51BF762471}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3", "Converters\ArcGIS\Speckle.Converters.ArcGIS3\Speckle.Converters.ArcGIS3.csproj", "{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ArcGIS3.DependencyInjection", "Converters\ArcGIS\Speckle.Converters.ArcGIS3.DependencyInjection\Speckle.Converters.ArcGIS3.DependencyInjection.csproj", "{7DFF1591-237D-499E-A767-EE37B93FB958}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcGIS", "ArcGIS", "{CCF48B65-33D1-4E8B-A57B-E03394730B21}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Autocad2023", "Connectors\Autocad\Speckle.Connectors.Autocad2023\Speckle.Connectors.Autocad2023.csproj", "{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.AutocadShared", "Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.shproj", "{41BC679F-887F-44CF-971D-A5502EE87DB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Common.DependencyInjection", "Sdk\Speckle.Converters.Common.DependencyInjection\Speckle.Converters.Common.DependencyInjection.csproj", "{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Autocad", "Autocad", "{804E065F-914C-414A-AF84-009312C3CFF6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.AutocadShared", "Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.shproj", "{9ADD1B7A-6401-4202-8613-F668E2FBC0A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023", "Converters\Autocad\Speckle.Converters.Autocad2023\Speckle.Converters.Autocad2023.csproj", "{631C295A-7CCF-4B42-8686-7034E31469E7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2023.DependencyInjection", "Converters\Autocad\Speckle.Converters.Autocad2023.DependencyInjection\Speckle.Converters.Autocad2023.DependencyInjection.csproj", "{D940853C-003A-482C-BDB0-665367F274A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.DUI.WebView", "DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj", "{7420652C-3046-4F38-BE64-9B9E69D76FA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "Build\Build.csproj", "{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostApps", "HostApps", "{42826721-9A18-4762-8BA9-F1429DD5C5B1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}"
ProjectSection(SolutionItems) = preProject
.github\workflows\ci.yml = .github\workflows\ci.yml
.github\workflows\main.yml = .github\workflows\main.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Civil3d2024", "Connectors\Autocad\Speckle.Connectors.Civil3d2024\Speckle.Connectors.Civil3d2024.csproj", "{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.Civil3dShared", "Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.shproj", "{35175682-DA83-4C0A-A49D-B191F5885D8E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024.DependencyInjection", "Converters\Civil3d\Speckle.Converters.Civil3d2024.DependencyInjection\Speckle.Converters.Civil3d2024.DependencyInjection.csproj", "{80F965C4-E2A8-4F54-985D-73D06E45F9CE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024", "Converters\Autocad\Speckle.Converters.Autocad2024\Speckle.Converters.Autocad2024.csproj", "{C2DE264A-AA87-4012-B954-17E3F403A237}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Autocad2024.DependencyInjection", "Converters\Autocad\Speckle.Converters.Autocad2024.DependencyInjection\Speckle.Converters.Autocad2024.DependencyInjection.csproj", "{AF507D61-6766-4C20-9F58-23DC29508219}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Civil3d2024", "Converters\Civil3d\Speckle.Converters.Civil3d2024\Speckle.Converters.Civil3d2024.csproj", "{25172C49-7AA4-4739-BB07-69785094C379}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RhinoShared", "Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.shproj", "{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Rhino7.Tests", "Converters\Rhino\Speckle.Converters.Rhino7.Tests\Speckle.Converters.Rhino7.Tests.csproj", "{AC2DB416-F05C-4296-9040-56D6AD4FCD27}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2023.Tests", "Converters\Revit\Speckle.Converters.Revit2023.Tests\Speckle.Converters.Revit2023.Tests.csproj", "{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Testing", "Sdk\Speckle.Testing\Speckle.Testing.csproj", "{A3869243-B462-4986-914B-94E407D8D20F}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared.DependencyInjection", "Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.shproj", "{6067BA60-D279-4156-8AE1-6B44E2D19187}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2024", "Connectors\Revit\Speckle.Connectors.Revit2024\Speckle.Connectors.Revit2024.csproj", "{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024", "Converters\Revit\Speckle.Converters.Revit2024\Speckle.Converters.Revit2024.csproj", "{67B888D9-C6C4-49F1-883C-5B964151D889}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2024.DependencyInjection\Speckle.Converters.Revit2024.DependencyInjection.csproj", "{7F3055BA-70AA-424C-8748-345AF35127E9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{57F59C0C-5687-4AF9-AE1C-1933B539F0E4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{FC224610-32D3-454E-9BC1-1219FE8ACD5F}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.RevitShared.Tests", "Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.shproj", "{E1C43415-3202-45F4-8BF9-A4DD7D7F2ED6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2024.Tests", "Converters\Revit\Speckle.Converters.Revit2024.Tests\Speckle.Converters.Revit2024.Tests.csproj", "{C32274D9-1B66-4D5C-82F9-EB3F10F46752}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.RevitShared.Cef", "Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.shproj", "{6A40CBE4-ECAB-4CED-9917-5C64CBF75DA6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2025", "2025", "{8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2025", "Connectors\Revit\Speckle.Connectors.Revit2025\Speckle.Connectors.Revit2025.csproj", "{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2025", "Converters\Revit\Speckle.Converters.Revit2025\Speckle.Converters.Revit2025.csproj", "{4D40A101-07E6-4FF2-8934-83434932591D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2025.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2025.DependencyInjection\Speckle.Converters.Revit2025.DependencyInjection.csproj", "{20751904-0DFC-4126-BF2A-834B53841010}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.Revit2022", "Connectors\Revit\Speckle.Connectors.Revit2022\Speckle.Connectors.Revit2022.csproj", "{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022", "Converters\Revit\Speckle.Converters.Revit2022\Speckle.Converters.Revit2022.csproj", "{19424B55-058C-4E9C-B86F-700AEF9EAEC3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022.DependencyInjection", "Converters\Revit\Speckle.Converters.Revit2022.DependencyInjection\Speckle.Converters.Revit2022.DependencyInjection.csproj", "{881D71A3-D276-4108-98C6-0FFD32129B9C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2022", "2022", "{0AF38BA3-65A0-481B-8CBB-B82E406E1575}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.DUI.Tests", "DUI3\Speckle.Connectors.DUI.Tests\Speckle.Connectors.DUI.Tests.csproj", "{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022.Tests", "Converters\Revit\Speckle.Converters.Revit2022.Tests\Speckle.Converters.Revit2022.Tests.csproj", "{D8069A23-AD2E-4C9E-8574-7E8C45296A46}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2023", "2023", "{2D5AE63D-85C0-43D1-84BF-04418ED93F63}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{2F45036E-D817-41E9-B82F-DBE013EC95D0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Civil3d", "Civil3d", "{91D70DE1-DC8E-4AE1-B100-0671D6263FC5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{4721AA15-AF6E-4A62-A2C3-65564DC563E6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01F98733-7352-47AD-A594-537D979DE3DE}.Release|Any CPU.Build.0 = Release|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E}.Release|Any CPU.Build.0 = Release|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26391930-F86F-47E0-A5F6-B89919E38CE1}.Release|Any CPU.Build.0 = Release|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D81C0B87-F0C1-4297-A147-02F001FB7E1E}.Release|Any CPU.Build.0 = Release|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6}.Release|Any CPU.Build.0 = Release|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC}.Release|Any CPU.Build.0 = Release|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434}.Release|Any CPU.Build.0 = Release|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21}.Release|Any CPU.Build.0 = Release|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5}.Release|Any CPU.Build.0 = Release|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D}.Release|Any CPU.Build.0 = Release|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A97F7177-86C7-4B38-A6ED-DA51BF762471}.Release|Any CPU.Build.0 = Release|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C}.Release|Any CPU.Build.0 = Release|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DFF1591-237D-499E-A767-EE37B93FB958}.Release|Any CPU.Build.0 = Release|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395}.Release|Any CPU.Build.0 = Release|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B}.Release|Any CPU.Build.0 = Release|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{631C295A-7CCF-4B42-8686-7034E31469E7}.Release|Any CPU.Build.0 = Release|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D940853C-003A-482C-BDB0-665367F274A0}.Release|Any CPU.Build.0 = Release|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7420652C-3046-4F38-BE64-9B9E69D76FA2}.Release|Any CPU.Build.0 = Release|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89}.Release|Any CPU.Build.0 = Release|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1}.Release|Any CPU.Build.0 = Release|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{80F965C4-E2A8-4F54-985D-73D06E45F9CE}.Release|Any CPU.Build.0 = Release|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2DE264A-AA87-4012-B954-17E3F403A237}.Release|Any CPU.Build.0 = Release|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF507D61-6766-4C20-9F58-23DC29508219}.Release|Any CPU.Build.0 = Release|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25172C49-7AA4-4739-BB07-69785094C379}.Release|Any CPU.Build.0 = Release|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC2DB416-F05C-4296-9040-56D6AD4FCD27}.Release|Any CPU.Build.0 = Release|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051}.Release|Any CPU.Build.0 = Release|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3869243-B462-4986-914B-94E407D8D20F}.Release|Any CPU.Build.0 = Release|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC}.Release|Any CPU.Build.0 = Release|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67B888D9-C6C4-49F1-883C-5B964151D889}.Release|Any CPU.Build.0 = Release|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F3055BA-70AA-424C-8748-345AF35127E9}.Release|Any CPU.Build.0 = Release|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C32274D9-1B66-4D5C-82F9-EB3F10F46752}.Release|Any CPU.Build.0 = Release|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C}.Release|Any CPU.Build.0 = Release|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D40A101-07E6-4FF2-8934-83434932591D}.Release|Any CPU.Build.0 = Release|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20751904-0DFC-4126-BF2A-834B53841010}.Release|Any CPU.Build.0 = Release|Any CPU
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Debug|Any CPU.ActiveCfg = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Debug|Any CPU.Build.0 = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Release|Any CPU.ActiveCfg = Debug|x64
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36}.Release|Any CPU.Build.0 = Debug|x64
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19424B55-058C-4E9C-B86F-700AEF9EAEC3}.Release|Any CPU.Build.0 = Release|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.Build.0 = Release|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.Build.0 = Release|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D92751C8-1039-4005-90B2-913E55E0B8BD} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{01F98733-7352-47AD-A594-537D979DE3DE} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{DC570FFF-6FE5-47BD-8BC1-B471A6067786} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{83EAD6F0-3CB3-456A-AD81-072127D0DE0E} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{26391930-F86F-47E0-A5F6-B89919E38CE1} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{D81C0B87-F0C1-4297-A147-02F001FB7E1E} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{C9D4CA21-182B-4ED2-81BB-280A6FD713F6} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{7291B93C-615D-42DE-B8C1-3F9DF643E0FC} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{8AEF06C0-CA5C-4460-BC2D-ADE5F35D0434} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{9584AEE5-CD59-46E6-93E6-2DC2B5285B75} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{1E2644A9-6B31-4350-8772-CEAAD6EE0B21} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{65A2F556-F14A-49F3-8A92-7F2E1E7ED3B5} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{9C1ECA1D-DE98-4FB7-92D0-FC45BB308E5D} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{A97F7177-86C7-4B38-A6ED-DA51BF762471} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{139F7A79-69E4-4B8A-B2A5-6A30A66C495C} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{7DFF1591-237D-499E-A767-EE37B93FB958} = {CCF48B65-33D1-4E8B-A57B-E03394730B21}
{CCF48B65-33D1-4E8B-A57B-E03394730B21} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{11F7D41B-AFCA-4D29-BC08-285A14BF3A3B} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{804E065F-914C-414A-AF84-009312C3CFF6} = {42826721-9A18-4762-8BA9-F1429DD5C5B1}
{7420652C-3046-4F38-BE64-9B9E69D76FA2} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{C50AA3E3-8C31-4131-9DEC-1D8B377D5A89} = {59E8E8F3-4E42-4E92-83B3-B1C2AB901D18}
{E1C43415-3200-45F4-8BF9-A4DD7D7F2ED9} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{AC2DB416-F05C-4296-9040-56D6AD4FCD27} = {9584AEE5-CD59-46E6-93E6-2DC2B5285B75}
{68CF9BDF-94AC-4D2D-A7BD-D1C064F97051} = {E9DEBA00-50A4-485D-BA65-D8AB3E3467AB}
{A3869243-B462-4986-914B-94E407D8D20F} = {2E00592E-558D-492D-88F9-3ECEE4C0C7DA}
{6067BA60-D279-4156-8AE1-6B44E2D19187} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{617BD3C7-87D9-4D28-8AC9-4910945BB9FC} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{67B888D9-C6C4-49F1-883C-5B964151D889} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{7F3055BA-70AA-424C-8748-345AF35127E9} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{E9DEBA00-50A4-485D-BA65-D8AB3E3467AB} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{57F59C0C-5687-4AF9-AE1C-1933B539F0E4} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{FC224610-32D3-454E-9BC1-1219FE8ACD5F} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{E1C43415-3202-45F4-8BF9-A4DD7D7F2ED6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{C32274D9-1B66-4D5C-82F9-EB3F10F46752} = {57F59C0C-5687-4AF9-AE1C-1933B539F0E4}
{6A40CBE4-ECAB-4CED-9917-5C64CBF75DA6} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
{8AC2AD6D-6C74-4B24-8DF6-42717FC9B804} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{A6DE3DA0-B242-4F49-AEF0-4E26AF92D16C} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{4D40A101-07E6-4FF2-8934-83434932591D} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{20751904-0DFC-4126-BF2A-834B53841010} = {8AC2AD6D-6C74-4B24-8DF6-42717FC9B804}
{7F1FDCF2-0CE8-4119-B3C1-F2CC6D7E1C36} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{19424B55-058C-4E9C-B86F-700AEF9EAEC3} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{881D71A3-D276-4108-98C6-0FFD32129B9C} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{0AF38BA3-65A0-481B-8CBB-B82E406E1575} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
{D8069A23-AD2E-4C9E-8574-7E8C45296A46} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
{2D5AE63D-85C0-43D1-84BF-04418ED93F63} = {804E065F-914C-414A-AF84-009312C3CFF6}
{89C4CBC7-1606-40DE-B6DA-FBE3AAC98395} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{631C295A-7CCF-4B42-8686-7034E31469E7} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{D940853C-003A-482C-BDB0-665367F274A0} = {2D5AE63D-85C0-43D1-84BF-04418ED93F63}
{2F45036E-D817-41E9-B82F-DBE013EC95D0} = {804E065F-914C-414A-AF84-009312C3CFF6}
{C2DE264A-AA87-4012-B954-17E3F403A237} = {2F45036E-D817-41E9-B82F-DBE013EC95D0}
{AF507D61-6766-4C20-9F58-23DC29508219} = {2F45036E-D817-41E9-B82F-DBE013EC95D0}
{91D70DE1-DC8E-4AE1-B100-0671D6263FC5} = {804E065F-914C-414A-AF84-009312C3CFF6}
{CA8EAE01-AB9F-4EC1-B6F3-73721487E9E1} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{25172C49-7AA4-4739-BB07-69785094C379} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{80F965C4-E2A8-4F54-985D-73D06E45F9CE} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{35175682-DA83-4C0A-A49D-B191F5885D8E} = {91D70DE1-DC8E-4AE1-B100-0671D6263FC5}
{4721AA15-AF6E-4A62-A2C3-65564DC563E6} = {804E065F-914C-414A-AF84-009312C3CFF6}
{41BC679F-887F-44CF-971D-A5502EE87DB0} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6}
{9ADD1B7A-6401-4202-8613-F668E2FBC0A4} = {4721AA15-AF6E-4A62-A2C3-65564DC563E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{19424b55-058c-4e9c-b86f-700aef9eaec3}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{20751904-0dfc-4126-bf2a-834b53841010}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5
Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5
Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{6067ba60-d279-4156-8ae1-6b44e2d19187}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{631c295a-7ccf-4b42-8686-7034e31469e7}*SharedItemsImports = 5
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{65a2f556-f14a-49f3-8a92-7f2e1e7ed3b5}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{67b888d9-c6c4-49f1-883c-5b964151d889}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{6a40cbe4-ecab-4ced-9917-5c64cbf75da6}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{7f3055ba-70aa-424c-8748-345af35127e9}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{83ead6f0-3cb3-456a-ad81-072127d0de0e}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.DependencyInjection\Speckle.Converters.RevitShared.DependencyInjection.projitems*{881d71a3-d276-4108-98c6-0ffd32129b9c}*SharedItemsImports = 5
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{89c4cbc7-1606-40de-b6da-fbe3aac98395}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{9add1b7a-6401-4202-8613-f668e2fbc0a4}*SharedItemsImports = 13
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{a6de3da0-b242-4f49-aef0-4e26af92d16c}*SharedItemsImports = 5
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{ac2db416-f05c-4296-9040-56d6ad4fcd27}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{c2de264a-aa87-4012-b954-17e3f403a237}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{c32274d9-1b66-4d5c-82f9-eb3f10f46752}*SharedItemsImports = 5
Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5
Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{dc570fff-6fe5-47bd-8bc1-b471a6067786}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{e1c43415-3200-45f4-8bf9-a4dd7d7f2ed9}*SharedItemsImports = 13
Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{e1c43415-3202-45f4-8bf9-a4dd7d7f2ed6}*SharedItemsImports = 13
EndGlobalSection
EndGlobal
+39 -29
View File
@@ -1,48 +1,49 @@
using Microsoft.Build.Construction;
using Microsoft.VisualStudio.SolutionPersistence.Model;
using Microsoft.VisualStudio.SolutionPersistence.Model;
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
namespace Build;
public static class Solutions
{
private static bool ValidProjects(KeyValuePair<string, ProjectInSolution> projectInSolution) =>
projectInSolution.Value.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat;
public static void CompareConnectorsToLocal()
#pragma warning disable CA1802
#pragma warning disable IDE1006
private static readonly string DIRECTORY = Environment.CurrentDirectory;
#pragma warning restore IDE1006
#pragma warning restore CA1802
public static async Task CompareConnectorsToLocal()
{
var localSln = SolutionFile.Parse(Path.Combine(Environment.CurrentDirectory, "Local.sln"));
var connectorsSln = SolutionFile.Parse(Path.Combine(Environment.CurrentDirectory, "Speckle.Connectors.sln"));
var localProjects = localSln.ProjectsByGuid.Where(ValidProjects).ToDictionary();
var localSln = await GetSolution("Local.sln");
var connectorsSln = await GetSolution("Speckle.Connectors.sln");
var localProjects = localSln.SolutionProjects.ToList();
foreach ((string? _, ProjectInSolution? value) in connectorsSln.ProjectsByGuid.Where(ValidProjects))
foreach (var value in connectorsSln.SolutionProjects)
{
var localProject = localProjects.Values.FirstOrDefault(x => x.ProjectName == value.ProjectName);
var localProject = localProjects.FirstOrDefault(x => x.ActualDisplayName == value.ActualDisplayName);
if (localProject is null)
{
throw new InvalidOperationException($"Could not find in LOCAL solution: {value.ProjectName}");
throw new InvalidOperationException($"Could not find in LOCAL solution: {value.ActualDisplayName}");
}
if (value.ProjectName != localProject.ProjectName)
if (value.ActualDisplayName != localProject.ActualDisplayName)
{
throw new InvalidOperationException(
"Projects with different names have same Guid in solution: "
+ value.ProjectName
+ value.ActualDisplayName
+ " and "
+ localProject.ProjectName
+ localProject.ActualDisplayName
);
}
localProjects.Remove(localProjects.Single(x => x.Value.ProjectName == value.ProjectName).Key);
localProjects.Remove(localProjects.Single(x => x.ActualDisplayName == value.ActualDisplayName));
}
void CheckAndRemoveKnown(string projectName)
{
var localProject = localProjects.Values.FirstOrDefault(x => x.ProjectName == projectName);
var localProject = localProjects.FirstOrDefault(x => x.ActualDisplayName == projectName);
if (localProject is null)
{
throw new InvalidOperationException($"Could not find in LOCAL solution: {projectName}");
}
localProjects.Remove(localProjects.Single(x => x.Value.ProjectName == projectName).Key);
localProjects.Remove(localProjects.Single(x => x.ActualDisplayName == projectName));
}
CheckAndRemoveKnown("Speckle.Objects");
@@ -51,7 +52,7 @@ public static class Solutions
if (localProjects.Count != 0)
{
throw new InvalidOperationException(
"Could not find in CONNECTOR solution: " + localProjects.First().Value.ProjectName
"Could not find in CONNECTOR solution: " + localProjects.First().ActualDisplayName
);
}
}
@@ -61,9 +62,9 @@ public static class Solutions
await GenerateLocalSlnx();
foreach (var group in Consts.ProjectGroups)
{
var path = group.Projects[0].ProjectPath.Split('/');
var folder = $"/{path[0]}/{path[1]}/";
await GenerateConnector(group.HostAppSlug, folder);
var connectors = await GetFullSlnx();
var slug = group.HostAppSlug;
await GenerateConnector(connectors, group, string.Concat(slug[0].ToString().ToUpper(), slug.AsSpan(1)));
}
}
@@ -73,16 +74,19 @@ public static class Solutions
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Objects\\Speckle.Objects.csproj");
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Sdk\\Speckle.Sdk.csproj");
connectors.AddProject("..\\speckle-sharp-sdk\\src\\Speckle.Sdk.Dependencies\\Speckle.Sdk.Dependencies.csproj");
var sln = Path.Combine("C:\\Users\\adam\\Git\\speckle-sharp-connectors", "Local.slnx");
var sln = Path.Combine(DIRECTORY, "Local.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
sln = Path.Combine(Environment.CurrentDirectory, "Local.sln");
sln = Path.Combine(DIRECTORY, "Local.sln");
await SolutionSerializers.SlnFileV12.SaveAsync(sln, connectors, default);
var revit = Consts.ProjectGroups.Single(x => x.HostAppSlug.Equals("revit"));
await GenerateConnector(connectors, revit, "Revit.Local");
}
public static async Task GenerateConnector(string slug, string folder)
public static async Task GenerateConnector(SolutionModel connectors, ProjectGroup group, string? name)
{
slug = string.Concat(slug[0].ToString().ToUpper(), slug.AsSpan(1));
var connectors = await GetFullSlnx();
var path = group.Projects[0].ProjectPath.Split('/');
var folder = $"/{path[0]}/{path[1]}/";
var foldersToRemove = connectors
.SolutionFolders.Where(x =>
//need base folder
@@ -95,13 +99,19 @@ public static class Solutions
{
connectors.RemoveFolder(folderToRemove);
}
var sln = Path.Combine(Environment.CurrentDirectory, $"Speckle.{slug}.slnx");
var sln = Path.Combine(DIRECTORY, $"Speckle.{name}.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
}
public static async Task<SolutionModel> GetFullSlnx()
{
var connectorsSln = Path.Combine(Environment.CurrentDirectory, "Speckle.Connectors.slnx");
var connectorsSln = Path.Combine(DIRECTORY, "Speckle.Connectors.slnx");
return await SolutionSerializers.SlnXml.OpenAsync(connectorsSln, default);
}
public static async Task<SolutionModel> GetSolution(string solutionName)
{
var connectorsSln = Path.Combine(DIRECTORY, solutionName);
return await SolutionSerializers.SlnFileV12.OpenAsync(connectorsSln, 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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -293,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -75,11 +75,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -215,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -249,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -293,34 +288,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -75,11 +75,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -215,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -249,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -293,34 +288,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,119 +1,59 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
public abstract class AutocadReceiveBaseBinding : IReceiveBinding
public abstract class AutocadReceiveBaseBinding(
IBrowserBridge parent,
ICancellationManager cancellationManager,
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
{
public string Name => "receiveBinding";
public IBrowserBridge Parent { get; }
public IBrowserBridge Parent { get; } = parent;
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<AutocadReceiveBinding> _logger;
private readonly ISpeckleApplication _speckleApplication;
private readonly IThreadContext _threadContext;
private ReceiveBindingUICommands Commands { get; } = new(parent);
private ReceiveBindingUICommands Commands { get; }
protected abstract void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc);
protected AutocadReceiveBaseBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
)
{
_store = store;
_cancellationManager = cancellationManager;
_serviceProvider = serviceProvider;
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
Parent = parent;
Commands = new ReceiveBindingUICommands(parent);
}
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId) =>
await _threadContext.RunOnMainAsync(async () => await ReceiveInternal(modelCardId));
await threadContext.RunOnMainAsync(async () => await ReceiveInternal(modelCardId));
private async Task ReceiveInternal(string modelCardId)
{
using var scope = _serviceProvider.CreateScope();
InitializeSettings(scope.ServiceProvider);
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
using var manager = receiveOperationManagerFactory.Create();
await manager.Process(
Commands,
modelCardId,
InitializeSettings,
async (_, processor) =>
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
try
{
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
return await processor();
}
finally
{
// reenable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
// regenerate doc to flush graphics, sometimes some objects (ellipses, nurbs curves) do not appear fully visible after receive.
// Adding a regen (must be run on main thread) here, but it doesn't seem to work:
// it's run on main thread, tried sending the "regen" string to execute, also tried regen after every object bake, but still can't fix.
// the objects should appear visible if you manually call the "regen" command after the operation finishes, or click on a view on the view cube which also calls regen.
Application.DocumentManager.CurrentDocument.Editor.Regen();
}
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
// Receive host objects
var operationResults = await scope
.ServiceProvider.GetRequiredService<ReceiveOperation>()
.Execute(
modelCard.GetReceiveInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelReceiveResult(
modelCardId,
operationResults.BakedObjectIds,
operationResults.ConversionResults
);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
// reenable document activation
Application.DocumentManager.DocumentActivationEnabled = true;
// regenerate doc to flush graphics, sometimes some objects (ellipses, nurbs curves) do not appear fully visible after receive.
// Adding a regen (must be run on main thread) here, but it doesn't seem to work:
// it's run on main thread, tried sending the "regen" string to execute, also tried regen after every object bake, but still can't fix.
// the objects should appear visible if you manually call the "regen" command after the operation finishes, or click on a view on the view cube which also calls regen.
Application.DocumentManager.CurrentDocument.Editor.Regen();
}
);
}
}
@@ -1,13 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -16,31 +14,18 @@ public sealed class AutocadReceiveBinding : AutocadReceiveBaseBinding
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public AutocadReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
)
: base(
store,
parent,
cancellationManager,
serviceProvider,
operationProgressManager,
logger,
speckleApplication,
threadContext
)
: base(parent, cancellationManager, threadContext, receiveOperationManagerFactory)
{
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
protected override void InitializeSettings(IServiceProvider serviceProvider)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
@@ -1,23 +1,16 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -33,14 +26,11 @@ public abstract class AutocadSendBaseBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly ISendConversionCache _sendConversionCache;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<AutocadSendBinding> _logger;
private readonly ISpeckleApplication _speckleApplication;
private readonly IThreadContext _threadContext;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IAppIdleManager _idleManager;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
/// <summary>
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
@@ -55,27 +45,21 @@ public abstract class AutocadSendBaseBinding : ISendBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager idleManager
IAppIdleManager idleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
{
_store = store;
_serviceProvider = serviceProvider;
_cancellationManager = cancellationManager;
_sendFilters = sendFilters.ToList();
_sendConversionCache = sendConversionCache;
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_idleManager = idleManager;
_sendOperationManagerFactory = sendOperationManagerFactory;
Parent = parent;
Commands = new SendBindingUICommands(parent);
@@ -152,55 +136,17 @@ public abstract class AutocadSendBaseBinding : ISendBinding
{
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
using var scope = _serviceProvider.CreateScope();
InitializeSettings(scope.ServiceProvider);
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
using var manager = _sendOperationManagerFactory.Create();
// Disable document activation (document creation and document switch)
// Not disabling results in DUI model card being out of sync with the active document
// The DocumentActivated event isn't usable probably because it is pushed to back of main thread queue
Application.DocumentManager.DocumentActivationEnabled = false;
// Get elements to convert
List<AutocadRootObject> autocadObjects = Application.DocumentManager.CurrentDocument.GetObjects(
modelCard.SendFilter.NotNull().RefreshObjectIds()
await manager.Process(
Commands,
modelCardId,
(sp, card) => InitializeSettings(sp),
card => Application.DocumentManager.CurrentDocument.GetObjects(card.SendFilter.NotNull().RefreshObjectIds())
);
if (autocadObjects.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<AutocadRootObject>>()
.Execute(
autocadObjects,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.VersionId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
return;
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
@@ -9,7 +8,6 @@ using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -22,29 +20,23 @@ public sealed class AutocadSendBinding : AutocadSendBaseBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
: base(
store,
parent,
sendFilters,
cancellationManager,
serviceProvider,
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager
appIdleManager,
sendOperationManagerFactory
)
{
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
@@ -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.
@@ -7,7 +7,6 @@ using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
@@ -28,7 +27,8 @@ public class AutocadHostObjectBuilder(
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
) : IHostObjectBuilder
{
public Task<HostObjectBuilderResult> Build(
@@ -90,11 +90,11 @@ public class AutocadHostObjectBuilder(
var count = 0;
foreach (var (layerPath, atomicObject) in atomicObjectsWithPath)
{
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
cancellationToken.ThrowIfCancellationRequested();
try
var ex = conversionHandler.TryConvert(() =>
{
cancellationToken.ThrowIfCancellationRequested();
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
applicationIdMap[objectId] = convertedObjects;
@@ -109,8 +109,8 @@ public class AutocadHostObjectBuilder(
);
bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId()));
}
catch (Exception ex) when (!ex.IsFatal())
});
if (ex != null)
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
@@ -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
)
@@ -106,13 +106,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
sendInfo.ProjectId
);
var result = ConvertAutocadEntity(entity, applicationId, objectCollection, instanceProxies, projectId);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
@@ -17,7 +17,7 @@ namespace Speckle.Connectors.Autocad.Plugin;
public class AutocadCommand
{
private static PaletteSet? PaletteSet { get; set; }
private static readonly Guid s_id = new("3223E594-1B09-4E54-B3DD-8EA0BECE7BA5");
private static readonly Guid s_id = new("7C27DD2B-86E8-4D31-B3DE-B34B267B1DC8");
public ServiceProvider? Container { get; private set; }
private IDisposable? _disposableLogger;
public const string COMMAND_STRING = "Speckle";
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -84,11 +84,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -224,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -259,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -303,34 +298,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -84,11 +84,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -224,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -259,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -303,34 +298,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,15 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.Bindings;
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;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Civil3dShared.Bindings;
@@ -19,27 +17,14 @@ public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Civil3dReceiveBinding(
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<AutocadReceiveBinding> logger,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext
IThreadContext threadContext,
IReceiveOperationManagerFactory receiveOperationManagerFactory
)
: base(
store,
parent,
cancellationManager,
serviceProvider,
operationProgressManager,
logger,
speckleApplication,
threadContext
)
: base(parent, cancellationManager, threadContext, receiveOperationManagerFactory)
{
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
@@ -47,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>>()
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
@@ -11,7 +10,6 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
using Speckle.Sdk;
namespace Speckle.Connectors.Civil3dShared.Bindings;
@@ -25,30 +23,24 @@ public sealed class Civil3dSendBinding : AutocadSendBaseBinding
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ILogger<AutocadSendBinding> logger,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
ISpeckleApplication speckleApplication,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
: base(
store,
parent,
sendFilters,
cancellationManager,
serviceProvider,
sendConversionCache,
operationProgressManager,
logger,
speckleApplication,
threadContext,
topLevelExceptionHandler,
appIdleManager
appIdleManager,
sendOperationManagerFactory
)
{
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
@@ -1,22 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
namespace Speckle.Connectors.CSiShared.Bindings;
@@ -26,43 +18,28 @@ public sealed class CsiSharedSendBinding : ISendBinding
public SendBindingUICommands Commands { get; }
public IBrowserBridge Parent { get; }
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISendFilter> _sendFilters;
private readonly ICancellationManager _cancellationManager;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<CsiSharedSendBinding> _logger;
private readonly ICsiApplicationService _csiApplicationService;
private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory;
private readonly ISpeckleApplication _speckleApplication;
private readonly ISdkActivityFactory _activityFactory;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
public CsiSharedSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
IOperationProgressManager operationProgressManager,
ILogger<CsiSharedSendBinding> logger,
ICsiConversionSettingsFactory csiConversionSettingsFactory,
ISpeckleApplication speckleApplication,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService
ICsiApplicationService csiApplicationService,
ISendOperationManagerFactory sendOperationManagerFactory
)
{
_store = store;
_serviceProvider = serviceProvider;
_sendFilters = sendFilters.ToList();
_cancellationManager = cancellationManager;
_operationProgressManager = operationProgressManager;
_logger = logger;
Parent = parent;
Commands = new SendBindingUICommands(parent);
_csiConversionSettingsFactory = csiConversionSettingsFactory;
_speckleApplication = speckleApplication;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_sendOperationManagerFactory = sendOperationManagerFactory;
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
@@ -71,52 +48,15 @@ public sealed class CsiSharedSendBinding : ISendBinding
public async Task Send(string modelCardId)
{
using var activity = _activityFactory.Start();
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
{
throw new InvalidOperationException("No publish model card was found.");
}
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel));
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
List<ICsiWrapper> wrappers = modelCard
.SendFilter.NotNull()
.RefreshObjectIds()
.Select(DecodeObjectIdentifier)
.ToList();
if (wrappers.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<ICsiWrapper>>()
.Execute(
wrappers,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.VersionId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
using var manager = _sendOperationManagerFactory.Create();
await manager.Process(
Commands,
modelCardId,
(sp, _) =>
sp.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel)),
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList()
);
}
private ICsiWrapper DecodeObjectIdentifier(string encodedId)
@@ -5,7 +5,7 @@ using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
using Timer = System.Timers.Timer;
@@ -82,7 +82,7 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
{
try
{
ModelPathHash = Crypt.Md5(_csiApplicationService.SapModel.GetModelFilename(), length: 32);
ModelPathHash = Md5.GetString(_csiApplicationService.SapModel.GetModelFilename());
HostAppUserDataPath = Path.Combine(
SpecklePathProvider.UserSpeckleFolderPath,
"ConnectorsFileData",
@@ -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
)
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -75,11 +75,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -215,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -241,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.etabs22": {
@@ -291,34 +286,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connector.Navisworks.Operations.Send.Filters;
using Speckle.Connector.Navisworks.Operations.Send.Settings;
using Speckle.Connector.Navisworks.Services;
@@ -9,16 +8,13 @@ using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
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;
using Speckle.Sdk.Logging;
namespace Speckle.Connector.Navisworks.Bindings;
@@ -30,45 +26,33 @@ public class NavisworksSendBinding : ISendBinding
public SendBindingUICommands Commands { get; }
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly ICancellationManager _cancellationManager;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<NavisworksSendBinding> _logger;
private readonly ISpeckleApplication _speckleApplication;
private readonly ISdkActivityFactory _activityFactory;
private readonly INavisworksConversionSettingsFactory _conversionSettingsFactory;
private readonly ToSpeckleSettingsManagerNavisworks _toSpeckleSettingsManagerNavisworks;
private readonly IElementSelectionService _selectionService;
private readonly IThreadContext _threadContext;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
public NavisworksSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IServiceProvider serviceProvider,
ICancellationManager cancellationManager,
IOperationProgressManager operationProgressManager,
ILogger<NavisworksSendBinding> logger,
ISpeckleApplication speckleApplication,
ISdkActivityFactory activityFactory,
INavisworksConversionSettingsFactory conversionSettingsFactory,
ToSpeckleSettingsManagerNavisworks toSpeckleSettingsManagerNavisworks,
IElementSelectionService selectionService,
IThreadContext threadContext
IThreadContext threadContext,
ISendOperationManagerFactory sendOperationManagerFactory
)
{
Parent = parent;
Commands = new SendBindingUICommands(parent);
_store = store;
_serviceProvider = serviceProvider;
_cancellationManager = cancellationManager;
_operationProgressManager = operationProgressManager;
_logger = logger;
_speckleApplication = speckleApplication;
_activityFactory = activityFactory;
_conversionSettingsFactory = conversionSettingsFactory;
_toSpeckleSettingsManagerNavisworks = toSpeckleSettingsManagerNavisworks;
_selectionService = selectionService;
_threadContext = threadContext;
_sendOperationManagerFactory = sendOperationManagerFactory;
SubscribeToNavisworksEvents();
}
@@ -78,7 +62,8 @@ public class NavisworksSendBinding : ISendBinding
public List<ISendFilter> GetSendFilters() =>
[
new NavisworksSelectionFilter() { IsDefault = true },
new NavisworksSavedSetsFilter(new ElementSelectionService())
new NavisworksSavedSetsFilter(new ElementSelectionService()),
new NavisworksSavedViewsFilter(new ElementSelectionService())
];
public List<ICardSetting> GetSendSettings() =>
@@ -95,60 +80,13 @@ public class NavisworksSendBinding : ISendBinding
private async Task SendInternal(string modelCardId)
{
using var activity = _activityFactory.Start();
try
{
var modelCard = GetModelCard(modelCardId);
using var scope = _serviceProvider.CreateScope();
InitializeConverterSettings(scope, modelCard);
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
var progress = _operationProgressManager.CreateOperationProgressEventHandler(
Parent,
modelCard.ModelCardId.NotNull(),
cancellationItem.Token
);
var navisworksModelItems = await GetNavisworksModelItems(modelCard, progress);
var sendResult = await ExecuteSendOperation(
scope,
modelCard,
navisworksModelItems,
progress,
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.VersionId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
// otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI.
_cancellationManager.CancelOperation(modelCardId);
}
using var manager = _sendOperationManagerFactory.Create();
await manager.Process(Commands, modelCardId, InitializeConverterSettings, GetNavisworksModelItems);
}
private SenderModelCard GetModelCard(string modelCardId) =>
_store.GetModelById(modelCardId) as SenderModelCard
?? throw new InvalidOperationException("No publish model card was found.");
private void InitializeConverterSettings(IServiceScope scope, SenderModelCard modelCard) =>
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<NavisworksConversionSettings>>()
private void InitializeConverterSettings(IServiceProvider serviceProvider, SenderModelCard modelCard) =>
serviceProvider
.GetRequiredService<IConverterSettingsStore<NavisworksConversionSettings>>()
.Initialize(
_conversionSettingsFactory.Create(
originMode: _toSpeckleSettingsManagerNavisworks.GetOriginMode(modelCard),
@@ -159,7 +97,7 @@ public class NavisworksSendBinding : ISendBinding
)
);
private async Task<List<NAV.ModelItem>> GetNavisworksModelItems(
private async Task<IReadOnlyList<NAV.ModelItem>> GetNavisworksModelItems(
SenderModelCard modelCard,
IProgress<CardProgress> onOperationProgressed
)
@@ -191,22 +129,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>
@@ -68,6 +68,7 @@ public static class NavisworksConnectorServiceRegistration
// register filters
serviceCollection.AddScoped<ISendFilter, NavisworksSelectionFilter>();
serviceCollection.AddScoped<ISendFilter, NavisworksSavedSetsFilter>();
serviceCollection.AddScoped<ISendFilter, NavisworksSavedViewsFilter>();
serviceCollection.AddScoped<IElementSelectionService, ElementSelectionService>();
}
}
@@ -2,7 +2,6 @@
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converter.Navisworks.Constants;
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
@@ -85,12 +84,14 @@ public class NavisworksSavedSetsFilter : DiscriminatedObject, ISendFilterSelect
{
List<NAV.SelectionSet> savedSetRecords = [];
CollectSavedSets(NavisworksApp.ActiveDocument.SelectionSets.RootItem, savedSetRecords);
var root = NavisworksApp.ActiveDocument.SelectionSets.RootItem;
CollectSavedSets(root, savedSetRecords);
Items = savedSetRecords
.Select(setRecord =>
{
string hierarchicalName = BuildHierarchicalName(setRecord);
string hierarchicalName = SavedItemHelpers.BuildHierarchicalName(setRecord, root);
return new SendFilterSelectItem(setRecord.Guid.ToString(), hierarchicalName);
})
.ToList();
@@ -103,7 +104,7 @@ public class NavisworksSavedSetsFilter : DiscriminatedObject, ISendFilterSelect
return;
}
foreach (NAV.SavedItem item in ((NAV.FolderItem)parentItem).Children)
foreach (NAV.SavedItem item in ((NAV.GroupItem)parentItem).Children)
{
if (item.IsGroup)
{
@@ -115,18 +116,4 @@ public class NavisworksSavedSetsFilter : DiscriminatedObject, ISendFilterSelect
}
}
}
private static string BuildHierarchicalName(NAV.SavedItem item)
{
var pathParts = new List<string> { item.DisplayName };
var current = item.Parent;
while (current != null && current != NavisworksApp.ActiveDocument.SelectionSets.RootItem)
{
pathParts.Insert(0, current.DisplayName);
current = current.Parent;
}
return string.Join(PathConstants.SET_SEPARATOR, pathParts);
}
}
@@ -0,0 +1,173 @@
using Speckle.Connector.Navisworks.Services;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
{
private readonly IElementSelectionService _selectionService;
public NavisworksSavedViewsFilter(IElementSelectionService selectionService)
{
_selectionService = selectionService;
Items = [];
SelectedItems = [];
GetSavedViews();
}
public string Id { get; set; } = "navisworksSavedViews";
public string Name { get; set; } = "Saved Views";
public string Type { get; set; } = "Select";
public string? Summary { get; set; }
public bool IsDefault { get; set; }
public List<string> SelectedObjectIds { get; set; } = [];
public Dictionary<string, string>? IdMap { get; set; }
public bool IsMultiSelectable { get; set; }
public List<SendFilterSelectItem> SelectedItems { get; set; }
public List<SendFilterSelectItem> Items { get; set; }
public List<string> RefreshObjectIds()
{
List<string> objectIds = [];
if (SelectedItems.Count == 0)
{
return objectIds;
}
var savedViews = NavisworksApp.ActiveDocument.SavedViewpoints;
foreach (var savedViewItem in SelectedItems.Select(item => ResolveSavedView(item.Id)))
{
// Get the visible elements in the saved view.
objectIds.AddRange(ResolvedSavedViewObjects(savedViewItem));
}
return objectIds;
}
private static NAV.SavedViewpoint ResolveSavedView(string savedViewReference)
{
if (Guid.TryParse(savedViewReference, out var guid))
{
// Even though we may have already got a match, that could be to a generic Guid from earlier versions of Navisworks
if (savedViewReference != Guid.Empty.ToString())
{
return (NAV.SavedViewpoint)NavisworksApp.ActiveDocument.SavedViewpoints.ResolveGuid(guid);
}
}
var savedRef = new NAV.SavedItemReference("LcOpSavedViewsElement", savedViewReference);
var resolvedReference = NavisworksApp.ActiveDocument.ResolveReference(savedRef) as NAV.SavedViewpoint;
return resolvedReference
?? throw new SpeckleSendFilterException($"Saved view with reference {savedViewReference} not found.");
}
private IEnumerable<string> ResolvedSavedViewObjects(NAV.SavedViewpoint savedView)
{
var objectIds = new List<string>();
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
// if (!savedView.ContainsVisibilityOverrides)
// {
// // We check this again as the view settings may have changed in the saved card.
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
// // This will need to be the documented behaviour.
// throw new SpeckleSendFilterException(
// "Saved view does not contain visibility overrides. This would effectively publish everything in the model."
// );
// }
NavisworksApp.ActiveDocument.SavedViewpoints.CurrentSavedViewpoint = savedView;
var models = NavisworksApp.ActiveDocument.Models;
NavisworksApp.ActiveDocument.CurrentSelection.Clear();
foreach (var model in models)
{
var rootItem = model.RootItem;
if (!_selectionService.IsVisible(rootItem))
{
// If the root item is hidden, we skip it and its descendants.
continue;
}
objectIds.AddRange(
rootItem.Descendants.Where(_selectionService.IsVisible).Select(_selectionService.GetModelItemPath).ToList()
);
}
return objectIds;
}
/// <summary>
/// Since it is called from constructor, it is re-called whenever UI calls SendBinding.GetSendFilters() on SendFilter dialog.
/// Do not change the behavior/scope of this class on send binding unless make sure the behavior is same. Otherwise, we might not be able to update list of saved sets.
/// </summary>
private void GetSavedViews()
{
List<NAV.SavedViewpoint> savedViewRecords = [];
var root = NavisworksApp.ActiveDocument.SavedViewpoints.RootItem;
CollectSavedViews(root, savedViewRecords);
Items = savedViewRecords
.Select(viewRecord =>
{
var reference = NavisworksApp.ActiveDocument.SavedViewpoints.CreateReference(viewRecord);
// If the guid is effectively empty, we can use the saved view's name as a fallback
var selectItemId =
viewRecord.Guid.ToString() == Guid.Empty.ToString() ? reference.SavedItemId : viewRecord.Guid.ToString();
string hierarchicalName = SavedItemHelpers.BuildHierarchicalName(viewRecord, root);
return new SendFilterSelectItem(selectItemId, hierarchicalName);
})
.ToList();
}
private static void CollectSavedViews(NAV.SavedItem parentItem, List<NAV.SavedViewpoint> collectedSets)
{
if (!parentItem.IsGroup)
{
return;
}
foreach (NAV.SavedItem item in ((NAV.GroupItem)parentItem).Children)
{
switch (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;
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.
}
}
}
}
@@ -0,0 +1,20 @@
using Speckle.Converter.Navisworks.Constants;
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
public static class SavedItemHelpers
{
internal static string BuildHierarchicalName(NAV.SavedItem item, NAV.FolderItem? root)
{
var pathParts = new List<string> { item.DisplayName };
var current = item.Parent;
while (current != null && current != root)
{
pathParts.Insert(0, current.DisplayName);
current = current.Parent;
}
return string.Join(PathConstants.SET_SEPARATOR, pathParts);
}
}
@@ -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);
@@ -63,23 +63,19 @@ 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 GetConvertHiddenElements(SenderModelCard modelCard)
@@ -20,6 +20,8 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksDocumentModelStore.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\NavisworksMaterialUnpacker.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\NavisworksSavedViewsFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\SavedItemHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -91,11 +91,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -231,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -256,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.revit2025": {
@@ -301,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Revit.API": {
@@ -316,25 +311,24 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -84,11 +84,6 @@
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
@@ -224,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.dui": {
@@ -249,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.revit2026": {
@@ -285,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Revit.API": {
@@ -300,25 +295,24 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -15,7 +15,18 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
public void ExecuteScript(string script)
{
Browser.Dispatcher.Invoke(() => Browser.ExecuteScriptAsync(script), DispatcherPriority.Background);
Browser.Dispatcher.Invoke(
() =>
{
//avoid exceptions by checking if IBrowser is there
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
{
return;
}
Browser.ExecuteScriptAsync(script);
},
DispatcherPriority.Background
);
}
public void SendProgress(string script) => ExecuteScript(script);
@@ -1,123 +1,66 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Sdk;
namespace Speckle.Connectors.Revit.Bindings;
internal sealed class RevitReceiveBinding : IReceiveBinding
public sealed class RevitReceiveBinding(
ICancellationManager cancellationManager,
IBrowserBridge parent,
ILogger<RevitReceiveBinding> logger,
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
{
public string Name => "receiveBinding";
public IBrowserBridge Parent { get; }
public IBrowserBridge Parent { get; } = parent;
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
private readonly IOperationProgressManager _operationProgressManager;
private readonly ILogger<RevitReceiveBinding> _logger;
private readonly ICancellationManager _cancellationManager;
private readonly DocumentModelStore _store;
private readonly IServiceProvider _serviceProvider;
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
private readonly ISpeckleApplication _speckleApplication;
private readonly RevitToHostCacheSingleton _revitToHostCacheSingleton;
private ReceiveBindingUICommands Commands { get; }
#pragma warning disable CA1024
public List<ICardSetting> GetReceiveSettings() =>
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
#pragma warning restore CA1024
public RevitReceiveBinding(
DocumentModelStore store,
ICancellationManager cancellationManager,
IBrowserBridge parent,
IServiceProvider serviceProvider,
IOperationProgressManager operationProgressManager,
ILogger<RevitReceiveBinding> logger,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
ISpeckleApplication speckleApplication,
RevitToHostCacheSingleton revitToHostCacheSingleton
)
{
Parent = parent;
_store = store;
_serviceProvider = serviceProvider;
_operationProgressManager = operationProgressManager;
_logger = logger;
_revitConversionSettingsFactory = revitConversionSettingsFactory;
_speckleApplication = speckleApplication;
_cancellationManager = cancellationManager;
_revitToHostCacheSingleton = revitToHostCacheSingleton;
Commands = new ReceiveBindingUICommands(parent);
}
public void CancelReceive(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
{
try
{
// Get receiver card
if (_store.GetModelById(modelCardId) is not ReceiverModelCard modelCard)
using var manager = receiveOperationManagerFactory.Create();
await manager.Process(
Commands,
modelCardId,
(sp, card) =>
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No download model card was found.");
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
revitConversionSettingsFactory.Create(
DetailLevelType.Coarse, // TODO figure out
toHostSettingsManager.GetReferencePointSetting(card),
false,
true,
false
)
);
},
async (_, processor) =>
{
try
{
return await processor();
}
catch (SpeckleRevitTaskException ex)
{
await SpeckleRevitTaskException.ProcessException(modelCardId, ex, logger, Commands);
return null;
}
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
DetailLevelType.Coarse, // TODO figure out
null,
false,
true,
false
)
);
// Receive host objects
HostObjectBuilderResult conversionResults = await scope
.ServiceProvider.GetRequiredService<ReceiveOperation>()
.Execute(
modelCard.GetReceiveInfo(_speckleApplication.Slug),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
modelCard.BakedObjectIds = conversionResults.BakedObjectIds.ToList();
await Commands.SetModelReceiveResult(
modelCardId,
conversionResults.BakedObjectIds,
conversionResults.ConversionResults
);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
}
catch (SpeckleRevitTaskException ex)
{
await SpeckleRevitTaskException.ProcessException(modelCardId, ex, _logger, Commands);
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
// otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI.
_cancellationManager.CancelOperation(modelCardId);
}
);
}
}
@@ -1,15 +1,11 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Logging;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
@@ -32,17 +28,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private readonly RevitContext _revitContext;
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly IServiceProvider _serviceProvider;
private readonly ISendConversionCache _sendConversionCache;
private readonly IOperationProgressManager _operationProgressManager;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
private readonly ILogger<RevitSendBinding> _logger;
private readonly ElementUnpacker _elementUnpacker;
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
private readonly ISpeckleApplication _speckleApplication;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly LinkedModelHandler _linkedModelHandler;
private readonly IThreadContext _threadContext;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
/// <summary>
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
@@ -58,18 +51,15 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
DocumentModelStore store,
ICancellationManager cancellationManager,
IBrowserBridge bridge,
IServiceProvider serviceProvider,
ISendConversionCache sendConversionCache,
IOperationProgressManager operationProgressManager,
ToSpeckleSettingsManager toSpeckleSettingsManager,
ILogger<RevitSendBinding> logger,
ElementUnpacker elementUnpacker,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler,
LinkedModelHandler linkedModelHandler,
IThreadContext threadContext,
IRevitTask revitTask
IRevitTask revitTask,
ISendOperationManagerFactory sendOperationManagerFactory
)
: base("sendBinding", bridge)
{
@@ -77,17 +67,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_revitContext = revitContext;
_store = store;
_cancellationManager = cancellationManager;
_serviceProvider = serviceProvider;
_sendConversionCache = sendConversionCache;
_operationProgressManager = operationProgressManager;
_toSpeckleSettingsManager = toSpeckleSettingsManager;
_logger = logger;
_elementUnpacker = elementUnpacker;
_revitConversionSettingsFactory = revitConversionSettingsFactory;
_speckleApplication = speckleApplication;
_topLevelExceptionHandler = topLevelExceptionHandler;
_linkedModelHandler = linkedModelHandler;
_threadContext = threadContext;
_sendOperationManagerFactory = sendOperationManagerFactory;
Commands = new SendBindingUICommands(bridge);
// TODO expiry events
@@ -123,69 +110,26 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public async Task Send(string modelCardId)
{
// Note: removed top level handling thing as it was confusing me
try
{
if (_store.GetModelById(modelCardId) is not SenderModelCard modelCard)
using var manager = _sendOperationManagerFactory.Create();
await manager.Process<DocumentToConvert>(
Commands,
modelCardId,
(sp, card) =>
{
// Handle as GLOBAL ERROR at BrowserBridge
throw new InvalidOperationException("No publish model card was found.");
}
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
using var scope = _serviceProvider.CreateScope();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
_toSpeckleSettingsManager.GetDetailLevelSetting(modelCard),
_toSpeckleSettingsManager.GetReferencePointSetting(modelCard),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard),
_toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard),
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(modelCard)
)
);
var elementsByTransform = await RefreshElementsIdsOnSender(modelCard.NotNull());
if (elementsByTransform.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<DocumentToConvert>>()
.Execute(
elementsByTransform,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
);
await Commands.SetModelSendResult(modelCardId, sendResult.VersionId, sendResult.ConversionResults);
}
catch (OperationCanceledException)
{
// SWALLOW -> UI handles it immediately, so we do not need to handle anything for now!
// Idea for later -> when cancel called, create promise from UI to solve it later with this catch block.
// So have 3 state on UI -> Cancellation clicked -> Cancelling -> Cancelled
}
catch (SpeckleRevitTaskException ex)
{
await SpeckleRevitTaskException.ProcessException(modelCardId, ex, _logger, Commands);
}
catch (Exception ex) when (!ex.IsFatal()) // UX reasons - we will report operation exceptions as model card error. We may change this later when we have more exception documentation
{
_logger.LogModelCardHandledError(ex);
await Commands.SetModelError(modelCardId, ex);
}
finally
{
// otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI.
_cancellationManager.CancelOperation(modelCardId);
}
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
_toSpeckleSettingsManager.GetDetailLevelSetting(card),
_toSpeckleSettingsManager.GetReferencePointSetting(card),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(card),
_toSpeckleSettingsManager.GetLinkedModelsSetting(card),
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(card)
)
);
},
async x => await RefreshElementsIdsOnSender(x.NotNull())
);
}
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
@@ -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);
}
@@ -40,7 +40,8 @@ public sealed class RevitHostObjectBuilder(
ITypedConverter<
(Base atomicObject, IReadOnlyCollection<Matrix4x4> matrix),
DirectShape
> localToGlobalDirectShapeConverter
> localToGlobalDirectShapeConverter,
IReceiveConversionHandler conversionHandler
) : IHostObjectBuilder, IDisposable
{
public Task<HostObjectBuilderResult> Build(
@@ -63,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!
@@ -183,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
)
}
)
)
@@ -216,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
@@ -234,11 +257,9 @@ public sealed class RevitHostObjectBuilder(
foreach (LocalToGlobalMap localToGlobalMap in localToGlobalMaps)
{
cancellationToken.ThrowIfCancellationRequested();
try
var ex = conversionHandler.TryConvert(() =>
{
using var activity = activityFactory.Start("BakeObject");
cancellationToken.ThrowIfCancellationRequested();
// actual conversion happens here!
var result = converter.Convert(localToGlobalMap.AtomicObject);
onOperationProgressed.Report(new("Converting", (double)++count / localToGlobalMaps.Count));
@@ -269,11 +290,10 @@ public sealed class RevitHostObjectBuilder(
{
throw new ConversionException($"Failed to cast {result.GetType()} to direct shape definition wrapper.");
}
}
catch (Exception ex) when (!ex.IsFatal())
});
if (ex is not null)
{
conversionResults.Add(new(Status.ERROR, localToGlobalMap.AtomicObject, null, null, ex));
logger.LogError(ex, $"Failed to convert object of type {localToGlobalMap.AtomicObject.speckle_type}");
}
}
return (new(bakedObjectIds, conversionResults), 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);
}
}
@@ -14,7 +14,7 @@ public class SpeckleRevitTaskException(Exception exception) : SpeckleException("
string modelCardId,
SpeckleRevitTaskException ex,
ILogger<T> logger,
BasicConnectorBindingCommands commands
IReceiveBindingUICommands commands
)
where T : IBinding
{
@@ -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" />
+2 -2
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?"
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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.logging": {
@@ -334,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.rhino7": {
@@ -379,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -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.3.6, )",
"Speckle.Sdk": "[3.3.6, )",
"Speckle.Sdk.Dependencies": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )",
"Speckle.Sdk": "[3.4.5, )",
"Speckle.Sdk.Dependencies": "[3.4.5, )"
}
},
"speckle.connectors.logging": {
@@ -334,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.3.6, )"
"Speckle.Objects": "[3.4.5, )"
}
},
"speckle.converters.rhino8": {
@@ -379,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "zSNOgVwTXu/27oG2OLfJbgi3Myhx23KWFdnVHF+feFEHlnE6PstpnEZRqduoZDQL0FJyEva+nmiBnpZSRe5LSw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "WMDYkTxoSbzh2WzuubMUKx37M6f7D/k/xOOV50oB9bQA0TiUAVcCFKAW0VHZZF4OhjBBxV8N2FM2yr2oaNc/Ww==",
"dependencies": {
"Speckle.Sdk": "3.3.6"
"Speckle.Sdk": "3.4.5"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "VHSah5DYRv6OIHPr7ztArgiZNKEs/SRCz0JfLnK+otVZb1awWj4xW2DA2Bb6I466IdUd24fOEJdFRaTHA/X+mw==",
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "w6vfOyckHVWqOqDjBO+PmVT5LeYu8voMMypOpa+w/2LrgMH6CxkCMYYjyOK8/rb3Ss989f2EjkpksQ3lcHPN/Q==",
"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.3.6"
"Speckle.Sdk.Dependencies": "3.4.5"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.3.6, )",
"resolved": "3.3.6",
"contentHash": "qwbk9BAR1QZIAwphhwMXz5ftCUYXy2oOm9/Jg57MNeaxLZ8MFooygVwX/ETG4avR8bO+VLqoteBJjWl/yYlRLQ=="
"requested": "[3.4.5, )",
"resolved": "3.4.5",
"contentHash": "8X9Qpksyp2MDb/G2Du7OFehdCtt0A0AclMKUFNsDSot5h8fTrvT620kW64ycm4l+PKXsPvCKDspOiGi4+9HrMQ=="
}
}
}
@@ -1,25 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.GrasshopperShared.Registration;
namespace Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
public abstract class SpeckleScopedTaskCapableComponent<TInput, TOutput>(
string name,
string nickname,
string description,
string category,
string subCategory
) : SpeckleTaskCapableComponent<TInput, TOutput>(name, nickname, description, category, subCategory)
{
protected override Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default)
{
/*using*/var scope = PriorityLoader.Container.CreateScope(); // NOTE: this component does not work as intended in e.g the receive component; the scope gets disposed before the task completes.
return PerformScopedTask(input, scope, cancellationToken);
}
protected abstract Task<TOutput> PerformScopedTask(
TInput input,
IServiceScope scope,
CancellationToken cancellationToken = default
);
}
@@ -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,29 +44,51 @@ 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();
foreach (var key in propGoo.Value.Keys)
{
outputParams.Add(CreateOutputParamByKeyValue(key, propGoo.Value[key].Value, GH_ParamAccess.item));
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value is SpecklePropertyGoo prop
? prop.Value
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
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;
}
@@ -105,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)
@@ -126,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:
@@ -157,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:
@@ -188,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.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));
@@ -217,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);
}
}
@@ -0,0 +1,11 @@
using Grasshopper.Kernel.Data;
namespace Speckle.Connectors.GrasshopperShared.Components;
public static class IGH_StructureExtensions
{
public static bool HasInputCountGreaterThan(this IGH_Structure data, int maximumCount, bool skipNulls = false)
{
return data.AllData(skipNulls).Skip(maximumCount).Any();
}
}
@@ -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,12 +36,18 @@ 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)
{
var properties = new Dictionary<string, SpecklePropertyGoo>();
var properties = new Dictionary<string, ISpecklePropertyGoo>();
// Validate for duplicate names
var paramNames = Params.Input.Select(p => p.NickName).ToList();
@@ -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)
{
@@ -71,24 +88,49 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
da.SetData(0, groupGoo);
}
private SpecklePropertyGoo? ExtractPropertyValue(IGH_DataAccess da, int index, string paramName)
private ISpecklePropertyGoo? ExtractPropertyValue(IGH_DataAccess da, int index, string paramName)
{
object? value = null;
da.GetData(index, ref value);
var propertyGoo = new SpecklePropertyGoo();
// check for a group input first
if (value is SpecklePropertyGroupGoo group)
{
return group;
}
var propertyGoo = new SpecklePropertyGoo();
if (value == null)
{
return propertyGoo; // Return empty property
}
if (!propertyGoo.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter '{paramName}' contains invalid data type. Only strings, numbers, booleans, and other Speckle properties are supported."
);
return null;
}
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;
}
@@ -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,162 +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);
for (int i = 0; i < paths.Count; i++)
{
var name = paths[i];
SpecklePropertyGoo objectProperty = FindProperty(objectWrapperGoo.Value.Properties, name);
da.SetData(i, objectProperty.Value);
}
}
}
private SpecklePropertyGoo FindProperty(SpecklePropertyGroupGoo root, string unifiedPath)
{
if (!root.Value.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
{
return new() { Path = unifiedPath, Value = "" };
}
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,47 +19,45 @@ 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.Value)
.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.Value)
.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)));
}
private static List<string> GetPropertyPaths(List<Dictionary<string, SpecklePropertyGoo>> objectPropertyGroups)
private static List<string> GetPropertyPaths(List<SpecklePropertyGroupGoo> objectPropertyGroups)
{
var result = new HashSet<string>();
foreach (var dict in objectPropertyGroups)
foreach (SpecklePropertyGroupGoo propGroup in objectPropertyGroups)
{
// flatten the props
Dictionary<string, SpecklePropertyGoo> flattenedProps = propGroup.Flatten();
result.AddRange(
dict.Keys.Where(k => !(k.EndsWith(".name") || k.EndsWith(".units") || k.EndsWith(".internalDefinitionName")))
flattenedProps.Keys.Where(k =>
!(k.EndsWith(".name") || k.EndsWith(".units") || k.EndsWith(".internalDefinitionName"))
)
);
}
return result.ToList();
@@ -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,131 @@
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 : GH_Component
{
public SpeckleBlockDefinitionPassthrough()
: base(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_def;
public override GH_Exposure Exposure => GH_Exposure.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,227 @@
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 : GH_Component
{
public SpeckleBlockInstancePassthrough()
: base(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
public override GH_Exposure Exposure => GH_Exposure.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,176 @@
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 : GH_Component
{
public SpeckleDataObjectPassthrough()
: base(
"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,253 @@
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("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class SpeckleGeometryPassthrough : GH_Component
{
public SpeckleGeometryPassthrough()
: base(
"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;
}
}
// 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 : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_properties;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
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);
}
}
}
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Input properties", GH_ParamAccess.item);
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);
@@ -4,11 +4,9 @@ using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using GrasshopperAsyncComponent;
using Microsoft.Extensions.DependencyInjection;
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;
@@ -27,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)
@@ -39,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; }
@@ -50,13 +50,6 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
// DI props
public IClient ApiClient { get; private set; }
public IMixPanelManager MixPanelManager { get; private set; }
public GrasshopperReceiveOperation ReceiveOperation { get; private set; }
public RootObjectUnpacker RootObjectUnpacker { get; private set; }
public static IServiceScope? Scope { get; private set; }
public IAccountService AccountService { get; private set; }
public IAccountManager AccountManager { get; private set; }
public IClientFactory ClientFactory { get; private set; }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -76,18 +69,17 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
protected override void SolveInstance(IGH_DataAccess da)
{
MultipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
if (MultipleResources)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
"Only one model can be loaded at a time. To load to multiple models, please use different load components."
);
return;
}
da.DisableGapLogic();
// Dependency Injection
Scope = PriorityLoader.Container.CreateScope();
ReceiveOperation = Scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
MixPanelManager = Scope.ServiceProvider.GetRequiredService<IMixPanelManager>();
RootObjectUnpacker = Scope.ServiceProvider.GetService<RootObjectUnpacker>();
AccountService = Scope.ServiceProvider.GetRequiredService<IAccountService>();
AccountManager = Scope.ServiceProvider.GetRequiredService<IAccountManager>();
ClientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
// We need to call this always in here to be able to react and set events :/
ParseInput(da);
@@ -120,6 +112,8 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
}
}
public bool MultipleResources { get; set; }
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
@@ -193,7 +187,6 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
public override void RemovedFromDocument(GH_Document document)
{
RequestCancellation();
Scope?.Dispose();
base.RemovedFromDocument(document);
}
@@ -287,18 +280,15 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
{
try
{
Account? account =
urlResource.AccountId != null
? AccountManager.GetAccount(urlResource.AccountId)
: AccountService.GetAccountWithServerUrlFallback("", new Uri(urlResource.Server)); // fallback the account that matches with URL if any
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Account? account = urlResource.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);
ApiClient.Subscription.CreateProjectVersionsUpdatedSubscription(urlResource.ProjectId).Listeners +=
ApiClient_OnVersionCreated;
}
@@ -309,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)
@@ -341,7 +335,7 @@ public class ReceiveComponentWorker : WorkerInstance
Parent.AddRuntimeMessage(level, message);
}
var parent = (ReceiveAsyncComponent)Parent;
var parent = Parent;
parent.CurrentComponentState = ComponentState.UpToDate;
@@ -355,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;
}
Root = await receiveComponent
.ReceiveOperation.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
if (CancellationToken.IsCancellationRequested)
{
return;
}
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
LocalToGlobalUnpacker localToGlobalUnpacker = new();
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = receiveComponent.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 receiveComponent.MixPanelManager.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())
{
@@ -486,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
@@ -544,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...";
@@ -580,14 +567,21 @@ public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
return GH_ObjectResponse.Handled;
}
if (((ReceiveAsyncComponent)Owner).AutoReceive)
// NOTE: why do we kill auto receive when clicking on the button?
// It's enabled via the context menu, I expect it to be disabled from the same place
// if (((ReceiveAsyncComponent)Owner).AutoReceive)
// {
// ((ReceiveAsyncComponent)Owner).AutoReceive = false;
// Owner.OnDisplayExpired(true);
// return GH_ObjectResponse.Handled;
// }
// TODO: check if owner has null account/client, and call the reset thing SYNC
if ((Owner as ReceiveAsyncComponent)?.MultipleResources == true)
{
((ReceiveAsyncComponent)Owner).AutoReceive = false;
Owner.OnDisplayExpired(true);
return GH_ObjectResponse.Handled;
}
// TODO: check if owner has null account/client, and call the reset thing SYNC
((ReceiveAsyncComponent)Owner).CurrentComponentState = ComponentState.Ready;
Owner.ExpireSolution(true);
return GH_ObjectResponse.Handled;
@@ -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;
@@ -35,10 +34,8 @@ public class ReceiveComponentOutput
public SpeckleCollectionWrapperGoo RootObject { get; set; }
}
public class ReceiveComponent : SpeckleScopedTaskCapableComponent<ReceiveComponentInput, ReceiveComponentOutput>
public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInput, ReceiveComponentOutput>
{
private readonly IMixPanelManager _mixpanel;
public ReceiveComponent()
: base(
"(Sync) Load",
@@ -46,10 +43,7 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<ReceiveCompone
"Load a model from Speckle, synchronously",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.DEVELOPER
)
{
_mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
}
) { }
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
protected override Bitmap Icon => Resources.speckle_operations_syncload;
@@ -99,29 +93,34 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<ReceiveCompone
}
}
protected override async Task<ReceiveComponentOutput> PerformScopedTask(
#pragma warning disable CA1506
protected override async Task<ReceiveComponentOutput> PerformTask(
#pragma warning restore CA1506
ReceiveComponentInput input,
IServiceScope scope,
CancellationToken cancellationToken = default
)
{
var multipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
if (multipleResources)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
"Only one model can be loaded at a time. To load to multiple models, please use different load components."
);
return new();
}
if (!input.Run)
{
return new();
}
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
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");
@@ -154,36 +153,44 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<ReceiveCompone
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
}
await _mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
// 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,24 +16,15 @@ using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
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>
{
private ResourceCollection<Project>? LastFetchedProjects { get; set; }
private ResourceCollection<Model>? LastFetchedModels { get; set; }
public GhContextMenuButton ProjectContextMenuButton { get; set; }
public GhContextMenuButton ModelContextMenuButton { get; set; }
private ToolStripDropDown? ProjectDropDown { get; set; }
private ToolStripDropDown? ModelDropDown { get; set; }
public SendAsyncComponent()
: base(
"Publish",
@@ -50,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; }
@@ -57,13 +49,12 @@ public class SendAsyncComponent : GH_AsyncComponent
public double OverallProgress { get; set; }
public string? Url { get; set; }
public IClient ApiClient { get; set; }
public IMixPanelManager MixPanelManager { get; set; }
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
public SpeckleUrlModelResource? OutputParam { get; set; }
public SendOperation<SpeckleCollectionWrapperGoo> SendOperation { get; private set; }
public static IServiceScope? Scope { get; set; }
public bool HasMultipleInputs { get; set; }
public string? VersionMessage { get; private set; }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -75,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)
@@ -139,17 +132,35 @@ public class SendAsyncComponent : GH_AsyncComponent
protected override void SolveInstance(IGH_DataAccess da)
{
// Dependency Injection
Scope = PriorityLoader.Container.CreateScope();
SendOperation = Scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
var multipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
var multipleCollections = Params.Input[1].VolatileData.HasInputCountGreaterThan(1);
MixPanelManager = Scope.ServiceProvider.GetRequiredService<IMixPanelManager>();
var accountService = Scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = Scope.ServiceProvider.GetRequiredService<IAccountManager>();
var clientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
HasMultipleInputs = multipleCollections || multipleResources;
if (HasMultipleInputs)
{
var mCollErrText =
"Only one single collection supported. Please group your input collections into one single one before sending.";
var mLinksErrText =
"Only one single model can be published to from this node. To send to multiple models, please use different publish components.";
if (multipleCollections)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mCollErrText);
}
if (multipleResources)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mLinksErrText);
}
return;
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
// 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)
@@ -181,13 +192,12 @@ public class SendAsyncComponent : GH_AsyncComponent
public override void RemovedFromDocument(GH_Document document)
{
RequestCancellation();
Scope?.Dispose();
base.RemovedFromDocument(document);
}
public override void DisplayProgress(object sender, ElapsedEventArgs e)
{
if (Workers.Count == 0)
if (WorkerCount == 0)
{
return;
}
@@ -229,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);
@@ -248,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())
{
@@ -274,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)
@@ -299,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;
}
@@ -321,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)
@@ -345,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)
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}";
});
SendOperationResult? result = await sendComponent
.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);
}
await sendComponent.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())
{
@@ -442,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
@@ -485,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,
@@ -495,7 +492,6 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
);
autoSendButton.Render(graphics, Selected, Owner.Locked, false);
autoSendButton.Dispose();
}
else
{
@@ -506,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,
@@ -515,7 +511,6 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
state == ComponentState.Expired ? 10 : 0
);
button.Render(graphics, Selected, Owner.Locked, false);
button.Dispose();
}
}
}
@@ -526,6 +521,11 @@ public class SendAsyncComponentAttributes : GH_ComponentAttributes
{
if (((RectangleF)ButtonBounds).Contains(e.CanvasLocation))
{
// Ignore button presses on a component with invalid inputs to prevent multiple sends
if ((Owner as SendAsyncComponent)?.HasMultipleInputs == true)
{
return GH_ObjectResponse.Handled;
}
if (((SendAsyncComponent)Owner).AutoSend)
{
((SendAsyncComponent)Owner).AutoSend = false;
@@ -34,10 +34,8 @@ public class SendComponentOutput(SpeckleUrlModelResource? resource)
public SpeckleUrlModelResource? Resource { get; } = resource;
}
public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInput, SendComponentOutput>
public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, SendComponentOutput>
{
private readonly IMixPanelManager _mixpanel;
public SendComponent()
: base(
"(Sync) Publish",
@@ -45,15 +43,14 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
"Publish a collection to Speckle, synchronously",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.DEVELOPER
)
{
_mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
}
) { }
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
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)
@@ -66,7 +63,8 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
"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);
}
@@ -91,8 +89,12 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
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);
}
@@ -129,27 +131,46 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
}
}
protected override async Task<SendComponentOutput> PerformScopedTask(
protected override async Task<SendComponentOutput> PerformTask(
SendComponentInput input,
IServiceScope scope,
CancellationToken cancellationToken = default
)
{
var multipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
var multipleCollections = Params.Input[1].VolatileData.HasInputCountGreaterThan(1);
var hasMultipleInputs = multipleCollections || multipleResources;
if (hasMultipleInputs)
{
var mCollErrText =
"Only one single collection supported. Please group your input collections into one single one before sending.";
var mLinksErrText =
"Only one single model can be published to from this node. To send to multiple models, please use multiple publish components.";
if (multipleCollections)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mCollErrText);
}
if (multipleResources)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mLinksErrText);
}
return new(null);
}
if (!input.Run)
{
return new(null);
}
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
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");
@@ -163,8 +184,14 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
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
@@ -173,18 +200,18 @@ public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInpu
{
customProperties.Add("workspace_id", sendInfo.WorkspaceId);
}
await _mixpanel.TrackEvent(MixPanelEvents.Send, account, customProperties);
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Send, account, customProperties);
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,18 +102,19 @@ 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)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
da.AbortComponentSolution();
}
return; // Fast exit!
}
@@ -154,6 +158,7 @@ public class SpeckleSelectModelComponent : GH_Component
ProjectContextMenuButton.Enabled = false;
ModelContextMenuButton.Enabled = false;
VersionContextMenuButton.Enabled = false;
da.AbortComponentSolution();
return;
}
@@ -172,6 +177,7 @@ public class SpeckleSelectModelComponent : GH_Component
{
// Create a workspace flow
SpeckleOperationWizard.CreateNewWorkspaceUIState();
da.AbortComponentSolution();
return;
}
}
@@ -191,6 +197,7 @@ public class SpeckleSelectModelComponent : GH_Component
}
else
{
da.AbortComponentSolution();
return;
}
}
@@ -212,6 +219,8 @@ public class SpeckleSelectModelComponent : GH_Component
{
ModelContextMenuButton.Enabled = false;
VersionContextMenuButton.Enabled = false;
da.AbortComponentSolution();
return;
}
@@ -231,6 +240,7 @@ public class SpeckleSelectModelComponent : GH_Component
if (SpeckleOperationWizard.SelectedModel == null)
{
VersionContextMenuButton.Enabled = false;
da.AbortComponentSolution();
return;
}
@@ -250,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
@@ -264,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,
@@ -279,10 +295,12 @@ public class SpeckleSelectModelComponent : GH_Component
GH_RuntimeMessageLevel.Error,
string.Join("\n", e.InnerExceptions.Select(innerE => innerE.Message))
);
da.AbortComponentSolution();
}
catch (Exception e) when (!e.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
da.AbortComponentSolution();
}
}
@@ -316,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);
@@ -16,6 +16,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 +39,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 +63,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 +108,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,12 +121,6 @@ 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
@@ -135,8 +132,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;
};
}
@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -26,6 +26,7 @@ public class SpeckleOperationWizard
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; }
@@ -49,7 +50,7 @@ public class SpeckleOperationWizard
var userSelectedAccountId = _accountService.GetUserSelectedAccountId();
Accounts = _accountManager.GetAccounts().ToList();
SelectedAccount = userSelectedAccountId == null ? null : _accountManager.GetAccount(userSelectedAccountId);
SelectedAccount = Accounts.FirstOrDefault(a => a.id == userSelectedAccountId);
WorkspaceMenuHandler = new WorkspaceMenuHandler(FetchWorkspaces, CreateNewWorkspace);
ProjectMenuHandler = new ProjectMenuHandler(FetchProjects);
@@ -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");
@@ -197,7 +202,7 @@ public class SpeckleOperationWizard
{
return;
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var workspace = client.Workspace.Get(workspaceId).Result;
SelectedWorkspace = workspace;
WorkspaceMenuHandler.RedrawMenuButton(SelectedWorkspace);
@@ -209,7 +214,7 @@ public class SpeckleOperationWizard
{
return;
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var project = client.Project.Get(projectId).Result;
SelectedProject = project;
ProjectMenuHandler.RedrawMenuButton(SelectedProject);
@@ -222,7 +227,7 @@ public class SpeckleOperationWizard
return;
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var model = client.Model.Get(modelId, SelectedProject.id).Result;
SelectedModel = model;
ModelMenuHandler.RedrawMenuButton(SelectedModel);
@@ -235,10 +240,10 @@ public class SpeckleOperationWizard
return;
}
IClient client = _clientFactory.Create(SelectedAccount);
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>
@@ -251,7 +256,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<Workspace>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var workspaces = await client.ActiveUser.GetWorkspaces(10, null, new UserWorkspacesFilter(searchText));
WorkspaceMenuHandler.Workspaces = workspaces;
return workspaces;
@@ -267,7 +272,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<Workspace>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var workspaces = client.ActiveUser.GetWorkspaces(10, null, new UserWorkspacesFilter(searchText)).Result;
WorkspaceMenuHandler.Workspaces = workspaces;
return workspaces;
@@ -283,7 +288,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<ProjectWithPermissions>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var workspaceId = SelectedWorkspace?.id ?? null;
var projects = await client.ActiveUser.GetProjectsWithPermissions(
10,
@@ -309,7 +314,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<ProjectWithPermissions>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var workspaceId = SelectedWorkspace?.id ?? null;
var projects = client
.ActiveUser.GetProjectsWithPermissions(
@@ -337,7 +342,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<Model>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var projectWithModels = await client
.Project.GetWithModels(SelectedProject.id, 10, modelsFilter: new ProjectModelsFilter(search: searchText))
.ConfigureAwait(true);
@@ -373,7 +378,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<Version>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var newVersionsResult = await client
.Model.GetWithVersions(SelectedModel.id, SelectedProject.id, versionCount)
.ConfigureAwait(true);
@@ -395,7 +400,7 @@ public class SpeckleOperationWizard
return new ResourceCollection<Version>();
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var newVersionsResult = client.Model.GetWithVersions(SelectedModel.id, SelectedProject.id, versionCount).Result;
if (VersionMenuHandler != null)
{
@@ -411,7 +416,7 @@ public class SpeckleOperationWizard
{
return;
}
IClient client = _clientFactory.Create(SelectedAccount);
using IClient client = _clientFactory.Create(SelectedAccount);
var activeWorkspace = client.ActiveUser.GetActiveWorkspace().Result;
Workspace? selectedWorkspace =
SelectedWorkspace
@@ -438,13 +443,14 @@ public class SpeckleOperationWizard
private void OnModelSelected(object sender, ModelSelectedEventArgs e)
{
SelectedModel = e.SelectedModel;
ResetVersions();
ResetVersions(true);
_refreshComponent.Invoke();
}
private void OnVersionSelected(object sender, VersionSelectedEventArgs e)
{
SelectedVersion = e.SelectedVersion;
IsLatestVersion = e.IsLatest;
_refreshComponent.Invoke();
}
@@ -469,10 +475,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());
}
}

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