Compare commits

...

38 Commits

Author SHA1 Message Date
Björn Steinhagen 3bb0860d14 feat(connectors): disable cache config (#1349)
.NET Test / test (push) Has been cancelled
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* feat(all): adds disable cache functionality

* feat(connectors): prevents StoreSendResult if cache disabled

* fix(connectors): restores IsBypassed state

* Explicit flag instead implicit

* Delete unused flag

* Add packfile support

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2026-04-08 11:27:22 +01:00
Oğuzhan Koral 0441cf7e3c Merge pull request #1356 from specklesystems/dev
UPDATE DEV INTO MAIN
2026-04-08 12:10:25 +03:00
Jedd Morgan 7860c44f4e feat(api)!: Implement new packfile based sends via SendPipline (aka DuckDB changes) (#1277)
* Dim/quack lets go (#1275)

* Add model ingestion to sharp connectors

* correct ingestion message

* Progress

* grasshopper

* GH exception messages

* fix GH

* file names

* revit file name

* grasshopper file names

* etabs file names

* delete tests

* tekla maybe

* ingestion  scope

* bad boolean logic

* Longer TimeSpan

* wip upload pipe

* 10s

* passthrough ingestion id

* happy hack time: prevent ingestion completion

this is handled server-side in the processing logic.

* add packfile send endpoint detection and routing

Route to SendViaPackfile when the server supports the upload-signing
endpoint (POST probe, 404 = unsupported) and a continuous traversal
builder is registered.

* Adds Continuous Traversal Builder

Introduces a Continuous Traversal Builder to manage the conversion and processing of Revit elements within a Send Pipeline.

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* feat(api): DI Refactor for Duck DB + Gergo's API endpoint changes (#1282)

* Di

* undo accidental change

* Feat (duck): dui ingestion updates post upload (#1295)

* Pass optional ingestion id to DUI

* Make ingestion id null for the SendViaIngestion, see the note :)

* feat!: Duckdev progress reporitng (#1296)

* Di

* throwaway from laptop

* Progress reporting

* Use matching logger

* Revit and revert rhino unpacker progress

* more revertion

* make pr even cleaner

* and this one

* fix build issues with other connectors

* SDK nuget (#1299)

* Bump to 3.14.0-alpha.2

* Feat(duck): grasshopper (#1297)

* Duck x Grasshopper - who would win?

* Fix registration for new builder

* missing imports

* return version id grasshopper

* Align sync resource to sync

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* Bump SDK

* feat(importer): rhino file importer changes for packfile (#1301)

* rhino importer changes

* correct deps

* Bump SDK

* Fix build issues

* ditto

* Fix build issue

* Lower standards

* Fix build

* feat: duck for acad, civil, navis, tekla, etabs (#1300)

* duck: acad, civil, etabs, tekla, navis and bump channels to 10.0.0

* notes

* fix conflicts

* more conflicts

* Ready for testing

* fix(duck): Fix send caching (#1302)

* potential fix

* undo-rhino chnages

* fix xml comment

* amended comment

* revit

* Fix build

* Aligned converting message

* fix: reoccurring object references

* Bump sdk and resolve merge conflict issues

* Merge pull request #1317 from specklesystems/jrm/importer-tracing

feat(otel): Tracing and OTEL changes for Rhino importer

* Fix revit linked model progress (#1312)

* Revert otel packages

* bump SDK

* Trace unpacking groups

* Align trace context nullability with app

* Disable send caching in Navisworks

* comments

* Update FileimportPayload.cs

* fix using directive

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>

* Fix merge conflicts

---------

Co-authored-by: Dimitrie Stefanescu <didimitrie@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastian Witt <sebastian.witt@rwth-aachen.de>
2026-04-08 10:07:56 +01:00
Jedd Morgan 38b9415e81 Update Plant3dSendBinding.cs (#1355)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
2026-04-02 17:44:57 +01:00
Mucahit Bilal GOKER a1f392a33b feat: plant 3d connector (#1344)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* wip

* feat: data extraction from db

* Fallback conversions

* Fix line segments

* Fix: do not skip empty values

* Remove claude generated receive boilerplates and civil related extractor

* Fix compile errors and custom assembly resolver

* Guids for bundle

* Nuget

* Use TagValue as object name

* add plant3d to slnx (#1347)

* add plant3d to slnx

* format

* and the local (#1348)

* Resolve comments

* final comments

* lockfiles

* don't swallow image exception

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2026-04-02 16:20:49 +01:00
Jedd Morgan 24442ab0a9 chore(sdk): bump SDK (#1352)
* Align tracing with server

* Bump sdk
2026-04-02 13:58:17 +02:00
Sebastian Witt 47344e0af3 Add dwg/dxf config to include material layer color conversion (#1351)
* Add dwg/dxf config to include material layer color conversion

* cleanup formatting
2026-04-02 10:12:03 +00:00
Björn Steinhagen 0d09ea5158 fix(rhino): resolve material type editing (#1345)
* fix(rhino): prevent material duplication on type change

* fix(rhino): modernise material baking and use RenderMaterial Guid for assignment

* chore(rhino): changes for rh7

* fix(rhino): resolve null reference and incorrect material assignment for layers

* refactor(rhino): simplifies material naming
2026-04-01 14:24:44 +02:00
dependabot[bot] 3be24d5b15 Merge pull request #1346 from specklesystems/dependabot/github_actions/codecov/codecov-action-6
chore(deps): bump codecov/codecov-action from 5 to 6
2026-03-30 22:10:33 +01:00
Björn Steinhagen 8bbb9b893e feat(revit): handles linked model elements and adds logging (#1333) 2026-03-27 19:35:06 +03:00
Jedd Morgan 85b2b21294 Merge pull request #1343 from specklesystems/dev
Dev -> Main
2026-03-27 10:56:43 +00:00
Björn Steinhagen 63d6d1a52b feat(revit): add shared location reference point setting (#1337) 2026-03-27 10:42:13 +02:00
Jedd Morgan 6e3eab30a4 feat(auth): New Auth in account binding (#1339)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* Update connectors to 3.15.0 sdk with logging changes

* Auth flow changes 3.15.1
2026-03-26 14:42:23 +00:00
Jedd Morgan 4d94cd76b6 Merge pull request #1340 from specklesystems/jrm/main-dev6
chore: Main -> Dev back merge
2026-03-26 14:10:20 +00:00
Jedd Morgan faae9bac62 Merge remote-tracking branch 'origin/dev' into jrm/main-dev6 2026-03-26 14:00:27 +00:00
Jedd Morgan eca1dab265 Update connectors to 3.15.0 sdk with logging changes (#1338) 2026-03-26 16:42:06 +03:00
Björn Steinhagen 8a3249d3db feat(grasshopper): accept a mix of data objects and collections (#1332)
* feat(grasshopper): allow mixed data objects and collections

* chore(grasshopper): reverts send components from data back to collections
2026-03-25 22:18:07 +02:00
Jedd Morgan c08c9559c7 ci: Update Actions (#1336)
* Update Actions

* experiment

* 15 min timeout
2026-03-25 12:58:45 +01:00
Mucahit Bilal GOKER a59f3179f1 fix(revit): sending rooms when volume calculation is disabled 2026-03-24 16:27:26 +03:00
Björn Steinhagen e4f4a5533d dev -> main
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev -> main
2026-03-19 17:15:49 +02:00
Björn Steinhagen 259b6a59f1 Merge pull request #1330 from specklesystems/main-dev
main-dev backmerge
2026-03-19 17:03:54 +02:00
Björn Steinhagen a4a2655a2a Merge remote-tracking branch 'origin/dev' into main-dev 2026-03-19 16:56:34 +02:00
Björn Steinhagen 9dd6397b01 fix(revit): restore sequential path prefix stripping (#1328) 2026-03-19 16:34:27 +02:00
Björn Steinhagen 2a04c02cba dev -> main (#1327)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* fix(revit): anchors missing display value

* chore(deps): bump actions/upload-artifact from 6 to 7 (#1303)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  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>

* seq-tokens (#1308)

* chore(grasshopper): rewording (#1320)

* feat(grasshopper): adds property value field to filter objects (#1319)

Co-authored-by: Mucahit Bilal GOKER <51519350+bimgeek@users.noreply.github.com>

* feat(grasshopper): adds support for nested properties (#1313)

* feat(grasshopper): dot notation refactor

* fix(grasshopper): expand properties to not flatten

* fix(grasshopper): handle null inputs in property selector

* fix: merge conflicts

* chore(deps): bump geekyeggo/delete-artifact from 5 to 6 (#1321)

Bumps [geekyeggo/delete-artifact](https://github.com/geekyeggo/delete-artifact) from 5 to 6.
- [Release notes](https://github.com/geekyeggo/delete-artifact/releases)
- [Changelog](https://github.com/GeekyEggo/delete-artifact/blob/main/CHANGELOG.md)
- [Commits](https://github.com/geekyeggo/delete-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: geekyeggo/delete-artifact
  dependency-version: '6'
  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>

* feat(revit): parameter updater (#1307)

* Oguzhan/cnx 2941 update mechanism with fake data (#1305)

* WIP

* some untested tweaks

* feat(revit): implement update parameters binding for parameter updater (#1304)

* feat(parameter-updater): first stab at wiring up

* feat(connectors): dummy base binding

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>

* refactor: dedicated parameter updater binding (#1306)

* refactor: extract parameter updates to dedicated IParametersBinding

* chore: delete old update

* fix: disables pop-ups while updating

* fix(dui): inject base binding into RevitParametersBinding for toasts

* chore: sneaky unused using directives

* fix(revit): prioritize internal param names and deduplicate error toasts

* chore: addressed pr comments

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>

* feat(revit): adds analytical model to supported categories (#1325)

* fix(gh): restore deduplication and Revit proxy resolution (#1324)

* feat(gh): expand speckle props to now show set not union (#1322)

* feat(grasshopper): prevent dynamic components from dropping wires (#1326)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Mucahit Bilal GOKER <51519350+bimgeek@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2026-03-19 13:22:57 +02:00
Björn Steinhagen 0670c1866f feat(grasshopper): prevent dynamic components from dropping wires (#1326) 2026-03-19 13:09:35 +02:00
Björn Steinhagen 60bb160a0d feat(gh): expand speckle props to now show set not union (#1322) 2026-03-19 11:41:14 +02:00
Björn Steinhagen 9127284774 fix(gh): restore deduplication and Revit proxy resolution (#1324) 2026-03-19 10:39:23 +02:00
Björn Steinhagen 0ea495e698 feat(revit): adds analytical model to supported categories (#1325) 2026-03-18 21:37:00 +02:00
Björn Steinhagen 8673879e48 feat(revit): parameter updater (#1307)
* Oguzhan/cnx 2941 update mechanism with fake data (#1305)

* WIP

* some untested tweaks

* feat(revit): implement update parameters binding for parameter updater (#1304)

* feat(parameter-updater): first stab at wiring up

* feat(connectors): dummy base binding

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>

* refactor: dedicated parameter updater binding (#1306)

* refactor: extract parameter updates to dedicated IParametersBinding

* chore: delete old update

* fix: disables pop-ups while updating

* fix(dui): inject base binding into RevitParametersBinding for toasts

* chore: sneaky unused using directives

* fix(revit): prioritize internal param names and deduplicate error toasts

* chore: addressed pr comments

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2026-03-18 12:32:31 +00:00
dependabot[bot] 8d04c9f9c8 chore(deps): bump geekyeggo/delete-artifact from 5 to 6 (#1321)
Bumps [geekyeggo/delete-artifact](https://github.com/geekyeggo/delete-artifact) from 5 to 6.
- [Release notes](https://github.com/geekyeggo/delete-artifact/releases)
- [Changelog](https://github.com/GeekyEggo/delete-artifact/blob/main/CHANGELOG.md)
- [Commits](https://github.com/geekyeggo/delete-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: geekyeggo/delete-artifact
  dependency-version: '6'
  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>
2026-03-17 15:46:37 +00:00
Björn Steinhagen d73ac2446a feat(grasshopper): adds support for nested properties (#1313)
* feat(grasshopper): dot notation refactor

* fix(grasshopper): expand properties to not flatten

* fix(grasshopper): handle null inputs in property selector

* fix: merge conflicts
2026-03-17 12:30:24 +00:00
Björn Steinhagen 3edc877466 feat(grasshopper): adds property value field to filter objects (#1319)
Co-authored-by: Mucahit Bilal GOKER <51519350+bimgeek@users.noreply.github.com>
2026-03-17 14:15:06 +02:00
Björn Steinhagen 9a3d41db3d chore(grasshopper): rewording (#1320) 2026-03-17 11:15:25 +02:00
Jedd Morgan eb166c0931 Merge pull request #1315 from specklesystems/jrm/main-dev
Main -> Dev
2026-03-11 13:00:34 +00:00
Jedd Morgan cb15106ca7 Merge branch 'dev' into jrm/main-dev 2026-03-11 12:59:14 +00:00
Jedd Morgan 3768157efe seq-tokens (#1308) 2026-03-11 12:52:47 +00:00
dependabot[bot] c51a0fda53 chore(deps): bump actions/upload-artifact from 6 to 7 (#1303)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  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>
2026-03-02 23:56:36 +00:00
Mucahit Bilal GOKER 4fdc522e42 fix(revit): anchors missing display value 2026-02-26 15:41:40 +03:00
247 changed files with 7117 additions and 1950 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
run: ./build.sh test-and-pack
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v6
with:
files: Converters/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
+7 -9
View File
@@ -35,7 +35,7 @@ jobs:
run: ./build.ps1 zip
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: output-${{ env.SEMVER }}
path: output/*.*
@@ -48,7 +48,7 @@ jobs:
run: |
echo "semver=${{ env.SEMVER }}" >> "$Env:GITHUB_OUTPUT"
echo "file_version=${{ env.FILE_VERSION }}" >> "$Env:GITHUB_OUTPUT"
deploy-installers:
runs-on: ubuntu-latest
needs: build-connectors
@@ -56,7 +56,7 @@ jobs:
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
steps:
- name: 🔫 Trigger Build Installers
uses: the-actions-org/workflow-dispatch@v4.0.0
uses: benc-uk/workflow-dispatch@v1
with:
workflow: Build Installers
repo: specklesystems/connector-installers
@@ -70,12 +70,10 @@ jobs:
}'
ref: main
wait-for-completion: true
wait-for-completion-interval: 10s
wait-for-completion-timeout: 10m
display-workflow-run-url: true
display-workflow-run-url-interval: 10s
sync-status: true
timeout-minutes: 15
# Allows us to inspect the artifacts of failed builds, since this below step will be skipped if the above step fails
- uses: geekyeggo/delete-artifact@v5
- uses: geekyeggo/delete-artifact@v6
with:
name: output-*
+2
View File
@@ -21,3 +21,5 @@ tools
coverage.xml
output/
Images/Thumbs.db
.claude/
+1
View File
@@ -43,6 +43,7 @@ public static class Consts
new("Connectors/Autocad/Speckle.Connectors.Civil3d2026", "net8.0-windows")
]
),
new("plant3d", [new("Connectors/Autocad/Speckle.Connectors.Plant3d2026", "net8.0-windows")]),
new(
"navisworks",
[
+1 -1
View File
@@ -1,4 +1,4 @@
CA1502: 25
CA1501: 5
CA1506(Method): 50
CA1506(Method): 60
CA1506(Type): 95
@@ -35,4 +35,23 @@
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
<StartArguments>/product C3D</StartArguments>
</PropertyGroup>
<Target AfterTargets="Clean" Name="CleanAddinPlant3D" Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<RemoveDir Directories="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\Contents\Windows\Speckle.Connectors.Plant3d$(Plant3DVersion);" />
</Target>
<Target AfterTargets="Build" Name="AfterBuildPlant3D" Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<ItemGroup>
<Plant3DDLLs Include="$(TargetDir)\**\*.*" />
</ItemGroup>
<Message Text="Plant3D Version $(Plant3DVersion)" Importance="high"/>
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\Contents\Windows\Speckle.Connectors.Plant3d$(Plant3DVersion)\%(RecursiveDir)" SourceFiles="@(Plant3DDLLs)" />
<Copy DestinationFolder="$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Plant3d.bundle\" SourceFiles="$(TargetDir)\Plugin\BundlePlant3D\PackageContents.xml" />
</Target>
<PropertyGroup Condition="'$(Plant3DVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<StartAction>Program</StartAction>
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Plant3DVersion)\acad.exe</StartProgram>
<StartArguments>/product PLNT3D</StartArguments>
</PropertyGroup>
</Project>
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -356,11 +358,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -356,11 +358,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -313,7 +315,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +262,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,11 +306,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +262,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,11 +306,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -18,6 +18,10 @@ public static class AutocadConnectorModule
// Send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, AutocadRootObjectBuilder>();
serviceCollection.AddScoped<
IRootContinuousTraversalBuilder<AutocadRootObject>,
AutocadContinuousTraversalBuilder
>();
// Receive
serviceCollection.LoadReceive();
@@ -1,9 +1,9 @@
using Autodesk.AutoCAD.Colors;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Models.Proxies;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.HostApp;
@@ -5,7 +5,6 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.DoubleNumerics;
@@ -16,6 +15,7 @@ using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.HostApp;
@@ -3,12 +3,12 @@ using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.GraphicsInterface;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.InterfaceGenerator;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Pipelines.Progress;
using Material = Autodesk.AutoCAD.DatabaseServices.Material;
using RenderMaterial = Speckle.Objects.Other.RenderMaterial;
@@ -12,6 +12,7 @@ using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.Operations.Receive;
@@ -0,0 +1,197 @@
using System.Diagnostics.CodeAnalysis;
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.Autocad.Operations.Send;
/// <summary>
/// Abstract base class for AutoCAD continuous traversal builders that stream objects through a
/// <see cref="SendPipeline"/> for packfile-based uploads. Same conversion logic as
/// <see cref="AutocadRootObjectBaseBuilder"/>, but processes elements through the pipeline.
/// </summary>
public abstract class AutocadContinuousTraversalBaseBuilder : IRootContinuousTraversalBuilder<AutocadRootObject>
{
private readonly IRootToSpeckleConverter _converter;
private readonly string[] _documentPathSeparator = ["\\"];
private readonly ISendConversionCache _sendConversionCache;
private readonly AutocadInstanceUnpacker _instanceUnpacker;
private readonly AutocadMaterialUnpacker _materialUnpacker;
private readonly AutocadColorUnpacker _colorUnpacker;
private readonly AutocadGroupUnpacker _groupUnpacker;
private readonly ILogger<AutocadRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
protected AutocadContinuousTraversalBaseBuilder(
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
{
_converter = converter;
_sendConversionCache = sendConversionCache;
_instanceUnpacker = instanceObjectManager;
_materialUnpacker = materialUnpacker;
_colorUnpacker = colorUnpacker;
_groupUnpacker = groupUnpacker;
_logger = logger;
_activityFactory = activityFactory;
}
[SuppressMessage(
"Maintainability",
"CA1506:Avoid excessive class coupling",
Justification = """
It is already simplified but has many different references since it is a builder. Do not know can we simplify it now.
Later we might consider to refactor proxies from one proxy manager? but we do not know the shape of it all potential
proxy classes yet. So I'm supressing this one now!!!
"""
)]
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// 0 - Init the root
Collection root =
new()
{
name = Application
.DocumentManager.CurrentDocument.Name.Split(_documentPathSeparator, StringSplitOptions.None)
.Reverse()
.First()
};
Document doc = Application.DocumentManager.CurrentDocument;
using Transaction tr = doc.Database.TransactionManager.StartTransaction();
// 1 - Unpack the instances
var (atomicObjects, _, instanceProxies, instanceDefinitionProxies) = _instanceUnpacker.UnpackSelection(objects);
root[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies;
// 2 - Unpack the groups
root[ProxyKeys.GROUP] = _groupUnpacker.UnpackGroups(atomicObjects);
using (var _ = _activityFactory.Start("Converting objects"))
{
// 3 - Convert atomic objects and process through pipeline
List<LayerTableRecord> usedAcadLayers = new();
List<SendConversionResult> results = new();
int count = 0;
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = await ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
projectId,
sendPipeline
);
results.Add(result);
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {atomicObjects.Count:N0})", (double)++count / atomicObjects.Count)
);
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
// 4 - Unpack the render material proxies
root[ProxyKeys.RENDER_MATERIAL] = _materialUnpacker.UnpackMaterials(atomicObjects, usedAcadLayers);
// 5 - Unpack the color proxies
root[ProxyKeys.COLOR] = _colorUnpacker.UnpackColors(atomicObjects, usedAcadLayers);
// add any additional properties (most likely from verticals)
AddAdditionalProxiesToRoot(root);
// Process root collection and wait for all uploads
await sendPipeline.Process(root);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(root, results);
}
}
public virtual (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
return (new(), null);
}
public virtual void AddAdditionalProxiesToRoot(Collection rootCollection)
{
return;
}
private async Task<SendConversionResult> ConvertAutocadEntity(
Entity entity,
string applicationId,
Collection collectionHost,
IReadOnlyDictionary<string, InstanceProxy> instanceProxies,
string projectId,
SendPipeline sendPipeline
)
{
string sourceType = entity.GetType().ToString();
try
{
Base converted;
if (entity is BlockReference && instanceProxies.TryGetValue(applicationId, out InstanceProxy? instanceProxy))
{
converted = instanceProxy;
}
else if (_sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
}
else
{
converted = _converter.Convert(entity);
converted.applicationId = applicationId;
}
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
collectionHost.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogSendConversionError(ex, sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
}
@@ -0,0 +1,46 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Common.Caching;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Autocad.Operations.Send;
public sealed class AutocadContinuousTraversalBuilder : AutocadContinuousTraversalBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
public AutocadContinuousTraversalBuilder(
AutocadLayerUnpacker layerUnpacker,
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
}
@@ -16,6 +16,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Autocad.Operations.Send;
@@ -7,6 +7,8 @@ public static class AppUtils
public static Speckle.Sdk.Application App =>
#if CIVIL3D
HostApplications.Civil3D;
#elif PLANT3D
HostApplications.Plant3D;
#elif AUTOCAD
HostApplications.AutoCAD;
#else
@@ -14,11 +16,11 @@ public static class AppUtils
#endif
public static HostAppVersion Version =>
#if AUTOCAD2026 || CIVIL3D2026
#if AUTOCAD2026 || CIVIL3D2026 || PLANT3D2026
HostAppVersion.v2026;
#elif AUTOCAD2025 || CIVIL3D2025
#elif AUTOCAD2025 || CIVIL3D2025 || PLANT3D2025
HostAppVersion.v2025;
#elif AUTOCAD2024 || CIVIL3D2024
#elif AUTOCAD2024 || CIVIL3D2024 || PLANT3D2024
HostAppVersion.v2024;
#elif AUTOCAD2023|| CIVIL3D2023
HostAppVersion.v2023;
@@ -11,6 +11,9 @@ using Speckle.Converters.Autocad;
#elif CIVIL3D
using Speckle.Converters.Civil3dShared;
using Speckle.Connectors.Civil3dShared.DependencyInjection;
#elif PLANT3D
using Speckle.Connectors.Plant3dShared.DependencyInjection;
using Speckle.Converters.Plant3dShared;
#endif
namespace Speckle.Connectors.Autocad.Plugin;
@@ -39,13 +42,18 @@ public class AutocadCommand
// init DI
var services = new ServiceCollection();
_disposableLogger = services.Initialize(AppUtils.App, AppUtils.Version);
#if AUTOCAD
services.AddAutocad();
services.AddAutocadConverters();
#elif CIVIL3D
services.AddCivil3d();
services.AddCivil3dConverters();
#elif PLANT3D
services.AddPlant3d();
services.AddPlant3dConverters();
#endif
Container = services.BuildServiceProvider();
Container.UseDUI();
@@ -3,7 +3,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using Autodesk.Windows;
using Speckle.Sdk;
#if !AUTOCAD2025_OR_GREATER && !CIVIL3D2025_OR_GREATER
#if !AUTOCAD2025_OR_GREATER && !CIVIL3D2025_OR_GREATER && !PLANT3D2025_OR_GREATER
using System.IO;
#endif
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<ApplicationPackage
SchemaVersion="1.0"
AppVersion="1.0"
Author="AEC SYSTEMS LTD"
Name="Speckle for Plant 3D"
Description="Speckle for Plant 3D"
Icon="./Contents/Resources/Logo.ico"
ProductCode="{b91352b6-01db-4084-93c8-684bb231947e}"
UpgradeCode="{91c7e43f-e962-40e9-85bd-d6bd733b6964}">
<CompanyDetails
Name="Speckle"
Url="https://speckle.systems"
Email="support@speckle.systems" />
<Components>
<!-- Plant 3D 2026 -->
<ComponentEntry
AppName="Speckle.Connectors.Plant3d2026"
ModuleName="./Contents/Windows/Speckle.Connectors.Plant3d2026/Speckle.Connectors.Plant3d2026.dll"
LoadOnAutoCADStartup="True">
<RuntimeRequirements
OS="Win64"
Platform="Plant3D"
SeriesMin="R25.1"
SeriesMax="R25.1" />
</ComponentEntry>
</Components>
</ApplicationPackage>
@@ -41,6 +41,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\AutocadHostObjectBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\AutocadHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadContinuousTraversalBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBaseBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\AutocadRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\AutocadRibbon.cs" />
@@ -60,5 +62,8 @@
<Content Include="$(MSBuildThisFileDirectory)Plugin\BundleAutoCAD\PackageContents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Plugin\BundlePlant3D\PackageContents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
@@ -82,8 +82,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -180,24 +180,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -290,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -82,8 +82,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -180,24 +180,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -290,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -82,8 +82,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -180,24 +180,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -290,7 +292,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +324,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,11 +368,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -239,7 +239,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +272,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,11 +316,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -239,7 +239,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +272,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,11 +316,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -22,6 +22,10 @@ public static class Civil3dConnectorModule
// add send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Civil3dRootObjectBuilder>();
serviceCollection.AddScoped<
IRootContinuousTraversalBuilder<AutocadRootObject>,
Civil3dContinuousTraversalBuilder
>();
serviceCollection.AddSingleton<IBinding, Civil3dSendBinding>();
// add receive
@@ -0,0 +1,57 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Civil3dShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Civil3dShared.Operations.Send;
public sealed class Civil3dContinuousTraversalBuilder : AutocadContinuousTraversalBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
private readonly PropertySetDefinitionHandler _propertySetDefinitionHandler;
public Civil3dContinuousTraversalBuilder(
AutocadLayerUnpacker layerUnpacker,
PropertySetDefinitionHandler propertySetDefinitionHandler,
IRootToSpeckleConverter converter,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
_propertySetDefinitionHandler = propertySetDefinitionHandler;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
public override void AddAdditionalProxiesToRoot(Collection rootObject)
{
rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] = _propertySetDefinitionHandler.Definitions;
}
}
@@ -13,6 +13,7 @@
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\PropertySetBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\Civil3dHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dSendBinding.cs" />
</ItemGroup>
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Plant3DVersion>2026</Plant3DVersion>
<DefineConstants>$(DefineConstants);PLANT3D2026;PLANT3D;PLANT3D2024_OR_GREATER;PLANT3D2025_OR_GREATER;PLANT3D2026_OR_GREATER</DefineConstants>
<Configurations>Debug;Release;Local</Configurations>
</PropertyGroup>
<PropertyGroup>
<!-- .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-->
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <!--This is needed just to keep folder paths the same as the netframework versions-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Plant3D.API" VersionOverride="2026.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Plant3d\Speckle.Converters.Plant3d2026\Speckle.Converters.Plant3d2026.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
</ItemGroup>
<Import Project="..\Speckle.Connectors.Plant3dShared\Speckle.Connectors.Plant3dShared.projitems" Label="Shared" />
<Import Project="..\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems" Label="Shared" />
</Project>
@@ -0,0 +1,341 @@
{
"version": 2,
"dependencies": {
"net8.0-windows7.0": {
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
"resolved": "1.0.3",
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"PolySharp": {
"type": "Direct",
"requested": "[1.14.1, )",
"resolved": "1.14.1",
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
},
"Speckle.AutoCAD.API": {
"type": "Direct",
"requested": "[2026.0.0, )",
"resolved": "2026.0.0",
"contentHash": "WlkV81PmbK/ftoM7aGpU6LGosKbePBQej9MO/m63rFMozX89tsitEhE12o58wu7K/4FmRUdAMolYtdK20EDBnw=="
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.6, )",
"resolved": "0.9.6",
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
},
"Speckle.Plant3D.API": {
"type": "Direct",
"requested": "[2026.0.0, )",
"resolved": "2026.0.0",
"contentHash": "VT8M6CGQl1mk3/5Ro3AEAOVrOs/cX58YRwYZnu+6xeHvsazK07Et/6kMJsde4Y7Xrpox7eBLKgYeOspiRVvX5g==",
"dependencies": {
"Speckle.AutoCAD.API": "2026.0.0"
}
},
"GraphQL.Client": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0",
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
"System.Reactive": "5.0.0"
}
},
"GraphQL.Client.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
"dependencies": {
"GraphQL.Primitives": "6.0.0"
}
},
"GraphQL.Client.Abstractions.Websocket": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0"
}
},
"GraphQL.Primitives": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Primitives": "2.2.0",
"System.ComponentModel.Annotations": "4.5.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
"dependencies": {
"System.Memory": "4.5.1",
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
"resolved": "1.0.3",
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
}
},
"SQLitePCLRaw.core": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"SQLitePCLRaw.provider.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Reactive": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )"
}
},
"speckle.connectors.dui.webview": {
"type": "Project",
"dependencies": {
"Microsoft.Web.WebView2": "[1.0.1938.49, )",
"Speckle.Connectors.DUI": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
"type": "Project"
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.plant3d2026": {
"type": "Project",
"dependencies": {
"Speckle.AutoCAD.API": "[2026.0.0, )",
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Plant3D.API": "[2026.0.0, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
"Microsoft.Extensions.Options": "2.2.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
},
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.15.3"
}
}
},
"net8.0-windows7.0/win-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"Microsoft.Web.WebView2": {
"type": "CentralTransitive",
"requested": "[1.0.1938.49, )",
"resolved": "1.0.1938.49",
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
}
}
}
}
@@ -0,0 +1,61 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Converters.Plant3dShared;
namespace Speckle.Connectors.Plant3dShared.Bindings;
public sealed class Plant3dSendBinding : AutocadSendBaseBinding
{
private readonly IPlant3dConversionSettingsFactory _plant3dConversionSettingsFactory;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Plant3dSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
IPlant3dConversionSettingsFactory plant3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
: base(
store,
parent,
sendFilters,
cancellationManager,
sendConversionCache,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
)
{
_plant3dConversionSettingsFactory = plant3dConversionSettingsFactory;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
// We need a separate send binding for Plant 3D due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider)
{
serviceProvider
.GetRequiredService<IConverterSettingsStore<Plant3dConversionSettings>>()
.Initialize(_plant3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
}
}
@@ -0,0 +1,27 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.DependencyInjection;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.Plant3dShared.Bindings;
using Speckle.Connectors.Plant3dShared.Operations.Send;
using Speckle.Sdk;
namespace Speckle.Connectors.Plant3dShared.DependencyInjection;
public static class Plant3dConnectorModule
{
public static void AddPlant3d(this IServiceCollection serviceCollection)
{
serviceCollection.AddAutocadBase();
// add send
serviceCollection.LoadSend();
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Plant3dRootObjectBuilder>();
serviceCollection.AddSingleton<IBinding, Plant3dSendBinding>();
// automatically detects the Class:IClass interface pattern to register all generated interfaces
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
}
}
@@ -0,0 +1,50 @@
using Autodesk.AutoCAD.DatabaseServices;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Common.Caching;
using Speckle.Converters.Autocad;
using Speckle.Converters.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Plant3dShared.Operations.Send;
public sealed class Plant3dRootObjectBuilder : AutocadRootObjectBaseBuilder
{
private readonly AutocadLayerUnpacker _layerUnpacker;
public Plant3dRootObjectBuilder(
AutocadLayerUnpacker layerUnpacker,
IRootToSpeckleConverter converter,
IConverterSettingsStore<AutocadConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
AutocadInstanceUnpacker instanceObjectManager,
AutocadMaterialUnpacker materialUnpacker,
AutocadColorUnpacker colorUnpacker,
AutocadGroupUnpacker groupUnpacker,
ILogger<AutocadRootObjectBuilder> logger,
ISdkActivityFactory activityFactory
)
: base(
converter,
converterSettings,
sendConversionCache,
instanceObjectManager,
materialUnpacker,
colorUnpacker,
groupUnpacker,
logger,
activityFactory
)
{
_layerUnpacker = layerUnpacker;
}
public override (Collection, LayerTableRecord?) CreateObjectCollection(Entity entity, Transaction tr)
{
Layer layer = _layerUnpacker.GetOrCreateSpeckleLayer(entity, tr, out LayerTableRecord? autocadLayer);
return (layer, autocadLayer);
}
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>B1C4A14C-2F4E-4C3D-9B71-A3F5D8E6C912</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Speckle.Connectors.Plant3dShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Plant3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Plant3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Plant3dSendBinding.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)DependencyInjection\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\" />
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{B1C4A14C-2F4E-4C3D-9B71-A3F5D8E6C912}</ProjectGuid>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<Import Project="Speckle.Connectors.Plant3dShared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>
@@ -0,0 +1,211 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
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;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.CSiShared.Builders;
/// <summary>
/// Continuous traversal builder for CSi that streams objects through a <see cref="SendPipeline"/>
/// for packfile-based uploads. Same conversion logic as <see cref="CsiRootObjectBuilder"/>.
/// </summary>
public class CsiContinuousTraversalBuilder : IRootContinuousTraversalBuilder<ICsiWrapper>
{
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettings;
private readonly CsiSendCollectionManager _sendCollectionManager;
private readonly IMaterialUnpacker _materialUnpacker;
private readonly ISectionUnpacker _sectionUnpacker;
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
private readonly AnalysisResultsExtractor _analysisResultsExtractor;
public CsiContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
IConverterSettingsStore<CsiConversionSettings> converterSettings,
CsiSendCollectionManager sendCollectionManager,
IMaterialUnpacker materialUnpacker,
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService,
AnalysisResultsExtractor analysisResultsExtractor
)
{
_converterSettings = converterSettings;
_sendCollectionManager = sendCollectionManager;
_materialUnpacker = materialUnpacker;
_sectionUnpacker = sectionUnpacker;
_rootToSpeckleConverter = rootToSpeckleConverter;
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_analysisResultsExtractor = analysisResultsExtractor;
}
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var activity = _activityFactory.Start("Build");
string modelFileName = _csiApplicationService.SapModel.GetModelFilename(false) ?? "Unnamed model";
(string forceUnit, string tempUnit) = GetForceAndTemperatureUnits();
Collection rootObjectCollection =
new()
{
name = modelFileName,
["units"] = _converterSettings.Current.SpeckleUnits,
["forceUnits"] = forceUnit,
["temperatureUnits"] = tempUnit
};
List<SendConversionResult> results = new(csiObjects.Count);
int count = 0;
using (var _ = _activityFactory.Start("Convert all"))
{
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
var result = await ConvertCsiObject(csiObject, rootObjectCollection, sendPipeline);
results.Add(result);
count++;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {csiObjects.Count:N0})", (double)count / csiObjects.Count)
);
await Task.Yield();
}
}
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects");
}
using (var _ = _activityFactory.Start("Process Proxies"))
{
rootObjectCollection[ProxyKeys.MATERIAL] = _materialUnpacker.UnpackMaterials().ToList();
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
// Extract analysis results (if applicable)
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[RootKeys.ANALYSIS_RESULTS] = analysisResults;
}
catch (Exception ex)
{
throw new SpeckleException("Analysis result extraction failed", ex);
}
}
// Process root collection and wait for all uploads
await sendPipeline.Process(rootObjectCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObjectCollection, results);
}
private async Task<SendConversionResult> ConvertCsiObject(
ICsiWrapper csiObject,
Collection typeCollection,
SendPipeline sendPipeline
)
{
string sourceType = csiObject.ObjectName;
string applicationId = csiObject switch
{
CsiJointWrapper jointWrapper => jointWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiFrameWrapper frameWrapper => frameWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiCableWrapper cableWrapper => cableWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiTendonWrapper tendonWrapper => tendonWrapper.ObjectName,
CsiShellWrapper shellWrapper => shellWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiSolidWrapper solidWrapper => solidWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
CsiLinkWrapper linkWrapper => linkWrapper.GetSpeckleApplicationId(_csiApplicationService.SapModel),
_ => throw new ArgumentException($"Unsupported wrapper type: {csiObject.GetType()}", nameof(csiObject))
};
try
{
Base converted = _rootToSpeckleConverter.Convert(csiObject);
var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection);
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(false);
collection.elements.Add(reference);
return new(Status.SUCCESS, applicationId, sourceType, reference);
}
catch (NotImplementedException ex)
{
_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, "Failed to convert object {sourceType}", sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
private Dictionary<ModelObjectType, List<string>> GetObjectSummary(IReadOnlyList<ICsiWrapper> csiObjects) =>
csiObjects
.GroupBy(csiObject => csiObject.ObjectType)
.ToDictionary(group => group.Key, group => group.Select(obj => obj.Name).ToList());
private (string, string) GetForceAndTemperatureUnits()
{
var forceUnit = eForce.NotApplicable;
var lengthUnit = eLength.NotApplicable;
var temperatureUnit = eTemperature.NotApplicable;
_converterSettings.Current.SapModel.GetDatabaseUnits_2(ref forceUnit, ref lengthUnit, ref temperatureUnit);
return (forceUnit.ToString(), temperatureUnit.ToString());
}
}
@@ -13,6 +13,7 @@ using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.CSiShared.Builders;
@@ -43,6 +43,7 @@ public static class ServiceRegistration
services.AddScoped<ISendFilter, CsiSharedSelectionFilter>();
services.AddScoped<CsiSendCollectionManager>();
services.AddScoped<IRootObjectBuilder<ICsiWrapper>, CsiRootObjectBuilder>();
services.AddScoped<IRootContinuousTraversalBuilder<ICsiWrapper>, CsiContinuousTraversalBuilder>();
services.AddScoped<SendOperation<ICsiWrapper>>();
services.AddScoped<CsiMaterialPropertyExtractor>();
@@ -29,6 +29,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IApplicationSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\CsiPluginBase.cs" />
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.etabs21": {
@@ -355,11 +357,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -157,8 +157,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -166,13 +166,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -230,7 +230,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.etabs22": {
@@ -304,11 +304,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2020": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2021": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2022": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2023": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -73,8 +73,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -171,24 +171,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -281,7 +283,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +307,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2024": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -79,8 +79,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -177,24 +177,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -287,7 +289,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -311,7 +313,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2025": {
@@ -357,11 +359,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -88,8 +88,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -186,24 +186,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -288,7 +290,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +314,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.navisworks2026": {
@@ -359,11 +361,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
},
@@ -5,7 +5,6 @@ using Speckle.Connector.Navisworks.Operations.Send.Filters;
using Speckle.Connector.Navisworks.Operations.Send.Settings;
using Speckle.Connector.Navisworks.Services;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -18,6 +17,7 @@ using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Sdk.Common;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connector.Navisworks.Bindings;
@@ -57,6 +57,7 @@ public static class NavisworksConnectorServiceRegistration
// Sending operations
serviceCollection.AddScoped<IRootObjectBuilder<NAV.ModelItem>, NavisworksRootObjectBuilder>();
serviceCollection.AddScoped<IRootContinuousTraversalBuilder<NAV.ModelItem>, NavisworksContinuousTraversalBuilder>();
serviceCollection.AddScoped<SendOperation<NAV.ModelItem>>();
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
@@ -0,0 +1,400 @@
using Microsoft.Extensions.Logging;
using Speckle.Connector.Navisworks.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Converter.Navisworks.Helpers;
using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Objects.Data;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
using static Speckle.Connectors.Common.Operations.ProxyKeys;
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
namespace Speckle.Connector.Navisworks.Operations.Send;
/// <summary>
/// Continuous traversal builder for Navisworks that streams objects through a <see cref="SendPipeline"/>
/// for packfile-based uploads. Same conversion/grouping logic as <see cref="NavisworksRootObjectBuilder"/>,
/// but processes final elements through the pipeline after all post-processing is complete.
/// </summary>
public class NavisworksContinuousTraversalBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
IConverterSettingsStore<NavisworksConversionSettings> converterSettings,
ILogger<NavisworksContinuousTraversalBuilder> logger,
ISdkActivityFactory activityFactory,
NavisworksMaterialUnpacker materialUnpacker,
NavisworksColorUnpacker colorUnpacker,
Speckle.Converter.Navisworks.Constants.Registers.IInstanceFragmentRegistry instanceRegistry,
IElementSelectionService elementSelectionService,
IUiUnitsCache uiUnitsCache
) : IRootContinuousTraversalBuilder<NAV.ModelItem>
{
private bool SkipNodeMerging { get; set; }
private bool DisableGroupingForInstanceTesting { get; set; }
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
#if DEBUG
SkipNodeMerging = false;
DisableGroupingForInstanceTesting = false;
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
var rootCollection = InitializeRootCollection();
(Dictionary<string, Base?> convertedElements, List<SendConversionResult> conversionResults) =
await ConvertModelItemsAsync(navisworksModelItems, onOperationProgressed, cancellationToken);
ValidateConversionResults(conversionResults);
var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems);
var finalElements = BuildFinalElements(convertedElements, groupedNodes);
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
AddInstanceDefinitionsToCollection(rootCollection, ref finalElements);
// Process each final element through the send pipeline
var processedElements = new List<Base>(finalElements.Count);
foreach (var element in finalElements)
{
cancellationToken.ThrowIfCancellationRequested();
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(element).ConfigureAwait(false);
processedElements.Add(reference);
}
rootCollection.elements = processedElements;
// Process the root collection and wait for all uploads to complete
await sendPipeline.Process(rootCollection);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootCollection, conversionResults);
}
private static void ValidateInputs(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
IProgress<CardProgress> onOperationProgressed
)
{
if (!navisworksModelItems.Any())
{
throw new SpeckleException("No objects to convert");
}
if (navisworksModelItems == null)
{
throw new ArgumentNullException(nameof(navisworksModelItems));
}
if (onOperationProgressed == null || projectId == null)
{
throw new ArgumentNullException(
onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(projectId)
);
}
}
private Collection InitializeRootCollection() =>
new()
{
name = NavisworksApp.ActiveDocument.Title ?? "Unnamed model",
["units"] = converterSettings.Current.Derived.SpeckleUnits
};
private Task<(Dictionary<string, Base?> converted, List<SendConversionResult> results)> ConvertModelItemsAsync(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
var results = new List<SendConversionResult>(navisworksModelItems.Count);
var convertedBases = new Dictionary<string, Base?>();
int processedCount = 0;
int totalCount = navisworksModelItems.Count;
foreach (var item in navisworksModelItems)
{
cancellationToken.ThrowIfCancellationRequested();
var converted = ConvertNavisworksItem(item, convertedBases);
results.Add(converted);
processedCount++;
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
}
return Task.FromResult((convertedBases, results));
}
private static void ValidateConversionResults(List<SendConversionResult> results)
{
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects.");
}
}
private List<Base> BuildFinalElements(
Dictionary<string, Base?> convertedBases,
Dictionary<string, List<NAV.ModelItem>> groupedNodes
)
{
var finalElements = new List<Base>();
var processedPaths = new HashSet<string>();
if (!DisableGroupingForInstanceTesting)
{
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
logger.LogInformation(
"After grouping: {grouped} paths processed, {elements} elements in collection",
processedPaths.Count,
finalElements.Count
);
}
else
{
logger.LogInformation("Grouping disabled for instance testing");
}
if (converterSettings.Current.User.PreserveModelHierarchy)
{
logger.LogInformation("Building hierarchy (PreserveModelHierarchy=true)");
var hierarchyBuilder = new NavisworksHierarchyBuilder(
convertedBases,
rootToSpeckleConverter,
elementSelectionService
);
return hierarchyBuilder.BuildHierarchy();
}
logger.LogInformation("Adding remaining elements (flat mode)");
AddRemainingElements(finalElements, convertedBases, processedPaths);
logger.LogInformation("Final elements count: {count}", finalElements.Count);
return finalElements;
}
private void AddGroupedElements(
List<Base> finalElements,
Dictionary<string, Base?> convertedBases,
Dictionary<string, List<NAV.ModelItem>> groupedNodes,
HashSet<string> processedPaths
)
{
foreach (var group in groupedNodes)
{
var siblingBases = new List<Base>(group.Value.Count);
foreach (var itemPath in group.Value.Select(elementSelectionService.GetModelItemPath))
{
processedPaths.Add(itemPath);
if (convertedBases.TryGetValue(itemPath, out var convertedBase) && convertedBase != null)
{
siblingBases.Add(convertedBase);
}
}
if (siblingBases.Count > 0)
{
finalElements.Add(CreateNavisworksObject(group.Key, siblingBases));
}
}
}
private void AddRemainingElements(
List<Base> finalElements,
Dictionary<string, Base?> convertedBases,
HashSet<string> processedPaths
)
{
foreach (var kvp in convertedBases.Where(kvp => !processedPaths.Contains(kvp.Key)))
{
switch (kvp.Value)
{
case null:
continue;
case Collection collection:
finalElements.Add(collection);
break;
default:
if (CreateNavisworksObject(kvp.Value) is { } navisworksObject)
{
finalElements.Add(navisworksObject);
}
break;
}
}
}
private (string name, string path) GetElementNameAndPath(string applicationId)
{
var modelItem = elementSelectionService.GetModelItemFromPath(applicationId);
var context = HierarchyHelper.ExtractContext(modelItem);
return (context.Name, context.Path);
}
private NavisworksObject CreateNavisworksObject(string groupKey, List<Base> siblingBases)
{
string cleanParentPath = ElementSelectionHelper.GetCleanPath(groupKey);
(string name, string path) = GetElementNameAndPath(cleanParentPath);
int estimatedCapacity = siblingBases.Sum(b => (b["displayValue"] as List<Base>)?.Count ?? 0);
var displayValues = new List<Base>(estimatedCapacity);
displayValues.AddRange(
siblingBases
.Where(sibling => sibling["displayValue"] is List<Base>)
.SelectMany(sibling => (List<Base>)sibling["displayValue"]!)
);
return new NavisworksObject
{
name = name,
displayValue = displayValues,
properties = siblingBases.First()["properties"] as Dictionary<string, object?> ?? [],
units = converterSettings.Current.Derived.SpeckleUnits,
applicationId = groupKey,
["path"] = path
};
}
private NavisworksObject? CreateNavisworksObject(Base convertedBase)
{
if (convertedBase.applicationId == null)
{
return null;
}
(string name, string path) = GetElementNameAndPath(convertedBase.applicationId);
var units = uiUnitsCache.Ensure();
return new NavisworksObject
{
name = name,
displayValue = convertedBase["displayValue"] as List<Base> ?? [],
properties = convertedBase["properties"] as Dictionary<string, object?> ?? [],
units = units.ToString(),
applicationId = convertedBase.applicationId,
["path"] = path
};
}
private Task AddProxiesToCollection(
Collection rootCollection,
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
Dictionary<string, List<NAV.ModelItem>> groupedNodes
)
{
using var _ = activityFactory.Start("UnpackProxies");
var renderMaterials = materialUnpacker.UnpackRenderMaterial(navisworksModelItems, groupedNodes);
if (renderMaterials.Count > 0)
{
rootCollection[RENDER_MATERIAL] = renderMaterials;
}
var colors = colorUnpacker.UnpackColor(navisworksModelItems, groupedNodes);
if (colors.Count > 0)
{
rootCollection[COLOR] = colors;
}
return Task.CompletedTask;
}
private void AddInstanceDefinitionsToCollection(Collection rootCollection, ref List<Base> finalElements)
{
using var _ = activityFactory.Start("BuildInstanceDefinitions");
var allDefinitions = instanceRegistry.GetAllDefinitionGeometries();
if (allDefinitions.Count == 0)
{
logger.LogInformation("No instance definitions found - instancing may be disabled");
return;
}
logger.LogInformation("Building instance structure for {count} definition groups", allDefinitions.Count);
var instanceDefinitionProxies = new List<InstanceDefinitionProxy>(allDefinitions.Count);
int estimatedGeometryCount = allDefinitions.Sum(kvp => kvp.Value.Count);
var allDefinitionGeometries = new List<Base>(estimatedGeometryCount);
foreach (var kvp in allDefinitions)
{
var groupKey = kvp.Key;
var geometries = kvp.Value;
var groupKeyPath = groupKey.ToPathString();
var defProxy = new InstanceDefinitionProxy
{
name = $"Shared Geometry {groupKeyPath}",
objects = geometries.Select(g => g.applicationId ?? "").Where(id => !string.IsNullOrEmpty(id)).ToList(),
applicationId = $"{DEFINITION_ID_PREFIX}{groupKeyPath}",
maxDepth = 0
};
instanceDefinitionProxies.Add(defProxy);
allDefinitionGeometries.AddRange(geometries);
}
rootCollection[INSTANCE_DEFINITION] = instanceDefinitionProxies;
var geometryDefinitionsCollection = new Collection
{
name = "Geometry Definitions",
elements = allDefinitionGeometries
};
var objectCollection = new Collection { name = "", elements = finalElements };
finalElements = [geometryDefinitionsCollection, objectCollection];
logger.LogInformation(
"Added {proxyCount} instance definition proxies and {geomCount} definition geometries",
instanceDefinitionProxies.Count,
allDefinitionGeometries.Count
);
}
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases
)
{
string applicationId = elementSelectionService.GetModelItemPath(navisworksItem);
string sourceType = navisworksItem.GetType().Name;
try
{
Base converted = rootToSpeckleConverter.Convert(navisworksItem);
convertedBases[applicationId] = converted;
return new SendConversionResult(Status.SUCCESS, applicationId, sourceType, converted);
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogError(ex, "Failed to convert model item {id}", applicationId);
return new SendConversionResult(Status.ERROR, applicationId, "ModelItem", null, ex);
}
}
}
@@ -3,7 +3,6 @@ using Speckle.Connector.Navisworks.HostApp;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Converter.Navisworks.Helpers;
using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
@@ -14,6 +13,7 @@ using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
using static Speckle.Connectors.Common.Operations.ProxyKeys;
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
@@ -24,6 +24,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\SavedItemHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksContinuousTraversalBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
@@ -95,8 +95,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -193,24 +193,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -310,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -333,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2022": {
@@ -385,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -95,8 +95,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -193,24 +193,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -310,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -333,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2023": {
@@ -385,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -95,8 +95,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -193,24 +193,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -310,7 +312,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -333,7 +335,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2024": {
@@ -385,11 +387,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -173,8 +173,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -182,13 +182,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -253,7 +253,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -276,7 +276,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2025": {
@@ -328,11 +328,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -166,8 +166,8 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -175,13 +175,13 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -246,7 +246,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -269,7 +269,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.revit2026": {
@@ -312,11 +312,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"Speckle.Revit.API": {
@@ -2,6 +2,8 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared;
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -24,6 +26,8 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
private readonly ISpeckleApplication _speckleApplication;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IRevitTask _revitTask;
private readonly ParameterUpdater _parameterUpdater;
private readonly IJsonSerializer _jsonSerializer;
public BasicConnectorBindingRevit(
DocumentModelStore store,
@@ -31,7 +35,9 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
RevitContext revitContext,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler,
IRevitTask revitTask
IRevitTask revitTask,
ParameterUpdater parameterUpdater,
IJsonSerializer jsonSerializer
)
{
Name = "baseBinding";
@@ -41,6 +47,8 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
_speckleApplication = speckleApplication;
_topLevelExceptionHandler = topLevelExceptionHandler;
_revitTask = revitTask;
_parameterUpdater = parameterUpdater;
_jsonSerializer = jsonSerializer;
Commands = new BasicConnectorBindingCommands(parent);
_store.DocumentChanged += (_, _) =>
@@ -0,0 +1,251 @@
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Connectors.Revit.Operations.Receive;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Connectors.RevitShared;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.Revit.Bindings;
public static class ParameterScopes
{
public const string INSTANCE = "Instance Parameters";
public const string TYPE = "Type Parameters";
public const string SYSTEM_TYPE = "System Type Parameters";
}
public record ParsedParameterPath(string Scope, string Category, string Name)
{
public string[] ToArray() => [Scope, Category, Name];
}
internal sealed class RevitParametersBinding : IParametersBinding
{
public string Name => "parametersBinding";
public IBrowserBridge Parent { get; }
private readonly RevitContext _revitContext;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IRevitTask _revitTask;
private readonly ParameterUpdater _parameterUpdater;
private readonly IJsonSerializer _jsonSerializer;
private readonly IBasicConnectorBinding _baseBinding;
private readonly ILogger<RevitParametersBinding> _logger;
public RevitParametersBinding(
IBrowserBridge parent,
RevitContext revitContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IRevitTask revitTask,
ParameterUpdater parameterUpdater,
IJsonSerializer jsonSerializer,
IBasicConnectorBinding baseBinding,
ILogger<RevitParametersBinding> logger
)
{
Parent = parent;
_revitContext = revitContext;
_topLevelExceptionHandler = topLevelExceptionHandler;
_revitTask = revitTask;
_parameterUpdater = parameterUpdater;
_jsonSerializer = jsonSerializer;
_baseBinding = baseBinding;
_logger = logger;
}
public async Task Update(string payload)
{
try
{
var wrapper = _jsonSerializer.Deserialize<ParameterChangesWrapper>(payload);
var requests = wrapper?.Changes;
if (requests == null || requests.Count == 0)
{
return;
}
var activeUIDoc =
_revitContext.UIApplication?.ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");
var doc = activeUIDoc.Document;
int successCount = 0;
List<string> errors = [];
await _revitTask
.RunAsync(() =>
{
using var t = new Transaction(doc, "Speckle: Apply Parameter Changes");
// silence pop-ups like "duplicate mark values" etc. which blocks our param updates
var failureOptions = t.GetFailureHandlingOptions();
failureOptions.SetFailuresPreprocessor(new HideWarningsFailuresPreprocessor());
t.SetFailureHandlingOptions(failureOptions);
t.Start();
foreach (var request in requests)
{
if (!TryValidateAndParseRequest(doc, request, out var element, out var parsedPath, out var errorMessage))
{
errors.Add(errorMessage!);
continue;
}
object? rawValue = request.To;
if (rawValue is Newtonsoft.Json.Linq.JValue jValue)
{
rawValue = jValue.Value;
}
var result = _parameterUpdater.Update(
element!,
parsedPath!.ToArray(),
rawValue,
request.InternalDefinitionName
);
if (result.IsSuccess)
{
successCount++;
}
else
{
errors.Add(result.ErrorMessage ?? "Unknown error");
}
}
t.Commit();
})
.ConfigureAwait(false);
if (errors.Count > 0)
{
var groupedErrors = errors.GroupBy(e => e).Select(g => $"{g.Count()} x {g.Key}");
var errorString = string.Join(", ", groupedErrors);
if (successCount > 0)
{
// Partial Success (Some worked, some failed)
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.WARNING,
"Parameters updated with errors",
$"Applied {successCount} updates. Encountered {errors.Count} errors: {errorString}",
autoClose: false
);
}
else
{
// Total Failure (None worked)
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.DANGER,
"No parameters updated",
$"All {errors.Count} updates failed: {errorString}",
autoClose: false
);
}
}
else if (successCount > 0)
{
// Total Success
await _baseBinding.Commands.SetGlobalNotification(
ToastNotificationType.SUCCESS,
"All parameters updated",
$"Successfully applied {successCount} updates."
);
}
}
catch (Exception ex)
{
_topLevelExceptionHandler.CatchUnhandled(
() => throw new SpeckleException("Failed to apply parameter updates", ex)
);
}
}
private bool TryValidateAndParseRequest(
Document doc,
ParameterChangeRequest request,
out Element? element,
out ParsedParameterPath? parsedPath,
out string? errorMessage
)
{
element = null;
parsedPath = null;
errorMessage = null;
if (string.IsNullOrEmpty(request.ApplicationId))
{
errorMessage = "Missing ApplicationId";
return false;
}
if (ContainsLinkedModelTransformHash(request.ApplicationId))
{
errorMessage = "Cannot modify elements from a linked model";
return false;
}
var elementId = ElementIdHelper.GetElementIdFromUniqueId(doc, request.ApplicationId);
if (elementId == null)
{
errorMessage = "Element(s) not found in document";
return false;
}
element = doc.GetElement(elementId);
if (element == null)
{
errorMessage = "Element(s) not found in document";
return false;
}
var rawPath = request.Path;
if (string.IsNullOrEmpty(rawPath))
{
_logger.LogError("Widget / DUI payload error: parameter path missing");
errorMessage = "Parameter path is missing";
return false;
}
if (rawPath.StartsWith("properties.", StringComparison.InvariantCultureIgnoreCase))
{
rawPath = rawPath[11..];
}
if (rawPath.StartsWith("parameters.", StringComparison.InvariantCultureIgnoreCase))
{
rawPath = rawPath[11..];
}
var pathParts = rawPath.Split(['.'], 3);
if (pathParts.Length != 3)
{
_logger.LogError(
"Path format error: Expected exactly 3 parts (Scope.Category.Name) but received '{RawPath}' for element",
rawPath
);
errorMessage = "Parameter path is incorrectly formatted";
return false;
}
parsedPath = new ParsedParameterPath(pathParts[0], pathParts[1], pathParts[2]);
return true;
}
private static bool ContainsLinkedModelTransformHash(string applicationId) =>
// Evaluates if the ID contains the standard transform hash for linked elements
System.Text.RegularExpressions.Regex.IsMatch(applicationId, @"_t[a-f0-9]+$");
}
public class ParameterChangesWrapper
{
public List<ParameterChangeRequest>? Changes { get; set; }
}
@@ -39,6 +39,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private readonly LinkedModelHandler _linkedModelHandler;
private readonly IThreadContext _threadContext;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly ParameterUpdater _parameterUpdater;
private bool _isDocChangedSubscribed;
private EventHandler<Autodesk.Revit.DB.Events.DocumentChangedEventArgs>? _documentChangedHandler;
private readonly ConnectorConfig _config;
@@ -67,6 +68,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
IThreadContext threadContext,
IRevitTask revitTask,
ISendOperationManagerFactory sendOperationManagerFactory,
ParameterUpdater parameterUpdater,
IConfigStore configStore
)
: base("sendBinding", bridge)
@@ -84,6 +86,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_linkedModelHandler = linkedModelHandler;
_threadContext = threadContext;
_sendOperationManagerFactory = sendOperationManagerFactory;
_parameterUpdater = parameterUpdater;
_config = configStore.GetConnectorConfig();
Commands = new SendBindingUICommands(bridge);
@@ -198,6 +201,44 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
);
}
public async Task UpdateParameters(List<ParameterChangeRequest> changes)
{
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (document == null)
{
throw new SpeckleException("No document is active.");
}
await _threadContext.RunOnMainAsync(() =>
{
using var transaction = new Transaction(document, "Speckle Parameter Updates");
transaction.Start();
foreach (var change in changes)
{
var element = document.GetElement(change.ApplicationId);
if (element == null)
{
continue;
}
var path = ParsePath(change.Path);
var result = _parameterUpdater.Update(element, path, change.To);
}
transaction.Commit();
return Task.FromResult(true);
});
}
private string[] ParsePath(string concatenatedPath)
{
// "properties.Parameters.Type Parameters.Other.Family Name"
// → ["Type Parameters", "Other", "Family Name"]
var segments = concatenatedPath.Split('.');
return segments.Skip(2).ToArray();
}
private static (string? fileName, long? fileBytes) GetFileInfo(Document document)
{
string fullPath = document.PathName;
@@ -53,6 +53,9 @@ public static class ServiceRegistration
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IParametersBinding>());
serviceCollection.AddSingleton<IParametersBinding, RevitParametersBinding>();
// serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
// send operation and dependencies
@@ -62,10 +65,12 @@ public static class ServiceRegistration
serviceCollection.AddScoped<ViewUnpacker>();
serviceCollection.AddScoped<SendCollectionManager>();
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
serviceCollection.AddScoped<IRootContinuousTraversalBuilder<DocumentToConvert>, RevitContinuousTraversalBuilder>();
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
serviceCollection.AddSingleton<ToSpeckleSettingsManager>();
serviceCollection.AddSingleton<ToHostSettingsManager>();
serviceCollection.AddSingleton<LinkedModelHandler>();
serviceCollection.AddSingleton<ParameterUpdater>();
// receive operation and dependencies
serviceCollection.AddScoped<IHostObjectBuilder, RevitHostObjectBuilder>();
@@ -0,0 +1,9 @@
namespace Speckle.Connectors.Revit.HostApp;
public class ParameterChangeRequest
{
public required string ApplicationId { get; init; }
public required string Path { get; init; }
public object? To { get; init; }
public string? InternalDefinitionName { get; set; }
}
@@ -0,0 +1,375 @@
using Microsoft.Extensions.Logging;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Services;
using Speckle.Sdk;
using DB = Autodesk.Revit.DB;
namespace Speckle.Connectors.Revit.HostApp;
/// <summary>
/// Updates parameter values on Revit elements. Mirrors the structure from ParameterExtractor.
/// Path format: ["Instance Parameters" | "Type Parameters" | "System Type Parameters", "GroupName", "ParameterName"]
/// </summary>
public class ParameterUpdater
{
private readonly RevitContext _revitContext;
private readonly ScalingServiceToHost _scalingServiceToHost;
private readonly ILogger<ParameterUpdater> _logger;
public ParameterUpdater(
RevitContext revitContext,
ScalingServiceToHost scalingServiceToHost,
ILogger<ParameterUpdater> logger
)
{
_revitContext = revitContext;
_scalingServiceToHost = scalingServiceToHost;
_logger = logger;
}
public UpdateResult Update(DB.Element element, string[] path, object? newValue, string? internalDefinitionName = null)
{
// path = ["Instance Parameters", "Identity Data", "Mark"]
if (path.Length != 3)
{
return UpdateResult.Fail(
$"Path must have exactly 3 segments: [scope, group, parameter]. Got: {string.Join(" ", path)}"
);
}
var parameterScope = path[0]; // "Instance Parameters" | "Type Parameters" | "System Type Parameters"
var groupName = path[1]; // "Identity Data", "Dimensions", etc.
var parameterKey = path[2]; // human-readable name (or internalDefinitionName if collision)
// get target element based on scope
var targetElement = GetTargetElement(element, parameterScope);
if (targetElement == null)
{
return UpdateResult.Fail($"Could not resolve target for scope: {parameterScope}");
}
// find the parameter (now using the robust lookup)
var parameter = FindParameter(targetElement, groupName, parameterKey, internalDefinitionName);
if (parameter == null)
{
return UpdateResult.Fail($"Parameter not found: {parameterKey} in group {groupName}");
}
if (parameter.IsReadOnly)
{
return UpdateResult.Fail($"Parameter '{parameterKey}' is readonly in Revit");
}
return SetParameterValue(parameter, newValue);
}
private DB.Element? GetTargetElement(DB.Element element, string scope) =>
scope switch
{
"Instance Parameters" => element,
"Type Parameters" => GetTypeElement(element),
"System Type Parameters" => GetSystemTypeElement(element),
_ => null
};
private DB.Element? GetTypeElement(DB.Element element)
{
var typeId = element.GetTypeId();
if (typeId == DB.ElementId.InvalidElementId)
{
return null;
}
return _revitContext.UIApplication?.ActiveUIDocument.Document.GetElement(typeId);
}
private DB.Element? GetSystemTypeElement(DB.Element element)
{
var system = GetMEPSystem(element);
if (system == null)
{
return null;
}
return _revitContext.UIApplication?.ActiveUIDocument.Document.GetElement(system.GetTypeId());
}
private DB.MEPSystem? GetMEPSystem(DB.Element element)
{
if (element is DB.MEPCurve curve)
{
return curve.MEPSystem;
}
if (element is DB.FamilyInstance fi)
{
var cm = fi.MEPModel?.ConnectorManager;
if (cm != null)
{
foreach (DB.Connector conn in cm.Connectors)
{
if (conn.ConnectorType == DB.ConnectorType.Physical && conn.IsConnected && conn.MEPSystem != null)
{
return conn.MEPSystem;
}
}
}
}
return null;
}
private DB.Parameter? FindParameter(
DB.Element element,
string groupName,
string parameterKey,
string? internalDefinitionName
)
{
// fast path: direct lookup using the internal definition name
if (!string.IsNullOrEmpty(internalDefinitionName))
{
// try as BuiltInParameter enum
if (Enum.TryParse(internalDefinitionName, out DB.BuiltInParameter bip) && bip != DB.BuiltInParameter.INVALID)
{
var param = element.get_Parameter(bip);
if (param != null)
{
return param;
}
}
// try as shared parameter Guid
if (Guid.TryParse(internalDefinitionName, out Guid guid))
{
var param = element.get_Parameter(guid);
if (param != null)
{
return param;
}
}
}
// fallback: iteration for project parameters or missing internal names
DB.Parameter? fallbackParameter = null;
foreach (DB.Parameter parameter in element.Parameters)
{
var definition = parameter.Definition;
if (definition == null)
{
continue;
}
var currentInternalName = GetInternalDefinitionName(parameter);
var humanName = definition.Name;
// exact internal name match (covers project params that aren't BuiltIn/Shared)
if (!string.IsNullOrEmpty(internalDefinitionName) && currentInternalName == internalDefinitionName)
{
return parameter;
}
// fallback human-readable name matching
if (humanName == parameterKey || currentInternalName == parameterKey)
{
var paramGroup = definition.GetGroupTypeId();
var groupLabel = DB.LabelUtils.GetLabelForGroup(paramGroup);
if (groupLabel == groupName)
{
return parameter;
}
fallbackParameter ??= parameter;
}
}
return fallbackParameter;
}
private string GetInternalDefinitionName(DB.Parameter parameter)
{
if (parameter.Definition is DB.InternalDefinition internalDef)
{
var bip = internalDef.BuiltInParameter;
if (bip != DB.BuiltInParameter.INVALID)
{
return bip.ToString();
}
}
return parameter.Definition.Name;
}
private UpdateResult SetParameterValue(DB.Parameter parameter, object? newValue)
{
var paramName = parameter.Definition.Name;
if (newValue == null)
{
if (parameter.StorageType == DB.StorageType.String)
{
return parameter.Set(string.Empty)
? UpdateResult.Success()
: UpdateResult.Fail("Failed to clear string parameter");
}
return UpdateResult.Fail("Cannot set non-string parameter to null");
}
try
{
var success = parameter.StorageType switch
{
DB.StorageType.String => parameter.Set(newValue.ToString()),
DB.StorageType.Integer => SetIntegerValue(parameter, newValue),
DB.StorageType.Double => SetDoubleValue(parameter, newValue),
DB.StorageType.ElementId => SetElementIdValue(parameter, newValue),
_ => false
};
return success ? UpdateResult.Success() : UpdateResult.Fail($"Failed to set parameter value to: {newValue}");
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(ex, "Failed to set parameter value");
return UpdateResult.Fail($"Exception for '{paramName}': {ex.Message}");
}
}
private bool SetIntegerValue(DB.Parameter parameter, object newValue)
{
if (newValue is int i)
{
return parameter.Set(i);
}
if (newValue is bool b)
{
return parameter.Set(b ? 1 : 0);
}
if (int.TryParse(newValue.ToString(), out var parsed))
{
return parameter.Set(parsed);
}
var strValue = newValue.ToString();
if (strValue == "Yes")
{
return parameter.Set(1);
}
if (strValue == "No")
{
return parameter.Set(0);
}
return parameter.SetValueString(strValue);
}
private bool SetDoubleValue(DB.Parameter parameter, object newValue)
{
double doubleValue;
if (newValue is double d)
{
doubleValue = d;
}
else if (newValue is int intVal)
{
doubleValue = intVal;
}
else if (double.TryParse(newValue.ToString(), out var parsed))
{
doubleValue = parsed;
}
else
{
return false;
}
var internalValue = _scalingServiceToHost.ScaleToNative(doubleValue, parameter.GetUnitTypeId());
return parameter.Set(internalValue);
}
private bool SetElementIdValue(DB.Parameter parameter, object newValue)
{
if (newValue is DB.ElementId eid)
{
return parameter.Set(eid);
}
// TODO: check this fckr later
// if (newValue is long idInt)
// {
// #if REVIT2024_OR_GREATER
// return parameter.Set(new DB.ElementId(idInt));
// #else
// return parameter.Set(new DB.ElementId((long)idInt));
// #endif
// }
//
// if (long.TryParse(newValue.ToString(), out var parsedId))
// {
// #if REVIT2024_OR_GREATER
// return parameter.Set(new DB.ElementId(parsedId));
// #else
// return parameter.Set(new DB.ElementId((long)parsedId));
// #endif
// }
var elementName = newValue.ToString();
if (elementName != null)
{
var foundElement = FindElementByName(elementName);
if (foundElement != null)
{
return parameter.Set(foundElement.Id);
}
}
return false;
}
private DB.Element? FindElementByName(string name)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
using var materialCollector = new DB.FilteredElementCollector(doc);
var material = materialCollector.OfClass(typeof(DB.Material)).FirstOrDefault(e => e.Name == name);
if (material != null)
{
return material;
}
using var levelCollector = new DB.FilteredElementCollector(doc);
var level = levelCollector.OfClass(typeof(DB.Level)).FirstOrDefault(e => e.Name == name);
if (level != null)
{
return level;
}
using var phaseCollector = new DB.FilteredElementCollector(doc);
var phase = phaseCollector.OfClass(typeof(DB.Phase)).FirstOrDefault(e => e.Name == name);
if (phase != null)
{
return phase;
}
return null;
}
}
// TODO: we will see, extract this guy out
public readonly struct UpdateResult
{
public bool IsSuccess { get; }
public string? ErrorMessage { get; }
private UpdateResult(bool success, string? error)
{
IsSuccess = success;
ErrorMessage = error;
}
public static UpdateResult Success() => new(true, null);
public static UpdateResult Fail(string message) => new(false, message);
}
@@ -4,7 +4,6 @@ using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Common;
using Speckle.Converters.Common.Objects;
using Speckle.Converters.RevitShared.Helpers;
@@ -18,6 +17,7 @@ using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
using DB = Autodesk.Revit.DB;
using Document = Autodesk.Revit.DB.Document;
@@ -45,6 +45,8 @@ public static class SupportedCategoriesUtils
#else
category.Name == "OST_Grids";
#endif
case CategoryType.AnalyticalModel:
return true;
case CategoryType.Model:
return
@@ -24,6 +24,7 @@ using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Revit.Operations.Receive;
@@ -75,7 +75,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
if (_revitContext.UIApplication is UIApplication uiApplication)
{
// first get the main doc base points and reference setting transform
// first get the main doc base points
using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document);
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
@@ -95,26 +95,23 @@ public class ToHostSettingsManager : IToHostSettingsManager
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReceiveReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
if (surveyPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
ProjectPosition projectPosition =
uiApplication.ActiveUIDocument.Document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
double angleToTrueNorth = projectPosition.Angle;
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
using Transform rotation = Transform.CreateRotation(XYZ.BasisZ, angleToTrueNorth);
referencePointTransform = translation.Multiply(rotation);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
throw new InvalidOperationException("Couldn't retrieve Survey Point from document");
}
break;
case ReceiveReferencePointType.Source:
break;
case ReceiveReferencePointType.InternalOrigin:
break;
}
@@ -0,0 +1,318 @@
using System.Diagnostics.CodeAnalysis;
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Settings;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
namespace Speckle.Connectors.Revit.Operations.Send;
public class RevitContinuousTraversalBuilder(
IRootToSpeckleConverter converter,
IConverterSettingsStore<RevitConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
LevelUnpacker levelUnpacker,
ViewUnpacker viewUnpacker,
IThreadContext threadContext,
SendCollectionManager sendCollectionManager,
ILogger<RevitRootObjectBuilder> logger,
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
LinkedModelHandler linkedModelHandler,
IConfigStore configStore
) : IRootContinuousTraversalBuilder<DocumentToConvert>
{
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
return await threadContext.RunOnMainAsync(
async () =>
await BuildMainThread(
documentElementContexts,
projectId,
sendPipeline,
onOperationProgressed,
cancellationToken
)
);
}
[SuppressMessage("Maintainability", "CA1502:Avoid excessive class coupling")]
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")]
private async Task<RootObjectBuilderResult> BuildMainThread(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
var doc = converterSettings.Current.Document;
if (doc.IsFamilyDocument)
{
throw new SpeckleException("Family Environment documents are not supported.");
}
// init the root
Collection rootObject =
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
rootObject["units"] = converterSettings.Current.SpeckleUnits;
var filteredDocumentsToConvert = new List<DocumentToConvert>();
bool sendWithLinkedModels = converterSettings.Current.SendLinkedModels;
List<SendConversionResult> results = new();
// Prepare linked model display names if needed
if (sendWithLinkedModels)
{
linkedModelHandler.PrepareLinkedModelNames(documentElementContexts);
}
foreach (var documentElementContext in documentElementContexts)
{
// add appropriate warnings for linked documents
if (documentElementContext.Doc.IsLinked && !sendWithLinkedModels)
{
results.Add(
new(
Status.WARNING,
documentElementContext.Doc.PathName,
typeof(RevitLinkInstance).ToString(),
null,
new SpeckleException("Enable linked model support from the settings to send this object")
)
);
continue;
}
// filter for valid elements
// if send linked models setting is disabled List<Elements> will be empty, and we won't enter foreach loop
var elementsInTransform = new List<Element>();
foreach (var el in documentElementContext.Elements)
{
if (el == null || el.Category == null)
{
continue;
}
elementsInTransform.Add(el);
}
// only add contexts with elements
if (elementsInTransform.Count > 0)
{
filteredDocumentsToConvert.Add(documentElementContext with { Elements = elementsInTransform });
}
}
// TODO: check the exception!!!!
if (filteredDocumentsToConvert.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found. Please update your publish filter!");
}
// Unpack groups (& other complex data structures)
var atomicObjectsByDocumentAndTransform = new List<DocumentToConvert>();
var atomicObjectCount = 0;
foreach (var filteredDocumentToConvert in filteredDocumentsToConvert)
{
using (
converterSettings.Push(currentSettings => currentSettings with { Document = filteredDocumentToConvert.Doc })
)
{
var atomicObjects = elementUnpacker
.UnpackSelectionForConversion(filteredDocumentToConvert.Elements, filteredDocumentToConvert.Doc)
.ToList();
atomicObjectsByDocumentAndTransform.Add(filteredDocumentToConvert with { Elements = atomicObjects });
atomicObjectCount += atomicObjects.Count;
}
}
var count = 0;
var cacheHitCount = 0;
var skippedObjectCount = 0;
var config = configStore.GetConnectorConfig();
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
{
string? modelDisplayName = null;
if (atomicObjectByDocumentAndTransform.Doc.IsLinked)
{
string id = linkedModelHandler.GetIdFromDocumentToConvert(atomicObjectByDocumentAndTransform);
linkedModelHandler.LinkedModelDisplayNames.TryGetValue(id, out modelDisplayName);
}
// here we do magic for changing the transform and the related document according to model. first one is always the main model.
using (
converterSettings.Push(currentSettings =>
currentSettings with
{
ReferencePointTransform = atomicObjectByDocumentAndTransform.Transform,
Document = atomicObjectByDocumentAndTransform.Doc,
}
)
)
{
var atomicObjects = atomicObjectByDocumentAndTransform.Elements;
foreach (Element revitElement in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
string applicationId = revitElement.UniqueId;
string sourceType = revitElement.GetType().Name;
try
{
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
{
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
results.Add(
new(
Status.WARNING,
revitElement.UniqueId,
cat,
null,
new SpeckleException($"Category {cat} is not supported.")
)
);
skippedObjectCount++;
continue;
}
Base converted;
bool hasTransform = atomicObjectByDocumentAndTransform.Transform != null;
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
if (
!hasTransform
&& !config.DocumentChangeListeningDisabled //This is experimental
&& sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value)
)
{
converted = value;
cacheHitCount++;
}
// not in cache means we convert
else
{
// if it has a transform we append transform hash to the applicationId to distinguish the elements from other instances
if (hasTransform)
{
string transformHash = linkedModelHandler.GetTransformHash(
atomicObjectByDocumentAndTransform.Transform.NotNull()
);
applicationId = $"{applicationId}_t{transformHash}";
}
// normal conversions
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
}
// NOTE: this is the main part that differentiate from the main root object builder
var reference = await sendPipeline.Process(converted).ConfigureAwait(true);
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
revitElement,
rootObject,
sendWithLinkedModels,
modelDisplayName
);
collection.elements.Add(reference);
results.Add(new(Status.SUCCESS, applicationId, sourceType, reference));
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogSendConversionError(ex, sourceType);
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
}
count++;
onOperationProgressed.Report(
new($"Converting objects... ({count:N0} / {atomicObjectCount:N0})", (double)count / 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.");
}
// STEP 5: Unpack proxies to attach to root collection
var flatElements = atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList();
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(flatElements);
var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds);
rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
var levelProxies = levelUnpacker.Unpack(flatElements);
rootObject[ProxyKeys.LEVEL] = levelProxies;
rootObject[ProxyKeys.INSTANCE_DEFINITION] = revitToSpeckleCacheSingleton.GetInstanceDefinitionProxiesForObjects(
idsAndSubElementIds
);
rootObject.elements.Add(
new Collection()
{
elements = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds),
name = "definitionGeometry"
}
);
// STEP 6: Unpack all other objects to attach to root collection
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
if (views.Count > 0)
{
rootObject[RootKeys.VIEW] = views;
}
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
// we want to store transform data for chosen reference point setting
if (converterSettings.Current.ReferencePointTransform is Transform transform)
{
var transformMatrix = ReferencePointHelper.CreateTransformDataForRootObject(transform);
rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] = transformMatrix;
}
await sendPipeline.Process(rootObject);
await sendPipeline.WaitForUpload();
return new RootObjectBuilderResult(rootObject, results);
}
}
@@ -17,6 +17,7 @@ using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.Revit.Operations.Send;
@@ -39,7 +40,7 @@ public class RevitRootObjectBuilder(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
CancellationToken ct
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
@@ -213,21 +213,18 @@ public class ToSpeckleSettingsManager(
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
if (surveyPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
ProjectPosition projectPosition = document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
double angleToTrueNorth = projectPosition.Angle;
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
using Transform rotation = Transform.CreateRotation(XYZ.BasisZ, angleToTrueNorth);
referencePointTransform = translation.Multiply(rotation);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
throw new InvalidOperationException("Couldn't retrieve Survey Point from document");
}
break;
@@ -15,6 +15,7 @@
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\BasicConnectorBindingRevit.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitBaseBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitParametersBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\SelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitSendBinding.cs" />
@@ -27,6 +28,8 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\FamilyTransformUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ParameterChangeRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ParameterUpdater.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitFamilyBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitViewBaker.cs" />
@@ -57,6 +60,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitCategoriesFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitViewsFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\LinkedModelsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
@@ -101,8 +101,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -204,24 +204,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -347,7 +349,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.logging": {
@@ -357,7 +359,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino7": {
@@ -402,11 +404,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -101,8 +101,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -204,24 +204,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -347,7 +349,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.logging": {
@@ -357,7 +359,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino8": {
@@ -401,11 +403,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
}
}
@@ -4,7 +4,6 @@ using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.GrasshopperShared.Components.Collections;
@@ -92,44 +91,8 @@ public class CreateCollection : VariableParameterComponentBase
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
}
/// <summary>
/// Recursively checks if collection or any descendants contain valid geometry/data objects
/// </summary>
private bool HasAnyValidContent(ISpeckleCollectionObject? element) =>
element switch
{
SpeckleGeometryWrapper => true,
SpeckleDataObjectWrapper => true,
SpeckleCollectionWrapper collection => collection.Elements.Any(HasAnyValidContent),
_ => false
};
private SpeckleCollectionWrapper CreateRootCollection() =>
new()
{
Base = new Collection(),
Name = "Unnamed",
Path = new List<string> { "Unnamed" },
Color = null,
Material = null,
ApplicationId = InstanceGuid.ToString()
};
private SpeckleCollectionWrapper? ProcessInputParameter(IGH_Param inputParam, List<IGH_Goo> data, string rootName)
{
var collections = data.OfType<SpeckleCollectionWrapperGoo>().Empty().ToList();
var nonCollections = data.Where(t => t is not SpeckleCollectionWrapperGoo).Empty().ToList();
// Validate input - cannot mix collections and objects
if (collections.Count > 0 && nonCollections.Count > 0)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter {inputParam.NickName} cannot contain both objects and collections."
);
return null;
}
var childPath = new List<string> { rootName, inputParam.NickName };
var childCollection = new SpeckleCollectionWrapper
{
@@ -142,83 +105,57 @@ public class CreateCollection : VariableParameterComponentBase
ApplicationId = inputParam.InstanceGuid.ToString()
};
if (collections.Count > 0)
{
ProcessCollectionInputs(collections, childCollection, childPath);
}
else
{
ProcessObjectInputs(nonCollections, childCollection, childPath);
}
return childCollection;
}
private void ProcessCollectionInputs(
List<SpeckleCollectionWrapperGoo> collections,
SpeckleCollectionWrapper parentCollection,
List<string> childPath
)
{
var duplicateNames = new HashSet<string>();
foreach (var collectionGoo in collections.Select(c => (SpeckleCollectionWrapperGoo)c.Duplicate()))
{
collectionGoo.Value.Path = childPath;
// Check for duplicate names within this collection
foreach (
var subCollectionName in collectionGoo
.Value.Elements.Where(e => e != null) // skip nulls (CNX-2855)
.OfType<SpeckleCollectionWrapper>()
.Select(c => c.Name)
)
{
if (!duplicateNames.Add(subCollectionName))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Duplicate collection name '{subCollectionName}' found. Collection names must be unique per level."
);
return;
}
}
parentCollection.Elements.AddRange(collectionGoo.Value.Elements);
}
}
private void ProcessObjectInputs(
List<IGH_Goo> objects,
SpeckleCollectionWrapper parentCollection,
List<string> childPath
)
{
int skippedCount = 0;
foreach (var obj in objects)
foreach (var obj in data)
{
if (obj is SpeckleCollectionWrapperGoo collectionGoo)
{
var colClone = (SpeckleCollectionWrapperGoo)collectionGoo.Duplicate();
colClone.Value.Path = childPath;
// Check for duplicate names within this collection
foreach (
var subCollectionName in colClone
.Value.Elements.Where(e => e != null) // skip nulls (CNX-2855)
.OfType<SpeckleCollectionWrapper>()
.Select(c => c.Name)
)
{
if (!duplicateNames.Add(subCollectionName))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Duplicate collection name '{subCollectionName}' found. Collection names must be unique per level."
);
return null;
}
}
childCollection.Elements.AddRange(colClone.Value.Elements);
}
// handle data objects directly (deep copy to avoid mutations)
// NOTE: DataObject first, since a DataObject with one geo is castable to speckle geometry
if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
else if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
{
var dataObjectWrapper = dataObjectWrapperGoo.Value.DeepCopy();
dataObjectWrapper.Path = childPath;
dataObjectWrapper.Parent = parentCollection;
parentCollection.Elements.Add(dataObjectWrapper);
dataObjectWrapper.Parent = childCollection;
childCollection.Elements.Add(dataObjectWrapper);
}
// handle geometry objects (deep copy to avoid mutations)
else if (obj?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper objWrapper)
{
SpeckleGeometryWrapper wrapper = objWrapper.DeepCopy();
wrapper.Path = childPath;
wrapper.Parent = parentCollection;
parentCollection.Elements.Add(wrapper);
wrapper.Parent = childCollection;
childCollection.Elements.Add(wrapper);
}
else
{
// add null placeholder to preserve topology (CNX-2855)
parentCollection.Elements.Add(null);
childCollection.Elements.Add(null);
skippedCount++;
}
}
@@ -231,6 +168,8 @@ public class CreateCollection : VariableParameterComponentBase
$"Skipped {skippedCount} unsupported object(s) (Leaders, TextDots, Dimensions, etc.)"
);
}
return childCollection;
}
// IGH_VariableParameterComponent implementation
@@ -26,8 +26,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_collections_expand;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Collection",
@@ -35,7 +34,6 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
"The Collection you want to expand",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
@@ -180,27 +178,68 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// TODO: better, nicer handling of creation/removal
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
bool needsMaintenance = false;
foreach (var newParam in outputParams)
// remove old parameters that are no longer present
for (int i = Params.Output.Count - 1; i >= 0; i--)
{
var param = new SpeckleOutputParam
var existingParam = Params.Output[i];
if (outputParams.All(p => p.Param.Name != existingParam.Name))
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
Params.UnregisterOutputParameter(existingParam);
needsMaintenance = true;
}
}
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
// add new parameters and update existing ones in place
for (int i = 0; i < outputParams.Count; i++)
{
var targetParam = outputParams[i].Param;
var existingParam = Params.Output.FirstOrDefault(p => p.Name == targetParam.Name);
if (existingParam != null)
{
if (existingParam.Access != targetParam.Access)
{
existingParam.Access = targetParam.Access;
needsMaintenance = true;
}
if (existingParam.NickName != targetParam.NickName)
{
existingParam.NickName = targetParam.NickName;
needsMaintenance = true;
}
int currentIndex = Params.Output.IndexOf(existingParam);
if (currentIndex != i)
{
Params.Output.RemoveAt(currentIndex);
Params.Output.Insert(i, existingParam);
needsMaintenance = true;
}
}
else
{
var newParam = new SpeckleOutputParam
{
Name = targetParam.Name,
NickName = targetParam.NickName,
MutableNickName = false,
Access = targetParam.Access,
Description = targetParam.Description
};
Params.RegisterOutputParam(newParam, i);
needsMaintenance = true;
}
}
if (needsMaintenance)
{
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
}
public void VariableParameterMaintenance() { }
@@ -45,13 +45,13 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
protected override void SolveInstance(IGH_DataAccess da)
{
var properties = new Dictionary<string, ISpecklePropertyGoo>();
var groupGoo = new SpecklePropertyGroupGoo();
// Validate for duplicate names
var paramNames = Params.Input.Select(p => p.NickName).ToList();
var duplicates = paramNames.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key);
var duplicates = paramNames.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (duplicates.Any())
if (duplicates.Count != 0)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
@@ -77,11 +77,10 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
if (propertyValue != null)
{
properties[paramName] = propertyValue;
groupGoo.SetValueByPath(paramName, propertyValue);
}
}
var groupGoo = new SpecklePropertyGroupGoo(properties);
da.SetData(0, groupGoo);
}
@@ -40,57 +40,91 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
protected override void SolveInstance(IGH_DataAccess da)
{
// ALWAYS run port generation on the first iteration, BEFORE validating the current item
// ensure that a null at index 0 doesn't prevent ports from being created.
if (da.Iteration == 0)
{
// gather all property groups from the input (skipNulls = true)
var allData = Params.Input[0].VolatileData.AllData(true).OfType<SpecklePropertyGroupGoo>().ToList();
// guard against empty data on file load / async operations to prevent stale ports from dropping (CNX-3245)
if (allData.Count > 0)
{
var outputParamsDict = new Dictionary<string, OutputParamWrapper>();
foreach (var propGroup in allData)
{
if (propGroup?.Value == null)
{
continue;
}
foreach (var key in propGroup.Value.Keys)
{
ISpecklePropertyGoo value = propGroup.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo pg => pg,
_ => value
};
if (!outputParamsDict.TryGetValue(key, out var existingWrapper))
{
var param = new SpeckleOutputParam
{
Name = key,
NickName = key,
Access = outputValue is IList ? GH_ParamAccess.list : GH_ParamAccess.item
};
outputParamsDict[key] = new OutputParamWrapper(param, outputValue);
}
else if (existingWrapper.Param.Access == GH_ParamAccess.item && outputValue is IList)
{
existingWrapper.Param.Access = GH_ParamAccess.list;
}
}
}
var outputParams = outputParamsDict.Values.ToList();
Name = $"Properties ({outputParams.Count})";
NickName = Name;
if (OutputMismatch(outputParams))
{
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(outputParams));
return;
}
}
}
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)
for (int i = 0; i < Params.Output.Count; i++)
{
ISpecklePropertyGoo value = properties.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
var outParam = Params.Output[i];
var param = new SpeckleOutputParam
if (properties.Value.TryGetValue(outParam.Name, out ISpecklePropertyGoo? value))
{
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)
object? outputValue = value switch
{
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;
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
if (outParam.Access == GH_ParamAccess.item)
{
da.SetData(i, outputValue);
}
else
{
da.SetDataList(i, outputValue as IList ?? new List<object?>());
}
}
}
@@ -101,29 +135,68 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
/// </summary>
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// remove all existing output parameters
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
bool needsMaintenance = false;
// add new output parameters
foreach (var newParam in outputParams)
// remove old parameters that are no longer present
for (int i = Params.Output.Count - 1; i >= 0; i--)
{
var param = new SpeckleOutputParam
var existingParam = Params.Output[i];
if (outputParams.All(p => p.Param.Name != existingParam.Name))
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
Params.UnregisterOutputParameter(existingParam);
needsMaintenance = true;
}
}
// notify gh of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
// add new parameters and update existing ones in place to preserve wires
for (int i = 0; i < outputParams.Count; i++)
{
var targetParam = outputParams[i].Param;
var existingParam = Params.Output.FirstOrDefault(p => p.Name == targetParam.Name);
if (existingParam != null)
{
if (existingParam.Access != targetParam.Access)
{
existingParam.Access = targetParam.Access;
needsMaintenance = true;
}
if (existingParam.NickName != targetParam.NickName)
{
existingParam.NickName = targetParam.NickName;
needsMaintenance = true;
}
int currentIndex = Params.Output.IndexOf(existingParam);
if (currentIndex != i)
{
Params.Output.RemoveAt(currentIndex);
Params.Output.Insert(i, existingParam);
needsMaintenance = true;
}
}
else
{
var newParam = new SpeckleOutputParam
{
Name = targetParam.Name,
NickName = targetParam.NickName,
MutableNickName = false,
Access = targetParam.Access
};
Params.RegisterOutputParam(newParam, i);
needsMaintenance = true;
}
}
if (needsMaintenance)
{
// notify gh of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
}
/// <summary>
@@ -37,19 +37,27 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
pManager.AddTextParameter(
"Property Key",
"P",
"K",
"Find objects with a property that has a matching key",
GH_ParamAccess.item
);
Params.Input[2].Optional = true;
pManager.AddTextParameter(
"Property Value",
"V",
"Find objects with a property that has a matching value",
GH_ParamAccess.item
);
Params.Input[3].Optional = true;
pManager.AddTextParameter(
"Material Name",
"M",
"Find objects with a render material that has a matching name",
GH_ParamAccess.item
);
Params.Input[3].Optional = true;
Params.Input[4].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
@@ -89,10 +97,12 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
string name = "";
dataAccess.GetData(1, ref name);
string property = "";
dataAccess.GetData(2, ref property);
string propertyKey = "";
dataAccess.GetData(2, ref propertyKey);
string propertyValue = "";
dataAccess.GetData(3, ref propertyValue);
string material = "";
dataAccess.GetData(3, ref material);
dataAccess.GetData(4, ref material);
// optional parameters - only read if they've been added via ⊕
string appId = "";
@@ -118,7 +128,19 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
foreach (SpeckleWrapper wrapper in objects.Cast<SpeckleWrapper>())
{
if (MatchesAllFilters(wrapper, name, property, material, appId, filterByAppId, speckleId, filterBySpeckleId))
if (
MatchesAllFilters(
wrapper,
name,
propertyKey,
propertyValue,
material,
appId,
filterByAppId,
speckleId,
filterBySpeckleId
)
)
{
matchedObjects.Add(wrapper);
}
@@ -149,7 +171,8 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
private bool MatchesAllFilters(
SpeckleWrapper wrapper,
string name,
string property,
string propertyKey,
string propertyValue,
string material,
string appId,
bool filterByAppId,
@@ -164,7 +187,7 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
}
// filter by property
if (!MatchesPropertyFilter(wrapper, property))
if (!MatchesPropertyFilter(wrapper, propertyKey, propertyValue))
{
return false;
}
@@ -190,9 +213,12 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
return true;
}
private bool MatchesPropertyFilter(SpeckleWrapper wrapper, string property)
private bool MatchesPropertyFilter(SpeckleWrapper wrapper, string propertyKey, string propertyValue)
{
if (string.IsNullOrEmpty(property))
bool hasKeyFilter = !string.IsNullOrEmpty(propertyKey);
bool hasValueFilter = !string.IsNullOrEmpty(propertyValue);
if (!hasKeyFilter && !hasValueFilter)
{
return true;
}
@@ -208,8 +234,30 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
return false;
}
// use flattened properties to search ALL nested property keys
return properties.Flatten().Keys.Any(key => MatchesSearchPattern(property, key));
var flattenedProps = properties.Flatten();
// Check both property key and value simultaneously
if (hasKeyFilter && hasValueFilter)
{
return flattenedProps.Any(kvp =>
MatchesSearchPattern(propertyKey, kvp.Key)
&& MatchesSearchPattern(propertyValue, kvp.Value.Value?.ToString() ?? "")
);
}
// Check just property key
if (hasKeyFilter)
{
return flattenedProps.Keys.Any(key => MatchesSearchPattern(propertyKey, key));
}
// Check just property value
if (hasValueFilter)
{
return flattenedProps.Values.Any(val => MatchesSearchPattern(propertyValue, val.Value?.ToString() ?? ""));
}
return false;
}
private bool MatchesMaterialFilter(SpeckleWrapper wrapper, string material)
@@ -258,23 +306,23 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
return false;
}
// only allow inserting after the fixed parameters (index 4+)
if (index < 4)
// only allow inserting after the fixed parameters (index 5+)
if (index < 5)
{
return false;
}
// check how many optional params are already added (total inputs - 4 fixed)
int addedOptionalCount = Params.Input.Count - 4;
// check how many optional params are already added (total inputs - 5 fixed)
int addedOptionalCount = Params.Input.Count - 5;
// we have 2 optional parameters available
return addedOptionalCount < 2;
}
public bool CanRemoveParameter(GH_ParameterSide side, int index) =>
// only allow removing optional input parameters (index 4+)
// only allow removing optional input parameters (index 5+)
side == GH_ParameterSide.Input
&& index >= 4;
&& index >= 5;
/// <remarks>
/// The ternary operator for NickName is needed due to a Grasshopper quirk where
@@ -316,12 +364,12 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
return new Param_String();
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Input && index >= 4;
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Input && index >= 5;
public void VariableParameterMaintenance()
{
// ensure all optional parameters stay marked as optional
for (int i = 4; i < Params.Input.Count; i++)
for (int i = 5; i < Params.Input.Count; i++)
{
Params.Input[i].Optional = true;
}
@@ -24,14 +24,14 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
protected override void LoadVolatileData()
{
List<SpecklePropertyGroupGoo> propertyGroups = VolatileData
.AllData(true)
.OfType<SpecklePropertyGroupGoo>()
.ToList();
var allData = VolatileData.AllData(true).ToList();
if (VolatileDataCount > propertyGroups.Count)
List<SpecklePropertyGroupGoo> propertyGroups = allData.OfType<SpecklePropertyGroupGoo>().ToList();
// compare against allData.Count to safely ignore nulls (CNX-3176)
if (allData.Count > propertyGroups.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Properties are accepted as inputs.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only Speckle Properties are accepted as inputs.");
return;
}
@@ -35,10 +35,8 @@ public class QueryProperties : GH_Component
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
pManager.AddGenericParameter("Values", "V", "The values of the specified keys", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
@@ -64,7 +62,7 @@ public class QueryProperties : GH_Component
List<object?> values = [];
foreach (string key in keys)
{
var value = GetValueByPath(properties, key);
var value = properties.GetValueByPath(key);
var extractedValue = (value as SpecklePropertyGoo)?.Value ?? value;
// NOTE: if property is a list, flatten into individual items for native gh list access
@@ -80,31 +78,4 @@ public class QueryProperties : GH_Component
da.SetDataList(0, values);
}
public static ISpecklePropertyGoo? GetValueByPath(SpecklePropertyGroupGoo data, string path)
{
string[] keys = path.Split('.');
ISpecklePropertyGoo? current = data;
foreach (var key in keys)
{
if (current is SpecklePropertyGroupGoo dict)
{
if (dict.Value.TryGetValue(key, out ISpecklePropertyGoo? next))
{
current = next;
}
else
{
return null;
}
}
else
{
return null; // Current is not a dictionary, path is invalid
}
}
return current;
}
}
@@ -30,17 +30,30 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
private enum PropertyMode
{
Merge, // this should be default mode
Merge,
Replace,
Remove
Remove,
Update, // pre rewording (cnx-3177), keeping for scripts with settings saved
Overwrite // pre rewording (cnx-3177), keeping for scripts with settings saved
}
private PropertyMode _mode = PropertyMode.Merge;
private PropertyMode _mode = PropertyMode.Update;
private PropertyMode Mode
{
get => _mode;
set
{
// auto-migrate legacy modes to new modes
if (value == PropertyMode.Update)
{
value = PropertyMode.Merge;
}
if (value == PropertyMode.Overwrite)
{
value = PropertyMode.Replace;
}
if (_mode != value)
{
_mode = value;
@@ -96,7 +109,7 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
}
// validate that keys and values are of valid length
if ((Mode == PropertyMode.Merge || Mode == PropertyMode.Replace) && inputKeys.Count != inputValues.Count)
if ((Mode == PropertyMode.Update || Mode == PropertyMode.Overwrite) && inputKeys.Count != inputValues.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Keys and values are mismatched in length");
return;
@@ -108,18 +121,16 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
return;
}
// process the properties
Dictionary<string, ISpecklePropertyGoo> result =
inputProperties is null || Mode == PropertyMode.Replace
? new()
: inputProperties.Value.ToDictionary(entry => entry.Key, entry => entry.Value);
// deep clone to prevent mutating upstream grasshopper data
SpecklePropertyGroupGoo resultGoo =
inputProperties is null || Mode == PropertyMode.Replace ? new SpecklePropertyGroupGoo() : inputProperties.Clone();
// process keys and values
if (hasKeys)
{
// check for duplicate keys
var duplicates = inputKeys.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key);
if (duplicates.Any())
var duplicates = inputKeys.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (duplicates.Count != 0)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
@@ -128,12 +139,12 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
return;
}
// set keyvalue pairs
// set key-value pairs
for (int i = 0; i < inputKeys.Count; i++)
{
string key = inputKeys[i];
object? value = Mode == PropertyMode.Remove ? null : inputValues[i];
ISpecklePropertyGoo? convertedValue = null;
ISpecklePropertyGoo? convertedValue;
switch (value)
{
case SpecklePropertyGroupGoo propGoo:
@@ -148,7 +159,7 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
);
return;
}
@@ -160,29 +171,19 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
switch (Mode)
{
case PropertyMode.Merge:
if (result.ContainsKey(key))
{
result[key] = convertedValue;
}
else
{
result.Add(key, convertedValue);
}
break;
case PropertyMode.Replace:
result.Add(key, convertedValue);
resultGoo.SetValueByPath(key, convertedValue);
break;
case PropertyMode.Remove:
result.Remove(key);
resultGoo.RemoveValueByPath(key);
break;
}
}
}
var groupGoo = new SpecklePropertyGroupGoo(result);
da.SetData(0, groupGoo);
da.SetDataList(1, result.Keys);
da.SetDataList(2, result.Values);
da.SetData(0, resultGoo);
da.SetDataList(1, resultGoo.Value.Keys);
da.SetDataList(2, resultGoo.Value.Values);
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
@@ -192,18 +193,24 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
Menu_AppendSeparator(menu); // modes section
foreach (PropertyMode mode in Enum.GetValues(typeof(PropertyMode)))
{
// hide "legacy modes" (before cnx-3177 rewording) from the dropdown
if (mode is PropertyMode.Update or PropertyMode.Overwrite)
{
continue;
}
var modeItem = Menu_AppendItem(menu, mode.ToString(), (_, _) => Mode = mode, true, mode == Mode);
switch (mode)
{
case PropertyMode.Merge:
modeItem.ToolTipText =
"Input keyvalue pairs will be merged with existing properties. Any existing keys will be updated with new values.";
@"Input key-value pairs will be merged with existing properties. Any existing keys will be updated with new values.";
break;
case PropertyMode.Replace:
modeItem.ToolTipText = "Existing properties will be cleared and replaced by input keyvalue pairs.";
modeItem.ToolTipText = @"Existing properties will be cleared and replaced by input key-value pairs.";
break;
case PropertyMode.Remove:
modeItem.ToolTipText = "Existing keyvalue pairs that match the input keys will be removed from properties.";
modeItem.ToolTipText = @"Existing key-value pairs that match the input keys will be removed from properties.";
break;
}
}
@@ -8,7 +8,6 @@ using GrasshopperAsyncComponent;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
@@ -22,6 +21,7 @@ using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
@@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -16,6 +15,7 @@ using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
@@ -0,0 +1,56 @@
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Enums;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
/// <summary>
/// Polls ingestion status via the SDK's GraphQL query API
/// and blocks until the ingestion reaches a terminal state (success/failed/cancelled).
/// </summary>
/// <remarks>
/// We use polling instead of subscriptions because GH components call WaitForIngestionCompletion
/// after SendViaPackfile returns — by that point the server may have already completed
/// the ingestion. Setting up a new WebSocket subscription is too slow to catch fast completions.
/// Polling with Ingestion.Get() is reliable regardless of timing.
/// </remarks>
public class IngestionTracker
{
private static readonly TimeSpan s_pollInterval = TimeSpan.FromSeconds(1);
public async Task<string> WaitForIngestionCompletion(
IClient client,
string projectId,
string ingestionId,
Action<string, double>? reportProgress,
string? reportProgressId,
CancellationToken cancellationToken
)
{
// NOTE: before start hating from this - read the class description
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
var ingestion = await client.Ingestion.Get(ingestionId, projectId, cancellationToken).ConfigureAwait(false);
var status = ingestion.statusData.status;
switch (status)
{
case ModelIngestionStatus.success:
return ingestion.statusData.versionId.NotNull();
case ModelIngestionStatus.failed:
throw new SpeckleException($"Server processing failed: {ingestion.statusData.progressMessage}");
case ModelIngestionStatus.cancelled:
throw new OperationCanceledException("Ingestion was cancelled by the server");
case ModelIngestionStatus.processing:
case ModelIngestionStatus.queued:
reportProgress?.Invoke(reportProgressId ?? "Server", 0);
break;
}
await Task.Delay(s_pollInterval, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -5,6 +5,7 @@ using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using Grasshopper.Kernel.Types;
using GrasshopperAsyncComponent;
using Microsoft.Extensions.DependencyInjection;
using Rhino;
@@ -19,6 +20,7 @@ using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Extensions;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
@@ -63,14 +65,15 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
// speckle model
pManager.AddParameter(new SpeckleUrlModelResourceParam());
// collection
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
// collection / data
pManager.AddGenericParameter(
"Collection",
"collection",
"The collection model object to send",
GH_ParamAccess.item
"The collections, data objects, or geometries to publish",
GH_ParamAccess.list
);
// version message
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
@@ -149,27 +152,15 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
protected override void SolveInstance(IGH_DataAccess da)
{
var multipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
var multipleCollections = Params.Input[1].VolatileData.HasInputCountGreaterThan(1);
HasMultipleInputs = multipleCollections || multipleResources;
HasMultipleInputs = multipleResources;
if (HasMultipleInputs)
{
var mCollErrText =
"Only one single collection supported. Please group your input collections into one single one before sending.";
var mLinksErrText =
"Only one single model can be published to from this node. To send to multiple models, please use different publish components.";
if (multipleCollections)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mCollErrText);
}
if (multipleResources)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mLinksErrText);
}
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
"Only one single model can be published to from this node. To send to multiple models, please use different publish components."
);
return;
}
@@ -194,7 +185,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
{
// Set output data in a "first run" event. Note: we are not persisting the actual "sent" object as it can be very big.
base.SolveInstance(da);
return;
}
else
{
@@ -283,15 +273,84 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
}
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
da.GetData(1, ref rootCollectionWrapper);
if (rootCollectionWrapper is null)
List<IGH_Goo> inputGoos = new();
da.GetDataList(1, inputGoos);
if (inputGoos.Count == 0)
{
RootCollectionWrapper = null;
TriggerAutoSave();
return;
}
RootCollectionWrapper = rootCollectionWrapper;
SpeckleCollectionWrapper? rootBase;
// filter out nulls just to check if we can use the fast path
var nonNullGoos = inputGoos.Where(x => x != null).ToList();
// fast path: if there's exactly one valid item and it's a collection, use it directly
if (nonNullGoos.Count == 1 && nonNullGoos[0] is SpeckleCollectionWrapperGoo singleCollection)
{
rootBase = singleCollection.Value.DeepCopy();
}
else
{
// mixed inputs: construct a root collection using the document name (CNX-3175)
var docName = SendComponent.GetGrasshopperFileInfo().fileName ?? "Unnamed Document";
if (
docName.EndsWith(".gh", StringComparison.OrdinalIgnoreCase)
|| docName.EndsWith(".ghx", StringComparison.OrdinalIgnoreCase)
)
{
docName = Path.GetFileNameWithoutExtension(docName);
}
rootBase = new SpeckleCollectionWrapper
{
Base = new Speckle.Sdk.Models.Collections.Collection(),
Path = [docName],
Color = null,
Material = null,
Name = docName
};
int skippedCount = 0;
foreach (var obj in inputGoos)
{
if (obj is SpeckleCollectionWrapperGoo collectionGoo)
{
var colClone = (SpeckleCollectionWrapperGoo)collectionGoo.Duplicate();
colClone.Value.Path = rootBase.Path;
rootBase.Elements.AddRange(colClone.Value.Elements);
}
else if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
{
var dataObjectWrapper = dataObjectWrapperGoo.Value.DeepCopy();
dataObjectWrapper.Path = rootBase.Path;
dataObjectWrapper.Parent = rootBase;
rootBase.Elements.Add(dataObjectWrapper);
}
else if (obj?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper objWrapper)
{
SpeckleGeometryWrapper wrapper = objWrapper.DeepCopy();
wrapper.Path = rootBase.Path;
wrapper.Parent = rootBase;
rootBase.Elements.Add(wrapper);
}
else
{
rootBase.Elements.Add(null);
skippedCount++;
}
}
if (skippedCount > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Skipped {skippedCount} unsupported object(s).");
}
}
RootCollectionWrapper = new SpeckleCollectionWrapperGoo(rootBase);
string? versionMessage = null;
da.GetData(2, ref versionMessage);
@@ -301,7 +360,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
da.GetData(3, ref rootPropsGoo);
// validate single properties group
// we can't support a list input here, what does that even mean? grafting the collection to each props entry?? scary.
if (Params.Input[3].VolatileData.DataCount > 1)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one Model Properties group is allowed");
@@ -446,10 +504,35 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
});
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
(SendOperationResult result, string versionId) = await sendOperation
.Send([rootCollectionWrapper], sendInfo, fileName, fileBytes, Parent.VersionMessage, progress, CancellationToken)
(SendOperationResult result, string versionId, string? ingestionId) = await sendOperation
.Send(
[rootCollectionWrapper],
sendInfo,
fileName,
fileBytes,
Parent.VersionMessage,
progress,
true,
CancellationToken
)
.ConfigureAwait(false);
if (ingestionId != null)
{
Parent.Message = "Remote processing";
var ingestionTracker = scope.ServiceProvider.GetRequiredService<IngestionTracker>();
versionId = await ingestionTracker
.WaitForIngestionCompletion(
Parent.ApiClient,
sendInfo.ProjectId,
ingestionId,
reportProgress,
Id,
CancellationToken
)
.ConfigureAwait(false);
}
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>() { { "isAsync", true }, { "auto", Parent.AutoSend } };
if (sendInfo.WorkspaceId != null)
@@ -1,6 +1,7 @@
using System.Diagnostics;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
@@ -13,6 +14,8 @@ using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
@@ -64,14 +67,15 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
// speckle model
pManager.AddParameter(new SpeckleUrlModelResourceParam());
// collection
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
// collection / data (Refactored to accept lists of mixed data)
pManager.AddGenericParameter(
"Collection",
"collection",
"The model collection to publish",
GH_ParamAccess.item
"The collections, data objects, or geometries to publish",
GH_ParamAccess.list
);
// version message
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
@@ -107,8 +111,78 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
throw new SpeckleException("Failed to get resource");
}
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
da.GetData(1, ref rootCollectionWrapper);
// read as generic list of Goos
List<IGH_Goo> inputGoos = new();
da.GetDataList(1, inputGoos);
SpeckleCollectionWrapper? rootBase;
// filter out nulls just to check if we can use the fast path
var nonNullGoos = inputGoos.Where(x => x != null).ToList();
// fast path: if there's exactly one valid item and it's a collection, use it directly
if (nonNullGoos.Count == 1 && nonNullGoos[0] is SpeckleCollectionWrapperGoo singleCollection)
{
rootBase = singleCollection.Value.DeepCopy();
}
else
{
// mixed inputs: construct a root collection using the document name (CNX-3175)
var docName = GetGrasshopperFileInfo().fileName ?? "Unnamed Document";
if (
docName.EndsWith(".gh", StringComparison.OrdinalIgnoreCase)
|| docName.EndsWith(".ghx", StringComparison.OrdinalIgnoreCase)
)
{
docName = Path.GetFileNameWithoutExtension(docName);
}
rootBase = new SpeckleCollectionWrapper
{
Base = new Collection(),
Name = docName,
Path = [docName],
Color = null,
Material = null
};
int skippedCount = 0;
foreach (var obj in inputGoos)
{
if (obj is SpeckleCollectionWrapperGoo collectionGoo)
{
var colClone = (SpeckleCollectionWrapperGoo)collectionGoo.Duplicate();
colClone.Value.Path = rootBase.Path;
rootBase.Elements.AddRange(colClone.Value.Elements);
}
else if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
{
var dataObjectWrapper = dataObjectWrapperGoo.Value.DeepCopy();
dataObjectWrapper.Path = rootBase.Path;
dataObjectWrapper.Parent = rootBase;
rootBase.Elements.Add(dataObjectWrapper);
}
else if (obj?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper objWrapper)
{
SpeckleGeometryWrapper wrapper = objWrapper.DeepCopy();
wrapper.Path = rootBase.Path;
wrapper.Parent = rootBase;
rootBase.Elements.Add(wrapper);
}
else
{
rootBase.Elements.Add(null);
skippedCount++;
}
}
if (skippedCount > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Skipped {skippedCount} unsupported object(s).");
}
}
SpeckleCollectionWrapperGoo rootCollectionWrapper = new(rootBase);
string? versionMessage = null;
da.GetData(2, ref versionMessage);
@@ -169,27 +243,13 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
)
{
var multipleResources = Params.Input[0].VolatileData.HasInputCountGreaterThan(1);
var multipleCollections = Params.Input[1].VolatileData.HasInputCountGreaterThan(1);
var hasMultipleInputs = multipleCollections || multipleResources;
if (hasMultipleInputs)
if (multipleResources)
{
var mCollErrText =
"Only one single collection supported. Please group your input collections into one single one before sending.";
var mLinksErrText =
"Only one single model can be published to from this node. To send to multiple models, please use multiple publish components.";
if (multipleCollections)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mCollErrText);
}
if (multipleResources)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, mLinksErrText);
}
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
"Only one single model can be published to from this node. To send to multiple models, please use multiple publish components."
);
return new(null);
}
@@ -198,9 +258,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
return new(null);
}
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly - it's only
// constructed here from the Collection + Model Properties inputs.
// if this changes, then we need to update below!
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly
var rootWrapper = new SpeckleRootCollectionWrapper(input.Input.Value, input.RootProperties?.Unwrap());
var collectionToSend = new SpeckleRootCollectionWrapperGoo(rootWrapper);
@@ -224,10 +282,26 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
using var client = clientFactory.Create(account);
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
var (result, versionId) = await sendOperation
.Send([collectionToSend], sendInfo, fileName, fileBytes, VersionMessage, progress, cancellationToken)
var (result, versionId, ingestionId) = await sendOperation
.Send([collectionToSend], sendInfo, fileName, fileBytes, VersionMessage, progress, true, cancellationToken)
.ConfigureAwait(false);
if (ingestionId != null)
{
Message = "Remote processing";
var ingestionTracker = scope.ServiceProvider.GetRequiredService<IngestionTracker>();
versionId = await ingestionTracker
.WaitForIngestionCompletion(
client,
sendInfo.ProjectId,
ingestionId,
reportProgress: null,
reportProgressId: null,
cancellationToken
)
.ConfigureAwait(false);
}
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object> { { "isAsync", false } };
if (sendInfo.WorkspaceId != null)
@@ -238,12 +312,13 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Send, account, customProperties);
SpeckleUrlLatestModelVersionResource createdVersionResource =
SpeckleUrlModelVersionResource createdVersionResource =
new(
new(sendInfo.Account.id, null, sendInfo.Account.serverInfo.url),
sendInfo.WorkspaceId,
sendInfo.ProjectId,
sendInfo.ModelId
sendInfo.ModelId,
versionId
);
Url = $"{sendInfo.Account.serverInfo.url}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
return new SendComponentOutput(createdVersionResource, versionId);
@@ -3,6 +3,7 @@ using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Api;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Transports;
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
@@ -26,7 +26,8 @@ namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
/// </remarks>
internal sealed class LocalToGlobalMapHandler
{
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = [];
private readonly HashSet<string> _processedDataObjects = [];
// injected via constructor (DI-managed)
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
@@ -113,7 +114,7 @@ internal sealed class LocalToGlobalMapHandler
var obj = atomicContext.Current;
var objId = obj.applicationId ?? obj.id;
if (objId is null || ConvertedObjectsMap.ContainsKey(objId))
if (objId is null || ConvertedObjectsMap.ContainsKey(objId) || _processedDataObjects.Contains(objId))
{
return;
}
@@ -132,6 +133,15 @@ internal sealed class LocalToGlobalMapHandler
{
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
// geometry-less data objects (cnx-2522)
bool isMetadataOnly = obj is DataObject { displayValue.Count: 0 };
// bypass the early return if this genuinely is a metadata-only DataObject (cnx-3237)
if (converted.Count == 0 && !isMetadataOnly)
{
return;
}
// get path and collection
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
@@ -143,6 +153,8 @@ internal sealed class LocalToGlobalMapHandler
// handle all DataObjects
if (obj is DataObject normalDataObject)
{
_processedDataObjects.Add(objId);
var geometries = ConvertToGeometryWrappers(converted);
var dataObjectWrapper = CreateDataObjectWrapper(normalDataObject, geometries, path, objectCollection);
@@ -150,12 +162,6 @@ internal sealed class LocalToGlobalMapHandler
return;
}
// nothing converted - nothing to do (for non-DataObjects)
if (converted.Count == 0)
{
return;
}
// handle normal geometry (not DataObject)
SpecklePropertyGroupGoo propertyGroup = new();
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
@@ -214,6 +220,12 @@ internal sealed class LocalToGlobalMapHandler
return;
}
// ensures we don't process the same registered DataObject multiple times due to multiple traversal encounters.
if (!_processedDataObjects.Add(dataObjectId))
{
return;
}
try
{
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
@@ -0,0 +1,221 @@
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using Speckle.Sdk.Pipelines.Send;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
/// <summary>
/// Continuous traversal builder for Grasshopper that processes each object through the <see cref="SendPipeline"/>
/// as it unwraps. This enables the packfile send path (streaming objects to S3 during build).
/// </summary>
public class GrasshopperContinuousTraversalBuilder(
IInstanceObjectsManager<SpeckleGeometryWrapper, List<string>> instanceObjectsManager
) : IRootContinuousTraversalBuilder<SpeckleCollectionWrapperGoo>
{
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<SpeckleCollectionWrapperGoo> objects,
string projectId,
SendPipeline sendPipeline,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// create root collection
var rootCollectionGoo = (SpeckleRootCollectionWrapperGoo)objects[0].Duplicate();
rootCollectionGoo.Value.Name = "Grasshopper Model";
RootCollection rootCollection =
new(rootCollectionGoo.Value.Name)
{
applicationId = rootCollectionGoo.Value.ApplicationId,
properties = rootCollectionGoo.Value.Properties ?? new()
};
// create packers for colors and render materials
GrasshopperColorPacker colorPacker = new();
GrasshopperMaterialPacker materialPacker = new();
GrasshopperBlockPacker blockPacker = new(instanceObjectsManager);
// unwrap the input collection, processing each object through the send pipeline
await Unwrap(
rootCollectionGoo.Value,
rootCollection,
colorPacker,
materialPacker,
blockPacker,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
// add proxies
rootCollection[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
rootCollection[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
// process the root collection through the pipeline and wait for all uploads
await sendPipeline.Process(rootCollection).ConfigureAwait(false);
await sendPipeline.WaitForUpload().ConfigureAwait(false);
// TODO: Not getting any conversion results yet
return new RootObjectBuilderResult(rootCollection, []);
}
private async Task Unwrap(
SpeckleCollectionWrapper wrapper,
Collection targetCollection,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker,
GrasshopperBlockPacker blockPacker,
SendPipeline sendPipeline,
CancellationToken cancellationToken
)
{
colorPacker.ProcessColor(wrapper.ApplicationId, wrapper.Color);
materialPacker.ProcessMaterial(wrapper.ApplicationId, wrapper.Material);
int skippedNulls = 0;
foreach (ISpeckleCollectionObject? element in wrapper.Elements)
{
cancellationToken.ThrowIfCancellationRequested();
switch (element)
{
case null:
skippedNulls++;
continue;
case SpeckleCollectionWrapper collWrapper:
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
targetCollection.elements.Add(collWrapper.Collection);
await Unwrap(
collWrapper,
collWrapper.Collection,
colorPacker,
materialPacker,
blockPacker,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
break;
case SpeckleGeometryWrapper so:
Base objectBase = UnwrapGeometry(so);
string applicationId = objectBase.applicationId!;
// NOTE: This is how it differentiate from 'GrasshopperSendOperation'
// It process through send pipeline before adding to collection
var reference = await sendPipeline.Process(objectBase).ConfigureAwait(false);
targetCollection.elements.Add(reference);
if (so is SpeckleBlockInstanceWrapper blockInstance)
{
await ProcessBlockInstanceDefinition(
blockInstance,
colorPacker,
materialPacker,
blockPacker,
targetCollection,
sendPipeline,
cancellationToken
)
.ConfigureAwait(false);
}
colorPacker.ProcessColor(applicationId, so.Color);
materialPacker.ProcessMaterial(applicationId, so.Material);
break;
case SpeckleDataObjectWrapper dataObjectWrapper:
DataObject dataObject = UnwrapDataObject(dataObjectWrapper, colorPacker, materialPacker);
// process data object through send pipeline
var dataRef = await sendPipeline.Process(dataObject).ConfigureAwait(false);
targetCollection.elements.Add(dataRef);
break;
}
}
// clear topology when nulls are present (CNX-2855)
if (skippedNulls > 0)
{
targetCollection[Constants.TOPOLOGY_PROP] = null;
}
}
private Base UnwrapGeometry(SpeckleGeometryWrapper wrapper)
{
Dictionary<string, object?> props = [];
Base baseObject = wrapper.Base;
if (wrapper.Properties.CastTo(ref props))
{
baseObject["properties"] = props;
}
return baseObject;
}
private async Task ProcessBlockInstanceDefinition(
SpeckleBlockInstanceWrapper blockInstance,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker,
GrasshopperBlockPacker blockPacker,
Collection currentColl,
SendPipeline sendPipeline,
CancellationToken cancellationToken
)
{
cancellationToken.ThrowIfCancellationRequested();
var definitionObjects = blockPacker.ProcessInstance(blockInstance);
if (definitionObjects != null)
{
foreach (var definitionObject in definitionObjects)
{
Base defObjectBase = UnwrapGeometry(definitionObject);
string applicationId = defObjectBase.applicationId!;
var reference = await sendPipeline.Process(defObjectBase).ConfigureAwait(false);
currentColl.elements.Add(reference);
colorPacker.ProcessColor(applicationId, definitionObject.Color);
materialPacker.ProcessMaterial(applicationId, definitionObject.Material);
}
}
}
private DataObject UnwrapDataObject(
SpeckleDataObjectWrapper wrapper,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker
)
{
DataObject dataObject = wrapper.DataObject;
var displayValue = new List<Base>();
foreach (var geometryWrapper in wrapper.Geometries)
{
Base geometryBase = UnwrapGeometry(geometryWrapper);
displayValue.Add(geometryBase);
if (geometryWrapper.ApplicationId != null)
{
colorPacker.ProcessColor(geometryWrapper.ApplicationId, geometryWrapper.Color);
materialPacker.ProcessMaterial(geometryWrapper.ApplicationId, geometryWrapper.Material);
}
}
dataObject.displayValue = displayValue;
return dataObject;
}
}
@@ -5,6 +5,7 @@ using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Pipelines.Progress;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
@@ -10,7 +10,7 @@ namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// </summary>
public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpecklePropertyGoo>>, ISpecklePropertyGoo
{
public override IGH_Goo Duplicate() => throw new NotImplementedException();
public override IGH_Goo Duplicate() => Clone();
public override string ToString() => $"Speckle Properties : ({Value.Count})";
@@ -213,5 +213,100 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
return dict;
}
/// <summary>
/// Performs a deep clone of the property group to prevent mutating upstream Grasshopper data.
/// </summary>
/// <returns>
/// A new SpecklePropertyGroupGoo instance with cloned nested properties.
/// Needed since adding support for dot notation [CNX-3179]
/// </returns>
public SpecklePropertyGroupGoo Clone()
{
var newDict = new Dictionary<string, ISpecklePropertyGoo>();
foreach (var kvp in Value)
{
newDict[kvp.Key] = kvp.Value is SpecklePropertyGroupGoo group ? group.Clone() : kvp.Value;
}
return new SpecklePropertyGroupGoo(newDict);
}
/// <summary>
/// Sets a property value using dot-notation path traversal. Creates nested groups if they do not exist.
/// </summary>
/// <param name="path">The dot-notation property path.</param>
/// <param name="value">The Speckle property to set.</param>
public void SetValueByPath(string path, ISpecklePropertyGoo value)
{
string[] parts = path.Split('.');
var current = Value;
for (int i = 0; i < parts.Length - 1; i++)
{
string part = parts[i];
if (!current.TryGetValue(part, out var existing) || existing is not SpecklePropertyGroupGoo group)
{
group = new SpecklePropertyGroupGoo();
current[part] = group;
}
current = group.Value;
}
current[parts[^1]] = value;
}
/// <summary>
/// Removes a property value using dot-notation path traversal.
/// </summary>
/// <param name="path">The dot-notation property path.</param>
public void RemoveValueByPath(string path)
{
string[] parts = path.Split('.');
var current = Value;
for (int i = 0; i < parts.Length - 1; i++)
{
string part = parts[i];
if (!current.TryGetValue(part, out var existing) || existing is not SpecklePropertyGroupGoo group)
{
return; // path does not exist
}
current = group.Value;
}
current.Remove(parts[^1]);
}
/// <summary>
/// Retrieves a property value using dot-notation path traversal. Attempts exact match first.
/// </summary>
/// <param name="path">The dot-notation property path.</param>
/// <returns>The matching property goo if found, otherwise null.</returns>
public ISpecklePropertyGoo? GetValueByPath(string path)
{
// attempt exact match first for literal dots in native keys
if (Value.TryGetValue(path, out var exactMatch))
{
return exactMatch;
}
string[] parts = path.Split('.');
ISpecklePropertyGoo? current = this;
foreach (var part in parts)
{
if (current is SpecklePropertyGroupGoo group && group.Value.TryGetValue(part, out var next))
{
current = next;
}
else
{
return null; // current is not a dictionary, or path is invalid
}
}
return current;
}
public override int GetHashCode() => base.GetHashCode();
}
@@ -9,6 +9,7 @@ using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.GrasshopperShared.Components;
using Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Operations.Send;
@@ -61,9 +62,14 @@ public class PriorityLoader : GH_AssemblyPriority
services.AddTransient<TraversalContextUnpacker>();
services.AddScoped<IDataObjectInstanceRegistry, DataObjectInstanceRegistry>();
services.AddTransient<LocalToGlobalMapHandler>();
services.AddTransient<IngestionTracker>();
// send
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
services.AddTransient<
IRootContinuousTraversalBuilder<SpeckleCollectionWrapperGoo>,
GrasshopperContinuousTraversalBuilder
>();
services.AddTransient<SendOperation<SpeckleCollectionWrapperGoo>>();
services.AddSingleton<IThreadContext>(new DefaultThreadContext());
services.AddScoped<
@@ -58,7 +58,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Receive\ReceiveComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperBlockPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperMaterialPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperContinuousTraversalBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperColorPacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\IngestionTracker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\SendAsyncComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\SendComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\SpeckleSelectModelComponent.cs" />
@@ -82,8 +82,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -185,24 +185,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -328,7 +330,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -361,7 +363,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino7": {
@@ -421,11 +423,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"System.Resources.Extensions": {
@@ -82,8 +82,8 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"resolved": "9.0.4",
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
@@ -185,24 +185,26 @@
},
"Speckle.Sdk": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
"resolved": "3.15.3",
"contentHash": "6gmPoWTv7DwqvUae57wCLF93upE9RIjaCZFue9UMY4I6FB8vLbMGfcyiUwnUY551WlGOual15ISS3G15/kMmnw==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.13.1"
"Speckle.Sdk.Dependencies": "3.15.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "Transitive",
"resolved": "3.13.1",
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
"resolved": "3.15.3",
"contentHash": "VLgyGi1kQNWe0fzRO0U3qnZZUQGDeFacnpn25Yy3esE0qeo4tqa1BrvXPv2ivEZbbhBkkg6+Gd+CztDIyw3Y/w==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "9.0.4"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
@@ -328,7 +330,7 @@
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.connectors.dui": {
@@ -361,7 +363,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.13.1, )"
"Speckle.Objects": "[3.15.3, )"
}
},
"speckle.converters.rhino8": {
@@ -420,11 +422,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.13.1, )",
"resolved": "3.13.1",
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
"requested": "[3.15.3, )",
"resolved": "3.15.3",
"contentHash": "zmHnLKR46in0xH5ashD+ENlYUPDktUMZhXYYOb8aWHAG3Zxai2WvmDJtdf7pV9GTafkpR6fCo2EQTeCoY+XXxQ==",
"dependencies": {
"Speckle.Sdk": "3.13.1"
"Speckle.Sdk": "3.15.3"
}
},
"System.Resources.Extensions": {

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