Compare commits

..

96 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
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
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 de662e4a2b adds material quantities for pipes 2025-07-30 21:00:17 +01:00
244 changed files with 7606 additions and 2388 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)
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -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>
@@ -112,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);
}
@@ -158,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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -73,6 +73,7 @@ public class NavisworksSendBinding : ISendBinding
new IncludeInternalPropertiesSetting(false),
new ConvertHiddenElementsSetting(false),
new PreserveModelHierarchySetting(false),
new RevitCategoryMappingSetting(false)
];
public async Task Send(string modelCardId) =>
@@ -93,7 +94,8 @@ public class NavisworksSendBinding : ISendBinding
visualRepresentationMode: _toSpeckleSettingsManagerNavisworks.GetVisualRepresentationMode(modelCard),
convertHiddenElements: _toSpeckleSettingsManagerNavisworks.GetConvertHiddenElements(modelCard),
includeInternalProperties: _toSpeckleSettingsManagerNavisworks.GetIncludeInternalProperties(modelCard),
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard)
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard),
mappingToRevitCategories: _toSpeckleSettingsManagerNavisworks.GetMappingToRevitCategories(modelCard)
)
);
@@ -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)
{
@@ -78,6 +79,28 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
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)
{
if (modelCard == null)
@@ -25,10 +25,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenEleementsSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\OriginModeSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\PreserveModelHierarchySetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\RevitCategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManagerNavisworks.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\VisualRepresentationSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\NavisworksSelectionFilter.cs"/>
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"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.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"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;
@@ -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;
@@ -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)
@@ -19,7 +19,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
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(
@@ -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;
}
}
@@ -225,7 +225,16 @@ 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.");
}
@@ -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)
@@ -59,7 +59,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
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(
@@ -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))
{
@@ -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>(
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino7": {
@@ -382,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -403,14 +403,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.logging": {
@@ -337,13 +337,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -382,18 +381,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -403,14 +402,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -1,7 +1,6 @@
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;
@@ -61,7 +60,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
{
var param = new Param_GenericObject()
var param = new SpeckleOutputParam
{
Name = "_objects",
NickName = "_objs",
@@ -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,
@@ -191,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,
@@ -214,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,
@@ -227,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);
@@ -28,100 +28,185 @@ 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)
{
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 SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null:
// get children elements from the wrapper to override the elements prop while parsing
List<IGH_Goo> children = collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList();
outputParams = ParseSpeckleWrapper(collectionGoo.Value, children);
break;
case SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null:
// get geometries from the wrapper to override the displayvalue prop while parsing
List<IGH_Goo> display = dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList();
outputParams = ParseSpeckleWrapper(dataObjectGoo.Value, null, display);
break;
case SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null:
outputParams = ParseSpeckleWrapper(objectGoo.Value);
break;
case SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null:
outputParams = ParseSpeckleWrapper(blockInstanceGoo.Value);
break;
case SpeckleBlockDefinitionWrapperGoo blockDef:
outputParams = ParseSpeckleWrapper(blockDef.Value);
break;
case SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null:
outputParams = ParseSpeckleWrapper(materialGoo.Value);
break;
var allFields = DiscoverAllFieldsFromInput();
case SpecklePropertyGroupGoo propGoo:
Name = $"properties ({propGoo.Value.Count})";
outputParams = new();
foreach (var key in propGoo.Value.Keys)
if (allFields.Count > 0)
{
var requiredOutputs = CreateOutputParamsFromFieldNames(allFields);
if (OutputMismatch(requiredOutputs))
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value is SpecklePropertyGoo prop
? prop.Value
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
OutputParamWrapper output =
outputValue is IList
? CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.list)
: CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item);
outputParams.Add(output);
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(requiredOutputs));
return;
}
break;
default:
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return;
}
}
// process current object normally
object data = new();
if (!da.GetData(0, ref data))
{
return;
}
var outputParams = DeconstructObject(data);
if (outputParams == null)
{
return;
}
// set component name based on the current object
NickName = Name;
if (da.Iteration == 0 && OutputMismatch(outputParams))
// set output data - fill missing fields with nulls for objects that don't have all fields
SetOutputData(da, outputParams);
}
/// <summary>
/// Discovers all unique field names 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> ParseSpeckleWrapper(
@@ -146,128 +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();
// override list value if base is a collection and this is the elements prop, since this is empty if coming from a collectionwrapper
if (@base is Collection && prop.Key == "elements" && elements != null)
{
list = elements;
}
// override list value if base is a dataobject and this is the displayvalue prop, since this is empty if coming from a dataobject wrapper
if (@base is Speckle.Objects.Data.DataObject && prop.Key == "displayValue" && displayValue != null)
{
list = displayValue;
}
foreach (var x in list)
{
switch (x)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base xBase:
nativeObjects.AddRange(ConvertOrCreateWrapper(xBase));
break;
default:
nativeObjects.Add(x);
break;
}
}
result.Add(CreateOutputParamByKeyValue(prop.Key, nativeObjects, GH_ParamAccess.list));
break;
case Dictionary<string, object?> dict: // this should be treated a properties dict
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
break;
case SpeckleWrapper wrapper:
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
break;
case Base baseValue:
result.Add(CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list));
break;
default:
// we don't want to output dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
continue;
}
result.Add(CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item));
break;
var outputParam = CreateOutputParamForProperty(prop, @base, elements, displayValue);
if (outputParam != null)
{
result.Add(outputParam);
}
}
return result;
}
/// <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
// attempt conversion to host geometry
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
List<SpeckleGeometryWrapperGoo> convertedWrappers = new();
foreach ((object o, Base b) in convertedBase)
{
GeometryBase? g = o as GeometryBase;
SpeckleGeometryWrapper convertedWrapper =
new()
{
Base = b,
GeometryBase = g,
Name = b["name"] as string ?? "",
Color = null,
Material = null
};
convertedWrappers.Add(new(convertedWrapper));
}
return convertedWrappers;
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.
SpeckleGeometryWrapper convertedWrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new() { new SpeckleGeometryWrapperGoo(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,
@@ -297,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,
@@ -322,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)
@@ -334,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
@@ -346,7 +454,6 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
{
return true;
}
count++;
}
return false;
@@ -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);
@@ -137,7 +137,10 @@ public class FilterSpeckleObjects : GH_Component
if (properties is not null)
{
foreach (string key in properties.Value.Keys)
// 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)
{
if (MatchesSearchPattern(property, key))
{
@@ -155,13 +158,29 @@ public class FilterSpeckleObjects : GH_Component
}
// filter by material name
if (wrapper is SpeckleGeometryWrapper geoWrapper)
bool materialMatches = true;
if (!string.IsNullOrEmpty(material))
{
if (!MatchesSearchPattern(material, geoWrapper.Material?.Name ?? ""))
materialMatches = false;
if (wrapper is SpeckleGeometryWrapper geoWrapper)
{
removedObjects.Add(wrapper);
continue;
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
@@ -1,7 +1,5 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.DocObjects;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
@@ -40,8 +38,8 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
pManager.AddTextParameter(
"Path",
"C",
"Get the Speckle objects in the subcollection indicated by this path",
"P",
"Get the Speckle objects in the sub-collection indicated by this path",
GH_ParamAccess.item
);
@@ -137,10 +135,28 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
// Set output objects
for (int i = 0; i < Params.Output.Count; i++)
{
List<SpeckleWrapper> outputValues =
i == 0 ? filteredObjects : _filterDict[Filters[i - 1]].Select(o => (SpeckleWrapper)o).ToList();
List<IGH_Goo> outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
// 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);
@@ -244,7 +260,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
_outputFilterIndices = null;
ObjectType filter = previousFilterIndex is null ? Filters.First() : Filters[(int)previousFilterIndex + 1];
return new Param_GenericObject
return new SpeckleOutputParam
{
Name = filter.ToString(),
NickName = GetFilterNickName(filter),
@@ -273,10 +289,8 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
base.RemovedFromDocument(document);
}
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args)
{
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();
}
}
@@ -84,7 +84,12 @@ public class SpeckleBlockDefinitionPassthrough()
// process the definition
// deep copy so we don't mutate the object
SpeckleBlockDefinitionWrapperGoo result = inputDefinition != null ? new(inputDefinition.Value.DeepCopy()) : new();
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)
@@ -151,8 +151,12 @@ public class SpeckleBlockInstancePassthrough()
// process the instance
// deep copy so we don't mutate the incoming object
SpeckleBlockInstanceWrapperGoo result =
inputInstance != null ? new((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy()) : new();
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)
@@ -98,6 +98,7 @@ public class SpeckleDataObjectPassthrough()
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();
@@ -125,6 +125,7 @@ public class SpeckleGeometryPassthrough()
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
{
@@ -98,13 +98,13 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
// 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");
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");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Only input keys to remove");
return;
}
@@ -0,0 +1,69 @@
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using Grasshopper.Kernel.Parameters;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// <summary>
/// Simple extension of Param_GenericObject that adds "Extract parameter" functionality.
/// Follows the existing v3 codebase patterns.
/// </summary>
public class SpeckleOutputParam : Param_GenericObject
{
public override Guid ComponentGuid => new("D2B4713D-FE8B-4EF0-8445-B6096DB15B24");
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
// only show extract parameter option for output parameters that have no connections
if (Kind == GH_ParamKind.output && Recipients.Count == 0)
{
Menu_AppendSeparator(menu);
Menu_AppendItem(menu, "Extract parameter", Menu_ExtractOutputParameterClicked, true);
}
}
/// <summary>
/// Extract parameter implementation - taken from v2 legacy and simplified for v3.
/// </summary>
private void Menu_ExtractOutputParameterClicked(object sender, EventArgs e)
{
var archive = new GH_Archive();
if (!archive.AppendObject(this, "Parameter"))
{
return;
}
var newParam = new SpeckleOutputParam();
newParam.CreateAttributes();
if (!archive.ExtractObject(newParam, "Parameter"))
{
return;
}
newParam.NewInstanceGuid();
newParam.Attributes.Selected = false;
newParam.Attributes.PerformLayout();
newParam.Attributes.Pivot = new PointF(
Attributes.Parent.Bounds.Right + newParam.Attributes.Bounds.Width * 0.5f + 15,
Attributes.Pivot.Y
);
newParam.MutableNickName = true;
if (newParam.Attributes is GH_FloatingParamAttributes floating)
{
floating.PerformLayout();
}
var document = OnPingDocument();
if (document != null)
{
document.AddObject(newParam, false);
newParam.AddSource(this);
newParam.ExpireSolution(true);
}
}
}
@@ -22,21 +22,51 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
return CastFromModelObject(modelObject.UserText);
case ModelUserText userText:
Dictionary<string, ISpecklePropertyGoo> dictionary = new();
foreach (KeyValuePair<string, string> entry in userText)
{
SpecklePropertyGoo value = new() { Value = entry.Value };
dictionary.Add(entry.Key, value);
}
Value = dictionary;
return true;
var processedDictionary = ConvertToNested(userText.ToDictionary(o => o.Key, o => (object)o.Value));
return CastFrom(processedDictionary);
default:
return false;
}
}
// Property keys may already be concatenated with the `.` char, eg if baked from grasshopper.
public Dictionary<string, object> ConvertToNested(Dictionary<string, object> flatDict)
{
var nestedDict = new Dictionary<string, object>();
foreach (string keyPath in flatDict.Keys)
{
var keys = keyPath.Split('.');
var current = nestedDict;
for (int i = 0; i < keys.Length; i++)
{
var key = keys[i];
if (i == keys.Length - 1)
{
current[key] = flatDict[keyPath];
}
else
{
if (!current.TryGetValue(key, out var next))
{
var newDict = new Dictionary<string, object>();
current[key] = newDict;
current = newDict;
}
else
{
current = (Dictionary<string, object>)next;
}
}
}
}
return nestedDict;
}
private bool CastToModelObject<T>(ref T target)
{
var type = typeof(T);
@@ -35,19 +35,20 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
private bool HandleModelObject(ModelObject modelObject)
{
modelObject.CastTo<GeometryBase>(out GeometryBase? geometryBase);
if (geometryBase is null)
modelObject.CastTo<IGH_GeometricGoo>(out IGH_GeometricGoo? geometryGoo);
if (geometryGoo is null)
{
throw new InvalidOperationException($"Could not retrieve geometry from model object.");
}
GeometryBase geometryBase = geometryGoo.ToGeometryBase();
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(geometryBase);
// get layer, props, color, and mat
SpeckleCollectionWrapper? collection = GetLayerCollectionFromModelObject(modelObject);
SpecklePropertyGroupGoo? props = GetPropsFromModelObjectAndAssignToBase(modelObject, converted);
Color? color = GetColorFromModelObject(modelObject);
SpeckleMaterialWrapper? material = GetMaterialFromModelObject(modelObject);
Color? color = GetColorFromModelObject(modelObject, material);
// get the definition if this is an instance
SpeckleBlockDefinitionWrapper? definition = GetBlockDefinition(geometryBase);
@@ -189,6 +190,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
Transform = instance.Xform,
Definition = definition, // May be null in pure Grasshopper workflows
Parent = parent,
Path = parent?.Path ?? new(),
Name = name,
Color = color,
Material = mat,
@@ -200,6 +202,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
GeometryBase = geometryBase,
Base = @base,
Parent = parent,
Path = parent?.Path ?? new(),
Name = name,
Color = color,
Material = mat,
@@ -268,7 +271,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
return null;
}
private Color? GetColorFromModelObject(ModelObject modelObject)
private Color? GetColorFromModelObject(ModelObject modelObject, SpeckleMaterialWrapper? material)
{
// we need to retrieve the actual color by the color source (otherwise will return default color for anything other than by object)
int? argb = null;
@@ -281,8 +284,10 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
argb = modelObject.Display.Color?.Color.ToArgb();
break;
case ObjectColorSource.ColorFromMaterial:
Rhino.Render.RenderMaterial? mat = GetRenderMaterial(modelObject);
argb = mat?.ToMaterial(Rhino.Render.RenderTexture.TextureGeneration.Skip)?.DiffuseColor.ToArgb();
if (material is not null)
{
argb = material.Material.diffuse;
}
break;
default:
break;
@@ -330,10 +330,22 @@ namespace Speckle.Connectors.GrasshopperShared.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap speckle_properties_properties {
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap speckle_properties_expand
{
get
{
object obj = ResourceManager.GetObject("speckle_properties_expand", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
public static System.Drawing.Bitmap speckle_properties_properties {
get {
object obj = ResourceManager.GetObject("speckle_properties_properties", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
@@ -208,4 +208,7 @@
<data name="speckle_state_access" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speckle_state_access.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="speckle_properties_expand" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\speckle_properties_expand.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
@@ -33,11 +33,12 @@ public class PriorityLoader : GH_AssemblyPriority
public static IServiceScope CreateScopeForActiveDocument()
{
// NOTE: introduction of AddVisualizationProperties setting not accounted for in GH, hence hardcoded as true (i.e. "as before")
var scope = Container.CreateScope();
var rhinoConversionSettingsFactory = scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc, true));
return scope;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -16,6 +16,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\CollectionPathsSelector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\CreateCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\ExpandCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\ExpandSpeckleProperties.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\SpeckleBlockInstancePassthrough.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\SpeckleBlockDefinitionPassthrough.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Dev\TokenUrlComponent.cs" />
@@ -65,6 +66,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SpeckleResource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SpeckleResourceBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleOutputParam.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpecklePropertyGroupParam.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapperParam.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapperGoo.cs" />
@@ -306,9 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -341,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino7": {
@@ -401,18 +401,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -422,14 +422,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
@@ -306,9 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -341,13 +341,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -401,18 +400,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -422,14 +421,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--This csproj is NOT designed to be run as a rhino plugin-->
<!--This csproj is NOT the rhino importer-->
<!--This csproj is the connector code used by the Speckle.Importers.Rhino project-->
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Configurations>Debug;Release;Local</Configurations>
<RhinoVersion>8</RhinoVersion>
<DefineConstants>$(DefineConstants);RHINO8;RHINO7_OR_GREATER;RHINO8_OR_GREATER</DefineConstants>
<TargetExt>.dll</TargetExt>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
<!-- .NET Core uses this to move native dependencies into a root for runtime selection and usage for non-windows development https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#enablewindowstargeting -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <!--This is needed for managed dependencies-->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <!--This is needed for the rest-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="EmbeddedResources\**\*" />
<EmbeddedResource Include="Resources\**\*.ico" />
</ItemGroup>
<Import Project="..\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems" Label="Shared" />
<ItemGroup>
<None Include="..\Toolbars\Speckle.Connectors.Rhino.rui" Link="$(AssemblyName).rui" CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>
</Project>
@@ -0,0 +1,370 @@
{
"version": 2,
"dependencies": {
"net8.0-windows7.0": {
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
"resolved": "1.0.3",
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"PolySharp": {
"type": "Direct",
"requested": "[1.14.1, )",
"resolved": "1.14.1",
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
},
"RhinoCommon": {
"type": "Direct",
"requested": "[8.21.25188.17001, )",
"resolved": "8.21.25188.17001",
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
"dependencies": {
"System.Drawing.Common": "7.0.0"
}
},
"RhinoWindows": {
"type": "Direct",
"requested": "[8.21.25188.17001, )",
"resolved": "8.21.25188.17001",
"contentHash": "9zqCorcLRBeiW/j1RTwUS4E7bnZetAdA9WDdtd/AQccjOpxdtw76wdN+ciyQ6qslseWkwZ9qSBeh7QaM800Ntw==",
"dependencies": {
"RhinoCommon": "[8.21.25188.17001]"
}
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.6, )",
"resolved": "0.9.6",
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
},
"GraphQL.Client": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0",
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
"System.Reactive": "5.0.0"
}
},
"GraphQL.Client.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
"dependencies": {
"GraphQL.Primitives": "6.0.0"
}
},
"GraphQL.Client.Abstractions.Websocket": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0"
}
},
"GraphQL.Primitives": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Primitives": "2.2.0",
"System.ComponentModel.Annotations": "4.5.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
"dependencies": {
"System.Memory": "4.5.1",
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
"resolved": "1.0.3",
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
}
},
"SQLitePCLRaw.core": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"SQLitePCLRaw.provider.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
},
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==",
"dependencies": {
"Microsoft.Win32.SystemEvents": "7.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Reactive": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1938.49, )",
"Speckle.Connectors.DUI": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
"type": "Project"
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.3, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
"Microsoft.Extensions.Options": "2.2.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
},
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"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",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
},
"net8.0-windows7.0/win-x64": {
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ=="
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==",
"dependencies": {
"Microsoft.Win32.SystemEvents": "7.0.0"
}
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
}
}
}
}
@@ -0,0 +1,394 @@
using Rhino;
using Rhino.DocObjects;
using Rhino.DocObjects.Tables;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.Rhino.HostApp;
using Speckle.Connectors.Rhino.Mapper.Revit;
namespace Speckle.Connectors.Rhino.Bindings;
/// <summary>
/// Binding for managing Rhino object mappings to Revit categories.
/// </summary>
public class RhinoMapperBinding : IBinding
{
private const string MAPPINGS_CHANGED_EVENT = "mappingsChanged";
private const string LAYERS_CHANGED_EVENT = "layersChanged";
private readonly DocumentModelStore _store;
private readonly IAppIdleManager _idleManager;
private readonly IBasicConnectorBinding _basicConnectorBinding;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly RevitMappingResolver _revitMappingResolver;
private readonly RhinoLayerHelper _rhinoLayerHelper;
private readonly RhinoObjectHelper _rhinoObjectHelper;
public string Name => "revitMapperBinding";
public IBrowserBridge Parent { get; }
public RhinoMapperBinding(
DocumentModelStore store,
IAppIdleManager idleManager,
IBrowserBridge parent,
IBasicConnectorBinding basicConnectorBinding,
ITopLevelExceptionHandler topLevelExceptionHandler,
RevitMappingResolver revitMappingResolver,
RhinoLayerHelper rhinoLayerHelper,
RhinoObjectHelper rhinoObjectHelper
)
{
_store = store;
_idleManager = idleManager;
Parent = parent;
_basicConnectorBinding = basicConnectorBinding;
_topLevelExceptionHandler = topLevelExceptionHandler;
_revitMappingResolver = revitMappingResolver;
_rhinoLayerHelper = rhinoLayerHelper;
_rhinoObjectHelper = rhinoObjectHelper;
// Subscribe to Rhino events so we know about changes
// Events fire on delete, undo delete, add and modify objects
RhinoDoc.DeleteRhinoObject += OnObjectChanged;
RhinoDoc.UndeleteRhinoObject += OnObjectChanged;
RhinoDoc.AddRhinoObject += OnObjectChanged;
RhinoDoc.ModifyObjectAttributes += OnObjectAttributesChanged;
// Subscribe to layer events so we know about layer changes
RhinoDoc.LayerTableEvent += OnLayerTableEvent;
// Subscribe to document changes to refresh mappings when switching documents
_store.DocumentChanged += OnDocumentChanged;
}
#region UI Methods - General
/// <summary>
/// Gets list of available layers for the UI dropdown.
/// </summary>
public LayerOption[] GetAvailableLayers()
{
var doc = RhinoDoc.ActiveDoc;
if (doc == null)
{
return [];
}
return doc
.Layers.Where(layer => !layer.IsDeleted)
.Select(layer => new LayerOption(layer.Id.ToString(), _rhinoLayerHelper.GetFullLayerPath(layer)))
.OrderBy(layer => layer.Name)
.ToArray();
}
/// <summary>
/// Selects/highlights specific objects in Rhino.
/// </summary>
public async Task HighlightObjects(string[] objectIds) => await _basicConnectorBinding.HighlightObjects(objectIds);
#endregion
#region UI Methods - Object Mapping Methods
/// <summary>
/// Assigns selected objects to a specific Revit category.
/// </summary>
public void AssignObjectsToCategory(string[] objectIds, string categoryValue)
{
foreach (var objectIdString in objectIds)
{
// NOTE: should we be checking if key already exists?
// For POC, straightforward set on object
var rhinoObject = _rhinoObjectHelper.GetRhinoObject(objectIdString);
var attrs = rhinoObject?.Attributes.Duplicate();
attrs?.SetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY, categoryValue);
RhinoDoc.ActiveDoc.Objects.ModifyAttributes(rhinoObject, attrs, true);
}
// Trigger single update after all changes
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Removes category assignments from specific objects.
/// </summary>
public void ClearObjectsCategoryAssignment(string[] objectIds)
{
foreach (var objectIdString in objectIds)
{
var rhinoObject = _rhinoObjectHelper.GetRhinoObject(objectIdString);
var attrs = rhinoObject?.Attributes.Duplicate();
attrs?.DeleteUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY);
RhinoDoc.ActiveDoc.Objects.ModifyAttributes(rhinoObject, attrs, true);
}
// Trigger single update after all changes
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Removes all category assignments in the doc.
/// </summary>
public void ClearAllObjectsCategoryAssignments()
{
foreach (var rhinoObject in RhinoDoc.ActiveDoc.Objects)
{
if (!string.IsNullOrEmpty(rhinoObject.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY)))
{
var attrs = rhinoObject.Attributes.Duplicate();
attrs.DeleteUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY);
RhinoDoc.ActiveDoc.Objects.ModifyAttributes(rhinoObject, attrs, true);
}
}
// Trigger single update
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Gets all current mappings to show in the UI table.
/// </summary>
/// <returns></returns>
public CategoryMapping[] GetCurrentObjectsMappings()
{
var mappedObjects = RhinoDoc
.ActiveDoc.Objects.Where(obj =>
!string.IsNullOrEmpty(obj.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
)
.GroupBy(obj => obj.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
.Select(group => new CategoryMapping(
group.Key,
group.Key,
group.Select(obj => obj.Id.ToString()).ToArray(),
group.Count()
))
.ToArray();
return mappedObjects;
}
/// <summary>
/// Gets category mappings for specific object IDs.
/// </summary>
public string[] GetCategoryMappingsForObjects(string[] objectIds) =>
objectIds
.Select(id =>
_rhinoObjectHelper.GetRhinoObject(id)?.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY)
)
.Where(category => category != null)
.Cast<string>()
.Distinct()
.ToArray();
#endregion
#region UI Methods - Layer Mapping Methods
/// <summary>
/// Assigns selected layers to a specific Revit category.
/// </summary>
public void AssignLayerToCategory(string[] layerIds, string categoryValue)
{
foreach (var layerId in layerIds)
{
var layer = _rhinoLayerHelper.GetLayer(layerId);
layer?.SetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY, categoryValue);
}
// Trigger single update
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Removes category assignments from specific layer(s).
/// </summary>
public void ClearLayerCategoryAssignment(string[] layerIds)
{
foreach (var layerId in layerIds)
{
// NOTE: clear user string by setting to null. Layer has not DeleteUserString() method 🙄
var layer = _rhinoLayerHelper.GetLayer(layerId);
layer?.SetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY, null);
}
// Trigger single update
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Removes all layer category assignments in the doc.
/// </summary>
public void ClearAllLayerCategoryAssignments()
{
foreach (var layer in RhinoDoc.ActiveDoc.Layers)
{
if (!string.IsNullOrEmpty(layer.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY)))
{
// NOTE: clear user string by setting to null. Layer has not DeleteUserString() method 🙄
layer.SetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY, null);
}
}
// Trigger single update
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
/// <summary>
/// Gets all current layer mappings to show in the UI table.
/// Layers with the same category mapping are grouped together.
/// </summary>
public LayerCategoryMapping[] GetCurrentLayerMappings()
{
var mappedLayers = RhinoDoc
.ActiveDoc.Layers.Where(layer =>
!layer.IsDeleted && !string.IsNullOrEmpty(layer.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
)
.GroupBy(layer => layer.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
.Select(group => new LayerCategoryMapping(
group.Key,
group.Key,
group.Select(layer => layer.Id.ToString()).ToArray(),
group.Select(layer => _rhinoLayerHelper.GetFullLayerPath(layer)).ToArray(),
group.Count()
))
.ToArray();
return mappedLayers;
}
/// <summary>
/// Gets category mappings for specific layer IDs.
/// </summary>
public string[] GetCategoryMappingsForLayers(string[] layerIds) =>
layerIds
.Select(id => _rhinoLayerHelper.GetLayer(id)?.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
.Where(category => !string.IsNullOrEmpty(category))
.Cast<string>()
.Distinct()
.ToArray();
public string[] GetEffectiveObjectsForLayerMapping(string[] layerIds, string categoryValue) =>
_revitMappingResolver.GetEffectiveObjectsForLayerMapping(layerIds, categoryValue);
#endregion
#region Event Handling
/// <summary>
/// Called when objects are added, deleted, or undeleted in Rhino.
/// </summary>
private void OnObjectChanged(object? sender, RhinoObjectEventArgs e)
{
if (!_store.IsDocumentInit)
{
return;
}
var rhinoObject = e.TheObject;
if (!string.IsNullOrEmpty(rhinoObject.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY)))
{
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
}
/// <summary>
/// Called when object attributes are modified in Rhino.
/// </summary>
/// <remarks>
/// Includes detection for when objects move between layers with mappings.
/// </remarks>
private void OnObjectAttributesChanged(object? sender, RhinoModifyObjectAttributesEventArgs e)
{
if (!_store.IsDocumentInit)
{
return;
}
var rhinoObject = e.RhinoObject;
// check if mapping user string changed (added, removed, or modified)
var oldMapping = e.OldAttributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY);
var newMapping = rhinoObject.Attributes.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY);
bool mappingChanged = !string.Equals(oldMapping, newMapping, StringComparison.Ordinal);
// check if layer change affects mappings
bool hasOldLayerMapping = _rhinoLayerHelper.HasLayerMapping(e.OldAttributes.LayerIndex);
bool hasNewLayerMapping = _rhinoLayerHelper.HasLayerMapping(rhinoObject.Attributes.LayerIndex);
// refresh if mapping changed OR layer change affects mapped layers
if (mappingChanged || hasOldLayerMapping || hasNewLayerMapping)
{
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
}
/// <summary>
/// Called when the document changes (e.g., switching to a different Rhino model).
/// Refreshes the mappings table and defined layers to reflect the new document's state.
/// </summary>
private void OnDocumentChanged(object? sender, EventArgs e)
{
if (!_store.IsDocumentInit)
{
return;
}
// Refresh mappings for the new document
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
// Refresh layer list for the new document
_idleManager.SubscribeToIdle(nameof(NotifyLayersChanged), NotifyLayersChanged);
}
/// <summary>
/// Called when layer table events occur in Rhino.
/// Refreshes layer list when layer structure changes.
/// </summary>
/// <remarks>
/// Layer mapping changes are handled by OnObjectAttributesChanged
/// </remarks>
private void OnLayerTableEvent(object? sender, LayerTableEventArgs e) =>
_topLevelExceptionHandler.CatchUnhandled(() =>
{
if (!_store.IsDocumentInit)
{
return;
}
// Refresh layer list for structural changes
if (
e.EventType == LayerTableEventType.Added
|| e.EventType == LayerTableEventType.Deleted
|| e.EventType == LayerTableEventType.Modified
)
{
_idleManager.SubscribeToIdle(nameof(NotifyLayersChanged), NotifyLayersChanged);
}
// Refresh mappings when layers are deleted or modified
// This ensures that mappings table updates when mapped layers are removed
if (e.EventType == LayerTableEventType.Deleted || e.EventType == LayerTableEventType.Modified)
{
_idleManager.SubscribeToIdle(nameof(NotifyMappingsChanged), NotifyMappingsChanged);
}
});
/// <summary>
/// Sends updated mappings to the frontend.
/// </summary>
private void NotifyMappingsChanged()
{
var currentMappings = GetCurrentObjectsMappings();
Parent.Send(MAPPINGS_CHANGED_EVENT, currentMappings);
}
/// <summary>
/// Sends updated layer list to the frontend.
/// </summary>
private void NotifyLayersChanged()
{
var availableLayers = GetAvailableLayers();
Parent.Send(LAYERS_CHANGED_EVENT, availableLayers);
}
#endregion
}
@@ -24,6 +24,7 @@ public class RhinoReceiveBinding(
public async Task Receive(string modelCardId)
{
// NOTE: introduction of AddVisualizationProperties setting not accounted for in receive pipeline, hence hardcoded as true (i.e. "as before")
using var manager = receiveOperationManagerFactory.Create();
await manager.Process(
Commands,
@@ -31,7 +32,7 @@ public class RhinoReceiveBinding(
(sp, card) =>
{
sp.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc, true));
},
async (modelName, processor) =>
{
@@ -13,7 +13,9 @@ using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Rhino.HostApp;
using Speckle.Connectors.Rhino.Operations.Send.Filters;
using Speckle.Connectors.Rhino.Operations.Send.Settings;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Sdk.Common;
@@ -29,11 +31,13 @@ public sealed class RhinoSendBinding : ISendBinding
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly ISendConversionCache _sendConversionCache;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
private readonly ILogger<RhinoSendBinding> _logger;
private readonly IRhinoConversionSettingsFactory _rhinoConversionSettingsFactory;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IAppIdleManager _idleManager;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly RhinoLayerHelper _rhinoLayerHelper;
/// <summary>
/// Used internally to aggregate the changed objects' id. Objects in this list will be reconverted.
@@ -59,21 +63,25 @@ public sealed class RhinoSendBinding : ISendBinding
IBrowserBridge parent,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
ToSpeckleSettingsManager toSpeckleSettingsManager,
ILogger<RhinoSendBinding> logger,
IRhinoConversionSettingsFactory rhinoConversionSettingsFactory,
ITopLevelExceptionHandler topLevelExceptionHandler,
ISendOperationManagerFactory sendOperationManagerFactory
ISendOperationManagerFactory sendOperationManagerFactory,
RhinoLayerHelper rhinoLayerHelper
)
{
_store = store;
_idleManager = idleManager;
_cancellationManager = cancellationManager;
_sendConversionCache = sendConversionCache;
_toSpeckleSettingsManager = toSpeckleSettingsManager;
_logger = logger;
_rhinoConversionSettingsFactory = rhinoConversionSettingsFactory;
Parent = parent;
_topLevelExceptionHandler = topLevelExceptionHandler;
_sendOperationManagerFactory = sendOperationManagerFactory;
_rhinoLayerHelper = rhinoLayerHelper;
Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory.
PreviousUnitSystem = RhinoDoc.ActiveDoc.ModelUnitSystem;
SubscribeToRhinoEvents();
@@ -271,9 +279,9 @@ public sealed class RhinoSendBinding : ISendBinding
}
public List<ISendFilter> GetSendFilters() =>
[new RhinoSelectionFilter() { IsDefault = true }, new RhinoLayersFilter()];
[new RhinoSelectionFilter() { IsDefault = true }, new RhinoLayersFilter(_rhinoLayerHelper)];
public List<ICardSetting> GetSendSettings() => [];
public List<ICardSetting> GetSendSettings() => [new AddVisualizationProperties(false)];
public async Task Send(string modelCardId)
{
@@ -282,9 +290,14 @@ public sealed class RhinoSendBinding : ISendBinding
await manager.Process(
Commands,
modelCardId,
(sp, _) =>
(sp, card) =>
sp.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(_rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc)),
.Initialize(
_rhinoConversionSettingsFactory.Create(
RhinoDoc.ActiveDoc,
_toSpeckleSettingsManager.GetAddVisualizationPropertiesSetting(card)
)
),
card =>
{
return Task.FromResult<IReadOnlyList<RhinoObject>>(
@@ -1,5 +1,7 @@
using Rhino;
using Rhino.Collections;
using Rhino.DocObjects;
using Speckle.Connectors.Rhino.Mapper.Revit;
using Speckle.Sdk;
namespace Speckle.Connectors.Rhino.HostApp.Properties;
@@ -9,12 +11,84 @@ namespace Speckle.Connectors.Rhino.HostApp.Properties;
/// </summary>
public class PropertiesExtractor
{
private readonly RevitMappingResolver _revitMappingResolver;
public PropertiesExtractor(RevitMappingResolver revitMappingResolver)
{
_revitMappingResolver = revitMappingResolver;
}
public Dictionary<string, object?> GetProperties(RhinoObject rhObject)
{
Dictionary<string, object?> properties = new();
var userStrings = rhObject.Attributes.GetUserStrings();
foreach (var key in userStrings.AllKeys)
Dictionary<string, object?> properties = GetUserStrings(rhObject);
Dictionary<string, object?> userDict = GetUserDict(rhObject);
if (userDict.Count > 0)
{
properties["User Dictionary"] = userDict;
}
return properties;
}
private Dictionary<string, object?> GetUserDict(RhinoObject rhObject)
{
Dictionary<string, object?> userDict = new();
if (rhObject.Attributes.UserDictionary != null && rhObject.Attributes.UserDictionary.Count > 0)
{
ParseArchivableToDictionary(userDict, rhObject.Attributes.UserDictionary);
}
return userDict;
}
/// <summary>
/// Copies an ArchivableDictionary to a Dictionary
/// </summary>
/// <param name="target"></param>
/// <param name="dict"></param>
private void ParseArchivableToDictionary(Dictionary<string, object?> target, ArchivableDictionary dict)
{
foreach (var key in dict.Keys)
{
var obj = dict[key];
switch (obj)
{
case ArchivableDictionary o:
Dictionary<string, object?> nested = new();
ParseArchivableToDictionary(nested, o);
target[key] = nested;
continue;
case double:
case bool:
case int:
case string:
case IEnumerable<double>:
case IEnumerable<bool>:
case IEnumerable<int>:
case IEnumerable<string>:
target[key] = obj;
continue;
default:
target[key] = obj.ToString();
continue;
}
}
}
private Dictionary<string, object?> GetUserStrings(RhinoObject rhObject)
{
Dictionary<string, object?> userStringDict = new();
var userStrings = rhObject.Attributes.GetUserStrings();
foreach (string? key in userStrings.AllKeys)
{
if (key == null)
{
continue;
}
try
{
if (key == "$block-instance-original-object-id$") // skip: this seems to be an invisible user string that shows up on block instances
@@ -22,10 +96,10 @@ public class PropertiesExtractor
continue;
}
if (userStrings[key].StartsWith("%<"))
if (userStrings[key]?.StartsWith("%<") ?? false)
{
var value = RhinoApp.ParseTextField(userStrings[key], rhObject, null);
properties[key] = value;
userStringDict[key] = value;
continue;
}
}
@@ -34,9 +108,19 @@ public class PropertiesExtractor
// Shh. We can fail silently here - it's not even worth logging. I expect users will complain properties are missing.
}
properties[key] = userStrings[key];
userStringDict[key] = userStrings[key];
}
return properties;
// NOTE: if no mapping was found on the object, check layer(s) recursively
if (!userStringDict.ContainsKey(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
{
var layerMapping = _revitMappingResolver.SearchLayerHierarchyForMapping(rhObject);
if (!string.IsNullOrEmpty(layerMapping))
{
userStringDict[RevitMappingConstants.CATEGORY_USER_STRING_KEY] = layerMapping;
}
}
return userStringDict;
}
}
@@ -54,7 +54,7 @@ public class RhinoGroupBaker
Dictionary<string, IReadOnlyCollection<string>> applicationIdMap
)
{
if (applicationIdMap.TryGetValue(oldObjId, out IReadOnlyCollection<string> value))
if (applicationIdMap.TryGetValue(oldObjId, out IReadOnlyCollection<string>? value))
{
return value;
}
@@ -13,14 +13,17 @@ namespace Speckle.Connectors.Rhino.HostApp;
public class RhinoInstanceUnpacker : IInstanceUnpacker<RhinoObject>
{
private readonly IInstanceObjectsManager<RhinoObject, List<string>> _instanceObjectsManager;
private readonly RhinoLayerHelper _rhinoLayerHelper;
private readonly ILogger<RhinoInstanceUnpacker> _logger;
public RhinoInstanceUnpacker(
IInstanceObjectsManager<RhinoObject, List<string>> instanceObjectsManager,
RhinoLayerHelper rhinoLayerHelper,
ILogger<RhinoInstanceUnpacker> logger
)
{
_instanceObjectsManager = instanceObjectsManager;
_rhinoLayerHelper = rhinoLayerHelper;
_logger = logger;
}
@@ -109,7 +112,12 @@ public class RhinoInstanceUnpacker : IInstanceUnpacker<RhinoObject>
_instanceObjectsManager.AddDefinitionProxy(instance.InstanceDefinition.Id.ToString(), definition);
foreach (var obj in instance.InstanceDefinition.GetObjects())
// NOTE: InstanceDefinition.GetObjects() returns all constituent objects of a block, but those constituent
// objects can be on layers, that are not visible. The publish should respect that.
// See request: [CNX-2254](https://linear.app/speckle/issue/CNX-2254/rhino-publish-blocks-with-hidden-objects)
var allDefinitionObjects = instance.InstanceDefinition.GetObjects();
var visibleDefinitionObjects = _rhinoLayerHelper.FilterByLayerVisibility(allDefinitionObjects);
foreach (var obj in visibleDefinitionObjects)
{
definition.objects.Add(obj.Id.ToString());
if (obj is InstanceObject localInstance)
@@ -0,0 +1,154 @@
using Rhino;
using Rhino.DocObjects;
using Speckle.Connectors.Rhino.Mapper.Revit;
namespace Speckle.Connectors.Rhino.HostApp;
/// <summary>
/// Helper class for common Rhino layer and object operations.
/// Consolidates layer utilities to eliminate duplication across the codebase.
/// </summary>
public class RhinoLayerHelper
{
/// <summary>
/// Gets list of available layers for UI dropdowns.
/// </summary>
public LayerOption[] GetAvailableLayers()
{
var doc = RhinoDoc.ActiveDoc;
if (doc == null)
{
return [];
}
return doc
.Layers.Where(layer => !layer.IsDeleted)
.Select(layer => new LayerOption(layer.Id.ToString(), GetFullLayerPath(layer)))
.OrderBy(layer => layer.Name)
.ToArray();
}
/// <summary>
/// Gets the full layer path with / delimiter
/// </summary>
public string GetFullLayerPath(Layer layer)
{
string fullPath = layer.Name;
Guid parentIndex = layer.ParentLayerId;
while (parentIndex != Guid.Empty)
{
Layer? parentLayer = RhinoDoc.ActiveDoc.Layers.FindId(parentIndex);
if (parentLayer == null)
{
break;
}
fullPath = parentLayer.Name + "/" + fullPath; // use "/" delimiter
parentIndex = parentLayer.ParentLayerId;
}
return fullPath;
}
/// <summary>
/// Converts a string layer ID to a Layer.
/// </summary>
/// <returns>Layer if found and valid, null otherwise</returns>
public Layer? GetLayer(string layerIdString) =>
Guid.TryParse(layerIdString, out var layerId) ? RhinoDoc.ActiveDoc.Layers.FindId(layerId) : null;
/// <summary>
/// Helper to check if a layer (by index) has a category mapping.
/// </summary>
/// <remarks>
/// This is arguably a very specific method pertaining to mapper and maybe shouldn't be in this class?
/// </remarks>
public bool HasLayerMapping(int layerIndex)
{
if (layerIndex < 0 || layerIndex >= RhinoDoc.ActiveDoc.Layers.Count)
{
return false;
}
var layer = RhinoDoc.ActiveDoc.Layers[layerIndex];
return !string.IsNullOrEmpty(layer.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY));
}
/// <summary>
/// Gets all RhinoObjects in the specified layer and all its child layers recursively.
/// </summary>
public IEnumerable<RhinoObject> GetObjectsInLayerHierarchy(Layer rootLayer)
{
var allObjects = new List<RhinoObject>();
var layersToSearch = GetLayerAndAllChildren(rootLayer);
foreach (var layer in layersToSearch)
{
var objectsOnLayer = RhinoDoc.ActiveDoc.Objects.FindByLayer(layer);
allObjects.AddRange(objectsOnLayer);
}
return allObjects;
}
/// <summary>
/// Gets the specified layer and all its child layers recursively.
/// </summary>
public IEnumerable<Layer> GetLayerAndAllChildren(Layer rootLayer)
{
// Return the root layer itself
yield return rootLayer;
// Get all child layers recursively
foreach (var childLayer in GetAllChildLayers(rootLayer))
{
yield return childLayer;
}
}
/// <summary>
/// Recursively gets all child layers of the specified parent layer.
/// </summary>
public IEnumerable<Layer> GetAllChildLayers(Layer parentLayer)
{
var doc = RhinoDoc.ActiveDoc;
if (doc?.Layers == null)
{
yield break;
}
// Find all direct child layers
var directChildren = doc.Layers.Where(layer => layer.ParentLayerId == parentLayer.Id);
foreach (var childLayer in directChildren)
{
// Return the direct child
yield return childLayer;
// Recursively get grandchildren
foreach (var grandChild in GetAllChildLayers(childLayer))
{
yield return grandChild;
}
}
}
/// <summary>
/// Checks if a layer is visible by its index.
/// </summary>
public bool IsLayerVisible(int layerIndex)
{
if (layerIndex < 0 || layerIndex >= RhinoDoc.ActiveDoc.Layers.Count)
{
return true; // default to visible for invalid indices (safe fallback)
}
var layer = RhinoDoc.ActiveDoc.Layers[layerIndex];
return layer != null && !layer.IsDeleted && layer.IsVisible;
}
/// <summary>
/// Filters a collection of objects to only include those on visible layers.
/// </summary>
public IEnumerable<T> FilterByLayerVisibility<T>(IEnumerable<T> objects)
where T : RhinoObject => objects.Where(obj => IsLayerVisible(obj.Attributes.LayerIndex));
}
@@ -14,6 +14,7 @@ namespace Speckle.Connectors.Rhino.HostApp;
/// </summary>
public class RhinoLayerUnpacker
{
private readonly RhinoLayerHelper _rhinoLayerHelper;
private readonly Dictionary<int, Collection> _layerCollectionCache = new();
private static readonly string s_pathSeparator =
@@ -24,6 +25,11 @@ public class RhinoLayerUnpacker
#endif
private static readonly string[] s_pathSeparatorSplit = [s_pathSeparator];
public RhinoLayerUnpacker(RhinoLayerHelper rhinoLayerHelper)
{
_rhinoLayerHelper = rhinoLayerHelper;
}
/// <summary>
/// Use this method to get all of the layers that correspond to collection created in the root collection.
/// </summary>
@@ -31,20 +37,16 @@ public class RhinoLayerUnpacker
/// <exception cref="SpeckleException">Throws when a layer could not be retrieved from a stored collection application id</exception>
public IEnumerable<Layer> GetUsedLayers()
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
foreach (string layerId in _layerCollectionCache.Values.Select(o => o.applicationId ?? string.Empty).ToList())
{
if (Guid.TryParse(layerId, out Guid layerGuid))
var layer = _rhinoLayerHelper.GetLayer(layerId);
if (layer != null)
{
if (currentDoc.Layers.FindId(layerGuid) is Layer layer)
{
yield return layer;
}
else
{
throw new SpeckleException($"Could not retrieve layer with guid: {layerId}.");
}
yield return layer;
}
else if (Guid.TryParse(layerId, out _))
{
throw new SpeckleException($"Could not retrieve layer with guid: {layerId}.");
}
else
{
@@ -177,7 +177,7 @@ public class RhinoMaterialUnpacker
}
// converts a rhino material to a rhino render material
private RenderMaterial? ConvertMaterialToRenderMaterial(Material material)
private static RenderMaterial? ConvertMaterialToRenderMaterial(Material material)
{
// get physically based render material
Material pbMaterial = material;
@@ -188,7 +188,18 @@ public class RhinoMaterialUnpacker
pbMaterial.ToPhysicallyBased();
}
return RenderMaterial.FromMaterial(pbMaterial, null);
var result = RenderMaterial.FromMaterial(pbMaterial, null);
if (result is null)
{
// We're seeing some weird behaviour running in the File Importer
// where RenderMaterial.FromMaterial returns null.
// Could be related to headless RhinoDoc or windowless RhinoCore (unsure the cause)
// CreateBasicMaterial appears to work, so we'll use it as fall back
result = RenderMaterial.CreateBasicMaterial(pbMaterial, null);
}
return result;
}
private SpeckleRenderMaterial ConvertRenderMaterialToSpeckle(RenderMaterial renderMaterial)
@@ -0,0 +1,17 @@
using Rhino;
using Rhino.DocObjects;
namespace Speckle.Connectors.Rhino.HostApp;
/// <summary>
/// Helper class for common Rhino object operations.
/// </summary>
public class RhinoObjectHelper
{
/// <summary>
/// Converts a string object ID to a RhinoObject.
/// </summary>
/// <returns>RhinoObject if found and valid, null otherwise</returns>
public RhinoObject? GetRhinoObject(string objectIdString) =>
Guid.TryParse(objectIdString, out var objectId) ? RhinoDoc.ActiveDoc.Objects.FindId(objectId) : null;
}
@@ -0,0 +1,11 @@
namespace Speckle.Connectors.Rhino.Mapper.Revit;
/// <summary>
/// Represents a group of objects that are all assigned to the same category.
/// </summary>
public record CategoryMapping(
string CategoryValue,
string CategoryLabel,
IReadOnlyList<string> ObjectIds,
int ObjectCount
);
@@ -0,0 +1,12 @@
namespace Speckle.Connectors.Rhino.Mapper.Revit;
/// <summary>
/// Represents layers that are all assigned to the same category.
/// </summary>
public record LayerCategoryMapping(
string CategoryValue,
string CategoryLabel,
IReadOnlyList<string> LayerIds,
IReadOnlyList<string> LayerNames,
int LayerCount
);
@@ -0,0 +1,6 @@
namespace Speckle.Connectors.Rhino.Mapper.Revit;
/// <summary>
/// Represents a layer option for the UI dropdown.
/// </summary>
public record LayerOption(string Id, string Name);
@@ -0,0 +1,10 @@
namespace Speckle.Connectors.Rhino.Mapper.Revit;
/// <summary>
/// Shared constants for the Rhino-to-Revit mapping system.
/// Centralizes string constant(s) to avoid duplication across three using classes.
/// </summary>
public static class RevitMappingConstants
{
public const string CATEGORY_USER_STRING_KEY = "builtInCategory";
}
@@ -0,0 +1,113 @@
using Rhino;
using Rhino.DocObjects;
using Speckle.Connectors.Rhino.HostApp;
namespace Speckle.Connectors.Rhino.Mapper.Revit;
/// <summary>
/// Responsible for resolving category mappings from layer hierarchy.
/// Used by the send pipeline to resolve mappings during property extraction.
/// </summary>
/// <remarks>
/// This gets called when no mapping found on the object level.
/// </remarks>
public class RevitMappingResolver
{
private readonly RhinoLayerHelper _rhinoLayerHelper;
public RevitMappingResolver(RhinoLayerHelper rhinoLayerHelper)
{
_rhinoLayerHelper = rhinoLayerHelper;
}
/// <summary>
/// Gets all objects that would effectively receive the specified layer mapping during send.
/// Takes into account hierarchical resolution - only returns objects that would actually
/// resolve to this specific category value through the layer hierarchy.
/// </summary>
public string[] GetEffectiveObjectsForLayerMapping(string[] layerIds, string categoryValue)
{
var effectiveObjects = new List<string>();
foreach (var layerId in layerIds)
{
var layer = _rhinoLayerHelper.GetLayer(layerId);
if (layer == null)
{
continue;
}
// Get all objects in this layer and its child layers
var allObjectsInHierarchy = _rhinoLayerHelper.GetObjectsInLayerHierarchy(layer);
foreach (var obj in allObjectsInHierarchy)
{
// Since we're in Layer mode, objects don't have direct mappings
// Check what category this object would actually resolve to through layer hierarchy
var resolvedCategory = SearchLayerHierarchyForMapping(obj);
// Only include if it resolves to THIS specific category
if (resolvedCategory == categoryValue)
{
effectiveObjects.Add(obj.Id.ToString());
}
}
}
return effectiveObjects.ToArray();
}
/// <summary>
/// Traverses layer hierarchy, returns first mapping found or null
/// </summary>
public string? SearchLayerHierarchyForMapping(RhinoObject rhinoObject)
{
// NOTE: we agreed on a hierarchical resolution strategy:
// - Object-level mappings have highest precedence
// - Layer-level mappings are fallback when no object mapping exists
// - Traverses layer hierarchy and stops at first mapping found
var layer = GetLayerByIndex(rhinoObject.Attributes.LayerIndex);
while (layer != null)
{
var layerMapping = layer.GetUserString(RevitMappingConstants.CATEGORY_USER_STRING_KEY);
if (!string.IsNullOrEmpty(layerMapping))
{
return layerMapping; // returns first mapping found
}
// move to parent layer
layer = GetParentLayer(layer);
}
return null;
}
/// <summary>
/// Gets a layer by its index from the active doc.
/// </summary>
private Layer? GetLayerByIndex(int layerIndex)
{
var doc = RhinoDoc.ActiveDoc;
if (doc?.Layers == null || layerIndex < 0 || layerIndex >= doc.Layers.Count)
{
return null;
}
return doc.Layers[layerIndex];
}
/// <summary>
/// Gets the parent layer of the given layer.
/// </summary>
private Layer? GetParentLayer(Layer layer)
{
if (layer.ParentLayerId == Guid.Empty)
{
return null; // no parent layer
}
var doc = RhinoDoc.ActiveDoc;
return doc?.Layers?.FindId(layer.ParentLayerId);
}
}
@@ -2,11 +2,14 @@ using Rhino;
using Rhino.DocObjects;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Rhino.HostApp;
namespace Speckle.Connectors.Rhino.Operations.Send.Filters;
public class RhinoLayersFilter : DiscriminatedObject, ISendFilter
{
private readonly RhinoLayerHelper _rhinoLayerHelper;
public string Id { get; set; } = "rhinoLayers";
public string Name { get; set; } = "Layers";
public string Type { get; set; } = "Select";
@@ -19,7 +22,10 @@ public class RhinoLayersFilter : DiscriminatedObject, ISendFilter
public List<SendFilterSelectItem> SelectedItems { get; set; }
public List<SendFilterSelectItem> Items => GetFilterItems();
public RhinoLayersFilter() { }
public RhinoLayersFilter(RhinoLayerHelper rhinoLayerHelper)
{
_rhinoLayerHelper = rhinoLayerHelper;
}
public List<string> RefreshObjectIds()
{
@@ -32,14 +38,11 @@ public class RhinoLayersFilter : DiscriminatedObject, ISendFilter
foreach (var item in SelectedItems)
{
if (Guid.TryParse(item.Id, out Guid layerId))
Layer? layer = _rhinoLayerHelper.GetLayer(item.Id);
if (layer != null)
{
Layer layer = doc.Layers.FindId(layerId);
if (layer != null)
{
var objectIds = doc.Objects.FindByLayer(layer).Select(obj => obj.Id.ToString());
SelectedObjectIds.AddRange(objectIds);
}
var objectIds = doc.Objects.FindByLayer(layer).Select(obj => obj.Id.ToString());
SelectedObjectIds.AddRange(objectIds);
}
}
@@ -59,28 +62,10 @@ public class RhinoLayersFilter : DiscriminatedObject, ISendFilter
{
if (!layer.IsDeleted)
{
filterItems.Add(new SendFilterSelectItem(layer.Id.ToString(), GetFullLayerPath(layer)));
filterItems.Add(new SendFilterSelectItem(layer.Id.ToString(), _rhinoLayerHelper.GetFullLayerPath(layer)));
}
}
return filterItems;
}
private string GetFullLayerPath(Layer layer)
{
string fullPath = layer.Name;
Guid parentIndex = layer.ParentLayerId;
while (parentIndex != Guid.Empty)
{
Layer parentLayer = RhinoDoc.ActiveDoc.Layers.FindId(parentIndex);
if (parentLayer == null)
{
break;
}
fullPath = parentLayer.Name + "/" + fullPath;
parentIndex = parentLayer.ParentLayerId;
}
return fullPath;
}
}
@@ -0,0 +1,12 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Rhino.Operations.Send.Settings;
public class AddVisualizationProperties(bool value) : ICardSetting
{
public string? Id { get; set; } = "addVisualizationProperties";
public string? Title { get; set; } = "Add visualization properties for meshes (will increase model size)";
public string? Type { get; set; } = "boolean";
public object? Value { get; set; } = value;
public List<string>? Enum { get; set; }
}
@@ -0,0 +1,38 @@
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Rhino.Operations.Send.Settings;
public class ToSpeckleSettingsManager
{
private readonly ISendConversionCache _sendConversionCache;
private readonly Dictionary<string, bool?> _addVisualizationPropertiesCache = [];
public ToSpeckleSettingsManager(ISendConversionCache sendConversionCache)
{
_sendConversionCache = sendConversionCache;
}
public bool GetAddVisualizationPropertiesSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "addVisualizationProperties")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_addVisualizationPropertiesCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_addVisualizationPropertiesCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
_sendConversionCache.EvictObjects(objectIds);
}
}
@@ -17,8 +17,10 @@ using Speckle.Connectors.Rhino.Bindings;
using Speckle.Connectors.Rhino.Filters;
using Speckle.Connectors.Rhino.HostApp;
using Speckle.Connectors.Rhino.HostApp.Properties;
using Speckle.Connectors.Rhino.Mapper.Revit;
using Speckle.Connectors.Rhino.Operations.Receive;
using Speckle.Connectors.Rhino.Operations.Send;
using Speckle.Connectors.Rhino.Operations.Send.Settings;
using Speckle.Connectors.Rhino.Plugin;
using Speckle.Sdk.Models.GraphTraversal;
@@ -34,10 +36,10 @@ public static class ServiceRegistration
serviceCollection.AddSingleton<PlugIn>(SpeckleConnectorsRhinoPlugin.Instance);
serviceCollection.AddSingleton<Command>(SpeckleConnectorsRhinoCommand.Instance);
serviceCollection.AddDUI<DefaultThreadContext, RhinoDocumentStore>();
serviceCollection.AddDUIView();
}
serviceCollection.AddConnectors();
serviceCollection.AddDUIView();
// Register bindings
serviceCollection.AddSingleton<IBinding, TestBinding>();
@@ -50,11 +52,15 @@ public static class ServiceRegistration
serviceCollection.AddSingleton<IBinding, RhinoSelectionBinding>();
serviceCollection.AddSingleton<IBinding, RhinoSendBinding>();
serviceCollection.AddSingleton<IBinding, RhinoReceiveBinding>();
serviceCollection.AddSingleton<IBinding, RhinoMapperBinding>();
// register send filters
serviceCollection.AddScoped<ISendFilter, RhinoSelectionFilter>();
serviceCollection.AddScoped<IHostObjectBuilder, RhinoHostObjectBuilder>();
// register send settings
serviceCollection.AddScoped<ToSpeckleSettingsManager>();
// register send conversion cache
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
serviceCollection.AddSingleton<IAppIdleManager, RhinoIdleManager>();
@@ -86,6 +92,11 @@ public static class ServiceRegistration
serviceCollection.AddScoped<RhinoColorUnpacker>();
serviceCollection.AddScoped<PropertiesExtractor>();
serviceCollection.AddScoped<RevitMappingResolver>();
// register helpers
serviceCollection.AddScoped<RhinoLayerHelper>();
serviceCollection.AddScoped<RhinoObjectHelper>();
// operation progress manager
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
@@ -18,15 +18,25 @@
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoBasicConnectorBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoMapperBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RhinoSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AttributeExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Properties\PropertiesExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoIdleManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoLayerHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoObjectHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RhinoUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\LayerCategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\LayerOption.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\CategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\RevitMappingConstants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mapper\Revit\RevitMappingResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RhinoSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RhinoLayersFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\AddVisualizationProperties.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RhinoEvents.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Registration\ServiceRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\BoundingBox.cs" />
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -360,7 +360,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"LibTessDotNet": {
@@ -410,18 +410,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -431,14 +431,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}
@@ -406,9 +406,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.0, )",
"Speckle.Sdk": "[3.5.0, )",
"Speckle.Sdk.Dependencies": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )",
"Speckle.Sdk": "[3.5.3, )",
"Speckle.Sdk.Dependencies": "[3.5.3, )"
}
},
"speckle.connectors.dui": {
@@ -441,7 +441,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.0, )"
"Speckle.Objects": "[3.5.3, )"
}
},
"LibTessDotNet": {
@@ -491,18 +491,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "k76bpvFvArisd0DbdtpLQbonkl57CYG3szD0sDw0fWZolh8nogw2hRrNL6U2KcrJqCvYVc+zGYSXTJfv9xgntA==",
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
"dependencies": {
"Speckle.Sdk": "3.5.0"
"Speckle.Sdk": "3.5.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "mOLFubk+co9rDtRLsB1wobvmkYaiEMvDV+3hRPlhnqe8WhV+vbBSjzj58EpKEprWXLwjyc0kVzsrvlM++PnL0g==",
"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",
@@ -512,14 +512,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.0"
"Speckle.Sdk.Dependencies": "3.5.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.0, )",
"resolved": "3.5.0",
"contentHash": "lPYk1E97tWJ5qO6B8F30VZ+vgOxMYQ5cspyUOALwWoyFloM1KZkvwyeBlm8FiQDx0su5KbTS4S10LQvnS2AqKg=="
"requested": "[3.5.3, )",
"resolved": "3.5.3",
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
}
}
}

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