Compare commits

...

118 Commits

Author SHA1 Message Date
Jedd Morgan 656ed709f3 Exit non-zero on fault (#1137)
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
2025-10-06 11:35:49 +01:00
Jedd Morgan 58c6370cda Merge pull request #1108 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
Main to Dev for release
2025-09-24 11:28:14 +01:00
Björn Steinhagen 0e72adba36 fix(etabs): improve frame section property extraction performance using database tables (#1107)
* refactor: using databases for section frame section proxies

* fix: encountered edge cases

* fix: unnecessary usings

* chore: docs
2025-09-24 11:50:20 +02:00
Björn Steinhagen d5084dc334 feat(etabs): add story force results extraction (#1104)
* feat: adds story forces extraction
* fix: const
2025-09-23 15:47:23 +00:00
Adam Hathcock 0aeecfd00a fix (revit): Make revit exception be a conversion exception (#1105)
* Make revit exception be a conversion exception

* Use ApplicationException

* chore: variable name

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-09-22 15:50:58 +00:00
Adam Hathcock 9ceb5621bc fix (CSI) CSI conversion exceptions should be speckle ones (#1106)
* CSI conversion exceptions should be speckle ones

* refactor: wording

* chore: docs

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-09-22 17:43:35 +02:00
Adam Hathcock 94c1d4921e Use Group ID for key of group def instead of a prop name (#1102) 2025-09-18 14:05:02 +01:00
Jedd Morgan a4ff20106c feat(job-processor): Deploy installers for the rhino importer windows service (#1097)
* test

* iss

* Windows Serviceing

* build the right app please

* include information logs

* Update release.yml
2025-09-18 10:23:09 +00:00
Adam Hathcock 289e25be6c fix (Rhino) Trying to avoid another Rhino null issue (#1099)
* Trying to avoid another Rhino issue

* Change to conversion exception
2025-09-18 10:13:24 +00:00
Adam Hathcock edebc8e98f Remove Revit file entity check as we're storing model card state only in DB and not the file (#1100) 2025-09-18 10:42:45 +01:00
Jedd Morgan 8c21e2362b Merge pull request #1098 from specklesystems/jrm/CA2254
Chore(build): Enforce CA2254
2025-09-18 10:25:27 +01:00
Björn Steinhagen 5d40645aef fix(revit): prevent exceptions from corrupted settings with defaults (#1096)
* fix: add defensive handling for corrupted settings in managers

* fix: add Newtonsoft attributes to protect settings Value properties

* refactor: extract Revit setting IDs to constants class

* refactor: adam comments

* fix: add try-catch for document deserialization failures

* fix: cleanup

* fix: pr comments

* fix: reduce defining defaults with constants

* fix: pr comments and touchups

* fix: remove unnecessary using statement

* fix: null string not needed

* chore: cleanup

* chore: default

* fix: send not receive setting

* chore: more cleanups

* refactor: ambiguity with reference point settings for publish and load
2025-09-18 11:11:34 +02:00
Adam Hathcock b5ad4ac32f trying to avoid null exceptions (#1095)
* trying to avoid null exceptions

* more document nulls?
2025-09-17 10:12:06 +00:00
Adam Hathcock 2a2801eced Avoid using blam operator to ignore nulls (#1071)
* Avoid using blam operator to ignore nulls

* Making more null docs be handled

* formatting

* throw instead of silent failure

* push dependencies upwards

* push up more document handling

* Pass doc rather than regetting it

* added comment

* move doc null check
2025-09-17 11:04:18 +01:00
Jedd Morgan 6289565f66 Scale units scalling text entities (#1094) 2025-09-16 06:32:12 +02:00
Claire Kuang 8ed6eebc2c fix(grasshopper): converts exceptions to warnings if non-acceptable inputs are detected (#1093) 2025-09-15 14:04:24 +00:00
Björn Steinhagen 58afaecce2 feat(etabs): adds modal results to analysis extractor (#1080)
* feat: adds ModalParticipatingMassRatios to analysis results

* feat: adds ModalParticipationFactors to analysis results

* feat: adds ModalPeriod to analysis results

* fix: add new extractors

* fix: duplicate const

* fix: service registrations

* refactor: mode instead of step

* chore: remove extractors with etabs api bug
2025-09-15 10:39:44 +00:00
Adam Hathcock 93c6df41fd Update to SDK 3.5.4 (#1092)
* Update to SDK 3.5.4

* chore: remove hack

---------

Co-authored-by: Björn <steinhagen.bjoern@gmail.com>
2025-09-15 07:49:13 +00:00
Mucahit Bilal GOKER f3bcb55d8a feat(revit): add groupName property (#1081)
* add group name
* chore: adds PR comment as TODO
2025-09-13 07:23:05 +02:00
Björn Steinhagen 0f32cb3c6d feat(grasshopper): add "Always select all" toggle to Collection and Property Selectors (#1090)
* fix: expose Select All and Invert Selection in ValueSet context menu

* feat: "Always select all" toggle to collection and property selectors

* refactor: auto-select instead of always select

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-13 06:59:54 +02:00
Björn Steinhagen f948417e31 fix(grasshopper): QueryProperties handles list-valued properties (#1089)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-12 15:50:12 +01:00
Claire Kuang 2ddd96ebea adds new png for expand icon (#1091) 2025-09-12 13:27:44 +01:00
Jedd Morgan 06fd46a7e3 Merge pull request #1088 from specklesystems/jrm/rhino-rc-testing
feat(importer): go back to using OpenHeadless for 3dm files
2025-09-11 15:29:14 +03:00
Jedd Morgan 333ef4bb71 Refactor(metrics)!: Refactor metrics tracking to avoid defaults (#1082)
* Refactor metrics to avoid fallbacks

* Bump sdk

* fix tests

* require email scope for grasshopper token url node
2025-09-10 16:54:50 +00:00
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
Jedd Morgan f1f17eea3d Merge pull request #1021 from specklesystems/jrm/dev-to-main
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Dev -> Main
2025-08-07 15:16:38 +01:00
Jedd Morgan 642607acad Merge branch 'main' into jrm/dev-to-main 2025-08-07 15:09:09 +01:00
Claire Kuang 7f3b23e71e Merge branch 'dev' into claire/cnx-2167-material-quantity-extraction-for-revit-railings 2025-08-07 09:00:26 +01:00
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
Claire Kuang 2cb7211734 Merge pull request #1009 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev to main
2025-07-30 12:42:01 +01:00
252 changed files with 7270 additions and 2791 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)
+2 -1
View File
@@ -69,7 +69,8 @@ public static class Consts
new("Connectors/CSi/Speckle.Connectors.ETABS21", "net48"),
new("Connectors/CSi/Speckle.Connectors.ETABS22", "net8.0-windows"),
]
)
),
new("rhino-importer", [new("Importers/Rhino/Speckle.Importers.JobProcessor", "net8.0-windows")]),
};
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -293,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"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()
);
}
@@ -89,7 +89,11 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
_speckleApplication.Slug
);
DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json");
_logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}");
_logger.LogDebug(
"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}",
ModelPathHash,
DocumentStateFile
);
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -20,23 +20,8 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
_settingsStore = settingsStore;
}
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
GetMaterialName(sectionName, properties);
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties) =>
GetSectionProperties(sectionName, properties);
GetPropertyModifiers(sectionName, properties);
}
private void GetMaterialName(string sectionName, Dictionary<string, object?> properties)
{
// get material name
string materialName = string.Empty;
_settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
// append to General Data of properties dictionary
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Material"] = materialName;
}
private void GetSectionProperties(string sectionName, Dictionary<string, object?> properties)
{
@@ -69,47 +54,20 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
ref radiusOfGyrationAboutMinorAxis
);
string distanceUnit = _settingsStore.Current.SpeckleUnits;
string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better
string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better
string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better
Dictionary<string, object?> mechanicalProperties = properties.EnsureNested(
SectionPropertyCategory.SECTION_PROPERTIES
);
mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit);
mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit);
mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit);
mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit);
}
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
{
double[] stiffnessModifiersArray = [];
_settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray);
Dictionary<string, object?> modifiers =
new()
{
["Cross-section (Axial) Area"] = stiffnessModifiersArray[0],
["Shear Area in 2 Direction"] = stiffnessModifiersArray[1],
["Shear Area in 3 Direction"] = stiffnessModifiersArray[2],
["Torsional Constant"] = stiffnessModifiersArray[3],
["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4],
["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5],
["Mass"] = stiffnessModifiersArray[6],
["Weight"] = stiffnessModifiersArray[7],
};
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Modifiers"] = modifiers;
mechanicalProperties.Add("Area", crossSectionalArea);
mechanicalProperties.Add("As2", shearAreaInMajorAxisDirection);
mechanicalProperties.Add("As3", shearAreaInMinorAxisDirection);
mechanicalProperties.Add("J", torsionalConstant);
mechanicalProperties.Add("I22", momentOfInertiaAboutMajorAxis);
mechanicalProperties.Add("I33", momentOfInertiaAboutMinorAxis);
mechanicalProperties.Add("S22", sectionModulusAboutMajorAxis);
mechanicalProperties.Add("S33", sectionModulusAboutMinorAxis);
mechanicalProperties.Add("Z22", plasticModulusAboutMajorAxis);
mechanicalProperties.Add("Z33", plasticModulusAboutMinorAxis);
mechanicalProperties.Add("R22", radiusOfGyrationAboutMajorAxis);
mechanicalProperties.Add("R33", radiusOfGyrationAboutMinorAxis);
}
}
@@ -0,0 +1,29 @@
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.MODAL_PERIOD => _serviceProvider.GetRequiredService<CsiModalPeriodExtractor>(),
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
ResultsKey.STORY_FORCES => _serviceProvider.GetRequiredService<CsiStoryForceResultsExtractor>(),
_ => 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>
@@ -100,7 +105,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
throw new SpeckleException("Failed to convert all objects");
}
using (var _ = _activityFactory.Start("Process Proxies"))
@@ -112,6 +117,42 @@ 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(
"Adjust publish settings - no result type input for the requested load cases and combinations"
);
}
if (!_csiApplicationService.SapModel.GetModelIsLocked())
{
throw new SpeckleException("Model unlocked, no access to analysis results");
}
try
{
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
selectedCasesAndCombinations,
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection["analysisResults"] = analysisResults;
}
catch (Exception ex)
{
throw new SpeckleException("Analysis result extraction failed", ex);
}
}
return new RootObjectBuilderResult(rootObjectCollection, results);
}
@@ -149,13 +190,31 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
// NOTE: CsiTendonWrapper - not typically modelled in ETABS, rather SAFE
catch (NotImplementedException ex)
{
_logger.LogError(ex, sourceType);
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
return new(Status.WARNING, applicationId, sourceType, null, ex);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, sourceType);
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -236,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -47,18 +47,17 @@ public class EtabsSectionUnpacker : ISectionUnpacker
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// initialize properties
Dictionary<string, object?> properties = [];
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
// sectionName is initialized with string.Empty, but api ref returns string "None"
if (sectionName != "None")
// Extract properties if valid section name
// "None" is weird but api returns that string if an opening, null element etc.
if (sectionName != "None" && !string.IsNullOrEmpty(sectionName))
{
properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName);
}
// create the section proxy
// create section proxy
GroupProxy sectionProxy =
new()
{
@@ -66,8 +65,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
name = sectionName,
applicationId = sectionName,
objects = frameIds,
["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell
["properties"] = properties // openings will just have an empty dict here
["type"] = "Frame Section",
["properties"] = properties
};
yield return sectionProxy;
@@ -81,8 +80,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// initialize properties outside the if statement
Dictionary<string, object?> properties = [];
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
@@ -92,7 +91,7 @@ public class EtabsSectionUnpacker : ISectionUnpacker
properties = _propertyExtractor.ExtractShellSectionProperties(sectionName);
}
// create the section proxy
// create section proxy
GroupProxy sectionProxy =
new()
{
@@ -1,4 +1,5 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Services;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
@@ -8,69 +9,96 @@ namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Extracts ETABS-specific frame section properties.
/// </summary>
/// <remarks>
/// The bulk loading strategy is necessary here because we can't know which database table contains which section
/// beforehand - there are multiple tables like "Frame Section Property Definitions - Steel",
/// "Frame Section Property Definitions - Concrete", etc.
/// </remarks>
public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly EtabsSectionPropertyDefinitionService _definitionService;
public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
public EtabsFrameSectionPropertyExtractor(
IConverterSettingsStore<CsiConversionSettings> settingsStore,
EtabsSectionPropertyDefinitionService definitionService
)
{
_settingsStore = settingsStore;
_definitionService = definitionService;
}
/// <summary>
/// Gets generalised frame section properties
/// Gets frame section properties from preloaded database table data
/// </summary>
/// <remarks>
/// Sap2000 doesn't support this method, unfortunately
/// Alternative is to account for extraction according to section type - we're talking over 40 section types!
/// This way, we get basic information with minimal computational costs.
/// Property categorization is done heuristically - order matters in the parsing logic.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
// Get all frame properties
int numberOfNames = 0;
string[] names = [];
eFramePropType[] propTypes = [];
double[] t3 = [],
t2 = [],
tf = [],
tw = [],
t2b = [],
tfb = [],
area = [];
_settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2(
ref numberOfNames,
ref names,
ref propTypes,
ref t3,
ref t2,
ref tf,
ref tw,
ref t2b,
ref tfb,
ref area
);
// Find the index of the current section
int sectionIndex = Array.IndexOf(names, sectionName);
if (sectionIndex != -1)
// get frame definitions from the service (which uses database table extraction)
// this is a fast dictionary lookup since all data is preloaded
if (!_definitionService.FrameDefinitions.TryGetValue(sectionName, out var rawDatabaseTableProperties))
{
// General Data
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Section Shape"] = propTypes[sectionIndex].ToString();
return; // no definitions found for this section
}
// Section Dimensions
string unit = _settingsStore.Current.SpeckleUnits;
var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS);
sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit);
sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit);
sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit);
sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit);
sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²");
// define table keys that we don't want to include in the section proxy properties
var keysToExclude = new HashSet<string>
{
"GUID",
"Name",
"Color",
"Notes",
"FileName",
"FromFile",
"SectInFile",
"NotAutoFact"
};
// get the section type / shape using the dedicated api query (exception to the database approach)
// this specific property isn't available in the database table extraction
eFramePropType framePropType = 0;
_settingsStore.Current.SapModel.PropFrame.GetTypeOAPI(sectionName, ref framePropType);
Dictionary<string, object?> generalProperties = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalProperties.Add("Section Shape", framePropType.ToString());
// heuristic property categorization based on key patterns and parse-ability
// NOTE: this is gross and quite dangerous 🤨 but beats specific frame prop sect. property extractions imo
// order matters here! we check for known string props first, then modifiers, then assume doubles are dimensions
foreach (KeyValuePair<string, string> rawDatabaseTableProperty in rawDatabaseTableProperties)
{
string key = rawDatabaseTableProperty.Key;
string value = rawDatabaseTableProperty.Value;
// skip metadata fields we don't care about
if (!keysToExclude.Contains(key))
{
// material is always a string, grab it first
if (key == "Material")
{
generalProperties.Add(key, value);
}
// modifier properties end with "Mod" and should be numeric
else if (key.EndsWith("Mod") && double.TryParse(value, out double parsedModValue))
{
Dictionary<string, object?> modificationProperties = properties.EnsureNested(
SectionPropertyCategory.MODIFIERS
);
modificationProperties.Add(key, parsedModValue);
}
// anything else that parses as a double is assumed to be a section dimension
// this covers things like t3, t2, tf, tw, area, etc. without having to enumerate them all
else if (double.TryParse(value, out double parsedDimensionValue))
{
Dictionary<string, object?> sectionDimensions = properties.EnsureNested(
SectionPropertyCategory.SECTION_DIMENSIONS
);
sectionDimensions.Add(key, parsedDimensionValue);
}
// if it doesn't parse as double and isn't a known string property, we skip it
// this is acceptable - we'd rather miss some edge case properties than crash
}
}
}
}
@@ -0,0 +1,105 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
namespace Speckle.Connectors.ETABSShared.HostApp.Services;
/// <summary>
/// Loads and caches section property definitions from database tables for both frame and shell sections.
/// </summary>
public class EtabsSectionPropertyDefinitionService
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> FrameDefinitions { get; }
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> ShellDefinitions { get; }
public EtabsSectionPropertyDefinitionService(
DatabaseTableExtractor databaseTableExtractor,
IConverterSettingsStore<CsiConversionSettings> settingsStore
)
{
_settingsStore = settingsStore;
var availableTableKeys = GetAvailableTableKeys();
FrameDefinitions = LoadFrameDefinitions(databaseTableExtractor, availableTableKeys);
ShellDefinitions = LoadShellDefinitions(databaseTableExtractor, availableTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadFrameDefinitions(
DatabaseTableExtractor databaseTableExtractor,
string[] availableTableKeys
)
{
var frameTableKeys = GetFrameSectionPropertyDefinitionTableKeys(availableTableKeys);
return LoadDefinitionsFromTables(databaseTableExtractor, frameTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadShellDefinitions(
DatabaseTableExtractor databaseTableExtractor,
string[] availableTableKeys
)
{
var shellTableKeys = GetShellSectionPropertyDefinitionTableKeys(availableTableKeys);
return LoadDefinitionsFromTables(databaseTableExtractor, shellTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadDefinitionsFromTables(
DatabaseTableExtractor databaseTableExtractor,
IEnumerable<string> tableKeys
)
{
var definitions = new Dictionary<string, IReadOnlyDictionary<string, string>>();
foreach (string tableKey in tableKeys)
{
var tableData = databaseTableExtractor.GetTableData(tableKey, "Name");
foreach (var row in tableData.Rows)
{
definitions[row.Key] = row.Value;
}
}
return definitions;
}
private static IEnumerable<string> GetFrameSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
{
var keysToExclude = new HashSet<string>
{
"Frame Section Property Definitions - Summary",
"Frame Section Property Definitions - Concrete Beam Reinforcing",
"Frame Section Property Definitions - Concrete Column Reinforcing"
};
return availableTableKeys.Where(key =>
key.StartsWith("Frame Section Property Definitions") && !keysToExclude.Contains(key)
);
}
private static IEnumerable<string> GetShellSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
{
var keysToExclude = new HashSet<string> { "Area Section Property Definitions - Summary" };
return availableTableKeys.Where(key =>
key.StartsWith("Area Section Property Definitions") && !keysToExclude.Contains(key)
);
}
private string[] GetAvailableTableKeys()
{
int numberTables = 0;
string[] tableKey = [],
tableName = [];
int[] importType = [];
_ = _settingsStore.Current.SapModel.DatabaseTables.GetAvailableTables(
ref numberTables,
ref tableKey,
ref tableName,
ref importType
);
return tableKey;
}
}
@@ -34,7 +34,7 @@ public class EtabsSectionPropertyExtractor
/// </summary>
public Dictionary<string, object?> ExtractFrameSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = new();
Dictionary<string, object?> properties = [];
_csiFrameExtractor.ExtractProperties(sectionName, properties);
_etabsFrameExtractor.ExtractProperties(sectionName, properties);
return properties;
@@ -45,7 +45,7 @@ public class EtabsSectionPropertyExtractor
/// </summary>
public Dictionary<string, object?> ExtractShellSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = new();
Dictionary<string, object?> properties = [];
_csiShellExtractor.ExtractProperties(sectionName, properties);
_etabsShellExtractor.ExtractProperties(sectionName, properties);
return properties;
@@ -3,6 +3,7 @@ using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp;
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Services;
using Speckle.Converters.ETABSShared;
namespace Speckle.Connectors.ETABSShared;
@@ -12,11 +13,12 @@ public static class ServiceRegistration
public static IServiceCollection AddEtabs(this IServiceCollection services)
{
services.AddEtabsConverters();
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<EtabsSectionPropertyDefinitionService>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
return services;
@@ -12,6 +12,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsFrameSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyDefinitionService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionResolver.cs" />
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -226,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -251,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -15,18 +15,26 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
public void ExecuteScript(string script)
{
Browser.Dispatcher.Invoke(
() =>
{
//avoid exceptions by checking if IBrowser is there
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
try
{
Browser.Dispatcher.Invoke(
() =>
{
return;
}
Browser.ExecuteScriptAsync(script);
},
DispatcherPriority.Background
);
//avoid exceptions by checking if IBrowser is there
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
{
return;
}
Browser.ExecuteScriptAsync(script);
},
DispatcherPriority.Background
);
}
catch (OperationCanceledException)
{
//do nothing, happens when closing Revit while a script is being executed
}
}
public void SendProgress(string script) => ExecuteScript(script);
@@ -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;
@@ -14,7 +14,7 @@ public sealed class RevitReceiveBinding(
ICancellationManager cancellationManager,
IBrowserBridge parent,
ILogger<RevitReceiveBinding> logger,
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
@@ -24,8 +24,7 @@ public sealed class RevitReceiveBinding(
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
#pragma warning disable CA1024
public List<ICardSetting> GetReceiveSettings() =>
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
public List<ICardSetting> GetReceiveSettings() => [new Operations.Receive.Settings.ReceiveReferencePointSetting()];
#pragma warning restore CA1024
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
@@ -97,11 +97,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public List<ICardSetting> GetSendSettings() =>
[
new DetailLevelSetting(DetailLevelType.Medium),
new ReferencePointSetting(ReferencePointType.InternalOrigin),
new SendParameterNullOrEmptyStringsSetting(false),
new LinkedModelsSetting(true),
new SendRebarsAsVolumetricSetting(false)
new DetailLevelSetting(),
new SendReferencePointSetting(),
new SendParameterNullOrEmptyStringsSetting(),
new LinkedModelsSetting(),
new SendRebarsAsVolumetricSetting()
];
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
@@ -135,7 +135,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
{
var activeUIDoc =
_revitContext.UIApplication.NotNull().ActiveUIDocument
_revitContext.UIApplication?.ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
@@ -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;
@@ -235,13 +236,16 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
ICollection<ElementId> modifiedElementIds = e.GetModifiedElementIds();
var doc = e.GetDocument();
if (doc == null)
{
return;
}
// NOTE: Whenever we save data into file this event also trigger changes on its DataStorage.
// On every add/remove/update model attempt triggers this handler and was causing unnecessary calls on `RunExpirationChecks`
// Re-check it once we implement Linked Documents
if (modifiedElementIds.Count == 1)
{
var doc = e.GetDocument();
if (modifiedElementIds.All(el => doc.GetElement(el) is DataStorage))
{
return;
@@ -271,7 +275,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
}
if (HaveUnitsChanged(e.GetDocument()))
if (HaveUnitsChanged(doc))
{
var objectIds = new List<string>();
foreach (var sender in _store.GetSenders().ToList())
@@ -284,7 +288,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
var selectedObjects = sender.SendFilter.NotNull().SelectedObjectIds;
objectIds.AddRange(selectedObjects);
}
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
_sendConversionCache.EvictObjects(unpackedObjectIds);
}
@@ -347,10 +351,13 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
// {
// await Commands.RefreshSendFilters();
// }
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
return;
}
if (
ChangedObjectIds.Any(e => _revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View)
)
if (ChangedObjectIds.Any(e => doc.GetElement(e) is View))
{
await Commands.RefreshSendFilters();
}
@@ -360,7 +367,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
var senders = _store.GetSenders().ToList();
// string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
var doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
@@ -402,7 +409,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
}
}
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds, doc);
_sendConversionCache.EvictObjects(unpackedObjectIds);
// Note: we're doing object selection and card expiry management by old school ids
@@ -37,7 +37,6 @@ public static class ServiceRegistration
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
// Storage Schema
serviceCollection.AddScoped<DocumentModelStorageSchema>();
serviceCollection.AddScoped<IdStorageSchema>();
// POC: we need to review the scopes and create a document on what the policy is
@@ -1,22 +0,0 @@
using Autodesk.Revit.DB.ExtensibleStorage;
namespace Speckle.Connectors.Revit.HostApp;
public class DocumentModelStorageSchema : IStorageSchema
{
private readonly Guid _schemaGuid = new("D690F2B4-BDB0-4CB4-8657-17844ADF42AA");
public Schema GetSchema()
{
Schema schema = Schema.Lookup(_schemaGuid);
if (schema != null)
{
return schema;
}
using SchemaBuilder builder = new(_schemaGuid);
builder.SetSchemaName("DUI3State");
builder.AddSimpleField("contents", typeof(string));
return builder.Finish();
}
}
@@ -1,8 +1,5 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.HostApp;
@@ -11,15 +8,6 @@ namespace Speckle.Connectors.Revit.HostApp;
/// </summary>
public class ElementUnpacker
{
private readonly RevitContext _revitContext;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
public ElementUnpacker(RevitContext revitContext, IConverterSettingsStore<RevitConversionSettings> converterSettings)
{
_revitContext = revitContext;
_converterSettings = converterSettings;
}
/// <summary>
/// Unpacks a random set of revit objects into atomic objects. It currently unpacks groups recurisvely, nested families into atomic family instances.
/// This method will also "pack" curtain walls if necessary (ie, if mullions or panels are selected without their parent curtain wall, they are sent independently; if the parent curtain wall is selected, they will be removed out as the curtain wall will include all its children).
@@ -29,7 +17,7 @@ public class ElementUnpacker
/// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.<br/>
/// 2- Evicting the cache while introducing the settings</param>
/// <returns></returns>
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document? doc = null)
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document doc)
{
// Note: steps kept separate on purpose.
// Step 1: unpack groups
@@ -50,24 +38,19 @@ public class ElementUnpacker
/// <remarks>
/// This is used to invalidate object ids in the send conversion cache when the selected object id is only the parent element id
/// </remarks>
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds)
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds, Document doc)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
var docElements = doc.GetElements(objectIds);
return UnpackSelectionForConversion(docElements).Select(o => o.UniqueId).ToList();
return UnpackSelectionForConversion(docElements, doc).Select(o => o.UniqueId).ToList();
}
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
// 2- Evicting the cache while introducing the settings
private List<Element> UnpackElements(IEnumerable<Element> elements, Document? doc = null)
private List<Element> UnpackElements(IEnumerable<Element> elements, Document doc)
{
var unpackedElements = new List<Element>(); // note: could be a hashset/map so we prevent duplicates (?)
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
foreach (var element in elements)
{
// UNPACK: Groups
@@ -77,7 +60,7 @@ public class ElementUnpacker
// We add null checks to handle cases where elements can't be properly resolved
// POC: this might screw up generating hosting rel generation here, because nested families in groups get flattened out by GetMemberIds().
var groupElements = g.GetMemberIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(groupElements));
unpackedElements.AddRange(UnpackElements(groupElements, doc));
}
else if (element is BaseArray baseArray)
{
@@ -85,8 +68,8 @@ public class ElementUnpacker
// This handles cases where some elements might not resolve in linked contexts
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement).Where(el => el != null);
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(arrayElements));
unpackedElements.AddRange(UnpackElements(originalElements));
unpackedElements.AddRange(UnpackElements(arrayElements, doc));
unpackedElements.AddRange(UnpackElements(originalElements, doc));
}
// UNPACK: Family instances (as they potentially have nested families inside)
else if (element is FamilyInstance familyInstance)
@@ -99,7 +82,7 @@ public class ElementUnpacker
if (familyElements.Length != 0)
{
unpackedElements.AddRange(UnpackElements(familyElements));
unpackedElements.AddRange(UnpackElements(familyElements, doc));
}
unpackedElements.Add(familyInstance);
@@ -107,7 +90,7 @@ public class ElementUnpacker
else if (element is MultistoryStairs multistoryStairs)
{
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(stairs));
unpackedElements.AddRange(UnpackElements(stairs, doc));
}
else
{
@@ -125,13 +108,11 @@ public class ElementUnpacker
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
// 2- Evicting the cache while introducing the settings
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document? doc = null)
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document doc)
{
var ids = elements.Select(el => el.Id).ToArray();
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
//just used for contains so use ToHashSet
var ids = elements.Select(el => el.Id).ToHashSet();
elements.RemoveAll(element =>
(element is Mullion { Host: not null } m && ids.Contains(m.Host.Id))
|| (
@@ -1,50 +1,43 @@
using Autodesk.Revit.DB;
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,103 +94,48 @@ 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);
if (stateEntity == null || !stateEntity.IsValid())
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)
{
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())
var state = _jsonCacheManager.GetObject(key);
if (state == null)
{
continue;
return;
}
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
LoadFromString(state);
}
return null;
}
private Entity? GetSpeckleEntity(Document? doc)
{
if (doc is null)
{
return null;
}
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingEntity = dataStorage.GetEntity(_documentModelStorageSchema.GetSchema());
if (!settingEntity.IsValid())
{
continue;
}
return settingEntity;
}
return null;
}
}
@@ -3,9 +3,13 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ReferencePointSetting(ReceiveReferencePointType value) : ICardSetting
public class ReceiveReferencePointSetting(ReceiveReferencePointType value = ReceiveReferencePointSetting.DEFAULT_VALUE)
: ICardSetting
{
public string? Id { get; set; } = "referencePoint";
public const string SETTING_ID = "referencePoint";
public const ReceiveReferencePointType DEFAULT_VALUE = ReceiveReferencePointType.Source;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReceiveReferencePointType)).ToList();
@@ -1,5 +1,6 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
@@ -11,18 +12,21 @@ namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ToHostSettingsManager : IToHostSettingsManager
{
private readonly RevitContext _revitContext;
private readonly ILogger<ToHostSettingsManager> _logger;
public ToHostSettingsManager(RevitContext revitContext)
public ToHostSettingsManager(RevitContext revitContext, ILogger<ToHostSettingsManager> logger)
{
_revitContext = revitContext;
_logger = logger;
}
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 == ReceiveReferencePointSetting.SETTING_ID)?.Value as string;
if (
referencePointString is not null
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
&& ReceiveReferencePointSetting.ReferencePointMap.TryGetValue(
referencePointString,
out ReceiveReferencePointType referencePoint
)
@@ -34,7 +38,16 @@ public class ToHostSettingsManager : IToHostSettingsManager
return currentTransform;
}
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
// log the issue
_logger.LogWarning(
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
referencePointString,
modelCard.ModelCardId,
ReceiveReferencePointSetting.DEFAULT_VALUE
);
// return default (null for Source means no transform)
return null;
}
private Transform? GetTransform(ReceiveReferencePointType referencePointType)
@@ -91,7 +104,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
}
throw new InvalidOperationException(
"Revit Context UI Application was null when retrieving reference point transform."
"Revit Context UI Application was null when retrieving reference point transform"
);
}
}
@@ -2,7 +2,9 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converters.RevitShared.Extensions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -75,8 +77,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
//this used to throw an exception, but we don't want to fail loudly if the view is not found
return [];
}
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var elementsInView = viewCollector.ToElements();
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -125,4 +127,52 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
// Below function collects list of ids to exclude from final list.
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
{
var elementsToExclude = new HashSet<ElementId>();
foreach (var element in elements)
{
// check if element is a part
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
{
try
{
// get source element ids from the part
var sourceIds = part.GetSourceElementIds();
if (sourceIds != null)
{
foreach (var sourceId in sourceIds)
{
elementsToExclude.Add(sourceId.HostElementId);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
// silently continue processing other Parts if one fails
// this follows the pattern used elsewhere in the codebase
}
}
}
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
{
var idsToExclude = GetSourceElementIdsToExclude(allElements);
return allElements.Where(e => !idsToExclude.Contains(e.Id));
}
return allElements;
}
}
@@ -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.");
}
@@ -3,9 +3,12 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class DetailLevelSetting(DetailLevelType value) : ICardSetting
public class DetailLevelSetting(DetailLevelType value = DetailLevelSetting.DEFAULT_VALUE) : ICardSetting
{
public string? Id { get; set; } = "detailLevel";
public const string SETTING_ID = "detailLevel";
public const DetailLevelType DEFAULT_VALUE = DetailLevelType.Medium;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Detail Level";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(DetailLevelType)).ToList();
@@ -2,9 +2,12 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class LinkedModelsSetting(bool value) : ICardSetting
public class LinkedModelsSetting(bool value = LinkedModelsSetting.DEFAULT_VALUE) : ICardSetting
{
public string? Id { get; set; } = "includeLinkedModels";
public const string SETTING_ID = "includeLinkedModels";
public const bool DEFAULT_VALUE = true;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Include Linked Models";
public string? Type { get; set; } = "boolean";
public object? Value { get; set; } = value;
@@ -2,9 +2,13 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class SendParameterNullOrEmptyStringsSetting(bool value) : ICardSetting
public class SendParameterNullOrEmptyStringsSetting(bool value = SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE)
: ICardSetting
{
public string? Id { get; set; } = "nullemptyparams";
public const string SETTING_ID = "nullemptyparams";
public const bool DEFAULT_VALUE = false;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Send null/empty parameters";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
@@ -2,9 +2,12 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class SendRebarsAsVolumetricSetting(bool value) : ICardSetting
public class SendRebarsAsVolumetricSetting(bool value = SendRebarsAsVolumetricSetting.DEFAULT_VALUE) : ICardSetting
{
public string? Id { get; set; } = "sendRebarsAsVolumetric";
public const string SETTING_ID = "sendRebarsAsVolumetric";
public const bool DEFAULT_VALUE = false;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Send Rebars As Volumetric (disable for better performance)";
public string? Type { get; set; } = "boolean";
public object? Value { get; set; } = value;
@@ -3,9 +3,13 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class ReferencePointSetting(ReferencePointType value) : ICardSetting
public class SendReferencePointSetting(ReferencePointType value = SendReferencePointSetting.DEFAULT_VALUE)
: ICardSetting
{
public string? Id { get; set; } = "referencePoint";
public const string SETTING_ID = "referencePoint";
public const ReferencePointType DEFAULT_VALUE = ReferencePointType.InternalOrigin;
public string? Id { get; set; } = SETTING_ID;
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReferencePointType)).ToList();
@@ -1,11 +1,13 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
@@ -16,28 +18,32 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
private readonly RevitContext _revitContext;
private readonly ISendConversionCache _sendConversionCache;
private readonly ElementUnpacker _elementUnpacker;
private readonly ILogger<ToSpeckleSettingsManager> _logger;
// cache invalidation process run with ModelCardId since the settings are model specific
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = new();
private readonly Dictionary<string, Transform?> _referencePointCache = new();
private readonly Dictionary<string, bool?> _sendNullParamsCache = new();
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = new();
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = new();
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = [];
private readonly Dictionary<string, Transform?> _referencePointCache = [];
private readonly Dictionary<string, bool?> _sendNullParamsCache = [];
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
public ToSpeckleSettingsManager(
RevitContext revitContext,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker
ElementUnpacker elementUnpacker,
ILogger<ToSpeckleSettingsManager> logger
)
{
_revitContext = revitContext;
_elementUnpacker = elementUnpacker;
_sendConversionCache = sendConversionCache;
_logger = logger;
}
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 == DetailLevelSetting.SETTING_ID)?.Value as string;
if (
fidelityString is not null
&& DetailLevelSetting.GeometryFidelityMap.TryGetValue(fidelityString, out DetailLevelType fidelity)
@@ -54,15 +60,27 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return fidelity;
}
throw new ArgumentException($"Invalid geometry fidelity value: {fidelityString}");
// log the issue
_logger.LogWarning(
"Invalid detail level setting received: '{FidelityString}' for model {ModelCardId}, using default: {DefaultValue}",
fidelityString,
modelCard.ModelCardId,
DetailLevelSetting.DEFAULT_VALUE
);
// return sensible default
DetailLevelType defaultValue = DetailLevelSetting.DEFAULT_VALUE;
_detailLevelCache[modelCard.ModelCardId.NotNull()] = defaultValue;
return defaultValue;
}
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 == SendReferencePointSetting.SETTING_ID)?.Value as string;
if (
referencePointString is not null
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
&& SendReferencePointSetting.ReferencePointMap.TryGetValue(
referencePointString,
out ReferencePointType referencePoint
)
@@ -85,62 +103,96 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return currentTransform;
}
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
// log the issue
_logger.LogWarning(
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
referencePointString,
modelCard.ModelCardId,
SendReferencePointSetting.DEFAULT_VALUE
);
// return default (null for InternalOrigin means no transform)
_referencePointCache[modelCard.ModelCardId.NotNull()] = null;
return null;
}
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "nullemptyparams").Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendNullParamsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_sendNullParamsCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
SendParameterNullOrEmptyStringsSetting.SETTING_ID,
SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE,
modelCard,
_sendNullParamsCache,
"Send null/empty parameters"
);
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
public bool GetLinkedModelsSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "includeLinkedModels").Value as bool?;
var returnValue = value != null && value.NotNull();
public bool GetLinkedModelsSetting(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
LinkedModelsSetting.SETTING_ID,
LinkedModelsSetting.DEFAULT_VALUE,
modelCard,
_sendLinkedModelsCache,
"Linked models"
);
if (_sendLinkedModelsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
SendRebarsAsVolumetricSetting.SETTING_ID,
SendRebarsAsVolumetricSetting.DEFAULT_VALUE,
modelCard,
_sendRebarsAsVolumetricCache,
"Send rebars as volumetric"
);
/// <summary>
/// Helper method to handle boolean settings with caching and logging
/// </summary>
private bool GetBooleanSettingWithCache(
string settingId,
bool defaultValue,
SenderModelCard modelCard,
Dictionary<string, bool?> cache,
string settingName
)
{
var settingValue = modelCard.Settings?.FirstOrDefault(s => s.Id == settingId)?.Value as bool?;
bool returnValue = settingValue ?? defaultValue;
if (cache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_sendLinkedModelsCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "sendRebarsAsVolumetric").Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendRebarsAsVolumetricCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
cache[modelCard.ModelCardId] = returnValue;
// NOTE: we probably don't need to log here BUT considering users might complain that a setting might not have been
// respected (linked models disabled but still sent linked models), I think we should note this occurence so we know
if (settingValue == null)
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
_logger.LogWarning(
"{SettingName} setting was null for model {ModelCardId}, using default: {DefaultValue}",
settingName,
modelCard.ModelCardId,
defaultValue
);
}
_sendRebarsAsVolumetricCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
throw new SpeckleException("Unable to retrieve active UI document");
}
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
_sendConversionCache.EvictObjects(unpackedObjectIds);
}
@@ -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>(
@@ -19,7 +19,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Bindings\SelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ElementIdHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
@@ -37,7 +36,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReceiveReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\RevitHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\TransactionManager.cs" />
@@ -50,7 +49,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsVolumetricSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino7": {
@@ -382,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.logging": {
@@ -337,13 +337,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"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.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -399,6 +399,33 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public string SearchPattern { get; set; } = string.Empty;
private bool _autoSelectAllItemsItems;
/// <summary>
/// When enabled, all available items will be selected automatically and persistently.
/// </summary>
public bool AutoSelectAllItems
{
get => _autoSelectAllItemsItems;
set
{
if (_autoSelectAllItemsItems == value)
{
return;
}
_autoSelectAllItemsItems = value;
if (value && _listItems.Count > 0)
{
SelectAllItems();
ResetPersistentData(_listItems.Select(x => x.Value), "Enable auto-select all items");
}
OnDisplayExpired(false);
}
}
protected internal int LayoutLevel { get; set; } = 1;
sealed class ListItem
@@ -437,16 +464,14 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public RectangleF BoxName;
}
private List<ListItem> _listItems = new List<ListItem>();
private List<ListItem> _listItems = [];
IEnumerable<ListItem> SelectedItems => _listItems.Where(x => x.Selected);
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
if (Kind == GH_ParamKind.floating || Kind == GH_ParamKind.input)
{
Menu_AppendDestroyPersistent(menu);
if (Exposure != GH_Exposure.hidden) { }
Menu_AppendInternaliseData(menu);
}
}
@@ -549,6 +574,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected override GH_GetterResult Prompt_Singular(ref T value) => GH_GetterResult.cancel;
// NOTE: removed from AppendAdditionalMenuItems as clearing selection simple enough. Keeping here just in case
// we want to bring it back
protected override void Menu_AppendDestroyPersistent(ToolStripDropDown menu) =>
Menu_AppendItem(menu, "Clear selection", Menu_DestroyPersistentData, PersistentDataCount > 0);
@@ -569,11 +596,36 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected override void Menu_AppendInternaliseData(ToolStripDropDown menu)
{
Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
// Disabled Invert selection and one-off select all according to Discord chat. These are easy enough
// Select all also enabled through ctrl+a
//Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
//Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
var alwaysSelectAllItem = Menu_AppendItem(
menu,
"Auto-select all items",
Menu_AlwaysSelectAllClicked,
true,
_autoSelectAllItemsItems
);
alwaysSelectAllItem.ToolTipText = _autoSelectAllItemsItems
? "Currently auto-selecting all available items. Click to disable."
: "Enable automatic selection of all available items. Will persist when new data is input.";
Menu_AppendItem(menu, "Internalise selection", Menu_InternaliseDataClicked, SourceCount > 0);
}
/// <summary>
/// Helper method that reduces code duplication to select all items
/// </summary>
private void SelectAllItems()
{
foreach (var item in _listItems)
{
item.Selected = true;
}
}
private void Menu_InternaliseDataClicked(object sender, EventArgs e)
{
if (SourceCount == 0)
@@ -610,14 +662,16 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected void Menu_SelectAllClicked(object sender, EventArgs e)
{
foreach (var item in _listItems)
{
item.Selected = true;
}
SelectAllItems();
ResetPersistentData(_listItems.Select(x => x.Value), "Select all");
}
/// <summary>
/// Event handler for auto-select all items menu item
/// </summary>
private void Menu_AlwaysSelectAllClicked(object sender, EventArgs e) =>
AutoSelectAllItems = !_autoSelectAllItemsItems;
sealed class ResizableAttributes : GH_ResizableAttributes<ValueSet<T>>
{
public override bool HasInputGrip => true;
@@ -1336,11 +1390,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
return GH_ObjectResponse.Ignore;
}
public override GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
{
return base.RespondToKeyDown(sender, e);
}
private sealed class SearchInputBox : Grasshopper.GUI.Base.GH_TextBoxInputBase
{
private readonly ResizableAttributes _parentAttributes;
@@ -1398,6 +1447,12 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
reader.TryGetInt32("LayoutLevel", ref layoutLevel);
LayoutLevel = Rhino.RhinoMath.Clamp(layoutLevel, 1, 2);
bool alwaysSelectAll = false;
if (reader.TryGetBoolean("AutoSelectAllItems", ref alwaysSelectAll))
{
_autoSelectAllItemsItems = alwaysSelectAll;
}
return true;
}
@@ -1423,6 +1478,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
writer.SetInt32("LayoutLevel", LayoutLevel);
}
writer.SetBoolean("AutoSelectAllItems", _autoSelectAllItemsItems);
return true;
}
@@ -1684,11 +1741,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public sealed override void PostProcessData()
{
LoadVolatileData();
PreProcessVolatileData();
ProcessVolatileData();
SortItems();
// Order by fuzzy token if suits.
@@ -1701,6 +1755,13 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
}
PostProcessVolatileData();
// auto-select if enabled
if (_autoSelectAllItemsItems && _listItems.Count > 0 && _listItems.Any(item => !item.Selected))
{
SelectAllItems();
ResetPersistentData(_listItems.Select(x => x.Value), null);
}
}
public override void RegisterRemoteIDs(GH_GuidTable id_list)
@@ -1,3 +1,4 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -12,7 +13,7 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
: base(
"Collection Selector",
"cSelect",
"Allows you to select a set of collection paths for querying",
"Allows you to select a set of collection paths for querying. Right-click for 'Auto-select all items' option.",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.COLLECTIONS
) { }
@@ -22,11 +23,18 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
protected override void LoadVolatileData()
{
var collections = VolatileData
List<SpeckleCollectionWrapper> collections = VolatileData
.AllData(true)
.OfType<SpeckleCollectionWrapperGoo>()
.Select(goo => goo.Value)
.ToList();
if (VolatileDataCount > collections.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Collections are accepted as inputs.");
return;
}
if (collections.Count == 0)
{
return;
@@ -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;
@@ -28,7 +28,12 @@ public class TokenUrlComponent : GH_Component
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
pManager.AddTextParameter("Speckle Token", "Token", "Speckle Authorization Token", GH_ParamAccess.item);
pManager.AddTextParameter(
"Speckle Token",
"Token",
"Speckle Authorization Token. Requires profile:read, profile:email, stream:read, and workspace:read (unless on a non-workspace enable server), as well as any other write scopes needed",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
@@ -99,6 +104,11 @@ public class TokenUrlComponent : GH_Component
throw new SpeckleException("No account found for server URL");
}
if (account.userInfo.id is null || account.userInfo.email is null)
{
throw new SpeckleException("Token requires profile:read and profile:email scopes");
}
IClient client = scope.Get<IClientFactory>().Create(account);
var project = client.Project.Get(resource.ProjectId).Result;
@@ -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
@@ -13,7 +13,7 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
: base(
"Property Selector",
"pSelect",
"Allows you to select a set of property keys for querying",
"Allows you to select a set of property keys for querying. Right-click for 'Auto-select all items' option.",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
@@ -24,18 +24,17 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
protected override void LoadVolatileData()
{
var propertyGroups = VolatileData
List<SpecklePropertyGroupGoo> propertyGroups = VolatileData
.AllData(true)
.Where(goo => goo is SpecklePropertyGroupGoo)
.Select(goo =>
goo switch
{
SpecklePropertyGroupGoo geometryGoo => geometryGoo,
_ => throw new InvalidOperationException("Unexpected goo type")
}
)
.OfType<SpecklePropertyGroupGoo>()
.ToList();
if (VolatileDataCount > propertyGroups.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Properties are accepted as inputs.");
return;
}
if (propertyGroups.Count == 0)
{
return;
@@ -1,3 +1,4 @@
using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
@@ -44,14 +45,14 @@ public class QueryProperties : GH_Component
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a Speckle Properties item");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a Speckle Properties item");
return;
}
List<string> keys = new();
List<string> keys = [];
if (!da.GetDataList(1, keys))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a key");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a key");
return;
}
@@ -60,11 +61,21 @@ public class QueryProperties : GH_Component
return;
}
List<object?> values = new();
List<object?> values = [];
foreach (string key in keys)
{
ISpecklePropertyGoo? value = GetValueByPath(properties, key);
values.Add(value);
var value = GetValueByPath(properties, key);
var extractedValue = (value as SpecklePropertyGoo)?.Value ?? value;
// NOTE: if property is a list, flatten into individual items for native gh list access
if (extractedValue is IList itemList)
{
values.AddRange(itemList.Cast<object?>());
}
else
{
values.Add(extractedValue);
}
}
da.SetDataList(0, values);
@@ -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,11 @@ 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());
}
// process geometry
if (inputObjects.Count > 0)
@@ -151,8 +151,11 @@ 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());
}
// process definition
if (inputDefinition != null)
@@ -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.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -341,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino7": {
@@ -401,18 +401,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"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.1, )",
"Speckle.Sdk": "[3.5.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -341,13 +341,12 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"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.1, )",
"resolved": "3.5.1",
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1, )",
"resolved": "3.5.1",
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"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>
@@ -235,9 +235,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.1-rc.1, )",
"Speckle.Sdk": "[3.5.1-rc.1, )",
"Speckle.Sdk.Dependencies": "[3.5.1-rc.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -261,7 +261,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.1-rc.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino8": {
@@ -311,18 +311,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.1-rc.1, )",
"resolved": "3.5.1-rc.1",
"contentHash": "kkHlYdOdTuky7BvyU3fRayS5P/3E3EDQ6yBbwWqQqjGN3c/fozqI+20Xm87KJrkMb/9XpMfQxMfZX1n4UN80sA==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.1-rc.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.1-rc.1, )",
"resolved": "3.5.1-rc.1",
"contentHash": "ZyNQI1IyWSAQ4Xc72v0WOeXon9BwtpIiw6wCIT7GrRjymSe5jXW/qMpG3YpBMccGPljCDZJFDmd87fKnWNRt3w==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -330,14 +330,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.1-rc.1"
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.1-rc.1, )",
"resolved": "3.5.1-rc.1",
"contentHash": "zDaocTPBFl2M5NkMsE9VqTLgbY7wy/MrE21JMBqOOcxHVtwDNOlSY9npBWDy8aAUEl6eJEKPoQr2B3Zd/RmJ9g=="
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,4 +1,5 @@
using Rhino;
using Rhino.Collections;
using Rhino.DocObjects;
using Speckle.Connectors.Rhino.Mapper.Revit;
using Speckle.Sdk;
@@ -19,7 +20,67 @@ public class PropertiesExtractor
public Dictionary<string, object?> GetProperties(RhinoObject rhObject)
{
Dictionary<string, object?> properties = new();
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)
{
@@ -38,7 +99,7 @@ public class PropertiesExtractor
if (userStrings[key]?.StartsWith("%<") ?? false)
{
var value = RhinoApp.ParseTextField(userStrings[key], rhObject, null);
properties[key] = value;
userStringDict[key] = value;
continue;
}
}
@@ -47,19 +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];
}
// NOTE: if no mapping was found on the object, check layer(s) recursively
if (!properties.ContainsKey(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
if (!userStringDict.ContainsKey(RevitMappingConstants.CATEGORY_USER_STRING_KEY))
{
var layerMapping = _revitMappingResolver.SearchLayerHierarchyForMapping(rhObject);
if (!string.IsNullOrEmpty(layerMapping))
{
properties[RevitMappingConstants.CATEGORY_USER_STRING_KEY] = layerMapping;
userStringDict[RevitMappingConstants.CATEGORY_USER_STRING_KEY] = layerMapping;
}
}
return properties;
return userStringDict;
}
}
@@ -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)
@@ -131,4 +131,24 @@ public class RhinoLayerHelper
}
}
}
/// <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));
}
@@ -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)
@@ -16,7 +16,7 @@ public class ToSpeckleSettingsManager
public bool GetAddVisualizationPropertiesSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "addVisualizationProperties").Value as bool?;
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))
{

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