Compare commits

..

174 Commits

Author SHA1 Message Date
Jedd Morgan 2c13c4ff79 Merge pull request #1086 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev -> main
2025-09-10 17:47:31 +01:00
Jedd Morgan 4bf7fc9ce1 Update dev with main for release (do not squash) (#1087) 2025-09-10 17:35:48 +01:00
Jedd Morgan 7e0014bdcc Merge branch 'main' into jrm/dev-main3 2025-09-10 17:33:13 +01:00
Björn Steinhagen b695a95032 fix(grasshopper): clear Base.id to force serializer recomputation of mutations (#1084)
* fix: clear `Base.id` to force serializer recomputation of mutations

* fix: same treatment for blocks

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-10 17:07:22 +01:00
Björn Steinhagen fa1a6d0ac2 fix: catch non-fatal exceptions when extracting workset properties from linked models (#1085)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-10 16:53:37 +01:00
Björn Steinhagen a42c8bd825 fix: early return and safety guard to ExpandSpeckleProperties parameter creation (#1083) 2025-09-10 16:48:13 +01:00
Björn Steinhagen 9ef3768845 fix: FilterSpeckleObjects material and property key filtering (#1079) 2025-09-09 14:09:21 +01:00
Björn Steinhagen 94f04c9aeb fix(grasshopper): working with SpeckleProperties list values (#1077)
* fix: preserve GH_ParamAccess types during parameter discovery in DeconstructSpeckleParam

* chore: unnecessary string interpolation

* feat: `ExpandSpeckleProperties` component

* adds icon

* chore: cleanup

* chore: capital P

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-09 08:16:03 +01:00
Jedd Morgan 0dcd9b2626 feat(rhino-importer): Implement compute budget and align retrying policy with py (#1074)
* first pass

* Jedd and Iains changes

* re-queue the job if we can't report it as failed

* log context

* Add file type to scoped context
2025-09-08 16:26:53 +01:00
Adam Hathcock 475a76f765 fix(sdk) move the logging addition for connectors (#1059)
* move the logging addition for connectors

* use SetMinimumLevel on both sides of the ilmerge barrier
2025-09-08 15:14:43 +00:00
dependabot[bot] b79c547027 chore(deps): bump actions/setup-dotnet from 4 to 5 (#1078)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 5.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 15:41:54 +01:00
Claire Kuang f09e60fe02 fix(grasshopper): updates sample scripts (#1076)
* updates sample scripts

* adds text
2025-09-08 14:08:44 +00:00
Björn Steinhagen b708d2d265 fix(grasshopper): QuerySpeckleObjects crashes with type-specific outputs when path is set (#1072)
* fix: `QuerySpeckleObjects` index out of range with type-specific outputs and path filtering

* chore: unnecessary using statement

* refactor: simplify ternary operator chain to if statements

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-08 15:49:17 +02:00
Björn Steinhagen 66302f5ab3 fix: collection order inconsistency in RootObjectUnpacker (#1075)
* fix: collection order inconsistency in RootObjectUnpacker

* fix: adam

* refactor: use List instead of HashSet in SplitAtomicObjectsAndInstances

* fix: no need for .ToArray()

* chore: sdk version bump
2025-09-08 13:14:37 +02:00
Adam Hathcock 7f343596fc Use FirstOrDefault more (#1073) 2025-09-05 13:09:07 +01:00
Adam Hathcock 3f74a7aa3e use FirstOrDefault again (#1069)
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-09-04 15:59:32 +00:00
Björn Steinhagen d63b6604fc feat(grasshopper): add extract parameter functionality (#1070) 2025-09-04 16:24:54 +02:00
Jedd Morgan 66c73e17bf Merge pull request #1066 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev to main
2025-09-03 16:21:40 +01:00
Jedd Morgan f03c4c00a3 Merge pull request #1068 from specklesystems/jrm/back-merge
Main -> Dev backmerge
2025-09-03 16:17:28 +01:00
Jedd Morgan 607c5dc58d Merge branch 'dev' into jrm/back-merge 2025-09-03 16:16:31 +01:00
Adam Hathcock dc94724800 fix(Revit) - Getting a key on exception could result in null reference exceptions (#1064)
* Getting a key on exception could result in null reference exceptions

* Unique id does not exist?

* Use CreationGUID which isn't null

* add comment

* Generate keys differently

* more robust solution that doesn't use hashcode
2025-09-03 16:13:39 +01:00
Adam Hathcock 62835613e6 Never use First() as it can be missing (#1065) 2025-09-03 15:32:15 +01:00
Jedd Morgan 6f88da92bb Skp import config (#1061) 2025-09-02 17:31:53 +01:00
Jedd Morgan 083548b33c Add metrics to rhino Importer (#1063) 2025-09-02 17:31:28 +01:00
Jedd Morgan 20bc675ad2 Disable 3dm config due to bug in OpenHeadless (#1062) 2025-09-02 15:54:33 +00:00
Claire Kuang fe3d4e5544 feat(rhino): adds user dictionary to object props (#1046)
* adds user dicts to rhino

* defaults to string value

---------

Co-authored-by: Björn <steinhagen.bjoern@gmail.com>
2025-09-02 15:56:56 +01:00
Björn Steinhagen ff5cdf47df feat(etabs): add result extraction with UI integration (#1044)
* feat: poc hack

- just send some results as Base to serve as a discussion point

* refactor: column forces extraction class

* feat: column forces compound keys

* feat: basic check if results available

* Revert "Merge remote-tracking branch 'origin/dev' into bjorn/properties-curation-structural-connectors-analysis-results"

This reverts commit 4b88fc150f, reversing
changes made to 855240b713.

* Reapply "Merge remote-tracking branch 'origin/dev' into bjorn/properties-curation-structural-connectors-analysis-results"

This reverts commit 57f66dea7b.

* feat (etabs): multi-selectable dropdowns for analysis result (#1019)

* integrated ui components

* populates the dropdown

* format

* removed filtering logic

* feat(etabs): replace database table extraction with direct Results API for analysis results (#1024)

* feat: first steps in linking ui to results extractor

* refactor:  simple frame force extractor

* refactor: flexible extractor

* chore: cleanup

* refactor: computed property

* feat(etabs): add UI integration for dynamic result type selection (#1025)

* refactor: linking up results type

* fix: send settings

* feat(etabs): adds more extractors (#1026)

* feat: adds `BaseReact` extractor

* refactor: repeating strings under constants

* fix: array processing only

* feat: adds `PierForce`extractor

* feat: adds `SpandrelForce` extractor

* feat: adds `StoryDrifts` extractor

* fix: missing key in selection shouldn't throw

* feat: adds `JointReact` extractor

* refactor(etabs): improve load case validation and error handling

* fix: case status validation

* fix(etabs): correct Zip method syntax for load case validation

* refactor(etabs): simplify validation by throwing `SpeckleException`inside `LoadCaseManager`

* refactor: add unit information

---------

Co-authored-by: Dogukan Karatas <61163577+dogukankaratas@users.noreply.github.com>
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-02 11:39:36 +02:00
Jedd Morgan c04cdacef9 chore(fileImport): Jedd/cnx 2444 rename rhino file importer to something else (#1060)
* Workaround for rhino materials

* update file importer name and slug

* fix

* Use host app name

* delete empty file
2025-09-01 15:58:27 +00:00
Jedd Morgan ee32f320ee Workaround for rhino materials (#1058) 2025-09-01 18:50:34 +03:00
Björn Steinhagen 57ede4cabd fix(revit): preserve selection intent while maintaining dynamic filter behavior (#1054)
* fix: poc hackady hack-hack

* docs: comments

* refactor: simpler
2025-09-01 11:09:28 +02:00
Iain Sproat dc575a9f78 Merge pull request #1056 from specklesystems/iain/web-4118-timing-out-the-background-job-does-not-time-out-the
fix(importer/rhino): server handles terminal job status transitions
2025-08-29 15:16:45 +01:00
Jedd Morgan 41e1faf655 Merge branch 'dev' into iain/web-4118-timing-out-the-background-job-does-not-time-out-the 2025-08-29 14:46:36 +01:00
Björn Steinhagen e4b26c92fd feat: publish blocks respecting hidden objects (#1057) 2025-08-29 15:10:42 +02:00
Jedd Morgan 12ea8a4597 Format 2025-08-29 10:41:25 +01:00
Jedd Morgan 0023ab1622 feat(rhinoImporter): Fbx config (#1055)
* Add fbx config

* actually use it
2025-08-29 08:38:46 +01:00
Iain Sproat c5a6208f7e fix typo 2025-08-29 08:33:15 +01:00
Iain Sproat 35a185c65f fix(importer/rhino): server handles terminal job status transitions
- do not pick up jobs which have exceeded max attempts
2025-08-29 08:19:45 +01:00
Jedd Morgan 1796cacee6 Merge pull request #1043 from specklesystems/jedd/cnx-2245-importing-3dm-files-causes-fatal-application-crash-on
refactor(rhino-importer): Run rhino as a sub process to avoid file locking and improve handling of crashes
2025-08-28 16:18:49 +01:00
Björn Steinhagen 9f99cb593f fix(revit): improve error messages when publishing fails (#1053)
* chore: some more descriptive logging?

* feat: display the message

* chore: updated comment
2025-08-28 14:48:04 +02:00
Adam Hathcock df4c065dfe fix(all) Progress the final sqlite message....can still take a while (#1042)
* Progress the final sqlite message....can still take a while

* Update SDK to alpha for testing

* fixes speed tests

* Add more tests from AI

* add more detail to progress message

* Fixed tests to make sense

* Update SDK to 3.5.2
2025-08-28 09:48:33 +00:00
Björn Steinhagen edb022f7c5 fix(revit): prevent null reference when extracting workset properties from linked model elements (#1052) 2025-08-27 11:44:38 +02:00
Jedd Morgan 27a7d72de3 Merge pull request #1051 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev -> main
2025-08-26 17:20:06 +01:00
Jedd Morgan 678f113d05 Merge pull request #1050 from specklesystems/jrm/main-to-dev2
Update dev with main
2025-08-26 16:54:05 +01:00
Jedd Morgan 92da66bbbb Merge branch 'dev' into jrm/main-to-dev2 2025-08-26 16:53:25 +01:00
Oğuzhan Koral 79a5228899 Fix: invert boolean flag (#1049)
* Introduce global config

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

* fmt

* fixes logger

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

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

---------

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

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

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

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

* commit transaction

* wip1

* rhino round2

* wip

* net8

* Got the importer importing!

* Refactor to separate containers

* New queuing queries

* generate solutions

* Generate solutions

* fixed tests

* Rhino headless imports

* minor fixes

* logging

* fix activity factory

* sketchup configs

* Add more logging

* Format

* Clean up the diff a bit

* relock

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

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

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

* feat: object-layer mapping detection for UI updates

* feat: add layer hierarchy mapping resolution

* refactor: unused methods

* feat: layer dropdown

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

* fix: event handling

* feat: generic model, duhh

* refactor: move records to mapper namespace

* refactor: consolidate Rhino layer and object utilities into helpers

* refactor: move `GetEffectiveObjectsForLayerMapping` to `RevitMappingResolver`

* chore: update category list (#1028)

* fix:  `ModifyAttributes` for object mapping changes

* fix: lol no need for static

* refactor: DI for helper class

* refactor: hardcoded list in dui

* fix: updating mapped layers

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

* feat: poc (#1030)

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

* chore: send settings

* chore: converter reacts to setting

* fix: knock-on effect

* fix: format

* fix: importer needs param

* feat: adds seperate setting for sendTextureCoordinates

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

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

* feat: revitmapper rhino connector binding (#1016)

* chore: `RhinoMapperBinding` class

* chore: `RhinoMapperBinding` structure

* chore: implement `GetAvailableCategories`

* chore: implement `AssignToCategory`

* refactor: common code to helper method

* chore: implement `ClearAllCategoryAssignments`

* chore: implement `GetCurrentMappings`

* chore: implement `GetObjectsByCategory`

* chore: implement event handling

* fix: compiler errors

* chore: service registration

* docs: cleanup

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

* fix: filter mapper events to only mapped objects

* refactor: simplify RhinoMapperBinding following existing patterns

* chore: remove unused method

* fix: add DocumentModelStore dependency for event handling

* refactor: mapper store

* fix: list sorted alphabetically

* fix: refresh mapper table on document switch

* chore: note

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

* fix: types

* chore: SDK version bump

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

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

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

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

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

* adds block def casting for non-baked definitions also

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

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

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

* update SDK to 3.4.6

* add SpeckleSolveInstance

* fix SDK update

* Update SDK to 3.4.8

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

* fixes material wrapper casting

---------

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

* fixed compile

* format

* fix build!

* Fix tests

* Use strings

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

* renamed function

* review comments

* cleanup

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

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

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

* Avoids processing empty property sets

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

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

* Uncomments continue statement.

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

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

* Rework to not have optional parameters

---------

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

* adjust things to not require SDK changes

* something like this

* rhino importer cli sketch

* fix deps and solutions

* things build

* move to files

* change signatures of things to not require sendinfo or accounts

* formatting

* Fix test

* Reuse some account

* Fix logging and possible error

* formatting

* add active doc disposal

* add global try/catch

* merge fix

* add rhino importer

* add SLN and use it

* have to put back the extension rename

* SDK update

* Try out loading the plugin manually

* don't need the mac SLN

* fix lock

* fix lock again?

* Use the location of the assembly, not current

* Fix lock file

* fix lock on windows

---------

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

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

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

* Update Speckle.Converters.RhinoShared.projitems

* merge conflict resolutions
2025-07-11 16:03:41 +01:00
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
400 changed files with 16523 additions and 5157 deletions
+4 -4
View File
@@ -7,12 +7,12 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -28,12 +28,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
+4 -4
View File
@@ -16,12 +16,12 @@ jobs:
file_version: ${{ steps.set-version.outputs.file_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -83,12 +83,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
-20
View File
@@ -81,7 +81,6 @@ public static class Solutions
var revit = Consts.ProjectGroups.Single(x => x.HostAppSlug.Equals("revit"));
await GenerateConnector(connectors, revit, "Revit.Local");
await GenerateMacSolutions();
}
public static async Task GenerateConnector(SolutionModel connectors, ProjectGroup group, string? name)
@@ -115,23 +114,4 @@ public static class Solutions
var connectorsSln = Path.Combine(DIRECTORY, solutionName);
return await SolutionSerializers.SlnFileV12.OpenAsync(connectorsSln, default);
}
public static async Task GenerateMacSolutions()
{
var connectors = await GetFullSlnx();
var foldersToRemove = connectors
.SolutionFolders.Where(x =>
//need base folder
!x.Path.Equals("/Connectors/")
//don't grab all
&& (x.Path.StartsWith("/Connectors/"))
)
.ToList();
foreach (var folderToRemove in foldersToRemove)
{
connectors.RemoveFolder(folderToRemove);
}
var sln = Path.Combine(DIRECTORY, $"Speckle.Connectors.Mac.slnx");
await SolutionSerializers.SlnXml.SaveAsync(sln, connectors, default);
}
}
+6 -6
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinAutocad" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildAutoCAD" Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<AutoCADDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
@@ -12,11 +12,11 @@
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Autocad$(AutoCADVersion)\%(RecursiveDir)" SourceFiles="@(AutoCADDLLs)" />
</Target>
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildCivil3D" Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<Civil3DDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
@@ -24,11 +24,11 @@
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Civil3d$(Civil3DVersion)\%(RecursiveDir)" SourceFiles="@(Civil3DDLLs)" />
</Target>
<PropertyGroup Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<PropertyGroup Condition="'$(AutoCADVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(AutoCADVersion)\acad.exe</StartProgram>
</PropertyGroup>
<PropertyGroup Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<PropertyGroup Condition="'$(Civil3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
<StartArguments>/product C3D</StartArguments>
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -293,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -2,6 +2,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
namespace Speckle.Connectors.Autocad.Bindings;
@@ -17,7 +18,7 @@ public abstract class AutocadReceiveBaseBinding(
private ReceiveBindingUICommands Commands { get; } = new(parent);
protected abstract void InitializeSettings(IServiceProvider serviceProvider);
protected abstract void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc);
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
@@ -3,6 +3,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
@@ -24,7 +25,7 @@ public sealed class AutocadReceiveBinding : AutocadReceiveBaseBinding
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
protected override void InitializeSettings(IServiceProvider serviceProvider)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Instances;
using Speckle.Converters.AutocadShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.DoubleNumerics;
using Speckle.Sdk;
@@ -17,16 +18,19 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
{
private readonly IHostToSpeckleUnitConverter<UnitsValue> _unitsConverter;
private readonly IInstanceObjectsManager<AutocadRootObject, List<Entity>> _instanceObjectsManager;
private readonly IPropertiesExtractor _propertiesExtractor;
private readonly ILogger<AutocadInstanceUnpacker> _logger;
public AutocadInstanceUnpacker(
IHostToSpeckleUnitConverter<UnitsValue> unitsConverter,
IInstanceObjectsManager<AutocadRootObject, List<Entity>> instanceObjectsManager,
IPropertiesExtractor propertiesExtractor,
ILogger<AutocadInstanceUnpacker> logger
)
{
_unitsConverter = unitsConverter;
_instanceObjectsManager = instanceObjectsManager;
_propertiesExtractor = propertiesExtractor;
_logger = logger;
}
@@ -71,6 +75,13 @@ public class AutocadInstanceUnpacker : IInstanceUnpacker<AutocadRootObject>
transform = GetMatrix(instance.BlockTransform.ToArray()),
units = _unitsConverter.ConvertOrThrow(Application.DocumentManager.CurrentDocument.Database.Insunits)
};
var properties = _propertiesExtractor.GetProperties(instance);
if (properties?.Count > 0)
{
instanceProxy["properties"] = properties;
}
_instanceObjectsManager.AddInstanceProxy(instanceId, instanceProxy);
// For each block instance that has the same definition, we need to keep track of the "maximum depth" at which is found.
@@ -60,7 +60,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
)]
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -95,28 +95,19 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using (var convertActivity = _activityFactory.Start("Converting object"))
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
sendInfo.ProjectId
);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(entity, applicationId, objectCollection, instanceProxies, projectId);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
if (results.All(x => x.Status == Status.ERROR))
@@ -3,6 +3,15 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{41BC679F-887F-44CF-971D-A5502EE87DB0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Local|AnyCPU' ">
<OutputPath>bin\Local\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"/>
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -4,6 +4,7 @@ using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
@@ -31,7 +32,7 @@ public sealed class Civil3dReceiveBinding : AutocadReceiveBaseBinding
// POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters.
// POC: We need a separate receive binding for civil3d due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider)
protected override void InitializeSettings(IServiceProvider serviceProvider, ModelCard mc)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Civil3dConversionSettings>>()
@@ -96,7 +96,7 @@ public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
var typeKey = (ModelObjectType)objectType[i];
var typeName = typeKey.ToString();
encodedIds.Add(ObjectIdentifier.Encode(objectType[i], objectName[i]));
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1; // NOTE: Cross-framework compatibility (net 48 and net8)
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1;
}
var summary =
encodedIds.Count == 0
@@ -1,6 +1,8 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
using Speckle.Connectors.CSiShared.Settings;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -23,6 +25,7 @@ public sealed class CsiSharedSendBinding : ISendBinding
private readonly ICsiApplicationService _csiApplicationService;
private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
public CsiSharedSendBinding(
IBrowserBridge parent,
@@ -30,7 +33,8 @@ public sealed class CsiSharedSendBinding : ISendBinding
ICancellationManager cancellationManager,
ICsiConversionSettingsFactory csiConversionSettingsFactory,
ICsiApplicationService csiApplicationService,
ISendOperationManagerFactory sendOperationManagerFactory
ISendOperationManagerFactory sendOperationManagerFactory,
ToSpeckleSettingsManager toSpeckleSettingsManager
)
{
_sendFilters = sendFilters.ToList();
@@ -40,11 +44,13 @@ public sealed class CsiSharedSendBinding : ISendBinding
_csiConversionSettingsFactory = csiConversionSettingsFactory;
_csiApplicationService = csiApplicationService;
_sendOperationManagerFactory = sendOperationManagerFactory;
_toSpeckleSettingsManager = toSpeckleSettingsManager;
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
public List<ICardSetting> GetSendSettings() => [];
public List<ICardSetting> GetSendSettings() =>
[new LoadCaseCombinationSetting([], _csiApplicationService.SapModel), new ResultTypeSetting([])];
public async Task Send(string modelCardId)
{
@@ -52,9 +58,17 @@ public sealed class CsiSharedSendBinding : ISendBinding
await manager.Process(
Commands,
modelCardId,
(sp, _) =>
(sp, card) =>
{
sp.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel)),
.Initialize(
_csiConversionSettingsFactory.Create(
_csiApplicationService.SapModel,
_toSpeckleSettingsManager.GetLoadCasesAndCombinations(card),
_toSpeckleSettingsManager.GetResultTypes(card)
)
);
},
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList()
);
}
@@ -0,0 +1,27 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
public class CsiResultsExtractorFactory
{
private readonly IServiceProvider _serviceProvider;
public CsiResultsExtractorFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IApplicationResultsExtractor GetExtractor(string resultsKey) =>
resultsKey switch
{
ResultsKey.BASE_REACT => _serviceProvider.GetRequiredService<CsiBaseReactResultsExtractor>(),
ResultsKey.FRAME_FORCES => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
ResultsKey.JOINT_REACT => _serviceProvider.GetRequiredService<CsiJointReactResultsExtractor>(),
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
};
}
@@ -4,9 +4,11 @@ using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Extensions;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
@@ -37,6 +39,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
private readonly AnalysisResultsExtractor _analysisResultsExtractor;
public CsiRootObjectBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
@@ -46,7 +49,8 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService
ICsiApplicationService csiApplicationService,
AnalysisResultsExtractor analysisResultsExtractor
)
{
_converterSettings = converterSettings;
@@ -57,6 +61,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_analysisResultsExtractor = analysisResultsExtractor;
}
/// <summary>
@@ -70,7 +75,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
/// </remarks>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -89,8 +94,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using var _2 = _activityFactory.Start("Convert");
var result = ConvertCsiObject(csiObject, rootObjectCollection);
results.Add(result);
@@ -114,6 +117,34 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
// Extract analysis results (if applicable)
// NOTE: objectSelectionSummary used to extract results for objects being published ONLY
// NOTE: etabs is complicated and we can't get specifics from original selection
var objectSelectionSummary = GetObjectSummary(csiObjects);
var selectedCasesAndCombinations = _converterSettings.Current.SelectedLoadCasesAndCombinations;
var requestedResultTypes = _converterSettings.Current.SelectedResultTypes;
if (selectedCasesAndCombinations?.Count > 0)
{
if (requestedResultTypes == null || requestedResultTypes.Count == 0)
{
throw new SpeckleException(
"No result type input for the requested load cases and combinations. Adjust publish settings."
);
}
if (!_csiApplicationService.SapModel.GetModelIsLocked())
{
throw new SpeckleException("Model unlocked. No access to analysis results.");
}
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
selectedCasesAndCombinations,
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection["analysisResults"] = analysisResults;
}
return new RootObjectBuilderResult(rootObjectCollection, results);
}
@@ -160,4 +191,22 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
/// <summary>
/// Generates a summary of object types and their associated names from the collection of CSI wrappers.
/// </summary>
/// <remarks>
/// A summary of object names for each object type is needed for getting analysis results of the selected objects only.
/// During object conversion, however, we lose the selection (like a clear selection)(presumably because of other api calls).
/// This has to be recreated since GetSelection() return type is bound by the interface.
/// The LINQ-based implementation is computationally inexpensive as it operates on an already-loaded collection without additional API calls.
/// Also, we don't want to rely on user selection remaining active, what if someone re-publishes using model card cache?
/// </remarks>
private Dictionary<ModelObjectType, List<string>> GetObjectSummary(IReadOnlyList<ICsiWrapper> csiObjects) =>
csiObjects
.GroupBy(csiObject => csiObject.ObjectType)
.ToDictionary(
group => group.Key, // ModelObjectType (FRAME, JOINT, etc.)
group => group.Select(obj => obj.Name).ToList() // Extract Name from each ICsiWrapper and convert to List<string>
);
}
@@ -0,0 +1,73 @@
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.InterfaceGenerator;
using Speckle.Newtonsoft.Json.Linq;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.CSiShared.Operations.Send.Settings;
[GenerateAutoInterface]
public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
{
private readonly ISendConversionCache _sendConversionCache;
private readonly Dictionary<string, List<string>?> _loadCaseCombinationCache = new();
private readonly Dictionary<string, List<string>?> _resultTypeCache = new();
public ToSpeckleSettingsManager(ISendConversionCache sendConversionCache)
{
_sendConversionCache = sendConversionCache;
}
public List<string> GetLoadCasesAndCombinations(SenderModelCard modelCard)
{
var setting = modelCard.Settings?.FirstOrDefault(s => s.Id == "loadCasesAndCombinations");
var returnValue = (setting?.Value as JArray)?.Select(x => x.ToString()).ToList() ?? [];
if (_loadCaseCombinationCache.TryGetValue(modelCard.ModelCardId.NotNull(), out List<string>? previousValue))
{
if (!AreListsEqual(previousValue, returnValue))
{
EvictCacheForModelCard(modelCard);
}
}
_loadCaseCombinationCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public List<string> GetResultTypes(SenderModelCard modelCard)
{
var setting = modelCard.Settings?.FirstOrDefault(s => s.Id == "resultTypes");
var returnValue = (setting?.Value as JArray)?.Select(x => x.ToString()).ToList() ?? [];
if (_resultTypeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out List<string>? previousValue))
{
if (!AreListsEqual(previousValue, returnValue))
{
EvictCacheForModelCard(modelCard);
}
}
_resultTypeCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private static bool AreListsEqual(List<string>? list1, List<string>? list2)
{
if (list1 == null && list2 == null)
{
return true;
}
if (list1 == null || list2 == null)
{
return false;
}
return list1.Count == list2.Count && list1.OrderBy(x => x).SequenceEqual(list2.OrderBy(x => x));
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().RefreshObjectIds() : [];
_sendConversionCache.EvictObjects(objectIds);
}
}
@@ -8,6 +8,8 @@ using Speckle.Connectors.CSiShared.Builders;
using Speckle.Connectors.CSiShared.Filters;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -44,13 +46,18 @@ public static class ServiceRegistration
services.AddScoped<SendOperation<ICsiWrapper>>();
services.AddScoped<CsiMaterialPropertyExtractor>();
services.AddScoped<CsiResultsExtractorFactory>();
services.AddScoped<MaterialUnpacker>();
services.AddScoped<IFrameSectionPropertyExtractor, CsiFrameSectionPropertyExtractor>();
services.AddScoped<IShellSectionPropertyExtractor, CsiShellSectionPropertyExtractor>();
services.AddScoped<AnalysisResultsExtractor>();
// add converter caches
services.AddScoped<CsiToSpeckleCacheSingleton>();
// add settings manager
services.AddScoped<ToSpeckleSettingsManager>();
return services;
}
}
@@ -0,0 +1,13 @@
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.CSiShared.Settings;
public class LoadCaseCombinationSetting(List<string> values, cSapModel sapModel) : ICardSetting
{
public string? Id { get; set; } = "loadCasesAndCombinations";
public string? Title { get; set; } = "Load Cases & Combinations";
public string? Type { get; set; } = "array";
public object? Value { get; set; } = values;
public List<string>? Enum { get; set; } = LoadCaseHelper.GetLoadCasesAndCombinations(sapModel);
}
@@ -0,0 +1,13 @@
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.Settings;
public class ResultTypeSetting(List<string> values) : ICardSetting
{
public string? Id { get; set; } = "resultTypes";
public string? Title { get; set; } = "Result Type";
public string? Type { get; set; } = "array";
public object? Value { get; set; } = values;
public List<string>? Enum { get; set; } = ResultsKey.All.OrderBy(x => x).ToList();
}
@@ -18,6 +18,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiResultsExtractorFactory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiFrameSectionPropertyExtractor.cs" />
@@ -27,12 +28,17 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\CsiPluginBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleFormBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiApplicationService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiDocumentModelStore.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Settings\LoadCaseCombinationSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Settings\ResultTypeSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\LoadCaseHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\AnalysisResultsExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\ObjectIdentifiers.cs" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,184 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.CSiShared.Utils;
public class AnalysisResultsExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettingsStore;
private readonly CsiResultsExtractorFactory _resultsExtractorFactory;
public AnalysisResultsExtractor(
IConverterSettingsStore<CsiConversionSettings> converterSettingsStore,
CsiResultsExtractorFactory resultsExtractorFactory
)
{
_converterSettingsStore = converterSettingsStore;
_resultsExtractorFactory = resultsExtractorFactory;
}
/// <summary>
/// Extracts complete analysis results including units retrieval, load case configuration, and results extraction.
/// Assumes inputs have been validated by caller.
/// </summary>
public Base ExtractAnalysisResults(
List<string> selectedCasesAndCombinations,
List<string> requestedResultTypes,
Dictionary<ModelObjectType, List<string>> objectSelectionSummary
)
{
// Step 1: get analysis units
var analysisResults = CreateAnalysisResultsWithUnits();
// Step 2: configure and validate load cases
ConfigureAndValidateSelectedLoadCases(selectedCasesAndCombinations);
// Step 3: extract results using clean factory pattern
ExtractResults(requestedResultTypes, objectSelectionSummary, analysisResults);
return analysisResults;
}
/// <summary>
/// Instantiates a Base object and pre-populates it with the models defined force units.
/// </summary>
/// <returns></returns>
/// <exception cref="SpeckleException"></exception>
private Base CreateAnalysisResultsWithUnits()
{
var forceUnit = eForce.NotApplicable;
var lengthUnit = eLength.NotApplicable;
var temperatureUnit = eTemperature.NotApplicable;
int success = _converterSettingsStore.Current.SapModel.GetDatabaseUnits_2(
ref forceUnit,
ref lengthUnit,
ref temperatureUnit
);
if (success != 0)
{
throw new SpeckleException("Failed to retrieve units for analysis results");
}
return new Base
{
["forceUnit"] = forceUnit.ToString(),
["lengthUnit"] = lengthUnit.ToString(),
["temperatureUnit"] = temperatureUnit.ToString()
};
}
private void ExtractResults(
List<string> requestedResultTypes,
Dictionary<ModelObjectType, List<string>> objectSelectionSummary,
Base analysisResults
)
{
foreach (var resultType in requestedResultTypes)
{
var extractor = _resultsExtractorFactory.GetExtractor(resultType);
objectSelectionSummary.TryGetValue(extractor.TargetObjectType, out var objectNames);
analysisResults[extractor.ResultsKey] = extractor.GetResults(objectNames);
}
}
/// <summary>
/// Responsible for two things. Firstly, we need to setup the results so that only the requested cases and combinations
/// are published. Secondly, we need to ensure that the requested cases and combinations are actually run.
/// </summary>
private void ConfigureAndValidateSelectedLoadCases(List<string> selectedLoadCases)
{
// step 1: configure load cases for output
ConfigureSelectedLoadCases(selectedLoadCases);
// step 2: validate they are complete (throws on failure)
ValidateSelectedCasesAreComplete(selectedLoadCases);
}
private void ConfigureSelectedLoadCases(List<string> selectedLoadCases)
{
// deselect all load cases and combos
_converterSettingsStore.Current.SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput();
// ui presents cases and combinations as a flat list. we need to distinguish if the string is a case or combo
// do this by seeing if the string is within the list of defined cases
// typically defined load cases << defined load combinations, so this approach should be more efficient
int numberOfLoadCases = 0;
string[] loadCaseNames = [];
_converterSettingsStore.Current.SapModel.LoadCases.GetNameList(ref numberOfLoadCases, ref loadCaseNames);
// set user selected combos to true (i.e. to export)
foreach (var selectedLoadCase in selectedLoadCases)
{
int success = loadCaseNames.Contains(selectedLoadCase)
? _converterSettingsStore.Current.SapModel.Results.Setup.SetCaseSelectedForOutput(selectedLoadCase)
: _converterSettingsStore.Current.SapModel.Results.Setup.SetComboSelectedForOutput(selectedLoadCase);
// ui should only present valid options
// `AnalysisResultsExtractor` only fetches load cases and load combinations (not patterns), so this should never throw
if (success != 0)
{
throw new InvalidOperationException($"Failed to set {selectedLoadCase} for output.");
}
}
}
private void ValidateSelectedCasesAreComplete(List<string> selectedCasesAndCombinations)
{
// get status for all load cases (combinations not included in this API call)
int numberItems = 0;
string[] caseNames = [];
int[] statuses = [];
int result = _converterSettingsStore.Current.SapModel.Analyze.GetCaseStatus(
ref numberItems,
ref caseNames,
ref statuses
);
if (result != 0)
{
throw new SpeckleException("Failed to retrieve load case status from model.");
}
// build lookup dictionary for load cases only
var caseStatusLookup = caseNames
.Zip(statuses, (name, status) => new { name, status })
.ToDictionary(x => x.name, x => x.status);
// separate selected items into cases and combinations
var selectedCases = selectedCasesAndCombinations.Where(item => caseStatusLookup.ContainsKey(item)).ToList();
var selectedCombinations = selectedCasesAndCombinations.Except(selectedCases).ToList();
// validate load cases status
var notFinishedCases = new List<string>();
foreach (var caseName in selectedCases)
{
int status = caseStatusLookup[caseName];
if (status != 4) // 1 = Not run, 2 = Could not start, 3 = Not finished, 4 = Finished
{
notFinishedCases.Add($"{caseName}");
}
}
// TODO: Validate load combinations status
// for now, assume combinations are valid if we can't validate them
if (selectedCombinations.Count != 0)
{
// combinations validation not implemented - assuming they're valid for now
// it'll get complicated, we can't get the status of a combination, so we need to check the constituent cases
}
if (notFinishedCases.Count != 0)
{
string errorMessage =
$"Analysis not complete for load cases: {string.Join(", ", notFinishedCases)}. Run analysis first.";
throw new SpeckleException(errorMessage);
}
}
}
@@ -0,0 +1,56 @@
namespace Speckle.Connectors.CSiShared.Utils;
public static class LoadCaseHelper
{
public static List<string> GetLoadCasesAndCombinations(cSapModel sapModel)
{
var loadCasesAndCombos = new List<string>();
try
{
// Check if model is loaded to prevent crashes
var modelFilename = sapModel.GetModelFilename();
if (string.IsNullOrEmpty(modelFilename))
{
return loadCasesAndCombos; // Return empty list if no model
}
// Get Load Cases
int numberItems = 0;
string[]? names = null;
int ret = sapModel.LoadCases.GetNameList(ref numberItems, ref names);
if (ret == 0 && names != null)
{
for (int i = 0; i < numberItems; i++)
{
loadCasesAndCombos.Add(names[i]);
}
}
// Get Load Combinations
numberItems = 0;
names = null;
ret = sapModel.RespCombo.GetNameList(ref numberItems, ref names);
if (ret == 0 && names != null)
{
for (int i = 0; i < numberItems; i++)
{
loadCasesAndCombos.Add(names[i]);
}
}
}
catch (System.Runtime.InteropServices.COMException)
{
// Return empty list on COM errors to prevent crashes
return new List<string>();
}
catch (System.InvalidOperationException)
{
// Return empty list on invalid operations to prevent crashes
return new List<string>();
}
return loadCasesAndCombos.Distinct().OrderBy(x => x).ToList();
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -356,14 +356,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -236,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -21,7 +21,7 @@
</ItemGroup>
<Target Name="PostBuild" AfterTargets="Build">
<Target Name="PostBuild" AfterTargets="Build" Condition="'$(NavisworksVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Message Text="Navisworks Version $(NavisworksVersion)" Importance="high"/>
<RemoveDir Directories="$(PluginVersionContentTarget)" Condition="Exists('$(PluginVersionContentTarget)')"/>
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -265,9 +265,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -266,9 +266,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +360,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -14,7 +14,6 @@ using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connector.Navisworks.Bindings;
@@ -28,7 +27,6 @@ public class NavisworksSendBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly ISpeckleApplication _speckleApplication;
private readonly INavisworksConversionSettingsFactory _conversionSettingsFactory;
private readonly ToSpeckleSettingsManagerNavisworks _toSpeckleSettingsManagerNavisworks;
private readonly IElementSelectionService _selectionService;
@@ -39,7 +37,6 @@ public class NavisworksSendBinding : ISendBinding
DocumentModelStore store,
IBrowserBridge parent,
ICancellationManager cancellationManager,
ISpeckleApplication speckleApplication,
INavisworksConversionSettingsFactory conversionSettingsFactory,
ToSpeckleSettingsManagerNavisworks toSpeckleSettingsManagerNavisworks,
IElementSelectionService selectionService,
@@ -51,7 +48,6 @@ public class NavisworksSendBinding : ISendBinding
Commands = new SendBindingUICommands(parent);
_store = store;
_cancellationManager = cancellationManager;
_speckleApplication = speckleApplication;
_conversionSettingsFactory = conversionSettingsFactory;
_toSpeckleSettingsManagerNavisworks = toSpeckleSettingsManagerNavisworks;
_selectionService = selectionService;
@@ -77,6 +73,7 @@ public class NavisworksSendBinding : ISendBinding
new IncludeInternalPropertiesSetting(false),
new ConvertHiddenElementsSetting(false),
new PreserveModelHierarchySetting(false),
new RevitCategoryMappingSetting(false)
];
public async Task Send(string modelCardId) =>
@@ -97,7 +94,8 @@ public class NavisworksSendBinding : ISendBinding
visualRepresentationMode: _toSpeckleSettingsManagerNavisworks.GetVisualRepresentationMode(modelCard),
convertHiddenElements: _toSpeckleSettingsManagerNavisworks.GetConvertHiddenElements(modelCard),
includeInternalProperties: _toSpeckleSettingsManagerNavisworks.GetIncludeInternalProperties(modelCard),
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard)
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard),
mappingToRevitCategories: _toSpeckleSettingsManagerNavisworks.GetMappingToRevitCategories(modelCard)
)
);
@@ -133,22 +131,6 @@ public class NavisworksSendBinding : ISendBinding
return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems;
}
private async Task<SendOperationResult> ExecuteSendOperation(
IServiceScope scope,
SenderModelCard modelCard,
List<NAV.ModelItem> navisworksModelItems,
IProgress<CardProgress> onOperationProgressed,
CancellationToken token
) =>
await scope
.ServiceProvider.GetRequiredService<SendOperation<NAV.ModelItem>>()
.Execute(
navisworksModelItems,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
onOperationProgressed,
token
);
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
/// <summary>
@@ -151,22 +151,22 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
foreach (NAV.SavedItem item in ((NAV.GroupItem)parentItem).Children)
{
switch (item.IsGroup)
switch (item)
{
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
// case false when item is NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
// // This will need to be the documented behaviour.
// break;
case false:
collectedSets.Add((NAV.SavedViewpoint)item);
// case NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// Legacy defensive behaviour: skip viewpoints without visibility overrides.
// Essentially, send everything, or whatever the current view state for hidden elements is.
// break;
case NAV.SavedViewpointAnimationCut:
// Skip animation cuts.
break;
default: // handles item.IsGroup == true
if (((NAV.GroupItem)item).Children.Count > 0) // Don't add empty groups
{
CollectSavedViews(item, collectedSets);
}
case NAV.SavedViewpoint savedViewpoint:
collectedSets.Add(savedViewpoint);
break;
case NAV.GroupItem groupItem when groupItem.Children.Count > 0:
CollectSavedViews(groupItem, collectedSets);
break;
// No action for empty groups or unknown types.
}
}
}
@@ -32,18 +32,9 @@ public class NavisworksRootObjectBuilder(
internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current;
/// <summary>
/// Asynchronously builds a Speckle object hierarchy from Navisworks model items.
/// </summary>
/// <param name="navisworksModelItems">The list of Navisworks items to convert.</param>
/// <param name="sendInfo">Information about the send operation.</param>
/// <param name="onOperationProgressed">Progress reporting callback.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>A result containing the root collection and conversion results.</returns>
/// <exception cref="SpeckleException">Thrown when no objects can be converted.</exception>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -54,7 +45,7 @@ public class NavisworksRootObjectBuilder(
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, sendInfo, onOperationProgressed);
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
// 2. Initialize root collection
var rootCollection = InitializeRootCollection();
@@ -62,7 +53,7 @@ public class NavisworksRootObjectBuilder(
// 3. Convert all model items and store results
var (convertedElements, conversionResults) = await ConvertModelItemsAsync(
navisworksModelItems,
sendInfo,
projectId,
onOperationProgressed,
cancellationToken
);
@@ -80,7 +71,7 @@ public class NavisworksRootObjectBuilder(
private static void ValidateInputs(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed
)
{
@@ -94,9 +85,11 @@ public class NavisworksRootObjectBuilder(
throw new ArgumentNullException(nameof(navisworksModelItems));
}
if (onOperationProgressed == null || sendInfo == null)
if (onOperationProgressed == null || projectId == null)
{
throw new ArgumentNullException(onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(sendInfo));
throw new ArgumentNullException(
onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(projectId)
);
}
}
@@ -109,7 +102,7 @@ public class NavisworksRootObjectBuilder(
private Task<(Dictionary<string, Base?> converted, List<SendConversionResult> results)> ConvertModelItemsAsync(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
SendInfo sendInfo,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -122,7 +115,7 @@ public class NavisworksRootObjectBuilder(
foreach (var item in navisworksModelItems)
{
cancellationToken.ThrowIfCancellationRequested();
var converted = ConvertNavisworksItem(item, convertedBases, sendInfo);
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
results.Add(converted);
processedCount++;
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
@@ -310,7 +303,7 @@ public class NavisworksRootObjectBuilder(
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases,
SendInfo sendInfo
string projectId
)
{
string applicationId = elementSelectionService.GetModelItemPath(navisworksItem);
@@ -318,7 +311,7 @@ public class NavisworksRootObjectBuilder(
try
{
Base converted = sendConversionCache.TryGetValue(applicationId, sendInfo.ProjectId, out ObjectReference? cached)
Base converted = sendConversionCache.TryGetValue(applicationId, projectId, out ObjectReference? cached)
? cached
: rootToSpeckleConverter.Convert(navisworksItem);
@@ -0,0 +1,12 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connector.Navisworks.Operations.Send.Settings;
public class RevitCategoryMappingSetting(bool value) : ICardSetting
{
public string? Id { get; set; } = "mappingToRevitCategories";
public string? Title { get; set; } = "Map to Revit Categories";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
public object? Value { get; set; } = value;
}
@@ -18,6 +18,7 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
private readonly Dictionary<string, bool?> _convertHiddenElementsCache = [];
private readonly Dictionary<string, bool?> _includeInternalPropertiesCache = [];
private readonly Dictionary<string, bool?> _preserveModelHierarchyCache = [];
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
{
@@ -63,23 +64,41 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
throw new ArgumentNullException(nameof(modelCard));
}
var originString = modelCard.Settings?.First(s => s.Id == "originMode").Value as string;
if (originString is not null && OriginModeSetting.OriginModeMap.TryGetValue(originString, out OriginMode origin))
var originString = modelCard.Settings?.FirstOrDefault(s => s.Id == "originMode")?.Value as string;
if (!OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
{
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out OriginMode previousType))
{
if (previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
return OriginMode.ModelOrigin; // Default to ModelOrigin if not specified or invalid
}
throw new ArgumentException($"Invalid origin mode value: {originString}");
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousType) && previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
}
public bool GetMappingToRevitCategories(SenderModelCard modelCard)
{
if (modelCard == null)
{
throw new ArgumentNullException(nameof(modelCard));
}
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public bool GetConvertHiddenElements(SenderModelCard modelCard)
@@ -25,10 +25,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenEleementsSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\OriginModeSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\PreserveModelHierarchySetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\RevitCategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManagerNavisworks.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\VisualRepresentationSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\NavisworksSelectionFilter.cs"/>
+2 -2
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Target AfterTargets="Clean" Name="CleanAddinsRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Clean" Name="CleanAddinsRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(TargetDir);$(ProjectDir)\..\Release\Release$(RevitVersion);$(AppData)\Autodesk\Revit\Addins\$(RevitVersion)\Speckle.Connectors.Revit$(RevitVersion);" />
<Delete Files="$(AppData)\Autodesk\Revit\Addins\$(RevitVersion)\Speckle.Connectors.Revit$(RevitVersion).addin" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildRevit" Condition="'$(RevitVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<RevitDLLs Include="$(TargetDir)\**\*.*" Exclude="$(TargetDir)*.addin" />
<SourceManifest Include="$(TargetDir)\Plugin\Speckle.Connectors.Revit$(RevitVersion).addin" />
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -226,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -251,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -321,14 +321,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -72,6 +72,7 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
return new DocumentInfo("", "", "") { Message = "Family environment files not supported by Speckle." };
}
//should this use the Hashcode of the document instead of something like CreationGUID?
var info = new DocumentInfo(doc.PathName, doc.Title, doc.GetHashCode().ToString());
return info;
@@ -3,16 +3,18 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Bindings;
internal sealed class RevitReceiveBinding(
public sealed class RevitReceiveBinding(
ICancellationManager cancellationManager,
IBrowserBridge parent,
ILogger<RevitReceiveBinding> logger,
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
@@ -21,6 +23,11 @@ internal sealed class RevitReceiveBinding(
public IBrowserBridge Parent { get; } = parent;
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
#pragma warning disable CA1024
public List<ICardSetting> GetReceiveSettings() =>
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
#pragma warning restore CA1024
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
public async Task Receive(string modelCardId)
@@ -29,13 +36,13 @@ internal sealed class RevitReceiveBinding(
await manager.Process(
Commands,
modelCardId,
(sp) =>
(sp, card) =>
{
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
revitConversionSettingsFactory.Create(
DetailLevelType.Coarse, // TODO figure out
null,
toHostSettingsManager.GetReferencePointSetting(card),
false,
true,
false
@@ -216,12 +216,13 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
newSelectedObjectIds.Add(element.UniqueId);
}
// We update the state on the UI SenderModelCard to prevent potential inconsistencies between hostApp IdMap in sendfilters.
await Commands.SetFilterObjectIds(
modelCard.ModelCardId.NotNull(),
modelCard.SendFilter.IdMap,
newSelectedObjectIds
);
// NOTE: preserve & persist original user selection for selection filter implemented during
// [CNX-2400](https://linear.app/speckle/issue/CNX-2400/object-dont-update-on-publish)
// NOTE: update with current document for views and categories filter since these represent dynamic queries
// View & categories filters self-update their SelectedObjectIds in RefreshObjectIds(), maintaining consistency
var objectIds =
modelCard.SendFilter is RevitSelectionFilter ? modelCard.SendFilter.SelectedObjectIds : newSelectedObjectIds;
await Commands.SetFilterObjectIds(modelCard.ModelCardId.NotNull(), modelCard.SendFilter.IdMap, objectIds);
}
return documentElementContexts;
@@ -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();
}
}
@@ -3,48 +3,45 @@ using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.SQLite;
namespace Speckle.Connectors.Revit.HostApp;
// POC: should be interfaced out
internal sealed class RevitDocumentStore : DocumentModelStore
{
// POC: move to somewhere central?
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
private readonly ILogger<RevitDocumentStore> _logger;
private readonly IAppIdleManager _idleManager;
private readonly RevitContext _revitContext;
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
private readonly IdStorageSchema _idStorageSchema;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
public RevitDocumentStore(
ILogger<DocumentModelStore> logger,
IAppIdleManager idleManager,
RevitContext revitContext,
IJsonSerializer jsonSerializer,
DocumentModelStorageSchema documentModelStorageSchema,
IdStorageSchema idStorageSchema,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext,
IRevitTask revitTask
IRevitTask revitTask,
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
ILogger<RevitDocumentStore> logger
)
: base(logger, jsonSerializer)
{
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
_idleManager = idleManager;
_revitContext = revitContext;
_documentModelStorageSchema = documentModelStorageSchema;
_idStorageSchema = idStorageSchema;
_topLevelExceptionHandler = topLevelExceptionHandler;
_threadContext = threadContext;
_logger = logger;
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
@@ -101,80 +98,52 @@ internal sealed class RevitDocumentStore : DocumentModelStore
return;
}
_threadContext
.RunOnMain(() =>
try
{
var key = GetKeyForDocument(document);
if (key != null)
{
//if not the same active document then don't save the current cards to a bad document!
if (!EnsureActiveDocumentIsSame(document))
{
return;
}
using Transaction t = new(document, "Speckle Write State");
t.Start();
using DataStorage ds = GetSettingsDataStorage(document) ?? DataStorage.Create(document);
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
string serializedModels = Serialize();
stateEntity.Set("contents", serializedModels);
using Entity idEntity = new(_idStorageSchema.GetSchema());
idEntity.Set("Id", s_revitDocumentStoreId);
ds.SetEntity(idEntity);
ds.SetEntity(stateEntity);
t.Commit();
})
.FireAndForget();
_jsonCacheManager.UpdateObject(key, modelCardState);
}
}
catch (Exception ex) when (!ex.IsFatal())
{
var key = GetKeyForDocument(document);
_logger.LogError(ex, "Failed to save model card state for document {DocumentId}", key);
}
}
private bool EnsureActiveDocumentIsSame(Document document)
private string? GetKeyForDocument(Document doc)
{
var localDoc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (localDoc == null)
{
return false;
}
return localDoc.Equals(document);
string? id = doc?.ProjectInformation?.UniqueId; //ProjectInformation Should only be null for family docs
#if REVIT_2024_OR_GREATER
id ??= doc.CreationGUID.ToString(); //fallback for family docs
#endif
return id;
}
protected override void LoadState()
{
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
// POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save)
if (document == null)
{
return;
}
var stateEntity = GetSpeckleEntity(document);
if (stateEntity == null || !stateEntity.IsValid())
{
ClearAndSave();
return;
}
string modelsString = stateEntity.Get<string>("contents");
LoadFromString(modelsString);
}
private DataStorage? GetSettingsDataStorage(Document doc)
{
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
var key = GetKeyForDocument(document);
if (key != null)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
if (!settingIdEntity.IsValid())
{
continue;
}
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
var state = _jsonCacheManager.GetObject(key);
LoadFromString(state);
}
return null;
}
private Entity? GetSpeckleEntity(Document? doc)
@@ -14,7 +14,7 @@ public class SendCollectionManager
{
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
private readonly Dictionary<string, Collection> _collectionCache = new();
private readonly Dictionary<ElementId, (string name, Dictionary<string, object?> props)> _levelCache = new(); // stores level id and its properties
private readonly Dictionary<ElementId, string> _levelCache = new(); // stores level id and its properties
private readonly Dictionary<string, Collection> _linkedModelCollections = new(); // cache for linked model collections
private Collection? _mainModelCollection; // collection for main model elements
@@ -91,13 +91,11 @@ public class SendCollectionManager
// get the level and its properties
string levelName = "No Level";
Dictionary<string, object?> levelProperties = new();
if (element.LevelId != ElementId.InvalidElementId)
{
if (_levelCache.TryGetValue(element.LevelId, out var cachedLevel))
{
levelName = cachedLevel.name;
levelProperties = cachedLevel.props;
levelName = cachedLevel;
}
else
{
@@ -105,9 +103,7 @@ public class SendCollectionManager
{
var level = (Level)doc.GetElement(element.LevelId);
levelName = level.Name;
levelProperties.Add("elevation", level.Elevation);
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
_levelCache.Add(element.LevelId, (levelName, levelProperties));
_levelCache.Add(element.LevelId, levelName);
}
// the exception is swallowed since if an exception occurs, we fall back to "No Level" for the element
catch (Exception e) when (!e.IsFatal()) { }
@@ -157,12 +153,6 @@ public class SendCollectionManager
else
{
childCollection = new Collection(pathItem);
// add properties to level collection
if (i == 0 && levelProperties.Count > 0)
{
childCollection["properties"] = levelProperties;
}
previousCollection.elements.Add(childCollection);
_collectionCache[flatPathName] = childCollection;
}
@@ -0,0 +1,18 @@
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ReferencePointSetting(ReceiveReferencePointType value) : ICardSetting
{
public string? Id { get; set; } = "referencePoint";
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReceiveReferencePointType)).ToList();
public object? Value { get; set; } = value.ToString();
public static readonly Dictionary<string, ReceiveReferencePointType> ReferencePointMap = System
.Enum.GetValues(typeof(ReceiveReferencePointType))
.Cast<ReceiveReferencePointType>()
.ToDictionary(v => v.ToString(), v => v);
}
@@ -64,14 +64,14 @@ public sealed class RevitHostObjectBuilder(
)
{
// TODO: formalise getting transform info from rootObject. this dict access is gross.
Autodesk.Revit.DB.Transform? referencePointTransform = null;
Autodesk.Revit.DB.Transform? referencePointTransformFromRootObject = null;
if (
rootObject.DynamicPropertyKeys.Contains(ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY)
&& rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] is Dictionary<string, object> transformDict
&& transformDict.TryGetValue("transform", out var transformValue)
)
{
referencePointTransform = ReferencePointHelper.GetTransformFromRootObject(transformValue);
referencePointTransformFromRootObject = ReferencePointHelper.GetTransformFromRootObject(transformValue);
}
var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors!
@@ -184,11 +184,15 @@ public sealed class RevitHostObjectBuilder(
{
using var _ = activityFactory.Start("Baking objects");
transactionManager.StartTransaction(true, "Baking objects");
using (
converterSettings.Push(currentSettings =>
currentSettings with
{
ReferencePointTransform = referencePointTransform
ReferencePointTransform = CalculateNewTransform(
currentSettings.ReferencePointTransform,
referencePointTransformFromRootObject
)
}
)
)
@@ -217,6 +221,24 @@ public sealed class RevitHostObjectBuilder(
return conversionResults.builderResult;
}
private Autodesk.Revit.DB.Transform? CalculateNewTransform(
Autodesk.Revit.DB.Transform? receiveTransform,
Autodesk.Revit.DB.Transform? rootTransform
)
{
if (receiveTransform == null)
{
return rootTransform;
}
if (rootTransform == null)
{
return receiveTransform;
}
return rootTransform.Multiply(receiveTransform);
}
private (
HostObjectBuilderResult builderResult,
List<(DirectShape res, string applicationId)> postBakePaintTargets
@@ -0,0 +1,97 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.InterfaceGenerator;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
[GenerateAutoInterface]
public class ToHostSettingsManager : IToHostSettingsManager
{
private readonly RevitContext _revitContext;
public ToHostSettingsManager(RevitContext revitContext)
{
_revitContext = revitContext;
}
public Transform? GetReferencePointSetting(ModelCard modelCard)
{
var referencePointString = modelCard.Settings?.FirstOrDefault(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."
);
}
}
@@ -2,7 +2,9 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converters.RevitShared.Extensions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -75,8 +77,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
//this used to throw an exception, but we don't want to fail loudly if the view is not found
return [];
}
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var elementsInView = viewCollector.ToElements();
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -125,4 +127,52 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
// Below function collects list of ids to exclude from final list.
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
{
var elementsToExclude = new HashSet<ElementId>();
foreach (var element in elements)
{
// check if element is a part
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
{
try
{
// get source element ids from the part
var sourceIds = part.GetSourceElementIds();
if (sourceIds != null)
{
foreach (var sourceId in sourceIds)
{
elementsToExclude.Add(sourceId.HostElementId);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
// silently continue processing other Parts if one fails
// this follows the pattern used elsewhere in the codebase
}
}
}
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
{
var idsToExclude = GetSourceElementIdsToExclude(allElements);
return allElements.Where(e => !idsToExclude.Contains(e.Id));
}
return allElements;
}
}
@@ -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++;
@@ -227,17 +225,29 @@ public class RevitRootObjectBuilder(
}
}
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjectCount)
// if we ended up skipping everything, there is a reason for this, that users can diagnose themselves
// this can occur if a published view contains only unsupported objects or if user trying to ONLY send linked model
// docs but the setting is disabled
if (skippedObjectCount == atomicObjectCount)
{
throw new SpeckleException("No supported objects visible. Update publish filter or check publish settings.");
}
// this is, I suppose, fully on us?
if (results.All(x => x.Status == Status.ERROR))
{
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;
@@ -37,7 +37,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
public DetailLevelType GetDetailLevelSetting(SenderModelCard modelCard)
{
var fidelityString = modelCard.Settings?.First(s => s.Id == "detailLevel").Value as string;
var fidelityString = modelCard.Settings?.FirstOrDefault(s => s.Id == "detailLevel")?.Value as string;
if (
fidelityString is not null
&& DetailLevelSetting.GeometryFidelityMap.TryGetValue(fidelityString, out DetailLevelType fidelity)
@@ -57,9 +57,9 @@ 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;
var referencePointString = modelCard.Settings?.FirstOrDefault(s => s.Id == "referencePoint")?.Value as string;
if (
referencePointString is not null
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
@@ -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);
}
}
@@ -90,7 +90,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "nullemptyparams").Value as bool?;
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "nullemptyparams")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendNullParamsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
@@ -108,7 +108,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
public bool GetLinkedModelsSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "includeLinkedModels").Value as bool?;
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "includeLinkedModels")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendLinkedModelsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
@@ -124,7 +124,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "sendRebarsAsVolumetric").Value as bool?;
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "sendRebarsAsVolumetric")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendRebarsAsVolumetricCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
@@ -38,6 +38,10 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -61,6 +65,10 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -104,6 +112,11 @@ public class RevitThreadContext : ThreadContext
ex = e;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -7,7 +7,7 @@ using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.Plugin;
#pragma warning disable CA1032
public class SpeckleRevitTaskException(Exception exception) : SpeckleException("Revit operation failed", exception)
public class SpeckleRevitTaskException(Exception exception) : SpeckleException(exception.Message, exception)
#pragma warning restore CA1032
{
public static async Task ProcessException<T>(
@@ -22,6 +22,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
@@ -36,7 +37,9 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\RevitHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\TransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\IRevitSendFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitCategoriesFilter.cs" />
@@ -58,4 +61,4 @@
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
</ItemGroup>
</Project>
</Project>
+3 -3
View File
@@ -3,13 +3,13 @@
<ItemGroup>
<PublicReleasePath Include="$(AppData)\McNeel\Rhinoceros\$(RhinoVersion).0\Plug-ins\Speckle.Connectors.Rhino$(RhinoVersion) (2153799A-0CEC-40DE-BC3A-01E5055222FF)" />
</ItemGroup>
<Target AfterTargets="Build" Name="WarnIfPublicReleaseVersionInstalled" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="WarnIfPublicReleaseVersionInstalled" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Warning
Text="Conflicting Rhino plugin detected - Do you have a public release installed?"
Text="Conflicting Rhino plugin detected - Do you have a public release installed? at '@(PublicReleasePath)'"
Condition="Exists(@(PublicReleasePath))" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildRhino" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true'">
<Target AfterTargets="Build" Name="AfterBuildRhino" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Message Text="Rhino Version $(RhinoVersion)" Importance="high"/>
</Target>
</Project>
@@ -13,9 +13,12 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"dependencies": {
"PolySharp": "1.14.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -322,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.logging": {
@@ -334,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino7": {
@@ -379,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -400,14 +403,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -13,9 +13,12 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"dependencies": {
"PolySharp": "1.14.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -322,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.4.2, )",
"Speckle.Sdk": "[3.4.2, )",
"Speckle.Sdk.Dependencies": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.logging": {
@@ -334,13 +337,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.4.2, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -379,18 +381,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "2TcYsGm+vG5mpZj+fiwPJv/4wXtstrWb9FasxsQN2JdUNDKvg1/rVFE9Pby1tPWN+4J0X0QaXutGj0yNwLRe7w==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.4.2"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "SOjUacHaCt+w19DhSgwQijhcwYZZSgmRVBOLk/O1Lwlq66cHVy3pzyBbsNR8zM+rVAtXrpahf60TjBg2EsD5Zg==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -400,14 +402,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.4.2"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.4.2, )",
"resolved": "3.4.2",
"contentHash": "mYjR5i5zaNxkR2VXi2Ills3XG3VZ4z3wLSQBn7GldAudtxI8yS09M6w4ltFNcBmgLlnYHYiIbsWEilIGUbi0Nw=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -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.");
}
}
}
@@ -1,7 +1,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 +30,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,29 +51,28 @@ 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)
{
var param = new Param_GenericObject()
var param = new SpeckleOutputParam
{
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 +85,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)
{
@@ -94,7 +93,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
nickName += "..." + childWrapper.Name[^6..];
}
var param = new Param_GenericObject()
var param = new SpeckleOutputParam
{
Name = childWrapper.Name,
NickName = nickName,
@@ -102,18 +101,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 +145,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());
@@ -188,7 +190,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
foreach (var newParam in outputParams)
{
var param = new Param_GenericObject
var param = new SpeckleOutputParam
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
@@ -211,7 +213,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new Param_GenericObject
var myParam = new SpeckleOutputParam
{
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
MutableNickName = true,
@@ -224,4 +226,4 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
}
public record OutputParamWrapper(Param_GenericObject Param, object Values, string? Topology);
public record OutputParamWrapper(SpeckleOutputParam Param, object Values, string? Topology);
@@ -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;
@@ -26,92 +28,202 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_deconstruct;
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
);
}
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
pManager.AddGenericParameter("Speckle Param", "SP", "Speckle param to deconstruct", GH_ParamAccess.item);
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
object data = new();
da.GetData(0, ref data);
List<OutputParamWrapper> outputParams = new();
switch (data)
// on first iteration, discover all fields from all objects to create stable output structure
if (da.Iteration == 0)
{
case SpeckleObjectWrapperGoo obj:
Name = string.IsNullOrEmpty(obj.Value.Name) ? obj.Value.Base.speckle_type : obj.Value.Name;
outputParams = CreateOutputParamsFromBase(obj.Value.Base);
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);
break;
case SpeckleMaterialWrapperGoo matGoo:
Name = string.IsNullOrEmpty(matGoo.Value.Name) ? matGoo.Value.Material.speckle_type : matGoo.Value.Name;
outputParams = CreateOutputParamsFromBase(matGoo.Value.Base);
break;
case SpecklePropertyGroupGoo propGoo:
Name = $"properties ({propGoo.Value.Count})";
outputParams = new();
foreach (var key in propGoo.Value.Keys)
var allFields = DiscoverAllFieldsFromInput();
if (allFields.Count > 0)
{
var requiredOutputs = CreateOutputParamsFromFieldNames(allFields);
if (OutputMismatch(requiredOutputs))
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value is SpecklePropertyGoo prop
? prop.Value
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
outputParams.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(requiredOutputs));
return;
}
break;
default:
return;
}
}
// process current object normally
object data = new();
if (!da.GetData(0, ref data))
{
return;
}
var outputParams = DeconstructObject(data);
if (outputParams == null)
{
return;
}
// set component name based on the current object
NickName = Name;
if (da.Iteration == 0 && OutputMismatch(outputParams))
// set output data - fill missing fields with nulls for objects that don't have all fields
SetOutputData(da, outputParams);
}
/// <summary>
/// Discovers all unique field names and their access types from all input objects by looking at volatile data directly.
/// </summary>
/// <returns>A dictionary mapping field names to their required parameter access types.</returns>
private IReadOnlyDictionary<string, GH_ParamAccess> DiscoverAllFieldsFromInput()
{
Dictionary<string, GH_ParamAccess> allFields = [];
foreach (var item in Params.Input[0].VolatileData.AllData(true))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(outputParams);
}
);
}
else
{
for (int i = 0; i < outputParams.Count; i++)
var objectOutputs = DeconstructObject(item);
if (objectOutputs != null)
{
var outParam = Params.Output[i];
var outParamWrapper = outputParams[i];
switch (outParam.Access)
foreach (var output in objectOutputs)
{
case GH_ParamAccess.item:
da.SetData(i, outParamWrapper.Value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outParamWrapper.Value as IList);
break;
string fieldName = output.Param.Name;
allFields[fieldName] = output.Param.Access;
}
}
}
return allFields;
}
/// <summary>
/// Creates output parameter wrappers from field names and their corresponding access types.
/// </summary>
/// <param name="fieldAccessTypes">Dictionary mapping field names to their required parameter access types.</param>
/// <returns>List of output parameter wrappers with correct access types.</returns>
private List<OutputParamWrapper> CreateOutputParamsFromFieldNames(
IReadOnlyDictionary<string, GH_ParamAccess> fieldAccessTypes
) => fieldAccessTypes.Select(kvp => CreateOutputParamByKeyValue(kvp.Key, null, kvp.Value)).ToList();
/// <summary>
/// Deconstructs a single object into its constituent fields/properties.
/// </summary>
private List<OutputParamWrapper>? DeconstructObject(object data) =>
data switch
{
// get children elements from wrapper to override elements prop while parsing
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
=> ParseSpeckleWrapper(
collectionGoo.Value,
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
),
// get geometries from wrapper to override displayValue prop while parsing
SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null
=> ParseSpeckleWrapper(
dataObjectGoo.Value,
null,
dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList()
),
SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null => ParseSpeckleWrapper(objectGoo.Value),
SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null
=> ParseSpeckleWrapper(blockInstanceGoo.Value),
SpeckleBlockDefinitionWrapperGoo blockDef when blockDef.Value != null => ParseSpeckleWrapper(blockDef.Value),
SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null => ParseSpeckleWrapper(materialGoo.Value),
SpecklePropertyGroupGoo propGoo when propGoo.Value != null => ParsePropertyGroup(propGoo),
_ => HandleUnsupportedType(data)
};
/// <summary>
/// Handles SpecklePropertyGroupGoo objects by extracting their key-value pairs.
/// </summary>
private List<OutputParamWrapper> ParsePropertyGroup(SpecklePropertyGroupGoo propGoo)
{
Name = $"properties ({propGoo.Value.Count})";
List<OutputParamWrapper> objectOutputs = new();
foreach (var key in propGoo.Value.Keys)
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
// determine access type based on the value
GH_ParamAccess access = outputValue is IList ? GH_ParamAccess.list : GH_ParamAccess.item;
objectOutputs.Add(CreateOutputParamByKeyValue(key, outputValue, access));
}
return objectOutputs;
}
/// <summary>
/// Handles unsupported object types by logging an error and returning null.
/// </summary>
private List<OutputParamWrapper>? HandleUnsupportedType(object data)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return null;
}
/// <summary>
/// Sets output data for the current iteration, filling missing fields with null values.
/// Uses a lookup dictionary for efficient field matching.
/// </summary>
private void SetOutputData(IGH_DataAccess da, List<OutputParamWrapper> currentOutputs)
{
if (Params.Output.Count == 0)
{
return;
}
// create a lookup for current outputs by field name
var outputLookup = currentOutputs.ToDictionary(o => o.Param.Name, o => o.Value);
// set data for each output parameter
for (int i = 0; i < Params.Output.Count; i++)
{
var outputParam = Params.Output[i];
// set the value if it exists, otherwise set null
object? value = outputLookup.TryGetValue(outputParam.Name, out var fieldValue) ? fieldValue : null;
switch (outputParam.Access)
{
case GH_ParamAccess.item:
da.SetData(i, value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, value as IList ?? new List<object?>());
break;
}
}
}
private List<OutputParamWrapper> 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)
@@ -119,127 +231,149 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return result;
}
// cycle through base props
// process each property of the Base object
foreach (var prop in @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic))
{
// Convert and add to corresponding output structure
var value = prop.Value;
switch (value)
// skip internal dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
case null:
result.Add(CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item));
break;
continue;
}
case IList list:
List<object> nativeObjects = new();
foreach (var x in list)
{
switch (x)
{
case SpeckleCollectionWrapper collWrapper:
nativeObjects.Add(new SpeckleCollectionWrapperGoo(collWrapper));
break;
case SpeckleObjectWrapper objWrapper:
nativeObjects.Add(new SpeckleObjectWrapperGoo(objWrapper));
break;
case Base xBase:
nativeObjects.AddRange(ConvertOrCreateWrapper(xBase));
break;
default:
nativeObjects.Add(x);
break;
}
}
result.Add(CreateOutputParamByKeyValue(prop.Key, nativeObjects, GH_ParamAccess.list));
break;
case Dictionary<string, object?> dict: // this should be treated a properties dict
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
break;
case 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)
);
break;
case Base baseValue:
result.Add(CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list));
break;
default:
// we don't want to output dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
continue;
}
result.Add(CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item));
break;
var outputParam = CreateOutputParamForProperty(prop, @base, elements, displayValue);
if (outputParam != null)
{
result.Add(outputParam);
}
}
return result;
}
private List<SpeckleObjectWrapperGoo> ConvertOrCreateWrapper(Base @base)
/// <summary>
/// Creates an output parameter for a single property, handling different value types appropriately.
/// </summary>
private OutputParamWrapper CreateOutputParamForProperty(
KeyValuePair<string, object?> prop,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
) =>
prop.Value switch
{
null => CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item),
IList list => CreateListOutputParam(prop.Key, list, @base, elements, displayValue),
Dictionary<string, object?> dict => CreateDictionaryOutputParam(prop.Key, dict),
SpeckleWrapper wrapper => CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item),
Base baseValue => CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list),
_ => CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item)
};
/// <summary>
/// Creates an output parameter for list properties, with special handling for collection elements and display values.
/// </summary>
private OutputParamWrapper CreateListOutputParam(
string key,
IList list,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
)
{
// override list value for special cases
IList actualList = key switch
{
"elements" when @base is Collection && elements != null => elements,
"displayValue" when @base is Speckle.Objects.Data.DataObject && displayValue != null => displayValue,
_ => list
};
List<object> nativeObjects = new();
foreach (var item in actualList)
{
switch (item)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base baseItem:
nativeObjects.AddRange(ConvertOrCreateWrapper(baseItem));
break;
default:
nativeObjects.Add(item);
break;
}
}
return CreateOutputParamByKeyValue(key, nativeObjects, GH_ParamAccess.list);
}
/// <summary>
/// Creates an output parameter for dictionary properties, converting them to SpecklePropertyGroupGoo.
/// </summary>
private OutputParamWrapper CreateDictionaryOutputParam(string key, Dictionary<string, object?> dict)
{
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
return CreateOutputParamByKeyValue(key, propertyGoo, GH_ParamAccess.item);
}
/// <summary>
/// Converts a Speckle Base object to host geometry or creates a wrapper if conversion fails.
/// Returns a list of SpeckleGeometryWrapperGoo objects.
/// </summary>
private List<SpeckleGeometryWrapperGoo> ConvertOrCreateWrapper(Base @base)
{
try
{
// convert the base and create a wrapper for each result
List<(GeometryBase, Base)> convertedBase = SpeckleConversionContext.ConvertToHost(@base);
List<SpeckleObjectWrapperGoo> convertedWrappers = new();
foreach ((GeometryBase g, Base b) in convertedBase)
{
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = b,
GeometryBase = g,
Name = b["name"] as string ?? "",
Color = null,
Material = null,
WrapperGuid = null
};
convertedWrappers.Add(new(convertedWrapper));
}
return convertedWrappers;
// attempt conversion to host geometry
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
return convertedBase.Select(CreateGeometryWrapper).ToList();
}
catch (ConversionException)
{
// some classes, like RawEncoding, have no direct conversion or fallback value.
// when this is the case, wrap it to allow users to further expand the object.
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null,
WrapperGuid = null,
};
return new() { new SpeckleObjectWrapperGoo(convertedWrapper) };
// fallback: create wrapper without conversion for objects that can't be converted
return new List<SpeckleGeometryWrapperGoo> { CreateFallbackWrapper(@base) };
}
}
/// <summary>
/// Creates a SpeckleGeometryWrapperGoo from a converted geometry and base object pair.
/// </summary>
private SpeckleGeometryWrapperGoo CreateGeometryWrapper((object geometry, Base @base) converted)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = converted.@base,
GeometryBase = converted.geometry as GeometryBase,
Name = converted.@base["name"] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
/// <summary>
/// Creates a fallback wrapper for Base objects that cannot be converted to host geometry.
/// </summary>
private SpeckleGeometryWrapperGoo CreateFallbackWrapper(Base @base)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
private OutputParamWrapper CreateOutputParamByKeyValue(string key, object? value, GH_ParamAccess access)
{
Param_GenericObject param =
SpeckleOutputParam param =
new()
{
Name = key,
@@ -269,22 +403,20 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Output;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// TODO: better, nicer handling of creation/removal
// remove all existing output parameters
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
// add new output parameters
foreach (var newParam in outputParams)
{
var param = new Param_GenericObject
var param = new SpeckleOutputParam
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
@@ -294,11 +426,15 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
Params.RegisterOutputParam(param);
}
// notify Grasshopper of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
/// <summary>
/// Determines if the current output parameter structure differs from the required structure.
/// </summary>
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
{
if (Params.Output.Count != outputParams.Count)
@@ -306,10 +442,10 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return true;
}
var count = 0;
foreach (var newParam in outputParams)
for (int i = 0; i < outputParams.Count; i++)
{
var oldParam = Params.Output[count];
var newParam = outputParams[i];
var oldParam = Params.Output[i];
if (
oldParam.NickName != newParam.Param.NickName
|| oldParam.Name != newParam.Param.Name
@@ -318,7 +454,6 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
{
return true;
}
count++;
}
return false;
@@ -0,0 +1,129 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
namespace Speckle.Connectors.GrasshopperShared.Components.Dev;
[Guid("18152AE4-4BE7-46F0-9826-09061897A5CC")]
public class TokenUrlComponent : GH_Component
{
public TokenUrlComponent()
: base(
"Speckle Model URL",
"URL",
"Create a Speckle model link using URL and developer token",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.DEVELOPER
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_inputs_token;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
pManager.AddTextParameter("Speckle Token", "Token", "Speckle Authorization Token", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
protected override void SolveInstance(IGH_DataAccess da)
{
// get inputs
string urlInput = "";
if (!da.GetData(0, ref urlInput))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Speckle Url is missing");
return;
}
string tokenInput = "";
if (!da.GetData(1, ref tokenInput))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Speckle token is missing");
return;
}
try
{
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
var (resource, hasPermission) = SolveInstanceWithUrAndToken(urlInput, tokenInput, true);
if (!hasPermission)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You do not have enough permission for this project.");
}
da.SetData(0, resource);
}
catch (SpeckleException e)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
da.AbortComponentSolution();
}
}
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrAndToken(
string input,
string token,
bool isSender
)
{
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
// Should perform validation, fill in all internal data of the component (project, model, version, account)
// Should notify user if any of this goes wrong.
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
if (resources.Length != 1)
{
// POC: this shouldn't ever hit since exceptions are thrown in the FromUrlString method
throw new SpeckleException($"FromUrlString parser returned an invalid resource");
}
var resource = resources.First();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var account = resource.Account.GetAccount(scope);
if (account != null)
{
scope.Get<IAccountService>().SetUserSelectedAccountId(account.id);
}
else
{
throw new SpeckleException("No account found for server URL");
}
IClient client = scope.Get<IClientFactory>().Create(account);
var project = client.Project.Get(resource.ProjectId).Result;
var projectPermissions = client.Project.GetPermissions(resource.ProjectId).Result;
if (project != null && project.workspaceId != null)
{
var workspace = client.Workspace.Get(project.workspaceId).Result;
}
switch (resource)
{
case SpeckleUrlLatestModelVersionResource latestVersionResource:
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
break;
case SpeckleUrlModelVersionResource versionResource:
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
// TODO: this wont be the case when we have separation between send and receive components
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
break;
case SpeckleUrlModelObjectResource:
throw new SpeckleException("Object URLs are not supported");
default:
throw new SpeckleException("Unknown Speckle resource type");
}
return (resource, isSender ? projectPermissions.canPublish.authorized : projectPermissions.canLoad.authorized);
}
}
@@ -1,241 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class CreateSpeckleObject : GH_Component
{
public CreateSpeckleObject()
: base(
"Speckle Object",
"SO",
"Create or modify a Speckle Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_object;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter(
"Object",
"O",
"Input Object. Speckle Objects, Model Objects, and geometry are accepted.",
GH_ParamAccess.item
);
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
Params.Input[1].Optional = true;
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
Params.Input[2].Optional = true;
pManager.AddGenericParameter(
"Properties",
"P",
"The properties of the Speckle Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[3].Optional = true;
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
Params.Input[4].Optional = true;
pManager.AddGenericParameter(
"Material",
"m",
"The material of the Speckle Object. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[5].Optional = true;
/* POC: disable for now as we are doing anything with this
pManager.AddTextParameter(
"Path",
"p",
"The Collection Path of the Speckle Object. Should be delimited with `:`",
GH_ParamAccess.item
);
Params.Input[6].Optional = true;
*/
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleObjectParam(), "Object", "O", "Speckle Object", GH_ParamAccess.item);
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Object",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Speckle Object.",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Object, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
IGH_Goo? inputObject = null;
da.GetData(0, ref inputObject);
IGH_GeometricGoo? inputGeometry = null;
da.GetData(1, ref inputGeometry);
string? inputName = null;
da.GetData(2, ref inputName);
IGH_Goo? inputProperties = null;
da.GetData(3, ref inputProperties);
Color? inputColor = null;
da.GetData(4, ref inputColor);
IGH_Goo? inputMaterial = null;
da.GetData(5, ref inputMaterial);
//string? inputPath = null;
//da.GetData(6, ref inputPath);
// keep track of mutation
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
bool mutated = false;
// process the object
SpeckleObjectWrapperGoo result = new();
if (inputObject != null)
{
if (!result.CastFrom(inputObject))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Object input is not valid. Only Speckle Objects, Baked Model Objects, and Geometry are accepted."
);
return;
}
}
// process geometry
// at this point, we can ensure that the Base in the wrapper is a DataObject.
if (inputObject == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Object or Geometry.");
return;
}
if (inputGeometry != null)
{
result.Value.GeometryBase = inputGeometry.GeometricGooToGeometryBase();
Base converted = SpeckleConversionContext.ConvertToSpeckle(result.Value.GeometryBase);
converted[Constants.NAME_PROP] = result.Value.Name;
converted.applicationId = result.Value.ApplicationId;
result.Value.Base = converted;
mutated = true;
}
// process name
if (inputName != null)
{
result.Value.Name = inputName;
mutated = true;
}
// process properties
if (inputProperties != null)
{
SpecklePropertyGroupGoo propGoo = new();
if (!propGoo.CastFrom(inputProperties))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Properties input is not valid. Only Speckle Properties and User Content are accepted."
);
return;
}
result.Value.Properties = propGoo;
mutated = true;
}
// process color (no mutation)
if (inputColor != null)
{
result.Value.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
SpeckleMaterialWrapperGoo matWrapperGoo = new();
if (!matWrapperGoo.CastFrom(inputMaterial))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Material input is not valid. Only Display Materials, Baked Model Materials, and Speckle Materials are accepted."
);
return;
}
result.Value.Material = matWrapperGoo.Value;
}
// process application Id. Use a new appId if mutated, or if this is a new object
result.Value.ApplicationId = mutated
? Guid.NewGuid().ToString()
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
// get the path
string path =
result.Value.Path.Count > 1
? string.Join(Constants.LAYER_PATH_DELIMITER, result.Value.Path)
: result.Value.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.Value);
da.SetData(1, result.Value.GeometryBase);
da.SetData(2, result.Value.Name);
da.SetData(3, result.Value.Properties);
da.SetData(4, result.Value.Color);
da.SetData(5, result.Value.Material);
da.SetData(6, path);
}
}
@@ -17,6 +17,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_create;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public CreateSpeckleProperties()
: base(
@@ -35,7 +36,13 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Properties", "P", "Properties for Speckle Objects", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Properties for Speckle Objects",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
@@ -59,7 +66,17 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
for (int i = 0; i < Params.Input.Count; i++)
{
var paramName = Params.Input[i].NickName;
var propertyValue = ExtractPropertyValue(da, i, paramName);
var data = Params.Input[i].VolatileData.AllData(true).ToList();
if (data.Count == 0)
{
continue;
}
ISpecklePropertyGoo? propertyValue =
Params.Input[i].Access == GH_ParamAccess.item
? ExtractPropertyValue(da, i, paramName)
: ExtractPropertyListValue(da, i, paramName);
if (propertyValue != null)
{
@@ -100,6 +117,26 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
return propertyGoo;
}
private ISpecklePropertyGoo? ExtractPropertyListValue(IGH_DataAccess da, int index, string paramName)
{
List<object?> value = new();
da.GetDataList(index, value);
var propertyGoo = new SpecklePropertyGoo();
if (!propertyGoo.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter '{paramName}' contains invalid data type. Only strings, numbers, and booleans are supported."
);
return null;
}
return propertyGoo;
}
protected override void AppendComponentSpecificMenuItems(ToolStripDropDown menu)
{
Menu_AppendSeparator(menu);
@@ -0,0 +1,170 @@
using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
// NOTE: Why all this madness? The properties passthrough node is restrictive in output type being uniform
// Properties whose values were lists were not being displayed and couldn't be given back to the user as native
// lists. This was (it seemed) the only viable approach.
// [CNX-2364](https://linear.app/speckle/issue/CNX-2364/grasshopper-properties-passthrough-does-not-handle-list-values)
[Guid("474F4699-D641-444F-BC78-E22AAF40B240")]
public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterComponent
{
public ExpandSpeckleProperties()
: base(
"Expand Properties",
"eP",
"Expands Speckle Properties into their individual outputs with correct access types",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_expand;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Speckle Properties to expand",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties) || properties?.Value == null)
{
return;
}
Name = $"Properties ({properties.Value.Count})";
NickName = Name;
var outputParams = new List<OutputParamWrapper>();
foreach (var key in properties.Value.Keys)
{
ISpecklePropertyGoo value = properties.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
var param = new SpeckleOutputParam
{
Name = key,
NickName = key,
Access = outputValue is IList ? GH_ParamAccess.list : GH_ParamAccess.item
};
outputParams.Add(new OutputParamWrapper(param, outputValue));
}
// handle parameter creation/update (only on first iteration)
if (da.Iteration == 0 && OutputMismatch(outputParams))
{
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(outputParams));
return; // exit early
}
// only set data if we have the correct parameter structure
if (Params.Output.Count == outputParams.Count)
{
for (int i = 0; i < outputParams.Count; i++)
{
var outputParam = outputParams[i];
switch (outputParam.Param.Access)
{
case GH_ParamAccess.item:
da.SetData(i, outputParam.Value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outputParam.Value as IList ?? new List<object?>());
break;
}
}
}
}
/// <summary>
/// Creates output parameters based on discovered properties.
/// </summary>
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// remove all existing output parameters
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
// add new output parameters
foreach (var newParam in outputParams)
{
var param = new SpeckleOutputParam
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
}
// notify gh of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
/// <summary>
/// Determines if the current output parameter structure differs from the required structure.
/// </summary>
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
{
if (Params.Output.Count != outputParams.Count)
{
return true;
}
for (int i = 0; i < outputParams.Count; i++)
{
var newParam = outputParams[i];
var oldParam = Params.Output[i];
if (
oldParam.NickName != newParam.Param.NickName
|| oldParam.Name != newParam.Param.Name
|| oldParam.Access != newParam.Param.Access
)
{
return true;
}
}
return false;
}
// IGH_VariableParameterComponent implementation
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
public void VariableParameterMaintenance() { }
public IGH_Param CreateParameter(GH_ParameterSide side, int index) => new SpeckleOutputParam();
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
}
public record OutputParamWrapper(SpeckleOutputParam Param, object? Value);
@@ -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,80 @@ 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))
// use flattened properties to search ALL nested property keys
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
foreach (string key in flattenedProps.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 ?? ""))
bool materialMatches = true;
if (!string.IsNullOrEmpty(material))
{
removedObjects.Add(inputObject.Value);
materialMatches = false;
if (wrapper is SpeckleGeometryWrapper geoWrapper)
{
materialMatches = MatchesSearchPattern(material, geoWrapper.Material?.Name ?? "");
}
else if (wrapper is SpeckleDataObjectWrapper dataObjWrapper)
{
// check if ANY geometry in the data object has a matching material (not sure about this...)
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
materialMatches = dataObjWrapper.Geometries.Any(geo =>
MatchesSearchPattern(material, geo.Material?.Name ?? "")
);
}
}
if (!materialMatches)
{
removedObjects.Add(wrapper);
continue;
}
// filter by application id
if (!MatchesSearchPattern(appId, inputObject.Value.Base.applicationId ?? ""))
if (!MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
// filter by speckle id
if (!MatchesSearchPattern(speckleId, inputObject.Value.Base.id ?? ""))
if (!MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
{
removedObjects.Add(inputObject.Value);
removedObjects.Add(wrapper);
continue;
}
matchedObjects.Add(inputObject.Value);
matchedObjects.Add(wrapper);
}
// Set output objects
dataAccess.SetDataList(0, matchedObjects);
dataAccess.SetDataList(1, removedObjects);
dataAccess.SetDataList(0, matchedObjects.Select(o => o.CreateGoo()));
dataAccess.SetDataList(1, removedObjects.Select(o => o.CreateGoo()));
}
private bool MatchesSearchPattern(string searchPattern, string target)
@@ -1,141 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// <summary>
/// Given a collection, this component will output the objects in the subcollection corresponding to the input path
/// </summary>
[Guid("77CAEE94-F0B9-4611-897C-71F2A22BA311")]
public class GetCollectionObjects : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_query;
public GetCollectionObjects()
: base(
"Query Objects",
"qO",
"Retrieves the objects inside a Speckle collection at the specified path",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(),
"Collection",
"C",
"Collection to retrieve objects from",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"C",
"Get the Speckle objects in the subcollection indicated by this path",
GH_ParamAccess.item
);
Params.Input[1].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"The objects in the input collection that match the queries",
GH_ParamAccess.tree
);
}
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
SpeckleCollectionWrapperGoo collectionWrapperGoo = new();
dataAccess.GetData(0, ref collectionWrapperGoo);
if (collectionWrapperGoo.Value == null)
{
return;
}
string path = "";
dataAccess.GetData(1, ref path);
// filter by collection path
// Note: the collection paths selector will omit the target collection from the path of nested collections.
// the discard ("_objects") will be used to indicate objects found directly in the target collection.
List<SpeckleObjectWrapper> filteredObjects = new();
SpeckleCollectionWrapper? targetCollectionWrapper = null;
if (!string.IsNullOrEmpty(path))
{
targetCollectionWrapper =
path == "_objects" ? collectionWrapperGoo.Value : FindCollection(collectionWrapperGoo.Value, path);
filteredObjects = targetCollectionWrapper
.Elements.Where(e => e is SpeckleObjectWrapper)
.Select(e => (SpeckleObjectWrapper)e)
.ToList();
}
else
{
filteredObjects = GetAllObjectsFromCollection(collectionWrapperGoo.Value).ToList();
}
// Set output objects
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
{
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, filteredObjects);
dataAccess.SetDataTree(0, tree);
}
else
{
dataAccess.SetDataList(0, filteredObjects);
}
}
private IEnumerable<SpeckleObjectWrapper> GetAllObjectsFromCollection(SpeckleCollectionWrapper collectionWrapper)
{
foreach (SpeckleWrapper element in collectionWrapper.Elements)
{
switch (element)
{
case SpeckleCollectionWrapper childCollectionWrapper:
foreach (var item in GetAllObjectsFromCollection(childCollectionWrapper))
{
yield return item;
}
break;
case SpeckleObjectWrapper objectWrapper:
yield return objectWrapper;
break;
}
}
}
private SpeckleCollectionWrapper FindCollection(SpeckleCollectionWrapper root, string unifiedPath)
{
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
List<string> paths = unifiedPath.Split([Constants.LAYER_PATH_DELIMITER], StringSplitOptions.None).ToList();
SpeckleCollectionWrapper currentCollectionWrapper = root;
while (paths.Count != 0)
{
currentCollectionWrapper = currentCollectionWrapper
.Elements.OfType<SpeckleCollectionWrapper>()
.First(wrapper => wrapper.Name == paths.First());
paths.RemoveAt(0);
if (paths.Count == 0)
{
return currentCollectionWrapper;
}
}
throw new SpeckleException("Did not find collection");
}
}
@@ -1,178 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class GetObjectProperties : GH_Component, IGH_VariableParameterComponent
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public GetObjectProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the properties inside Speckle Objects at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"Speckle Objects to retrieve properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
List<string> paths = new();
da.GetDataList(1, paths);
if (paths.Count == 0)
{
return;
}
if (OutputMismatch(paths))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(paths);
}
);
}
else
{
SpeckleObjectWrapperGoo objectWrapperGoo = new();
da.GetData(0, ref objectWrapperGoo);
// flatten object properties, if any
SpecklePropertyGroupGoo properties = objectWrapperGoo.Value.Properties;
if (properties.Value.Count == 0)
{
return;
}
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
for (int i = 0; i < paths.Count; i++)
{
var name = paths[i];
if (FindProperty(flattenedProps, name) is SpecklePropertyGoo prop)
{
da.SetData(i, prop.Value);
}
else
{
da.SetData(i, null);
}
}
}
}
// attempts to find a property by concatenated key, or returns null if not
private SpecklePropertyGoo? FindProperty(Dictionary<string, SpecklePropertyGoo> props, string unifiedPath)
{
if (!props.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
{
return null;
}
return currentGoo;
}
private bool OutputMismatch(List<string> outputParams)
{
if (Params.Output.Count != outputParams.Count)
{
return true;
}
var count = 0;
foreach (var newParam in outputParams)
{
var oldParam = Params.Output[count];
if (oldParam.NickName != newParam || oldParam.Name != newParam)
{
return true;
}
count++;
}
return false;
}
private void CreateOutputs(List<string> outputParams)
{
// Ensure we have the required count of output parameters
while (Params.Output.Count != outputParams.Count)
{
if (Params.Output.Count > outputParams.Count) // if too many, unregister
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
if (Params.Output.Count < outputParams.Count) // if too little, add some
{
var param = new Param_GenericObject
{
Name = "newParam",
NickName = "newParam",
MutableNickName = false,
Access = GH_ParamAccess.item
};
Params.RegisterOutputParam(param);
}
}
// now unify names and nicknames
int index = 0;
foreach (var newParam in outputParams)
{
Params.Output[index].NickName = newParam;
Params.Output[index].Name = newParam;
index++;
}
// now we can update the output params
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new Param_GenericObject
{
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
MutableNickName = true,
Optional = true
};
myParam.NickName = myParam.Name;
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
public void VariableParameterMaintenance() { }
}
@@ -1,11 +1,9 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
#if RHINO8_OR_GREATER
using Grasshopper.Rhinoceros.Model;
#endif
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
@@ -21,36 +19,29 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
) { }
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
protected override Bitmap Icon => Resources.speckle_inputs_property;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
protected override void LoadVolatileData()
{
var objectPropertyGroups = VolatileData
var propertyGroups = VolatileData
.AllData(true)
.OfType<SpeckleObjectWrapperGoo>()
.Select(goo => goo.Value.Properties)
.Where(goo => goo is SpecklePropertyGroupGoo)
.Select(goo =>
goo switch
{
SpecklePropertyGroupGoo geometryGoo => geometryGoo,
_ => throw new InvalidOperationException("Unexpected goo type")
}
)
.ToList();
#if RHINO8_OR_GREATER
// support model objects direct piping also
if (objectPropertyGroups.Count != VolatileData.DataCount)
{
var modelObjects = VolatileData
.AllData(true)
.OfType<ModelObject>()
.Select(mo => new SpeckleObjectWrapperGoo(mo).Value.Properties)
.ToList();
objectPropertyGroups.AddRange(modelObjects);
}
#endif
if (objectPropertyGroups.Count == 0)
if (propertyGroups.Count == 0)
{
return;
}
var paths = GetPropertyPaths(objectPropertyGroups);
var paths = GetPropertyPaths(propertyGroups);
m_data.Clear();
m_data.AppendRange(paths.Select(s => new GH_String(s)));
}
@@ -0,0 +1,99 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class QueryProperties : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public QueryProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the Properties at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Speckle Properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Values", "V", "The values of the specified keys", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a Speckle Properties item");
return;
}
List<string> keys = new();
if (!da.GetDataList(1, keys))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a key");
return;
}
if (properties == null || properties.Value.Count == 0 || keys.Count == 0)
{
return;
}
List<object?> values = new();
foreach (string key in keys)
{
ISpecklePropertyGoo? value = GetValueByPath(properties, key);
values.Add(value);
}
da.SetDataList(0, values);
}
public static ISpecklePropertyGoo? GetValueByPath(SpecklePropertyGroupGoo data, string path)
{
string[] keys = path.Split('.');
ISpecklePropertyGoo? current = data;
foreach (var key in keys)
{
if (current is SpecklePropertyGroupGoo dict)
{
if (dict.Value.TryGetValue(key, out ISpecklePropertyGoo? next))
{
current = next;
}
else
{
return null;
}
}
else
{
return null; // Current is not a dictionary, path is invalid
}
}
return current;
}
}
@@ -0,0 +1,296 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
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",
"P",
"Get the Speckle objects in the sub-collection 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++)
{
// determine output values based on parameter type
List<SpeckleWrapper> outputValues;
if (i == 0)
{
outputValues = filteredObjects;
}
else if (
Enum.TryParse(Params.Output[i].Name, out ObjectType filterType)
&& _filterDict.TryGetValue(filterType, out var filteredList)
)
{
outputValues = filteredList.Cast<SpeckleWrapper>().ToList();
}
else
{
outputValues = [];
}
var outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
// only use topology for the first output when we have a path
if (i == 0 && 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 SpeckleOutputParam
{
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,134 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
public class SpeckleBlockDefinitionPassthrough()
: SpeckleSolveInstance(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_def;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Block Definition",
"BD",
"Input Block Definition. Speckle definitions and Model definitions are accepted.",
GH_ParamAccess.item
);
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry to include in the Block Definition. Speckle geometry and instances or Model objects and instances are accepted.",
GH_ParamAccess.list
);
Params.Input[1].Optional = true;
pManager.AddTextParameter("Name", "N", "Name of the Speckle Definition", GH_ParamAccess.item);
Params.Input[2].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Block Definition",
"BD",
"Speckle Block Definition",
GH_ParamAccess.item
);
pManager.AddGenericParameter("Geometry", "G", "Geometry contained in the Block Definition", GH_ParamAccess.list);
pManager.AddTextParameter("Name", "N", "Name of the Block Definition", GH_ParamAccess.item);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
da.GetData(0, ref inputDefinition);
List<IGH_Goo> inputObjects = new();
da.GetDataList(1, inputObjects);
if (inputDefinition == null && inputObjects.Count == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Geometry.");
return;
}
string? inputName = null;
da.GetData(2, ref inputName);
if (inputDefinition == null && string.IsNullOrWhiteSpace(inputName))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Name for the definition.");
return;
}
// process the definition
// deep copy so we don't mutate the object
SpeckleBlockDefinitionWrapperGoo result = new();
if (inputDefinition != null)
{
result = new SpeckleBlockDefinitionWrapperGoo(inputDefinition.Value.DeepCopy());
result.Value.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
}
// 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,229 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
public class SpeckleBlockInstancePassthrough()
: SpeckleSolveInstance(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int instanceIndex = pManager.AddParameter(
new SpeckleBlockInstanceParam(),
"Block Instance",
"BI",
"Input Block Instance. Speckle instances and Grasshopper instances are accepted.",
GH_ParamAccess.item
);
Params.Input[instanceIndex].Optional = true;
int definitionIndex = pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Definition",
"D",
"Block Instance Definition. Speckle definitions and Grasshopper definitions are accepted.",
GH_ParamAccess.item
);
Params.Input[definitionIndex].Optional = true;
int transformIndex = pManager.AddGenericParameter(
"Transform",
"T",
"Transform of the Speckle instance. Transforms and Planes are accepted.",
GH_ParamAccess.item
);
Params.Input[transformIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Instance", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Instance. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
int colorIndex = pManager.AddColourParameter(
"Color",
"c",
"The color of the Speckle Instance",
GH_ParamAccess.item
);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Instance. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleBlockInstanceParam(),
"Block Instance",
"BI",
"Speckle Block Instance",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpeckleBlockDefinitionWrapperParam(),
"Definition",
"D",
"Block Definition of the instance",
GH_ParamAccess.item
);
pManager.AddGenericParameter("Transform", "T", "Transform of the Block Instance", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Block Instance", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Properties of the Block Instance",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Block Instance.",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpeckleBlockInstanceWrapperGoo? inputInstance = null;
da.GetData(0, ref inputInstance);
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
da.GetData(1, ref inputDefinition);
if (inputInstance == null && inputDefinition == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Instance or Definition.");
return;
}
IGH_Goo? inputTransform = null;
da.GetData(2, ref inputTransform);
string? inputName = null;
da.GetData(3, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(4, ref inputProperties);
Color? inputColor = null;
da.GetData(5, ref inputColor);
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(6, ref inputMaterial);
// process the instance
// deep copy so we don't mutate the incoming object
SpeckleBlockInstanceWrapperGoo result = new();
if (inputInstance != null)
{
result = new SpeckleBlockInstanceWrapperGoo((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy());
result.Value.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
}
// 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,173 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("5CE8AA40-7706-4893-853D-4C77604548FA")]
public class SpeckleDataObjectPassthrough()
: SpeckleSolveInstance(
"Speckle Data Object",
"SDO",
"Create or modify a Speckle Data Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_dataobject;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Input Speckle DataObject. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
int geoIndex = pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object. Speckle Geometry and Grasshopper geometry are accepted.",
GH_ParamAccess.list
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object.",
GH_ParamAccess.list
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
// process the object
// deep copy so we don't mutate the object
SpeckleDataObjectWrapperGoo inputObject = new();
SpeckleDataObjectWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
result = inputObject.Value.DeepCopy();
result.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
}
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,252 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class SpeckleGeometryPassthrough()
: SpeckleSolveInstance(
"Speckle Geometry",
"SG",
"Create or modify a Speckle Geometry",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_geometry;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddGenericParameter(
"Speckle Geometry",
"SG",
"Input Speckle Geometry. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
int geoIndex = pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Geometry.",
GH_ParamAccess.item
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Geometry. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
int colorIndex = pManager.AddColourParameter(
"Color",
"c",
"The color of the Speckle Geometry",
GH_ParamAccess.item
);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Geometry. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
/* POC: disable for now as we are doing anything with this
pManager.AddTextParameter(
"Path",
"p",
"The Collection Path of the Speckle Object. Should be delimited with `:`",
GH_ParamAccess.item
);
Params.Input[6].Optional = true;
*/
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Speckle Geometry", "SG", "Speckle Geometry", GH_ParamAccess.item);
pManager.AddGeometryParameter("Geometry", "G", "Geometry of the Speckle Geometry.", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Geometry",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Speckle Geometry.",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
// process the object
// deep copy so we don't mutate the object
IGH_Goo? inputObject = null;
SpeckleGeometryWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
if (inputObject?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
{
result = gooWrapper.DeepCopy();
result.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Unsupported object type: {inputObject?.TypeName}");
return;
}
}
IGH_GeometricGoo? inputGeometry = null;
da.GetData(1, ref inputGeometry);
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
return;
}
string? inputName = null;
da.GetData(2, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(3, ref inputProperties);
Color? inputColor = null;
da.GetData(4, ref inputColor);
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(5, ref inputMaterial);
// process geometry
// deep copy so we don't mutate the input geo which may be speckle objects
if (inputGeometry != null)
{
if (inputGeometry.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper geoWrapper)
{
SpeckleGeometryWrapper mutatingGeo = geoWrapper.DeepCopy();
if (result is null)
{
result = mutatingGeo;
}
else
{
// we need to switch to the actual object wrapper type of the incoming geo if this is a mutation on the object
if (mutatingGeo is SpeckleBlockInstanceWrapper mutatingInstance && result is not SpeckleBlockInstanceWrapper)
{
MatchNonGeometryProps(mutatingInstance, result);
result = mutatingInstance;
}
else if (mutatingGeo is not SpeckleBlockInstanceWrapper && result is SpeckleBlockInstanceWrapper)
{
MatchNonGeometryProps(mutatingGeo, result);
result = mutatingGeo;
}
mutatingGeo.Base[Constants.NAME_PROP] = result.Name; // assign these before assigning base since otherwise wrapper name and app will reset
mutatingGeo.Base.applicationId = result.ApplicationId; // assign these before assigning base since otherwise wrapper name and app will reset
result.Base = mutatingGeo.Base;
result.GeometryBase = mutatingGeo.GeometryBase;
}
}
else
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"{inputGeometry.TypeName} is not a valid type for Speckle Geometry."
);
return;
}
}
result.NotNull();
// process name
if (inputName != null)
{
result.Name = inputName;
}
// process properties
if (inputProperties != null)
{
result.Properties = inputProperties;
}
// process color (no mutation)
if (inputColor != null)
{
result.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
result.Material = inputMaterial.Value;
}
// no need to process application Id.
// New definitions should have a new appID generated in the new() constructor, and we want to preserve old appID otherwise for changetracking.
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.CreateGoo());
da.SetData(1, result.GeometryBase);
da.SetData(2, result.Name);
da.SetData(3, result.Properties);
da.SetData(4, result.Color);
da.SetData(5, result.Material);
da.SetData(6, path);
}
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
private void MatchNonGeometryProps(SpeckleGeometryWrapper wrapper, SpeckleGeometryWrapper wrapperToMatch)
{
wrapper.Name = wrapperToMatch.Name;
wrapper.ApplicationId = wrapperToMatch.ApplicationId;
wrapper.Properties = wrapperToMatch.Properties;
wrapper.Parent = wrapperToMatch.Parent;
wrapper.Path = wrapperToMatch.Path;
wrapper.Color = wrapperToMatch.Color;
wrapper.Material = wrapperToMatch.Material;
}
}
@@ -0,0 +1,235 @@
using System.Runtime.InteropServices;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// <summary>
/// CreateSpeckleProperties passthrough component by key value pairs
/// </summary>
[Guid("FED2298C-0D2B-4868-94B5-B8D17F9385A5")]
public class SpecklePropertiesPassthrough : SpeckleSolveInstance
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_properties;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
private enum PropertyMode
{
Merge, // this should be default mode
Replace,
Remove
}
private PropertyMode _mode = PropertyMode.Merge;
private PropertyMode Mode
{
get => _mode;
set
{
if (_mode != value)
{
_mode = value;
Message = Mode.ToString();
ExpireSolution(true);
}
}
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Input properties", GH_ParamAccess.item);
Params.Input[0].Optional = true;
pManager.AddTextParameter("Keys", "k", "Keys of all properties", GH_ParamAccess.list);
Params.Input[1].Optional = true;
pManager.AddGenericParameter(
"Values",
"v",
"Values of all properties. Accepts text, number, bool, null, or other properties as inputs.",
GH_ParamAccess.list
);
Params.Input[2].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Properties", GH_ParamAccess.item);
pManager.AddTextParameter("Keys", "k", "Keys of all properties", GH_ParamAccess.list);
pManager.AddGenericParameter("Values", "v", "Values of all properties", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(0, ref inputProperties);
List<string> inputKeys = new();
bool hasKeys = da.GetDataList(1, inputKeys);
List<object?> inputValues = new();
bool hasValues = da.GetDataList(2, inputValues);
// if no props or keyvalues were input, create empty props and return
if (inputProperties == null && !hasKeys)
{
SpecklePropertyGroupGoo emptyProps = new();
da.SetData(0, emptyProps);
da.SetDataList(1, emptyProps.Value.Keys);
da.SetDataList(2, emptyProps.Value.Values);
return;
}
// validate that keys and values are of valid length
if ((Mode == PropertyMode.Merge || Mode == PropertyMode.Replace) && inputKeys.Count != inputValues.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Keys and values are mismatched in length");
return;
}
if (Mode == PropertyMode.Remove && (inputKeys.Count == 0 || inputValues.Count > 0))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Only input keys to remove");
return;
}
// process the properties
Dictionary<string, ISpecklePropertyGoo> result =
inputProperties is null || Mode == PropertyMode.Replace
? new()
: inputProperties.Value.ToDictionary(entry => entry.Key, entry => entry.Value);
// process keys and values
if (hasKeys)
{
// check for duplicate keys
var duplicates = inputKeys.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key);
if (duplicates.Any())
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Duplicate property keys found: {string.Join(", ", duplicates)}"
);
return;
}
// set keyvalue pairs
for (int i = 0; i < inputKeys.Count; i++)
{
string key = inputKeys[i];
object? value = Mode == PropertyMode.Remove ? null : inputValues[i];
ISpecklePropertyGoo? convertedValue = null;
switch (value)
{
case SpecklePropertyGroupGoo propGoo:
convertedValue = propGoo;
break;
case null:
convertedValue = new SpecklePropertyGoo();
break;
default:
SpecklePropertyGoo propValue = new();
if (!propValue.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
);
return;
}
convertedValue = propValue;
break;
}
switch (Mode)
{
case PropertyMode.Merge:
if (result.ContainsKey(key))
{
result[key] = convertedValue;
}
else
{
result.Add(key, convertedValue);
}
break;
case PropertyMode.Replace:
result.Add(key, convertedValue);
break;
case PropertyMode.Remove:
result.Remove(key);
break;
}
}
}
var groupGoo = new SpecklePropertyGroupGoo(result);
da.SetData(0, groupGoo);
da.SetDataList(1, result.Keys);
da.SetDataList(2, result.Values);
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
Menu_AppendSeparator(menu); // modes section
foreach (PropertyMode mode in Enum.GetValues(typeof(PropertyMode)))
{
var modeItem = Menu_AppendItem(menu, mode.ToString(), (_, _) => Mode = mode, true, mode == Mode);
switch (mode)
{
case PropertyMode.Merge:
modeItem.ToolTipText =
"Input keyvalue pairs will be merged with existing properties. Any existing keys will be updated with new values.";
break;
case PropertyMode.Replace:
modeItem.ToolTipText = "Existing properties will be cleared and replaced by input keyvalue pairs.";
break;
case PropertyMode.Remove:
modeItem.ToolTipText = "Existing keyvalue pairs that match the input keys will be removed from properties.";
break;
}
}
Menu_AppendSeparator(menu);
}
public override bool Write(GH_IWriter writer)
{
var result = base.Write(writer);
writer.SetString("Mode", Mode.ToString());
return result;
}
public override bool Read(GH_IReader reader)
{
var result = base.Read(reader);
string mode = "";
if (reader.TryGetString("Mode", ref mode))
{
if (Enum.TryParse(mode, out PropertyMode modeEnum))
{
Mode = modeEnum;
}
}
return result;
}
}
@@ -0,0 +1,188 @@
using System.Diagnostics;
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Credentials;
using Timer = System.Timers.Timer;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations;
public class AccountManagerComponent : GH_Component, IDisposable
{
private bool _disposed;
private bool _isAddingAccount;
private Timer _timeoutTimer;
private Timer? _accountCheckerTimer;
private List<Account>? Accounts { get; set; }
private string? CustomUrlInput { get; set; }
private readonly IAccountManager _accountManager;
public override Guid ComponentGuid => new("c8ede281-acdf-49bf-8611-e9579be1bd41");
protected override Bitmap Icon => Resources.speckle_operations_account;
public override GH_Exposure Exposure => GH_Exposure.primary;
public GhContextMenuButton SignInButton { get; }
public AccountManagerComponent()
: base(
"Sign In",
"SI",
"Sign in to a Speckle Account",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
)
{
Attributes = new AccountManagerComponentAttributes(this);
_accountManager = PriorityLoader.Container.GetRequiredService<IAccountManager>();
Accounts = _accountManager.GetAccounts().ToList();
SignInButton = new GhContextMenuButton("Sign In", "Sign In", "Click to sign into Speckle account.", AuthFlow);
}
private bool AuthFlow(ToolStripDropDown menu)
{
_isAddingAccount = true;
ResumeAccountChecker();
string url = string.IsNullOrEmpty(CustomUrlInput)
? "http://localhost:29364/auth/add-account"
: $"http://localhost:29364/auth/add-account?serverUrl={new Uri(CustomUrlInput).GetLeftPart(UriPartial.Authority)}";
// Open the auth URL in the default browser
try
{
Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
}
catch (Exception ex) when (!ex.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Sign in failed: {ex.Message}");
_isAddingAccount = false;
return false;
}
_timeoutTimer = new Timer(30_000);
_timeoutTimer.Elapsed += (s, e) =>
{
_timeoutTimer.Stop();
if (_isAddingAccount)
{
_isAddingAccount = false;
PauseAccountChecker();
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
"Sign in timed out. This may have happened because you tried adding an existing account."
);
}
};
_timeoutTimer.Start();
return true;
}
private bool CheckIfAccountAdded()
{
var previousAccountCount = Accounts?.Count ?? 0;
Accounts = _accountManager.GetAccounts().ToList();
return previousAccountCount < Accounts.Count;
}
private void PauseAccountChecker()
{
_accountCheckerTimer?.Stop();
_accountCheckerTimer?.Dispose();
_accountCheckerTimer = null;
}
private void ResumeAccountChecker()
{
_accountCheckerTimer?.Dispose();
_accountCheckerTimer = new Timer(1000); // check every 1 second
_accountCheckerTimer.Elapsed += (s, e) =>
{
bool accountAdded = CheckIfAccountAdded();
if (accountAdded)
{
_accountCheckerTimer.Stop();
_isAddingAccount = false;
// Optionally cancel timeout timer
_timeoutTimer?.Stop();
OnPingDocument()
?.ScheduleSolution(
100,
doc =>
{
ExpireSolution(true);
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Account added successfully!");
}
);
}
};
_accountCheckerTimer.Start();
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
var urlIndex = pManager.AddTextParameter(
"Server Url",
"Url",
"Optional URL for signing into a self deployed Speckle server.",
GH_ParamAccess.item
);
pManager[urlIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddTextParameter($"Accounts", "Accounts", "List of available accounts", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
string? urlInput = null;
if (da.GetData(0, ref urlInput))
{
CustomUrlInput = urlInput;
}
if (Accounts != null)
{
da.SetDataList(0, Accounts);
}
else
{
da.SetDataList(0, new List<Account>());
}
}
public override void ExpirePreview(bool redraw)
{
SignInButton.ExpirePreview(redraw);
base.ExpirePreview(redraw);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_timeoutTimer?.Dispose();
_accountManager.Dispose();
_accountCheckerTimer?.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
@@ -0,0 +1,54 @@
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations;
public class AccountManagerComponentAttributes : GH_ComponentAttributes
{
private readonly AccountManagerComponent _typedOwner;
public AccountManagerComponentAttributes(IGH_Component component)
: base(component)
{
_typedOwner = (AccountManagerComponent)component;
}
public override void AppendToAttributeTree(List<IGH_Attributes> attributes)
{
base.AppendToAttributeTree(attributes);
_typedOwner.SignInButton.Attributes?.AppendToAttributeTree(attributes);
}
private void InitialiseAttributes()
{
_typedOwner.SignInButton.Attributes ??= new GhContextMenuButtonAttributes(_typedOwner.SignInButton)
{
Parent = this,
};
}
protected override void Layout()
{
base.Layout();
var baseRec = GH_Convert.ToRectangle(Bounds);
baseRec.Height += 26;
var btnRec = baseRec;
btnRec.Y = baseRec.Bottom - 26;
btnRec.Height = 26;
btnRec.Inflate(-2, -2);
Bounds = baseRec;
InitialiseAttributes();
_typedOwner.SignInButton.Attributes.Pivot = btnRec.Location;
_typedOwner.SignInButton.Attributes.Bounds = btnRec;
}
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
_typedOwner.SignInButton.Attributes.RenderToCanvas(canvas, channel);
}
}
@@ -1,10 +1,10 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
public record GrasshopperReceiveInfo(
string AccountId,
Uri ServerUrl,
Account Account,
string? WorkspaceId,
string ProjectId,
string ProjectName,
@@ -13,4 +13,4 @@ public record GrasshopperReceiveInfo(
string SelectedVersionId,
string SourceApplication,
string? SelectedVersionUserId
) : ReceiveInfo(AccountId, ServerUrl, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, SourceApplication);
) : ReceiveInfo(Account, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, SourceApplication);
@@ -7,7 +7,6 @@ using GrasshopperAsyncComponent;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -26,7 +25,7 @@ using Speckle.Sdk.Models.Extensions;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
[Guid("1587DF34-83E5-4AFE-B42E-F7C5C37ECD68")]
public class ReceiveAsyncComponent : GH_AsyncComponent
public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
{
public ReceiveAsyncComponent()
: base("Load", "L", "Load a model from Speckle", ComponentCategories.PRIMARY_RIBBON, ComponentCategories.OPERATIONS)
@@ -38,6 +37,8 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_operations_load;
public override GH_Exposure Exposure => GH_Exposure.secondary;
public string InputType { get; set; }
public bool AutoReceive { get; set; }
public string ReceivedVersionId { get; set; }
@@ -270,7 +271,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
ResetApiClient(UrlModelResource);
}
private void ApiClient_OnVersionCreated(object sender, ProjectVersionsUpdatedMessage e)
private void ApiClient_OnVersionCreated(object? sender, ProjectVersionsUpdatedMessage e)
{
HandleNewCommit();
}
@@ -280,11 +281,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
try
{
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Account? account =
urlResource.AccountId != null
? scope.Get<IAccountManager>().GetAccount(urlResource.AccountId)
: scope.Get<IAccountService>().GetAccountWithServerUrlFallback("", new Uri(urlResource.Server)); // fallback the account that matches with URL if any
Account? account = urlResource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
@@ -302,24 +299,28 @@ public class ReceiveAsyncComponent : GH_AsyncComponent
}
}
public class ReceiveComponentWorker : WorkerInstance
public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponent>
{
public ReceiveComponentWorker(GH_Component p)
: base(p) { }
public ReceiveComponentWorker(
ReceiveAsyncComponent p,
string id = "baseWorker",
CancellationToken cancellationToken = default
)
: base(p, id, cancellationToken) { }
public Base Root { get; set; }
public SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo Result { get; set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance Duplicate()
public override WorkerInstance<ReceiveAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
{
return new ReceiveComponentWorker(Parent);
return new ReceiveComponentWorker(Parent, id, cancellationToken);
}
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
{
UrlModelResource = ((ReceiveAsyncComponent)Parent).UrlModelResource;
UrlModelResource = Parent.UrlModelResource;
}
public override void SetData(IGH_DataAccess da)
@@ -334,7 +335,7 @@ public class ReceiveComponentWorker : WorkerInstance
Parent.AddRuntimeMessage(level, message);
}
var parent = (ReceiveAsyncComponent)Parent;
var parent = Parent;
parent.CurrentComponentState = ComponentState.UpToDate;
@@ -348,130 +349,19 @@ public class ReceiveComponentWorker : WorkerInstance
da.SetData(0, Result);
}
#pragma warning disable CA1506
public override void DoWork(Action<string, double> reportProgress, Action done)
#pragma warning restore CA1506
public override async Task DoWork(Action<string, double> reportProgress, Action done)
{
var receiveComponent = (ReceiveAsyncComponent)Parent;
try
{
if (UrlModelResource is null)
{
throw new InvalidOperationException("Model Resource was null");
}
// Means it's a copy paste of an empty non-init component; set the record and exit fast.
if (receiveComponent.JustPastedIn && !receiveComponent.AutoReceive)
{
receiveComponent.JustPastedIn = false;
return;
}
receiveComponent.CurrentComponentState = ComponentState.Receiving;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
receiveComponent.OnDisplayExpired(true);
}
);
var t = Task.Run(async () =>
{
// Step 1 - RECEIVE FROM SERVER
var receiveInfo = await UrlModelResource
.GetReceiveInfo(receiveComponent.ApiClient, CancellationToken)
.ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//eceiveComponent.Message = $"{p.Status}";
});
if (CancellationToken.IsCancellationRequested)
{
return;
}
if (receiveInfo == null)
{
done();
return;
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
if (CancellationToken.IsCancellationRequested)
{
return;
}
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
LocalToGlobalUnpacker localToGlobalUnpacker = new();
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
);
// TODO: unpack colors and render materials
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
GrasshopperCollectionRebuilder collectionRebuilder =
new((Root as Collection) ?? new Collection() { name = "unnamed" });
LocalToGlobalMapHandler mapHandler =
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
int count = 0;
int total = localToGlobalMaps.Count;
foreach (var map in localToGlobalMaps)
{
mapHandler.CreateGrasshopperObjectFromMap(map);
count++;
}
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", receiveComponent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add(
"isMultiplayer",
receiveInfo.SelectedVersionUserId != receiveComponent.ApiClient.Account.userInfo.id
);
}
await scope
.Get<IMixPanelManager>()
.TrackEvent(MixPanelEvents.Receive, receiveComponent.ApiClient.Account, customProperties);
// DONE
done();
});
t.Wait();
await Receive(reportProgress);
done();
}
catch (OperationCanceledException) when (CancellationToken.IsCancellationRequested)
{
RuntimeMessages.Add((GH_RuntimeMessageLevel.Remark, "Operation cancelled"));
Parent.CurrentComponentState = ComponentState.Expired;
//No need to call `done()` - GrasshopperAsyncComponent assumes immediate cancel,
//thus it has already performed clean-up actions that would normally be done on `done()`
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -479,6 +369,110 @@ public class ReceiveComponentWorker : WorkerInstance
done();
}
}
#pragma warning disable CA1506
private async Task Receive(Action<string, double> reportProgress)
#pragma warning restore CA1506
{
if (UrlModelResource is null)
{
throw new InvalidOperationException("Model Resource was null");
}
// Means it's a copy paste of an empty non-init component; set the record and exit fast.
if (Parent.JustPastedIn && !Parent.AutoReceive)
{
Parent.JustPastedIn = false;
return;
}
Parent.CurrentComponentState = ComponentState.Receiving;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
Parent.OnDisplayExpired(true);
}
);
// Step 1 - RECEIVE FROM SERVER
var receiveInfo = await UrlModelResource.GetReceiveInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//eceiveComponent.Message = $"{p.Status}";
});
CancellationToken.ThrowIfCancellationRequested();
if (receiveInfo == null)
{
return;
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
CancellationToken.ThrowIfCancellationRequested();
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// separate atomic objects from block instances
var (atomicObjects, blockInstances) = scope
.Get<RootObjectUnpacker>()
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
// initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(Root as Collection) ?? new Collection { name = "unnamed" }
);
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
foreach (var atomicContext in atomicObjects)
{
mapHandler.ConvertAtomicObject(atomicContext);
}
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", Parent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id);
}
await scope.Get<IMixPanelManager>().TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
}
}
public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
@@ -537,7 +531,7 @@ public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
else
{
var palette =
state == ComponentState.Expired || state == ComponentState.UpToDate || state == ComponentState.Cancelled
state == ComponentState.Expired || state == ComponentState.UpToDate
? GH_Palette.Black
: GH_Palette.Transparent;
var text = state != ComponentState.Receiving ? "Load" : "Loading...";
@@ -2,7 +2,6 @@ using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
@@ -116,18 +115,12 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var accountService = scope.ServiceProvider.GetRequiredService<IAccountService>();
var accountManager = scope.ServiceProvider.GetRequiredService<IAccountManager>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
// Do the thing 👇🏼
Account? account =
input.Resource.AccountId != null
? accountManager.GetAccount(input.Resource.AccountId)
: accountService.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server)); // fallback the account that matches with URL if any
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
@@ -165,32 +158,39 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
// We need to rethink these lovely unpackers, there's a bit too many of 'em
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
var traversalContextUnpacker = new TraversalContextUnpacker();
var unpackedRoot = rootObjectUnpacker.Unpack(root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
// split atomic objects from block components before conversion
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
// unpack colors and render materials
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
// Initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(root as Collection) ?? new Collection { name = "unnamed" }
);
GrasshopperCollectionRebuilder collectionRebuilder =
new((root as Collection) ?? new Collection() { name = "unnamed" });
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
LocalToGlobalMapHandler mapHandler =
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
foreach (var map in localToGlobalMaps)
foreach (var atomicContext in atomicObjects)
{
mapHandler.CreateGrasshopperObjectFromMap(map);
mapHandler.ConvertAtomicObject(atomicContext);
}
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo };
@@ -1,12 +1,12 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
public record GrasshopperSendInfo(
string AccountId,
Uri ServerUrl,
Account Account,
string? WorkspaceId,
string ProjectId,
string ModelId,
string SourceApplication
) : SendInfo(AccountId, ServerUrl, ProjectId, ModelId, SourceApplication);
) : SendInfo(Account, ProjectId, ModelId, SourceApplication);

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