Compare commits

..

72 Commits

Author SHA1 Message Date
JR-Morgan 4fef78143d blobs-poc 2025-11-13 10:08:21 +03:00
Mucahit Bilal GOKER d4ee1f2a55 feat(revit): add roomId and spaceId parameters (#1181)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* add roomId and spaceId parameters

* clean up
2025-11-11 10:30:27 +03:00
Jedd Morgan 4f960cc670 readme tweaks (#1179) 2025-11-10 13:52:43 +00:00
Jedd Morgan 1f63c1f8b3 Merge pull request #1165 from specklesystems/main
chore: Back merge main -> Dev
2025-11-10 13:48:30 +00:00
Jedd Morgan 2ed9ffbca7 Use Openheadless for unit support (#1178) 2025-11-10 12:53:55 +00:00
Jedd Morgan d87b862e2b add readme (#1173) 2025-11-10 12:49:39 +00:00
Mucahit Bilal GOKER 3ad3ad2f01 feat(rhino): use existing materials when loading (#1177)
* reuse existing materials

* comment out purge material

* fix: unnecessary using directives

* more details in exception message and remove material purge.

---------

Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
2025-11-10 15:43:04 +03:00
Björn Steinhagen 6db7e46401 fix(grasshopper): refresh parameter UI when toggling empty properties mode (#1174) 2025-11-10 15:07:57 +03:00
Björn Steinhagen 13fc24c7c7 fix(rhino): handles null active doc (#1180) 2025-11-10 14:51:06 +03:00
Jedd Morgan cf86158b83 Ignore (#1171) 2025-11-03 12:57:17 +00:00
Björn Steinhagen 25eb955636 feat(etabs): add volume calculation for frames and shells (#1167)
* feat: adds frame volume prop

* feat: adds shell volume prop

* refactor: simplify caching

* fix(etabs): use IsInfinity for etabs 21
2025-10-28 17:39:30 +02:00
dependabot[bot] 7862a858ae chore(deps): bump actions/upload-artifact from 4 to 5 (#1166)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 18:09:02 +00:00
Jedd Morgan bc18d3b494 fix(rhino-importer): Use main thread always for document creation (#1161)
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
* Use main thread

* format

* configure await false

* pass args only once
2025-10-27 11:11:56 +00:00
Björn Steinhagen fd34f22028 fix(grasshopper): applicationId for SpeckleDataObject on creation (#1159)
* chore: formatting

* fix: generate appId on object creation

* feat: validate uniqueness of objects within a collection
2025-10-25 20:46:20 +01:00
Jedd Morgan 958c9e5e94 Merge pull request #1158 from specklesystems/main
Main -> Dev back merge
2025-10-20 15:42:23 +01:00
Jedd Morgan 7c7260c603 Merge pull request #1157 from specklesystems/dev
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev into main
2025-10-20 15:39:33 +01:00
Oğuzhan Koral bae9e3e0f1 Prevent crashes on unnamed files (#1154)
Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-10-20 17:33:00 +03:00
Oğuzhan Koral 26b0394613 feat(revit): display value proxies (#1140)
* POC

* some fixes

* Handle autocad, rhino and sketchup receives

* Handle revit receive

* Fix transform issues

* fix: custom mesh id logic

* Hash function

* Merge pull request #1142 from specklesystems/jedd/cnx-2657-hashing-the-meshes

feat(revit)!: Use Hash function for mesh geometry instance ids

* Use v2 style transform

* extra comments

* experiment1

* correct transform logic and disposal

* corrected transform logic

* simplify (maybe) the transform combination

* refactor(revit): replace tuples with DisplayValueResult record for display values (#1145)

* Clear instance proxies per conversion

* fix: material assignment on revit receive (#1146)

* Fix: enable send caching (#1148)

* Enable caching without definition proxy noise

* Log element id relationship while creating cache to filter after conversion to attach root

* Update RevitRootObjectBuilder.cs

* Clear cache on document swap

* More clean up

* fix(revit): defer material proxy population to prevent duplicate instance mesh IDs (#1155)

* fix(revit): defer material proxy population to prevent duplicate instance mesh IDs

* chore: formatting

* chore: campsite

* refactor: throwing on cache error

* refactor: move material proxy population into cache singleton

* fix: di

* fix(rhino): match cleaned block names when purging instance definitions (#1156)

* fix(rhino): match cleaned block names when purging instance definitions

* refactor: simplification

* chore: comments

* refactor: use .Contains

---------

Co-authored-by: Björn <steinhagen.bjoern@gmail.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
2025-10-20 15:26:56 +01:00
Dogukan Karatas 689ef0bcbe Merge pull request #1149 from specklesystems/dogukan/cnx-2490-receive-property-sets-in-civil-3d
feat (civil): receive property sets
2025-10-17 18:09:25 +02:00
Dogukan Karatas 461585b782 adds additional cast 2025-10-17 17:58:01 +02:00
Claire Kuang ea33f35a7d removes unnecessary casting on send 2025-10-17 16:57:38 +01:00
Claire Kuang 7427f1a2f3 adds constants and better host object builder methods 2025-10-17 16:08:40 +01:00
Dogukan Karatas b7984bf97e add additional cleaning 2025-10-17 15:25:35 +02:00
Dogukan Karatas 9b24a45b6e property set pruge 2025-10-17 12:50:58 +02:00
Claire Kuang 4ace81a422 Merge branch 'dev' into dogukan/cnx-2490-receive-property-sets-in-civil-3d 2025-10-17 10:02:40 +01:00
Björn Steinhagen a60790c92c fix(grasshopper): account selection not respected when url input connected (#1150)
* fix: account switching on urlInput
* chore: good ol' comments
* chore: adding server url to exception message
2025-10-17 10:02:12 +01:00
Dogukan Karatas fd0d00cac3 Merge branch 'dev' into dogukan/cnx-2490-receive-property-sets-in-civil-3d 2025-10-16 18:48:01 +02:00
Dogukan Karatas 498396e611 Merge pull request #1153 from specklesystems/dogukan/build-package-version-bump
chore(build): bump Microsoft.Build version
2025-10-16 18:47:18 +02:00
Dogukan Karatas 5444377398 bump version 2025-10-16 18:29:20 +02:00
Dogukan Karatas 9d981f9800 ci check 2025-10-16 17:59:13 +02:00
Dogukan Karatas 14e17fb67d prefix and purge 2025-10-16 17:52:30 +02:00
Dogukan Karatas 0ffa7685fd rather cast than convert 2025-10-16 17:16:20 +02:00
Dogukan Karatas dc7d4671e4 default value simplification 2025-10-16 16:40:37 +02:00
Dogukan Karatas 10cb5cd66f removed the parser 2025-10-16 16:24:53 +02:00
Jedd Morgan cb15d9f77a Merge pull request #1152 from specklesystems/jrm/trigger-ci
Remove arcgis from readme
2025-10-16 15:05:53 +01:00
Jedd Morgan da74faef9b read me change to trigger ci 2025-10-16 15:02:58 +01:00
Claire Kuang 4368833c7e Merge branch 'dev' into dogukan/cnx-2490-receive-property-sets-in-civil-3d 2025-10-16 09:56:57 +01:00
Dogukan Karatas a20df41316 readonly dict 2025-10-15 22:56:50 +02:00
Dogukan Karatas ccf48dbad1 process property set definitions from root object 2025-10-15 22:42:33 +02:00
Björn Steinhagen 6700aa27bc fix(revit): handle level extraction for face-based family instances (#1151)
* fix: get levels for face-based instances

* fix: level unpacker
2025-10-15 22:06:01 +02:00
Dogukan Karatas df525eab63 moved the baker to the connector 2025-10-15 21:25:12 +02:00
Dogukan Karatas 275901626f created an abstract class 2025-10-15 16:35:19 +02:00
Claire Kuang fac0dc31b2 Merge branch 'dev' into dogukan/cnx-2490-receive-property-sets-in-civil-3d 2025-10-15 12:48:26 +01:00
Dogukan Karatas 8696eca1f0 use active transaction 2025-10-15 13:12:41 +02:00
Dogukan Karatas d647c71cf5 baker added 2025-10-15 12:25:01 +02:00
Dogukan Karatas 9b218dd808 Revert "created PropertySetConverter"
This reverts commit 112093f914.
2025-10-15 12:06:04 +02:00
Björn Steinhagen 9f39dc521d fix(revit): clamp PBR material properties to avoid receive failures (#1147)
* fix: clamping roughness vaues

* chore: extending to other 0 - 1 values
2025-10-14 16:15:52 +02:00
Dogukan Karatas 112093f914 created PropertySetConverter 2025-10-13 14:08:35 +02:00
Oğuzhan Koral d174597770 Merge pull request #1141 from specklesystems/dev
.NET Build and Publish / build-connectors (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev into main
2025-10-08 16:06:07 +03:00
Björn Steinhagen d9289787b7 fix(grasshopper): add auto-load persistence and reactive behavior to sync receive component (#1134)
* fix: poc `(Sync) Load` component reacting to new versions

* fix: persist setting between sessions

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-10-08 12:59:18 +02:00
Björn Steinhagen 77c1c3b511 fix(revit): handle null SelectedObjectIds in view filters on send (#1139) 2025-10-07 15:19:05 +02:00
Adam Hathcock 5a1c542832 Push doc to callers if possible (#1129)
* Push doc to callers if possible

* use check for active doc earlier.  Use doc instead of linked doc

* Use passed in current doc instead of getting active doc each time

* fmt

* Remove extra using
2025-10-07 07:25:36 +00:00
Adam Hathcock 091d7cc897 Stop exceptions we can't do anything about (#1138) 2025-10-06 17:19:30 +01:00
Adam Hathcock 21f4fb52a8 cache parameters by group and name instead of caching groups and assuming units are all the same (#1128)
* cache parameters by group and name

* fmt
2025-10-06 12:01:04 +01:00
Adam Hathcock 868ca8db66 Key for docs should fallback correctly and load when not found. (#1130)
* fix for revit 2024 and greater

* fallback to path name for 22/23

* adjust comment

---------

Co-authored-by: Björn Steinhagen <88777268+bjoernsteinhagen@users.noreply.github.com>
Co-authored-by: Mucahit Bilal GOKER <51519350+bimgeek@users.noreply.github.com>
2025-10-06 10:22:19 +01:00
Björn Steinhagen a9360e5fac fix(grasshopper): optimize CreateSpeckleProperties for large data trees (#1135)
* perf: remove expensive tree flattening in CreateSpeckleProperties

* chore: format
2025-10-04 10:42:01 +01:00
Björn Steinhagen 3414599f72 fix(etabs): units not supported (#1132)
* fix: etabs units not supported

* chore: notes
2025-10-03 15:15:54 +00:00
Björn Steinhagen 84e92aa8a8 fix: prevent internal parameters from appearing in Params/Primitives tab (#1133) 2025-10-03 09:56:31 +01:00
Jedd Morgan edd842763f remove obsolete (#1131) 2025-10-02 16:44:26 +01:00
Björn Steinhagen 867ee0f928 feat(etabs): adds design data material props (#1111) 2025-10-02 16:50:39 +02:00
Jedd Morgan 29ee648d7f Swallow IOEx (#1125) 2025-10-01 22:38:56 +00:00
Jedd Morgan 5b9c610856 Only call IsConnected on Physical connectors (#1127) 2025-10-01 15:41:48 +00:00
Adam Hathcock 769ddf6407 merge update (#1121) 2025-10-01 15:37:35 +00:00
Claire Kuang dd7205f855 fix(rhino): adds area and volume props to breps (#1124)
* adds area and volume props to breps

* removes bbox props and adds area and volume to extrusion and mesh
2025-10-01 14:34:01 +01:00
Mucahit Bilal GOKER 30ee410309 fix(revit): Missing Type Mark parameter added (#1112)
* type mark fix

* fix: format

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-10-01 15:35:15 +03:00
Jedd Morgan 2d0b1a3a24 chore: Remove legacy .NET based IFC importer (#1118)
* remove IFC source files

* re-generate slnx files
2025-10-01 12:06:43 +01:00
Jedd Morgan 2cdf036172 Merge pull request #1123 from specklesystems/installer-test/linux-ci
chore(ci): Refactor out nuget publishing from CI, use linux for pr builds
2025-10-01 11:58:37 +01:00
Björn Steinhagen c14aa28e76 fix(grasshopper): type-specific outputs not refreshing in QuerySpeckleObjects component (#1114)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-29 16:50:27 +00:00
Björn Steinhagen 0e7d2554f8 fix(grasshopper): sync SpeckleGeometryWrapper Properties with Base["properties"] (#1113)
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-09-29 18:45:27 +02:00
Adam Hathcock cdfc618bab Avoid a null points collection (#1116) 2025-09-29 07:55:29 +00:00
Adam Hathcock 1005edb609 feat(Navisworks) Expose send cache options to all (#1115) 2025-09-26 19:23:44 +02:00
Adam Hathcock 3b4da8de52 check connection before using? (#1109) 2025-09-25 08:23:08 +01:00
268 changed files with 5128 additions and 8893 deletions
+14 -28
View File
@@ -1,29 +1,11 @@
name: .NET Build
name: .NET Test
on: pull_request
on:
pull_request: {}
push:
branches: ["main"]
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run build
run: ./build.ps1 test
test:
runs-on: ubuntu-latest
steps:
@@ -43,14 +25,18 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run Build on Linux
run: ./build.sh build-linux
- name: ⚒️ Run tests
run: ./build.sh test-only
- name: ⚒️ Run Test
run: ./build.sh test-and-pack
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
with:
files: Converters/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
# Disabling this code for now, since we no longer need to publish nugets from this repo
# But keeping it around incase we ever need in the future.
# Ideally, I'd also like to move the nuget token to be an environment secret, and to have tight package scopes
# - name: Push to nuget.org
# if: ${{ inputs.deployNugets }}
# run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{ secrets.CONNECTORS_NUGET_TOKEN }}
+10 -44
View File
@@ -6,8 +6,8 @@ on:
tags: ["v3.*.*"] # Manual delivery on every 3.x tag
jobs:
build-windows:
runs-on: windows-latest
build-connectors:
runs-on: windows-latest # Keeping on windows for now, for cross platform building of exe projects, we need to use dotnet publish
env:
SEMVER: "unset"
FILE_VERSION: "unset"
@@ -31,11 +31,11 @@ jobs:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run build on Windows
- name: ⚒️ Run build and zip connectors
run: ./build.ps1 zip
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: output-${{ env.SEMVER }}
path: output/*.*
@@ -48,10 +48,10 @@ 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-windows
needs: build-connectors
env:
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
steps:
@@ -63,8 +63,8 @@ jobs:
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
inputs: '{
"run_id": "${{ github.run_id }}",
"semver": "${{ needs.build-windows.outputs.semver }}",
"file_version": "${{ needs.build-windows.outputs.file_version }}",
"semver": "${{ needs.build-connectors.outputs.semver }}",
"file_version": "${{ needs.build-connectors.outputs.file_version }}",
"repo": "${{ github.repository }}",
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
}'
@@ -74,42 +74,8 @@ jobs:
wait-for-completion-timeout: 10m
display-workflow-run-url: true
display-workflow-run-url-interval: 10s
# 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
with:
name: output-*
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
- name: ⚒️ Run tests on Linux
run: ./build.sh test-only
- name: ⚒️ Run Build and Pack on Linux
run: ./build.sh build-linux
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v5
with:
files: Converters/**/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
- name: Push to nuget.org
if: (github.ref_type == 'tag')
run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
+17 -21
View File
@@ -7,9 +7,9 @@ using static SimpleExec.Command;
const string CLEAN = "clean";
const string RESTORE = "restore";
const string BUILD = "build";
const string BUILD_LINUX = "build-linux";
const string PACK = "pack";
const string TEST_AFFECTED = "test-affected";
const string TEST = "test";
const string TEST_ONLY = "test-only";
const string FORMAT = "format";
const string ZIP = "zip";
const string RESTORE_TOOLS = "restore-tools";
@@ -19,6 +19,7 @@ const string GEN_SOLUTIONS = "generate-solutions";
const string DEEP_CLEAN = "deep-clean";
const string DEEP_CLEAN_LOCAL = "deep-clean-local";
const string DETECT_AFFECTED = "detect-affected";
const string TEST_AND_PACK = "test-and-pack";
//need to pass arguments
/*var arguments = new List<string>();
@@ -150,7 +151,7 @@ Target(
Target(
RESTORE,
DependsOn(FORMAT, DETECT_AFFECTED),
DependsOn(FORMAT),
Consts.Solutions,
async s =>
{
@@ -181,8 +182,8 @@ Target(CHECK_SOLUTIONS, Solutions.CompareConnectorsToLocal);
Target(GEN_SOLUTIONS, Solutions.GenerateSolutions);
Target(
TEST,
DependsOn(BUILD, CHECK_SOLUTIONS),
TEST_AFFECTED,
DependsOn(DETECT_AFFECTED, BUILD, CHECK_SOLUTIONS),
async () =>
{
foreach (var s in await Affected.GetTestProjects())
@@ -192,14 +193,12 @@ Target(
}
);
//all tests on purpose
Target(
TEST_ONLY,
DependsOn(FORMAT),
TEST,
DependsOn(BUILD, CHECK_SOLUTIONS),
Glob.Files(".", "**/*.Tests.csproj"),
file =>
{
Run("dotnet", $"build \"{file}\" -c Release --no-incremental");
Run(
"dotnet",
$"test \"{file}\" -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
@@ -207,31 +206,28 @@ Target(
}
);
Target(TEST_AND_PACK, DependsOn(TEST, PACK));
Target(
BUILD_LINUX,
DependsOn(FORMAT),
Glob.Files(".", "**/Speckle.Importers.Ifc.csproj"),
async file =>
PACK,
DependsOn(BUILD),
Consts.Solutions,
async solution =>
{
await RunAsync("dotnet", $"restore \"{file}\" --locked-mode");
var version = await Versions.ComputeVersion();
var fileVersion = await Versions.ComputeFileVersion();
Console.WriteLine($"Version: {version} & {fileVersion}");
await RunAsync(
"dotnet",
$"build \"{file}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
await RunAsync(
"dotnet",
$"pack \"{file}\" -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
$"pack \"{solution}\" -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
);
}
);
Target(
ZIP,
DependsOn(TEST),
DependsOn(TEST_AFFECTED),
async () =>
{
var version = await Versions.ComputeVersion();
@@ -282,6 +278,6 @@ Target(
}
);
Target("default", DependsOn(TEST), () => Console.WriteLine("Done!"));
Target("default", DependsOn(TEST_AFFECTED), () => Console.WriteLine("Done!"));
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
+9 -9
View File
@@ -16,12 +16,12 @@
},
"Microsoft.Build": {
"type": "Direct",
"requested": "[17.11.4, )",
"resolved": "17.11.4",
"contentHash": "UMC7DfeFEHY2GGHHaghybUuUlLaByFHEFudR2PehMgDBuRuLAUePp1iaa4eFtVzepRzMtIbeSCVJCzzX3NV2Gg==",
"requested": "[17.11.48, )",
"resolved": "17.11.48",
"contentHash": "g8Kn575mNAKcuFotV3C7xvF+IbxuHennl67LH2shL2au1U6UqwReTDygCHyU04+koc2Yn7fHIbVQaC08HqEWow==",
"dependencies": {
"Microsoft.Build.Framework": "17.11.4",
"Microsoft.NET.StringTools": "17.11.4",
"Microsoft.Build.Framework": "17.11.48",
"Microsoft.NET.StringTools": "17.11.48",
"System.Collections.Immutable": "8.0.0",
"System.Configuration.ConfigurationManager": "8.0.0",
"System.Reflection.Metadata": "8.0.0",
@@ -82,8 +82,8 @@
},
"Microsoft.Build.Framework": {
"type": "Transitive",
"resolved": "17.11.4",
"contentHash": "u28uDihlqxtt8h2dL1ZJOZ7TRkxBK+HGr+3FgQpILVo7Q7gErkw8mYW9R+RM5PtxvZTdYb/4MWDL66vdIsANBQ=="
"resolved": "17.11.48",
"contentHash": "C3WIMt2wBl4++NX3jSEpTq5KXBhvAV154R4JrYHkfy9JSBcXWiL0mkgpspk5xSdOj+fS/uz7zluIy6bMM1fkkQ=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
@@ -97,8 +97,8 @@
},
"Microsoft.NET.StringTools": {
"type": "Transitive",
"resolved": "17.11.4",
"contentHash": "mudqUHhNpeqIdJoUx2YDWZO/I9uEDYVowan89R6wsomfnUJQk6HteoQTlNjZDixhT2B4IXMkMtgZtoceIjLRmA=="
"resolved": "17.11.48",
"contentHash": "0IQo089IGBEC4jgtishauZMVr9ZxOWNiGKeDvyzZlvw7p2r253lJh6IJCLLFWXvZnVrVO5mnsYIPamxFPzM08w=="
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -292,9 +292,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -333,38 +356,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -292,9 +292,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -333,38 +356,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -293,9 +293,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -244,9 +244,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -285,36 +306,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -244,9 +244,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -285,36 +306,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -0,0 +1,296 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.Operations.Receive;
/// <summary>
/// <para>Base class for AutoCAD host object builders. Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public abstract class AutocadHostObjectBaseBuilder : IHostObjectBuilder
{
private readonly IRootToHostConverter _converter;
private readonly AutocadLayerBaker _layerBaker;
private readonly AutocadGroupBaker _groupBaker;
private readonly AutocadInstanceBaker _instanceBaker;
private readonly IAutocadMaterialBaker _materialBaker;
private readonly IAutocadColorBaker _colorBaker;
private readonly AutocadContext _autocadContext;
private readonly RootObjectUnpacker _rootObjectUnpacker;
private readonly IReceiveConversionHandler _conversionHandler;
protected AutocadHostObjectBaseBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
)
{
_converter = converter;
_layerBaker = layerBaker;
_groupBaker = groupBaker;
_instanceBaker = instanceBaker;
_materialBaker = materialBaker;
_colorBaker = colorBaker;
_autocadContext = autocadContext;
_rootObjectUnpacker = rootObjectUnpacker;
_conversionHandler = conversionHandler;
}
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed.Report(new("Converting", null));
// Layer filter for received commit with project and model name
_layerBaker.CreateLayerFilter(projectName, modelName);
// 0 - Clean then Rock n Roll!
string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-");
PreReceiveDeepClean(baseLayerPrefix);
// 1 - Unpack objects and proxies from root commit object
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
// 2 - Split atomic objects and instance components with their path
var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents);
// POC: these are not captured by traversal, so we need to re-add them here
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
{
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
(Array.Empty<Collection>(), proxy as IInstanceComponent)
);
instanceComponentsWithPath.AddRange(transformed);
}
// 3 - Parse and bake proxies (materials and colors), as they are used later down the line by layers and objects
if (unpackedRoot.RenderMaterialProxies != null)
{
_materialBaker.ParseAndBakeRenderMaterials(
unpackedRoot.RenderMaterialProxies,
baseLayerPrefix,
onOperationProgressed
);
}
if (unpackedRoot.ColorProxies != null)
{
_colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
}
// 3.5 - Parse and bake additional proxies that are needed for conversion
ParseAndBakeAdditionalProxies(rootObject, baseLayerPrefix);
// 4 - Convert atomic objects
HashSet<ReceiveConversionResult> results = new();
HashSet<string> bakedObjectIds = new();
Dictionary<string, IReadOnlyCollection<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerPath, atomicObject) in atomicObjectsWithPath)
{
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
var ex = _conversionHandler.TryConvert(() =>
{
cancellationToken.ThrowIfCancellationRequested();
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
applicationIdMap[objectId] = convertedObjects;
results.UnionWith(
convertedObjects.Select(e => new ReceiveConversionResult(
Status.SUCCESS,
atomicObject,
e.GetSpeckleApplicationId(),
e.GetType().ToString()
))
);
bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId()));
});
if (ex != null)
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
}
// 5 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances(
instanceComponentsWithPath,
applicationIdMap,
baseLayerPrefix,
onOperationProgressed
);
bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id));
bakedObjectIds.UnionWith(createdInstanceIds);
results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.UnionWith(instanceConversionResults);
// 6 - Create groups
if (unpackedRoot.GroupProxies != null)
{
IReadOnlyCollection<ReceiveConversionResult> groupResults = _groupBaker.CreateGroups(
unpackedRoot.GroupProxies,
applicationIdMap
);
results.UnionWith(groupResults);
}
return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results));
}
protected void PreReceiveDeepClean(string baseLayerPrefix)
{
_layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix);
_instanceBaker.PurgeInstances(baseLayerPrefix);
_materialBaker.PurgeMaterials(baseLayerPrefix);
PreReceiveAdditionalDeepClean(baseLayerPrefix);
}
/// <summary>
/// Method for adding app-specific additional deep clean of the document prior to receiving.
/// </summary>
protected virtual void PreReceiveAdditionalDeepClean(string baseLayerPrefix) { }
/// <summary>
/// Method for parsing and baking additional app-specific proxies on the root prior to converting and baking objects
/// </summary>
protected virtual void ParseAndBakeAdditionalProxies(Base rootObject, string baseLayerPrefix) { }
private IReadOnlyCollection<Entity> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix)
{
string layerName = _layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix);
var convertedEntities = new HashSet<Entity>();
using var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
// 1: convert
var converted = _converter.Convert(obj);
// 2: handle result
switch (converted)
{
case Entity entity:
var bakedEntity = BakeObject(entity, obj, layerName, tr);
convertedEntities.Add(bakedEntity);
break;
case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx/polycurve
var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix, tr);
convertedEntities.UnionWith(bakedFallbackEntities);
break;
default:
// TODO: capture defualt case with report object here? Same as in Rhino
break;
}
tr.Commit();
return convertedEntities.Freeze();
}
private Entity BakeObject(
Entity entity,
Base originalObject,
string layerName,
Transaction tr,
Base? parentObject = null
)
{
var objId = originalObject.applicationId ?? originalObject.id.NotNull();
if (_colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color))
{
entity.Color = color;
}
if (_materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId))
{
entity.MaterialId = matId;
}
entity.AppendToDb(layerName);
// Hook for derived classes to perform additional operations after entity is added to database
PostBakeEntity(entity, originalObject, tr);
return entity;
}
/// <summary>
/// Method for additional app-specific operations on entities after the entity has been added to the document database.
/// Called after the entity is added to the database in an open transaction
/// </summary>
/// <param name="entity"></param>
/// <param name="originalObject"></param>
/// <param name="tr"></param>
protected virtual void PostBakeEntity(Entity entity, Base originalObject, Transaction tr)
{
// Default implementation does nothing - override in derived classes
}
private List<Entity> BakeObjectsAsGroup(
List<(Entity, Base)> fallbackConversionResult,
Base parentObject,
string layerName,
string baseLayerName,
Transaction tr
)
{
var ids = new ObjectIdCollection();
var entities = new List<Entity>();
foreach (var (conversionResult, originalObject) in fallbackConversionResult)
{
BakeObject(conversionResult, originalObject, layerName, tr, parentObject);
ids.Add(conversionResult.ObjectId);
entities.Add(conversionResult);
}
if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups.
{
return entities;
}
var groupDictionary = (DBDictionary)
tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite);
var groupName = _autocadContext.RemoveInvalidChars(
$@"{parentObject.speckle_type.Split('.').Last()} - {parentObject.applicationId ?? parentObject.id} ({baseLayerName})"
);
var newGroup = new Group(groupName, true);
newGroup.Append(ids);
groupDictionary.UpgradeOpen();
groupDictionary.SetAt(groupName, newGroup);
tr.AddNewlyCreatedDBObject(newGroup, true);
return entities;
}
}
@@ -1,238 +1,35 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.Operations.Receive;
/// <summary>
/// <para>Expects to be a scoped dependency per receive operation.</para>
/// <para>AutoCAD-specific host object builder. Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public class AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
) : IHostObjectBuilder
public sealed class AutocadHostObjectBuilder : AutocadHostObjectBaseBuilder
{
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
public AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed.Report(new("Converting", null));
// Layer filter for received commit with project and model name
layerBaker.CreateLayerFilter(projectName, modelName);
// 0 - Clean then Rock n Roll!
string baseLayerPrefix = autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-");
PreReceiveDeepClean(baseLayerPrefix);
// 1 - Unpack objects and proxies from root commit object
var unpackedRoot = rootObjectUnpacker.Unpack(rootObject);
// 2 - Split atomic objects and instance components with their path
var (atomicObjects, instanceComponents) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
var atomicObjectsWithPath = layerBaker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = layerBaker.GetInstanceComponentsWithPath(instanceComponents);
// POC: these are not captured by traversal, so we need to re-add them here
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
{
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
(Array.Empty<Collection>(), proxy as IInstanceComponent)
);
instanceComponentsWithPath.AddRange(transformed);
}
// 3 - Bake materials and colors, as they are used later down the line by layers and objects
if (unpackedRoot.RenderMaterialProxies != null)
{
materialBaker.ParseAndBakeRenderMaterials(
unpackedRoot.RenderMaterialProxies,
baseLayerPrefix,
onOperationProgressed
);
}
if (unpackedRoot.ColorProxies != null)
{
colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
}
// 4 - Convert atomic objects
HashSet<ReceiveConversionResult> results = new();
HashSet<string> bakedObjectIds = new();
Dictionary<string, IReadOnlyCollection<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerPath, atomicObject) in atomicObjectsWithPath)
{
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
var ex = conversionHandler.TryConvert(() =>
{
cancellationToken.ThrowIfCancellationRequested();
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
applicationIdMap[objectId] = convertedObjects;
results.UnionWith(
convertedObjects.Select(e => new ReceiveConversionResult(
Status.SUCCESS,
atomicObject,
e.GetSpeckleApplicationId(),
e.GetType().ToString()
))
);
bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId()));
});
if (ex != null)
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
}
// 5 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = instanceBaker.BakeInstances(
instanceComponentsWithPath,
applicationIdMap,
baseLayerPrefix,
onOperationProgressed
);
bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id));
bakedObjectIds.UnionWith(createdInstanceIds);
results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.UnionWith(instanceConversionResults);
// 6 - Create groups
if (unpackedRoot.GroupProxies != null)
{
IReadOnlyCollection<ReceiveConversionResult> groupResults = groupBaker.CreateGroups(
unpackedRoot.GroupProxies,
applicationIdMap
);
results.UnionWith(groupResults);
}
return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results));
}
private void PreReceiveDeepClean(string baseLayerPrefix)
{
layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix);
instanceBaker.PurgeInstances(baseLayerPrefix);
materialBaker.PurgeMaterials(baseLayerPrefix);
}
private IReadOnlyCollection<Entity> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix)
{
string layerName = layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix);
var convertedEntities = new HashSet<Entity>();
using var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
// 1: convert
var converted = converter.Convert(obj);
// 2: handle result
switch (converted)
{
case Entity entity:
var bakedEntity = BakeObject(entity, obj, layerName);
convertedEntities.Add(bakedEntity);
break;
case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx/polycurve
var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix);
convertedEntities.UnionWith(bakedFallbackEntities);
break;
default:
// TODO: capture defualt case with report object here? Same as in Rhino
break;
}
tr.Commit();
return convertedEntities.Freeze();
}
private Entity BakeObject(Entity entity, Base originalObject, string layerName, Base? parentObject = null)
{
var objId = originalObject.applicationId ?? originalObject.id.NotNull();
if (colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color))
{
entity.Color = color;
}
if (materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId))
{
entity.MaterialId = matId;
}
entity.AppendToDb(layerName);
return entity;
}
private List<Entity> BakeObjectsAsGroup(
List<(Entity, Base)> fallbackConversionResult,
Base parentObject,
string layerName,
string baseLayerName
)
{
var ids = new ObjectIdCollection();
var entities = new List<Entity>();
foreach (var (conversionResult, originalObject) in fallbackConversionResult)
{
BakeObject(conversionResult, originalObject, layerName, parentObject);
ids.Add(conversionResult.ObjectId);
entities.Add(conversionResult);
}
if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups.
{
return entities;
}
var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.TopTransaction;
var groupDictionary = (DBDictionary)
tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite);
var groupName = autocadContext.RemoveInvalidChars(
$@"{parentObject.speckle_type.Split('.').Last()} - {parentObject.applicationId ?? parentObject.id} ({baseLayerName})"
);
var newGroup = new Group(groupName, true);
newGroup.Append(ids);
groupDictionary.UpgradeOpen();
groupDictionary.SetAt(groupName, newGroup);
tr.AddNewlyCreatedDBObject(newGroup, true);
return entities;
}
: base(
converter,
layerBaker,
groupBaker,
instanceBaker,
materialBaker,
colorBaker,
autocadContext,
rootObjectUnpacker,
conversionHandler
) { }
}
@@ -38,6 +38,7 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\EntityExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Extensions\SpeckleApplicationIdExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\TransactionContext.cs" />
<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\AutocadRootObjectBaseBuilder.cs" />
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -302,9 +302,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -343,38 +366,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -302,9 +302,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -343,38 +366,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -302,9 +302,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -343,38 +366,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -254,9 +254,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -295,36 +316,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -254,9 +254,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -295,36 +316,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.DependencyInjection;
using Speckle.Connectors.Autocad.Operations.Send;
using Speckle.Connectors.Civil3dShared.Bindings;
using Speckle.Connectors.Civil3dShared.HostApp;
using Speckle.Connectors.Civil3dShared.Operations.Receive;
using Speckle.Connectors.Civil3dShared.Operations.Send;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.DUI.Bindings;
@@ -24,10 +26,12 @@ public static class Civil3dConnectorModule
// add receive
serviceCollection.LoadReceive();
serviceCollection.AddScoped<IHostObjectBuilder, Civil3dHostObjectBuilder>();
serviceCollection.AddSingleton<IBinding, Civil3dReceiveBinding>();
// additional classes
serviceCollection.AddScoped<PropertySetDefinitionHandler>();
serviceCollection.AddScoped<PropertySetBaker>();
// automatically detects the Class:IClass interface pattern to register all generated interfaces
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
@@ -0,0 +1,404 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Operations;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Civil3dShared.Helpers;
using Speckle.Converters.Civil3dShared.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Models;
using AAEC = Autodesk.Aec;
using AAECPDB = Autodesk.Aec.PropertyData.DatabaseServices;
using ADB = Autodesk.AutoCAD.DatabaseServices;
namespace Speckle.Connectors.Civil3dShared.HostApp;
/// <summary>
/// Helper class to bake property sets to entities on receive.
/// </summary>
public class PropertySetBaker
{
private const string PROP_SET_DEF_DICT_NAME = "AecPropertySetDefs";
private readonly IConverterSettingsStore<Civil3dConversionSettings> _settingsStore;
private readonly ILogger<PropertySetBaker> _logger;
private readonly PropertyHandler _propertyHandler;
/// <summary>
/// Map of property set definition name to its ObjectId. Populated during ParsePropertySetDefinitions.
/// </summary>
private readonly Dictionary<string, ADB.ObjectId> _propertySetDefinitionMap = new();
public PropertySetBaker(
IConverterSettingsStore<Civil3dConversionSettings> settingsStore,
ILogger<PropertySetBaker> logger
)
{
_settingsStore = settingsStore;
_logger = logger;
_propertyHandler = new PropertyHandler();
}
/// <summary>
/// Removes all property set definitions with a prefix before receive operation.
/// </summary>
public void PurgePropertySets(string namePrefix)
{
ADB.Database db = _settingsStore.Current.Document.Database;
using var tr = db.TransactionManager.StartTransaction();
List<ADB.ObjectId> definitionsToDelete = new();
// Access the property set definition dictionary from the named object dictionary
var nod = (ADB.DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, ADB.OpenMode.ForRead);
if (nod.Contains(PROP_SET_DEF_DICT_NAME))
{
ADB.ObjectId propSetDefsDictId = nod.GetAt(PROP_SET_DEF_DICT_NAME);
var propSetDefsDict = (ADB.DBDictionary)tr.GetObject(propSetDefsDictId, ADB.OpenMode.ForRead);
// Iterate through all property set definitions in the dictionary
foreach (ADB.DBDictionaryEntry entry in propSetDefsDict)
{
if (entry.Key.Contains(namePrefix))
{
definitionsToDelete.Add(entry.Value);
}
}
}
// Delete the matching definitions
foreach (ADB.ObjectId defId in definitionsToDelete)
{
try
{
var propSetDef = (AAECPDB.PropertySetDefinition)tr.GetObject(defId, ADB.OpenMode.ForWrite);
propSetDef.Erase();
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(ex, "Failed to purge property set definition");
}
}
tr.Commit();
}
/// <summary>
/// Parse and bake all property set definitions from the root object.
/// Should be called after purging and after materials/colors are parsed.
/// </summary>
public void ParseAndBakePropertySetDefinitions(Base rootObject, string namePrefix)
{
_propertySetDefinitionMap.Clear();
if (rootObject[ProxyKeys.PROPERTYSET_DEFINITIONS] is not Dictionary<string, object?> definitions)
{
return;
}
if (definitions.Count == 0)
{
return;
}
using var tr = _settingsStore.Current.Document.Database.TransactionManager.StartTransaction();
foreach (var definition in definitions)
{
string setName = definition.Key;
object? setDefObj = definition.Value;
if (setDefObj is not Dictionary<string, object?> setDefData)
{
_logger.LogWarning("Property set definition {SetName} has invalid data format", setName);
continue;
}
if (!setDefData.TryGetValue(PropertySetDefinitionHandler.PROP_SET_PROP_DEFS_KEY, out var propDefsObj))
{
_logger.LogWarning("Property set definition {SetName} missing propertyDefinitions", setName);
continue;
}
if (propDefsObj is not Dictionary<string, object?> propertyDefinitions)
{
_logger.LogWarning("Property set definition {SetName} propertyDefinitions has invalid format", setName);
continue;
}
ADB.ObjectId defId = CreatePropertySetDefinition(setName, propertyDefinitions, namePrefix, tr);
if (!defId.IsNull)
{
_propertySetDefinitionMap[setName] = defId;
}
}
tr.Commit();
}
/// <summary>
/// Try to bake property sets from a Speckle object to a Civil3D entity.
/// </summary>
public bool TryBakePropertySets(ADB.Entity entity, Base sourceObject, ADB.Transaction tr)
{
if (
sourceObject["properties"] is not Dictionary<string, object?> properties
|| !properties.TryGetValue("Property Sets", out var propertySetsObj)
|| propertySetsObj is not Dictionary<string, object?> propertySets
|| propertySets.Count == 0
)
{
return false;
}
try
{
foreach (var propertySet in propertySets)
{
string setName = propertySet.Key;
object? setDataObj = propertySet.Value;
if (setDataObj is not Dictionary<string, object?> setData)
{
_logger.LogWarning("Property set {SetName} has invalid data format", setName);
continue;
}
if (!TryBakePropertySet(entity, setName, setData, tr))
{
_logger.LogWarning("Failed to bake property set {SetName} onto entity", setName);
}
}
return true;
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to bake property sets onto entity {Handle}", entity.Handle);
return false;
}
}
private bool TryBakePropertySet(
ADB.Entity entity,
string setName,
Dictionary<string, object?> setData,
ADB.Transaction tr
)
{
try
{
if (!_propertySetDefinitionMap.TryGetValue(setName, out ADB.ObjectId propertySetDefId))
{
_logger.LogWarning("Property set definition {SetName} not found in definition map", setName);
return false;
}
if (propertySetDefId.IsNull)
{
return false;
}
if (ObjectHasPropertySet(entity, propertySetDefId))
{
throw new SpeckleException($"Property set '{setName}' already exists on entity.");
}
return AddPropertySetToEntity(entity, propertySetDefId, setData, tr);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(ex, "Failed to process property set {SetName}", setName);
return false;
}
}
private ADB.ObjectId CreatePropertySetDefinition(
string setName,
Dictionary<string, object?> propertyDefinitions,
string namePrefix,
ADB.Transaction tr
)
{
var db = _settingsStore.Current.Document.Database;
using AAECPDB.DictionaryPropertySetDefinitions propSetDefs = new(db);
string prefixedName = $"{setName}-{namePrefix}";
AAECPDB.PropertySetDefinition propSetDef = new();
propSetDef.SetToStandard(db);
propSetDef.SubSetDatabaseDefaults(db);
//propSetDef.Description = "Property Set Definition added by Speckle"; // POC: should use the description that was published. can this back in if needed
propSetDef.AppliesToAll = true;
foreach (var propertyDefinition in propertyDefinitions)
{
string propertyName = propertyDefinition.Key;
object? propertyDefObj = propertyDefinition.Value;
if (propertyDefObj is not Dictionary<string, object?> propertyDefDict)
{
continue;
}
if (
!propertyDefDict.TryGetValue(PropertySetDefinitionHandler.PROP_DEF_TYPE_KEY, out var dataTypeStr)
|| dataTypeStr is not string dataTypeString
)
{
_logger.LogError(
"Property set definition {SetName} is invalid: property {PropertyName} missing or invalid dataType",
setName,
propertyName
);
return ADB.ObjectId.Null;
}
if (!Enum.TryParse(dataTypeString, out AAEC.PropertyData.DataType dataType))
{
_logger.LogError(
"Property set definition {SetName} is invalid: unsupported data type {DataType} for property {PropertyName}",
setName,
dataTypeString,
propertyName
);
return ADB.ObjectId.Null;
}
AAECPDB.PropertyDefinition propDef = new() { DataType = dataType, Name = propertyName };
propDef.SetToStandard(db);
propDef.SubSetDatabaseDefaults(db);
if (
propertyDefDict.TryGetValue(PropertySetDefinitionHandler.PROP_DEF_DEFAULT_VALUE_KEY, out object? defaultValue)
&& defaultValue != null
)
{
try
{
// Cast numeric types to avoid bad numeric value errors
var convertedValue = dataType switch
{
AAEC.PropertyData.DataType.Integer => (int)(long)defaultValue,
AAEC.PropertyData.DataType.AutoIncrement => (int)(long)defaultValue,
_ => defaultValue
};
propDef.DefaultData = convertedValue;
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(
ex,
"Failed to set default value for property {PropertyName}, continuing without default",
propertyName
);
}
}
propSetDef.Definitions.Add(propDef);
}
propSetDefs.AddNewRecord(prefixedName, propSetDef);
tr.AddNewlyCreatedDBObject(propSetDef, true);
return propSetDef.ObjectId;
}
private bool ObjectHasPropertySet(ADB.DBObject obj, ADB.ObjectId propertySetId)
{
try
{
ADB.ObjectId tempId = AAECPDB.PropertyDataServices.GetPropertySet(obj, propertySetId);
return !tempId.IsNull;
}
catch (Autodesk.AutoCAD.Runtime.Exception ex) when (!ex.IsFatal())
{
return false;
}
}
private bool AddPropertySetToEntity(
ADB.Entity entity,
ADB.ObjectId propertySetDefId,
Dictionary<string, object?> setData,
ADB.Transaction tr
)
{
try
{
if (!entity.IsWriteEnabled)
{
entity.UpgradeOpen();
}
AAECPDB.PropertyDataServices.AddPropertySet(entity, propertySetDefId);
return TrySetPropertyValues(entity, propertySetDefId, setData, tr);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(ex, "Failed to add property set to entity");
return false;
}
}
private bool TrySetPropertyValues(
ADB.Entity entity,
ADB.ObjectId propertySetDefId,
Dictionary<string, object?> setData,
ADB.Transaction tr
)
{
try
{
ADB.ObjectId propertySetId = AAECPDB.PropertyDataServices.GetPropertySet(entity, propertySetDefId);
var propertySet = (AAECPDB.PropertySet)tr.GetObject(propertySetId, ADB.OpenMode.ForWrite);
var setDefinition = (AAECPDB.PropertySetDefinition)tr.GetObject(propertySetDefId, ADB.OpenMode.ForRead);
// Build a map of property names to definition IDs
Dictionary<string, int> propertyNameToId = new();
foreach (AAECPDB.PropertyDefinition propDef in setDefinition.Definitions)
{
propertyNameToId[propDef.Name] = propDef.Id;
}
foreach (var propertyEntry in setData)
{
string propertyName = propertyEntry.Key;
object? propertyDataObj = propertyEntry.Value;
if (propertyDataObj is not Dictionary<string, object?> propertyDataDict)
{
continue;
}
if (!propertyDataDict.TryGetValue("value", out var value) || value == null)
{
continue;
}
if (!propertyNameToId.TryGetValue(propertyName, out int propertyId))
{
continue;
}
_propertyHandler.TryGetValue(
() =>
{
propertySet.SetAt(propertyId, value);
return true;
},
out _
);
}
return true;
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogWarning(ex, "Failed to update property set values");
return false;
}
}
}
@@ -0,0 +1,60 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Receive;
using Speckle.Connectors.Civil3dShared.HostApp;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.Civil3dShared.Operations.Receive;
/// <summary>
/// <para>Civil3D specific host object builder with property set support. Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public sealed class Civil3dHostObjectBuilder : AutocadHostObjectBaseBuilder
{
private readonly PropertySetBaker _propertySetBaker;
public Civil3dHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler,
PropertySetBaker propertySetBaker
)
: base(
converter,
layerBaker,
groupBaker,
instanceBaker,
materialBaker,
colorBaker,
autocadContext,
rootObjectUnpacker,
conversionHandler
)
{
_propertySetBaker = propertySetBaker;
}
protected override void PreReceiveAdditionalDeepClean(string baseLayerPrefix)
{
_propertySetBaker.PurgePropertySets(baseLayerPrefix);
}
protected override void ParseAndBakeAdditionalProxies(Base rootObject, string baseLayerPrefix)
{
_propertySetBaker.ParseAndBakePropertySetDefinitions(rootObject, baseLayerPrefix);
}
protected override void PostBakeEntity(Entity entity, Base originalObject, Transaction tr)
{
_propertySetBaker.TryBakePropertySets(entity, originalObject, tr);
}
}
@@ -11,11 +11,15 @@
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dReceiveBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\Civil3dConnectorModule.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\PropertySetBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\Civil3dHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dSendBinding.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)DependencyInjection\" />
<Folder Include="$(MSBuildThisFileDirectory)HostApp\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Receive\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\" />
</ItemGroup>
</Project>
@@ -11,7 +11,7 @@ namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// Currently, all material property extraction can happen on a CsiShared level which simplifies things a lot.
/// Properties depend on the directional symmetry of the material, hence the switch statements.
/// </remarks>
public class CsiMaterialPropertyExtractor
public class CsiMaterialPropertyExtractor : IMaterialPropertyExtractor
{
/// <summary>
/// Property strings for all mechanical properties, used by numerous methods.
@@ -35,11 +35,11 @@ public class CsiMaterialPropertyExtractor
_settingsStore = settingsStore;
}
public void ExtractProperties(string materialName, Dictionary<string, object?> properties)
public void ExtractProperties(string name, Dictionary<string, object?> properties)
{
GetGeneralProperties(materialName, properties);
GetWeightAndMassProperties(materialName, properties); // TODO: Add units
GetMechanicalProperties(materialName, properties); // TODO: Add units
GetGeneralProperties(name, properties);
GetWeightAndMassProperties(name, properties);
GetMechanicalProperties(name, properties);
}
private void GetGeneralProperties(string materialName, Dictionary<string, object?> properties)
@@ -76,7 +76,7 @@ public class CsiMaterialPropertyExtractor
ref massPerUnitVolume
);
var weightAndMass = properties.EnsureNested("Weight and Mass");
var weightAndMass = properties.EnsureNested(SectionPropertyCategory.WEIGHT_AND_MASS);
weightAndMass["Weight per Unit Volume"] = weightPerUnitVolume;
weightAndMass["Mass per Unit Volume"] = massPerUnitVolume;
}
@@ -101,7 +101,7 @@ public class CsiMaterialPropertyExtractor
_ => throw new ArgumentException($"Unknown symmetry type: {materialDirectionalSymmetryKey}")
};
var mechanicalProperties = properties.EnsureNested("Mechanical Properties");
var mechanicalProperties = properties.EnsureNested(SectionPropertyCategory.MECHANICAL_DATA);
mechanicalProperties["Directional Symmetry Type"] = materialDirectionalSymmetryValue.ToString();
GetMechanicalPropertiesByType(materialName, materialDirectionalSymmetryValue, mechanicalProperties);
@@ -0,0 +1,13 @@
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Contract for host application specific material property extraction.
/// </summary>
/// <remarks>
/// Mirrors property extraction system pattern by composing with base extractor.
/// Enables both shared and application-specific property extraction in one call.
/// </remarks>
public interface IApplicationMaterialPropertyExtractor
{
void ExtractProperties(string name, Dictionary<string, object?> properties);
}
@@ -0,0 +1,9 @@
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
/// <summary>
/// Core contract for material property extraction common across CSi products.
/// </summary>
public interface IMaterialPropertyExtractor
{
void ExtractProperties(string name, Dictionary<string, object?> properties);
}
@@ -0,0 +1,10 @@
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
// NOTE: Interface because Etabs and Sap2000 material unpacking and extraction is different.
// At ServiceRegistration, we inject the correct implementation of the IMaterialUnpacker
public interface IMaterialUnpacker
{
IEnumerable<IProxyCollection> UnpackMaterials();
}
@@ -1,51 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.CSiShared.HostApp;
/// <summary>
/// Creates material proxies based on stored entries from the materials cache
/// </summary>
public class MaterialUnpacker
{
private readonly CsiMaterialPropertyExtractor _propertyExtractor;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
public MaterialUnpacker(
CsiMaterialPropertyExtractor propertyExtractor,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
)
{
_propertyExtractor = propertyExtractor;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
// Creates a list of material proxies from the csi materials cache
public IEnumerable<IProxyCollection> UnpackMaterials()
{
foreach (var kvp in _csiToSpeckleCacheSingleton.MaterialCache)
{
// get the cached entry
string materialName = kvp.Key;
List<string> sectionIds = kvp.Value;
// get the properties of the material
Dictionary<string, object?> properties = new(); // create empty dictionary
_propertyExtractor.ExtractProperties(materialName, properties); // dictionary mutated with respective properties
// create the material proxy
GroupProxy materialProxy =
new()
{
id = materialName,
name = materialName,
applicationId = materialName,
objects = sectionIds,
["properties"] = properties
};
yield return materialProxy;
}
}
}
@@ -34,7 +34,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
private readonly IRootToSpeckleConverter _rootToSpeckleConverter;
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettings;
private readonly CsiSendCollectionManager _sendCollectionManager;
private readonly MaterialUnpacker _materialUnpacker;
private readonly IMaterialUnpacker _materialUnpacker;
private readonly ISectionUnpacker _sectionUnpacker;
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
@@ -45,7 +45,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
IRootToSpeckleConverter rootToSpeckleConverter,
IConverterSettingsStore<CsiConversionSettings> converterSettings,
CsiSendCollectionManager sendCollectionManager,
MaterialUnpacker materialUnpacker,
IMaterialUnpacker materialUnpacker,
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
@@ -83,8 +83,16 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
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 };
new()
{
name = modelFileName,
["units"] = _converterSettings.Current.SpeckleUnits,
["forceUnits"] = forceUnit,
["temperatureUnits"] = tempUnit
};
List<SendConversionResult> results = new(csiObjects.Count);
int count = 0;
@@ -217,4 +225,20 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
group => group.Key, // ModelObjectType (FRAME, JOINT, etc.)
group => group.Select(obj => obj.Name).ToList() // Extract Name from each ICsiWrapper and convert to List<string>
);
/// <summary>
/// Instantiates a Base object and pre-populates it with the models defined force units.
/// </summary>
/// <returns></returns>
/// <exception cref="SpeckleException"></exception>
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());
}
}
@@ -47,7 +47,7 @@ public static class ServiceRegistration
services.AddScoped<CsiMaterialPropertyExtractor>();
services.AddScoped<CsiResultsExtractorFactory>();
services.AddScoped<MaterialUnpacker>();
services.AddScoped<IMaterialPropertyExtractor, CsiMaterialPropertyExtractor>();
services.AddScoped<IFrameSectionPropertyExtractor, CsiFrameSectionPropertyExtractor>();
services.AddScoped<IShellSectionPropertyExtractor, CsiShellSectionPropertyExtractor>();
services.AddScoped<AnalysisResultsExtractor>();
@@ -19,7 +19,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiResultsExtractorFactory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IApplicationMaterialPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IMaterialPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IMaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiFrameSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiMaterialPropertyExtractor.cs" />
@@ -31,8 +31,8 @@ public class AnalysisResultsExtractor
Dictionary<ModelObjectType, List<string>> objectSelectionSummary
)
{
// Step 1: get analysis units
var analysisResults = CreateAnalysisResultsWithUnits();
// Step 1: create base object that will hold analysis results
var analysisResults = new Base();
// Step 2: configure and validate load cases
ConfigureAndValidateSelectedLoadCases(selectedCasesAndCombinations);
@@ -43,36 +43,6 @@ public class AnalysisResultsExtractor
return analysisResults;
}
/// <summary>
/// Instantiates a Base object and pre-populates it with the models defined force units.
/// </summary>
/// <returns></returns>
/// <exception cref="SpeckleException"></exception>
private Base CreateAnalysisResultsWithUnits()
{
var forceUnit = eForce.NotApplicable;
var lengthUnit = eLength.NotApplicable;
var temperatureUnit = eTemperature.NotApplicable;
int success = _converterSettingsStore.Current.SapModel.GetDatabaseUnits_2(
ref forceUnit,
ref lengthUnit,
ref temperatureUnit
);
if (success != 0)
{
throw new SpeckleException("Failed to retrieve units for analysis results");
}
return new Base
{
["forceUnit"] = forceUnit.ToString(),
["lengthUnit"] = lengthUnit.ToString(),
["temperatureUnit"] = temperatureUnit.ToString()
};
}
private void ExtractResults(
List<string> requestedResultTypes,
Dictionary<ModelObjectType, List<string>> objectSelectionSummary,
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.etabs21": {
@@ -294,6 +294,29 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -332,38 +355,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -236,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.etabs22": {
@@ -245,6 +245,27 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -283,36 +304,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -0,0 +1,50 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Sdk.Models.Proxies;
namespace Speckle.Connectors.ETABSShared.HostApp;
public class EtabsMaterialUnpacker : IMaterialUnpacker
{
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
private readonly IMaterialPropertyExtractor _csiMaterialPropertyExtractor;
private readonly IApplicationMaterialPropertyExtractor _etabsMaterialPropertyExtractor;
public EtabsMaterialUnpacker(
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton,
IMaterialPropertyExtractor csiMaterialPropertyExtractor,
IApplicationMaterialPropertyExtractor etabsMaterialPropertyExtractor
)
{
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
_csiMaterialPropertyExtractor = csiMaterialPropertyExtractor;
_etabsMaterialPropertyExtractor = etabsMaterialPropertyExtractor;
}
public IEnumerable<IProxyCollection> UnpackMaterials()
{
foreach (var kvp in _csiToSpeckleCacheSingleton.MaterialCache)
{
string name = kvp.Key;
var sectionIds = kvp.Value;
// get the properties of the material
Dictionary<string, object?> properties = [];
_csiMaterialPropertyExtractor.ExtractProperties(name, properties);
_etabsMaterialPropertyExtractor.ExtractProperties(name, properties);
// create the material proxy
GroupProxy materialProxy =
new()
{
id = name,
name = name,
applicationId = name,
objects = sectionIds,
["properties"] = properties
};
yield return materialProxy;
}
}
}
@@ -0,0 +1,219 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.ETABS22.HostApp.Helpers;
public class EtabsMaterialPropertyExtractor : IApplicationMaterialPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly Dictionary<int, string?> _ssTypeDict =
new()
{
{ 0, "User defined" },
{ 1, "Parametric - Simple" },
{ 2, "Parametric - Mander" }
};
private readonly Dictionary<int, string?> _ssHysTypeDict =
new()
{
{ 0, "Elastic" },
{ 1, "Kinematic" },
{ 2, "Takeda" },
{ 3, "Pivot" },
{ 4, "Concrete" },
{ 5, "BRB Hardening" },
{ 6, "Degrading" },
{ 7, "Isotropic" }
};
private const int TEMP = 0;
public EtabsMaterialPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
}
public void ExtractProperties(string name, Dictionary<string, object?> properties)
{
// we want to get some of the "other" material property data that is type specific
// csi extractor populates "type" string, but api query arguably simpler and more reliable than dict string access
int symType = 0;
eMatType matType = 0;
_settingsStore.Current.SapModel.PropMaterial.GetTypeOAPI(name, ref matType, ref symType);
// we don't have design data api queries for these, so early return to avoid creating that specific dictionary
if (matType is eMatType.NoDesign or eMatType.Aluminum or eMatType.ColdFormed or eMatType.Masonry)
{
return;
}
// ensure design data specific properties dictionary that will be mutated in switch expression
var designData = properties.EnsureNested(SectionPropertyCategory.DESIGN_DATA);
// can't do a switch expression here because not all enums have an api query (e.g. masonry, aluminium)
switch (matType)
{
case eMatType.Steel:
ExtractSteelProperties(name, designData);
break;
case eMatType.Concrete:
ExtractConcreteProperties(name, designData);
break;
case eMatType.Rebar:
ExtractRebarProperties(name, designData);
break;
case eMatType.Tendon:
ExtractTendonProperties(name, designData);
break;
}
}
private void ExtractSteelProperties(string name, Dictionary<string, object?> designData)
{
// step 1: stubs for api query
int ssType = 0,
ssHysType = 0;
double fy = 0,
fu = 0,
eFy = 0,
eFu = 0,
strainAtHardening = 0,
strainAtMaxStress = 0,
strainAtRupture = 0;
// step 2: api query
// NOTE: using the "older" method. Not sure if _1 is unsupported in etabs 21
// also, _1 doesn't give a lot more MEANINGFUL data
_settingsStore.Current.SapModel.PropMaterial.GetOSteel(
name,
ref fy,
ref fu,
ref eFy,
ref eFu,
ref ssType,
ref ssHysType,
ref strainAtHardening,
ref strainAtMaxStress,
ref strainAtRupture
);
// step 3: mutate properties dictionary
designData["Fy"] = fy;
designData["Fu"] = fu;
designData["EFy"] = eFy;
designData["EFu"] = eFu;
designData["SSType"] = _ssTypeDict.TryGetValue(ssType, out string? ssTypeValue) ? ssTypeValue : "";
designData["SSHysType"] = _ssHysTypeDict.TryGetValue(ssHysType, out string? ssHysTypeValue) ? ssHysTypeValue : "";
designData["StrainAtHardening"] = strainAtHardening;
designData["StrainAtMaxStress"] = strainAtMaxStress;
designData["StrainAtRupture"] = strainAtRupture;
designData["Temp"] = TEMP;
}
private void ExtractConcreteProperties(string name, Dictionary<string, object?> designData)
{
// step 1: stubs for api query
int ssType = 0,
ssHysType = 0;
bool isLightweight = false;
double fc = 0,
fcsFactor = 0,
strainAtFc = 0,
strainUltimate = 0,
frictionAngle = 0,
dilatationalAngle = 0;
// step 2: api query
// NOTE: using the "older" method. Not sure if _1 or _2 are unsupported in etabs 21
// also, _1 or _2 doesn't give a lot more MEANINGFUL data
_settingsStore.Current.SapModel.PropMaterial.GetOConcrete(
name,
ref fc,
ref isLightweight,
ref fcsFactor,
ref ssType,
ref ssHysType,
ref strainAtFc,
ref strainUltimate,
ref frictionAngle,
ref dilatationalAngle
);
// step 3: mutate properties dictionary
designData["Fc"] = fc;
designData["FcsFactor"] = fcsFactor;
designData["StrainAtFc"] = strainAtFc;
designData["StrainUltimate"] = strainUltimate;
designData["FrictionAngle"] = frictionAngle;
designData["DilatationalAngle"] = dilatationalAngle;
designData["IsLightweight"] = isLightweight.ToString();
designData["SSType"] = _ssTypeDict.TryGetValue(ssType, out string? ssTypeValue) ? ssTypeValue : "";
designData["SSHysType"] = _ssHysTypeDict.TryGetValue(ssHysType, out string? ssHysTypeValue) ? ssHysTypeValue : "";
designData["Temp"] = TEMP;
}
private void ExtractRebarProperties(string name, Dictionary<string, object?> designData)
{
// step 1: stubs for api query
bool useCaltransSsDefaults = false;
int ssType = 0,
ssHysType = 0;
double fy = 0,
fu = 0,
eFy = 0,
eFu = 0,
strainAtHardening = 0,
strainUltimate = 0;
// step 2: api query
// NOTE: using the "older" method. Not sure if _1 is unsupported in etabs 21
// also, _1 doesn't give a lot more MEANINGFUL data
_settingsStore.Current.SapModel.PropMaterial.GetORebar(
name,
ref fy,
ref fu,
ref eFy,
ref eFu,
ref ssType,
ref ssHysType,
ref strainAtHardening,
ref strainUltimate,
ref useCaltransSsDefaults
);
// step 3: mutate properties dictionary
designData["Fy"] = fy;
designData["Fu"] = fu;
designData["EFy"] = eFy;
designData["EFu"] = eFu;
designData["StrainAtHardening"] = strainAtHardening;
designData["StrainUltimate"] = strainUltimate;
designData["SSType"] = _ssTypeDict.TryGetValue(ssType, out string? ssTypeValue) ? ssTypeValue : "";
designData["SSHysType"] = _ssHysTypeDict.TryGetValue(ssHysType, out string? ssHysTypeValue) ? ssHysTypeValue : "";
designData["UseCaltransSsDefaults"] = useCaltransSsDefaults.ToString();
designData["Temp"] = TEMP;
}
private void ExtractTendonProperties(string name, Dictionary<string, object?> designData)
{
// step 1: stubs for api query
int ssType = 0,
ssHysType = 0;
double fy = 0,
fu = 0;
// step 2: api query
_settingsStore.Current.SapModel.PropMaterial.GetOTendon(name, ref fy, ref fu, ref ssType, ref ssHysType);
// step 3: mutate properties dictionary
designData["Fy"] = fy;
designData["Fu"] = fu;
designData["SSType"] = _ssTypeDict.TryGetValue(ssType, out string? ssTypeValue) ? ssTypeValue : "";
designData["SSHysType"] = _ssHysTypeDict.TryGetValue(ssHysType, out string? ssHysTypeValue) ? ssHysTypeValue : "";
designData["Temp"] = TEMP;
}
}
@@ -1,8 +1,7 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Converters.ETABSShared.ToSpeckle.Helpers;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
@@ -11,54 +10,55 @@ namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// </summary>
public class EtabsShellSectionPropertyExtractor : IApplicationShellSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly ILogger<EtabsShellSectionPropertyExtractor> _logger;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
private readonly EtabsShellSectionResolver _etabsShellSectionResolver;
public EtabsShellSectionPropertyExtractor(
IConverterSettingsStore<CsiConversionSettings> settingsStore,
ILogger<EtabsShellSectionPropertyExtractor> logger,
EtabsShellSectionResolver etabsShellSectionResolver
EtabsShellSectionResolver etabsShellSectionResolver,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
)
{
_settingsStore = settingsStore;
_logger = logger;
_etabsShellSectionResolver = etabsShellSectionResolver;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
/// <summary>
/// Extract shell section properties
/// Extract shell section properties from cache.
/// </summary>
/// <remarks>
/// sectionName is unique across all types (Wall, Slab and Deck)
/// There is no general query such as PropArea.GetShell() - rather we have to be specific on the type, for example
/// PropArea.GetWall() or PropArea.GetDeck() BUT we can't get the building type given a SectionName.
/// Hence the introduction of ResolveSection.
/// By the time this method is called during section unpacking, all sections should already be
/// resolved and cached by <see cref="EtabsShellPropertiesExtractor"/> during object conversion.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
// Step 01: Finding the appropriate api query for the unknown section type (wall, deck or slab)
Dictionary<string, object?> resolvedProperties = _etabsShellSectionResolver.ResolveSection(sectionName);
var sectionProps = GetSectionProperties(sectionName);
// Step 02: Mutate properties dictionary with resolved properties
foreach (var nestedDictionary in resolvedProperties)
// shallow copy nested dictionaries into provided properties dict to mutate it (required by interface contract)
foreach (var kvp in sectionProps)
{
if (nestedDictionary.Value is not Dictionary<string, object?> nestedValues)
{
_logger.LogWarning(
"Unexpected value type for key {Key} in section {SectionName}. Expected Dictionary<string, object?>, got {ActualType}",
nestedDictionary.Key,
sectionName,
nestedDictionary.Value?.GetType().Name ?? "null"
);
continue;
}
var nestedProperties = properties.EnsureNested(nestedDictionary.Key);
foreach (var kvp in nestedValues)
{
nestedProperties[kvp.Key] = kvp.Value;
}
properties[kvp.Key] = kvp.Value;
}
}
private Dictionary<string, object?> GetSectionProperties(string sectionName)
{
// return cached properties directly
if (_csiToSpeckleCacheSingleton.ShellSectionPropertiesCache.TryGetValue(sectionName, out var cachedProperties))
{
return cachedProperties;
}
// fallback - shouldn't happen because cached populated on the fly as sections appear in the extractor
_logger.LogWarning(
"Section {SectionName} not in cache during unpacking - resolving via API (expensive)",
sectionName
);
var resolved = _etabsShellSectionResolver.ResolveSection(sectionName);
_csiToSpeckleCacheSingleton.ShellSectionPropertiesCache[sectionName] = resolved;
return resolved;
}
}
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABS22.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp;
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Services;
@@ -14,12 +15,17 @@ public static class ServiceRegistration
{
services.AddEtabsConverters();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<EtabsSectionPropertyDefinitionService>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
// material services
services.AddScoped<IMaterialUnpacker, EtabsMaterialUnpacker>();
services.AddScoped<IApplicationMaterialPropertyExtractor, EtabsMaterialPropertyExtractor>();
// section services
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
services.AddScoped<EtabsSectionPropertyDefinitionService>();
services.AddScoped<EtabsSectionPropertyExtractor>();
return services;
}
@@ -9,13 +9,14 @@
<Import_RootNamespace>Speckle.Connectors.ETABSShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsMaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsFrameSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsMaterialPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyDefinitionService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionResolver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\EtabsPluginBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\EtabsSpeckleFormBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2020": {
@@ -296,6 +296,29 @@
"Speckle.Navisworks.API": "[2020.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2021": {
@@ -296,6 +296,29 @@
"Speckle.Navisworks.API": "[2021.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2022": {
@@ -296,6 +296,29 @@
"Speckle.Navisworks.API": "[2022.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2023": {
@@ -296,6 +296,29 @@
"Speckle.Navisworks.API": "[2023.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2024": {
@@ -296,6 +296,29 @@
"Speckle.Navisworks.API": "[2024.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -265,9 +265,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2025": {
@@ -302,6 +302,29 @@
"Speckle.Navisworks.API": "[2025.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -334,38 +357,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -266,9 +266,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.navisworks2026": {
@@ -304,6 +304,29 @@
"System.Reactive": "[6.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -336,38 +359,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -6,6 +6,7 @@ using Speckle.Connector.Navisworks.DependencyInjection;
using Speckle.Connector.Navisworks.HostApp;
using Speckle.Connector.Navisworks.Plugin.Tools;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.WebView;
using Speckle.Converter.Navisworks.DependencyInjection;
@@ -46,6 +47,8 @@ internal sealed class Connector : NAV.Plugins.DockPanePlugin
Container = services.BuildServiceProvider();
Container.UseDUI();
Container.GetRequiredService<NavisworksDocumentEvents>();
Container.GetRequiredService<ISerializationOptions>().SkipCacheRead = true;
Container.GetRequiredService<ISerializationOptions>().SkipCacheWrite = true;
var u = Container.GetRequiredService<DUI3ControlWebView>();
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.revit2022": {
@@ -316,6 +316,29 @@
"Speckle.Revit.API": "[2022.0.2.1, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -349,43 +372,11 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2022.0.2.1",
"contentHash": "IrLN4WyI2ix+g3zCpo7sX8zNB3FrtrdQ3E2RpceGVPNG00v8OfD+Kei7o1bn1u/ML46iBYRAr/JcsLbwfUQsBw=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.revit2023": {
@@ -316,6 +316,29 @@
"Speckle.Revit.API": "[2023.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -349,43 +372,11 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2023.0.0",
"contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.revit2024": {
@@ -316,6 +316,29 @@
"Speckle.Revit.API": "[2024.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -349,43 +372,11 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2024.0.0",
"contentHash": "a4dsvZ00ocvzTgCD6dUdydf0jIZDVcDhs6dUX9cv+y3aTDbU8rmzhYXWt8sThedIG+IPSVa0vHmAH9pKiJL3SQ=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -226,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -251,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.revit2025": {
@@ -261,6 +261,27 @@
"Speckle.Revit.API": "[2025.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -294,41 +315,11 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2025.0.0",
"contentHash": "Hwf/3Ydc7KxvjgD9pSZKLSJRsFTsxYg95YyTm6f43hcsGjmk49GsLFQt921Z9OcvUVewOggQHcmBgti+P2EPHw=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.revit2026": {
@@ -254,6 +254,27 @@
"Speckle.Revit.API": "[2026.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -278,41 +299,11 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
"type": "CentralTransitive",
"requested": "[2023.0.0, )",
"resolved": "2026.0.0",
"contentHash": "SiqqKbF1pXyZWXZhAl2JhjYhTt7RiYO5JaQrAjq+OlleAjT4zatwAp/DnTwQspFbP7UZr3b2Ed2kuWNN0ZFelw=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,3 +1,4 @@
using System.Runtime.InteropServices;
using System.Windows.Controls;
using System.Windows.Threading;
using Autodesk.Revit.UI;
@@ -31,6 +32,10 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
DispatcherPriority.Background
);
}
catch (SEHException)
{
//do nothing as we can't control external components
}
catch (OperationCanceledException)
{
//do nothing, happens when closing Revit while a script is being executed
@@ -106,7 +106,7 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
if (senderModelCard.SendFilter is RevitViewsFilter revitViewsFilter)
{
var view = revitViewsFilter.GetView();
var view = revitViewsFilter.GetView(activeUIDoc.Document);
if (view is not null)
{
await _revitTask
@@ -29,9 +29,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly ISendConversionCache _sendConversionCache;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
private readonly ElementUnpacker _elementUnpacker;
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly LinkedModelHandler _linkedModelHandler;
private readonly IThreadContext _threadContext;
@@ -55,6 +57,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
ToSpeckleSettingsManager toSpeckleSettingsManager,
ElementUnpacker elementUnpacker,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
ITopLevelExceptionHandler topLevelExceptionHandler,
LinkedModelHandler linkedModelHandler,
IThreadContext threadContext,
@@ -71,6 +74,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_toSpeckleSettingsManager = toSpeckleSettingsManager;
_elementUnpacker = elementUnpacker;
_revitConversionSettingsFactory = revitConversionSettingsFactory;
_revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton;
_topLevelExceptionHandler = topLevelExceptionHandler;
_linkedModelHandler = linkedModelHandler;
_threadContext = threadContext;
@@ -110,6 +114,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public async Task Send(string modelCardId)
{
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (document == null)
{
throw new SpeckleException("No document is active for sending.");
}
using var manager = _sendOperationManagerFactory.Create();
await manager.Process<DocumentToConvert>(
@@ -120,24 +129,20 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
_toSpeckleSettingsManager.GetDetailLevelSetting(card),
_toSpeckleSettingsManager.GetReferencePointSetting(card),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(card),
_toSpeckleSettingsManager.GetLinkedModelsSetting(card),
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(card)
_toSpeckleSettingsManager.GetDetailLevelSetting(document, card),
_toSpeckleSettingsManager.GetReferencePointSetting(document, card),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(document, card),
_toSpeckleSettingsManager.GetLinkedModelsSetting(document, card),
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(document, card)
)
);
},
async x => await RefreshElementsIdsOnSender(x.NotNull())
async x => await RefreshElementsIdsOnSender(document, x.NotNull())
);
}
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(Document document, SenderModelCard modelCard)
{
var activeUIDoc =
_revitContext.UIApplication?.ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
{
viewFilter.SetContext(_revitContext);
@@ -147,10 +152,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
() => Task.FromResult(modelCard.SendFilter.NotNull().RefreshObjectIds())
);
var allElements = selectedObjects
.Select(uid => activeUIDoc.Document.GetElement(uid))
.Where(el => el is not null)
.ToList();
var allElements = selectedObjects.Select(uid => document.GetElement(uid)).Where(el => el is not null).ToList();
// split elements between main model and linked models
var elementsOnMainModel = allElements.Where(el => el is not RevitLinkInstance).ToList();
@@ -158,14 +160,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
// should ideally reuse the initialized value from the scoped IConverterSettingsStore<RevitConversionSettings>.
// but, it's scoped and to avoid bigger scarier changes I'm re-fetching the setting here (inexpensive operation?)
Transform? mainModelTransform = _toSpeckleSettingsManager.GetReferencePointSetting(modelCard);
List<DocumentToConvert> documentElementContexts =
[
new(mainModelTransform, activeUIDoc.Document, elementsOnMainModel)
];
Transform? mainModelTransform = _toSpeckleSettingsManager.GetReferencePointSetting(document, modelCard);
List<DocumentToConvert> documentElementContexts = [new(mainModelTransform, document, elementsOnMainModel)];
// get the linked models setting - this decision belongs at this level
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard);
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(document, modelCard);
// ⚠️ process linked models - RevitSendBinding controls the flow based on settings!
// If setting not enabled, we won't unpack (see if-else block)
@@ -192,7 +191,12 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
if (includeLinkedModels)
{
// handler is only responsible for element collection mechanics
var linkedElements = _linkedModelHandler.GetLinkedModelElements(modelCard.SendFilter, linkedDoc, transform);
var linkedElements = _linkedModelHandler.GetLinkedModelElements(
document,
modelCard.SendFilter,
linkedDoc,
transform
);
linkedDocumentContexts.Add(new(transform, linkedDoc, linkedElements));
}
// ⚠️ when disabled, still adds empty contexts to maintain warning generation in RevitRootObjectBuilder
@@ -332,9 +336,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private async Task PostSetObjectIds()
{
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (document == null)
{
return;
}
foreach (var sender in _store.GetSenders().ToList())
{
await RefreshElementsIdsOnSender(sender);
await RefreshElementsIdsOnSender(document, sender);
}
}
@@ -444,6 +453,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private async Task OnDocumentChanged()
{
_sendConversionCache.ClearCache();
_revitToSpeckleCacheSingleton.ClearCache();
if (_cancellationManager.NumberOfOperations > 0)
{
@@ -34,18 +34,22 @@ public class LevelUnpacker
Dictionary<string, LevelProxy> levelProxies = new();
foreach (var element in elements)
{
if (levelProxies.TryGetValue(element.LevelId.ToString(), out LevelProxy? levelProxy))
// NOTE: Use level.UniqueId (not element.LevelId) as key
// face-based instances don't have a valid element.LevelId, hence all the changes in the LevelExtractor
var level = _levelExtractor.GetLevel(element);
if (level is null)
{
continue;
}
string levelKey = level.UniqueId;
if (levelProxies.TryGetValue(levelKey, out LevelProxy? levelProxy))
{
levelProxy.objects.Add(element.UniqueId);
}
else
{
var level = _levelExtractor.GetLevel(element);
if (level is null)
{
continue;
}
var levelDataObject = new DataObject()
{
name = level.Name,
@@ -53,11 +57,11 @@ public class LevelUnpacker
properties = _propertiesExtractor.GetProperties(level)
};
var unitSettings = _converterSettings.Current.Document.GetUnits();
var lengthUnitType = unitSettings.GetFormatOptions(Autodesk.Revit.DB.SpecTypeId.Length).GetUnitTypeId();
var lengthUnitType = unitSettings.GetFormatOptions(SpecTypeId.Length).GetUnitTypeId();
levelDataObject["elevation"] = UnitUtils.ConvertFromInternalUnits(level.Elevation, lengthUnitType);
levelDataObject["units"] = _converterSettings.Current.SpeckleUnits;
levelProxies[element.LevelId.ToString()] = new LevelProxy()
levelProxies[levelKey] = new LevelProxy()
{
applicationId = level.UniqueId,
objects = [element.UniqueId],
@@ -3,7 +3,6 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.RevitShared;
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
@@ -17,20 +16,19 @@ namespace Speckle.Connectors.Revit.HostApp;
/// </summary>
public class LinkedModelHandler
{
private readonly RevitContext _revitContext;
public Dictionary<string, string> LinkedModelDisplayNames { get; } = new();
public LinkedModelHandler(RevitContext revitContext)
{
_revitContext = revitContext;
}
/// <summary>
/// Gets elements from a linked document based on the provided send filter.
/// This method handles the specifics of element collection but doesn't make decisions
/// about whether the linked model should be processed - that's the caller's responsibility.
/// </summary>
public List<Element> GetLinkedModelElements(ISendFilter sendFilter, Document linkedDocument, Transform? transform)
public List<Element> GetLinkedModelElements(
Document currentDocument,
ISendFilter sendFilter,
Document linkedDocument,
Transform? transform
)
{
// send mode → Categories
if (sendFilter is RevitCategoriesFilter categoryFilter && categoryFilter.SelectedCategories is not null)
@@ -48,19 +46,15 @@ public class LinkedModelHandler
}
// send mode → Views (taken from the legacy code)
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView() != null)
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView(currentDocument) != null)
{
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(
linkedDocument.PathName,
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document,
transform
);
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(linkedDocument.PathName, currentDocument, transform);
#if REVIT2024_OR_GREATER
// revit 2024 and 2025 we can use the three-parameter constructor to get only visible elements
using var viewCollector = new FilteredElementCollector(
_revitContext.UIApplication.ActiveUIDocument.Document,
viewFilter.GetView().NotNull().Id,
currentDocument,
viewFilter.GetView(currentDocument).NotNull().Id,
linkInstance.Id
);
@@ -70,7 +64,7 @@ public class LinkedModelHandler
// 🚨 LIMITATION: in Revit 2023 and below, we can only check if the entire linked model is visible,
// not individual elements within it. If the linked model is visible, all its elements will be included.
// constructor overload pertaining to searching and filtering visible elements from a revit link only added 2024.
if (linkInstance.IsHidden(viewFilter.GetView().NotNull()))
if (linkInstance.IsHidden(viewFilter.GetView(currentDocument).NotNull()))
{
return new List<Element>(); // if the linked model is hidden, return no elements
}
@@ -97,10 +97,12 @@ internal sealed class RevitDocumentStore : DocumentModelStore
try
{
var key = GetKeyForDocument(document);
if (key != null)
if (key is null)
{
_jsonCacheManager.UpdateObject(key, modelCardState);
LoadFromString(null);
return;
}
_jsonCacheManager.UpdateObject(key, modelCardState);
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -111,11 +113,17 @@ internal sealed class RevitDocumentStore : DocumentModelStore
private string? GetKeyForDocument(Document doc)
{
string? id = doc?.ProjectInformation?.UniqueId; //ProjectInformation Should only be null for family docs
#if REVIT_2024_OR_GREATER
id ??= doc.CreationGUID.ToString(); //fallback for family docs
#if REVIT2024_OR_GREATER
return doc.CreationGUID.ToString();
#else
//basically, no document state will ever be saved when it's a new document. It must be saved first for path name to be a valid value.
var x = doc.PathName;
if (string.IsNullOrEmpty(x))
{
return doc.Title;
}
return x;
#endif
return id;
}
protected override void LoadState()
@@ -128,14 +136,12 @@ internal sealed class RevitDocumentStore : DocumentModelStore
}
var key = GetKeyForDocument(document);
if (key != null)
if (key is null)
{
var state = _jsonCacheManager.GetObject(key);
if (state == null)
{
return;
}
LoadFromString(state);
LoadFromString(null);
return;
}
var state = _jsonCacheManager.GetObject(key);
LoadFromString(state);
}
}
@@ -120,9 +120,14 @@ public class RevitMaterialBaker
try
{
// all values assumed to be on the 0 - 1 scale need to pass through this validation and logging (if assumption wrong)
double roughness = ClampToUnitRange(speckleRenderMaterial.roughness, "roughness", speckleRenderMaterial.name);
double opacity = ClampToUnitRange(speckleRenderMaterial.opacity, "opacity", speckleRenderMaterial.name);
double metalness = ClampToUnitRange(speckleRenderMaterial.metalness, "metalness", speckleRenderMaterial.name);
var diffuse = System.Drawing.Color.FromArgb(speckleRenderMaterial.diffuse);
double transparency = 1 - speckleRenderMaterial.opacity;
double smoothness = 1 - speckleRenderMaterial.roughness;
double transparency = 1 - opacity;
double smoothness = 1 - roughness;
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}");
@@ -130,7 +135,7 @@ public class RevitMaterialBaker
var revitMaterial = (Material)_converterSettings.Current.Document.GetElement(newMaterialId);
revitMaterial.Color = new Color(diffuse.R, diffuse.G, diffuse.B);
revitMaterial.Transparency = (int)(transparency * 100);
revitMaterial.Shininess = (int)(speckleRenderMaterial.metalness * 128);
revitMaterial.Shininess = (int)(metalness * 128);
revitMaterial.Smoothness = (int)(smoothness * 128);
foreach (var objectId in proxy.objects)
@@ -163,4 +168,30 @@ public class RevitMaterialBaker
document.Delete(materialIds);
}
}
/// <summary>
/// After CNX-2661, we've seen some edge cases contradicting the expected 0 - 1 range for PRB properties.
/// Defensively, we'd rather clamp these values than throw.
/// </summary>
/// <remarks>
/// Created a method so that we can extend the checks to any numerical value potentially leading to a negative value,
/// which would throw an exception. Generalised method since Math.Clamp() only available since C# 8.0 and this method
/// handles logging (in the hope that we can get a better feel for these "weird" models, e.g. 0 - 100 scale??)
/// </remarks>
private double ClampToUnitRange(double value, string propertyName, string materialName)
{
if (value is < 0 or > 1)
{
_logger.LogWarning(
"Material '{MaterialName}' has an invalid {PropertyName} value of {Value} and was clamped to 0 - 1 range",
materialName,
propertyName,
value
);
value = Math.Min(Math.Max(0, value), 1);
}
return value;
}
}
@@ -110,7 +110,8 @@ public sealed class RevitHostObjectBuilder(
// TODO: TransformTo and material baking needs to be fixed in Revit!!
// create a mapping from original to modified IDs <- so that we can actually map ids in the proxies to the objects
Dictionary<string, string> originalToModifiedIds = new();
// as part of CNX-2677, we have a one-to-many problem. many instances share the same reference, so we use a list
Dictionary<string, List<string>> originalToModifiedIds = new();
// modify application IDs BEFORE material baking
foreach (LocalToGlobalMap localToGlobalMap in localToGlobalMaps)
@@ -139,7 +140,13 @@ public sealed class RevitHostObjectBuilder(
string modifiedAppId = $"{originalAppId}_{Guid.NewGuid().ToString("N")[..8]}";
if (originalAppId != null)
{
originalToModifiedIds[originalAppId] = modifiedAppId;
if (!originalToModifiedIds.TryGetValue(originalAppId, out List<string>? modifiedIds))
{
modifiedIds = new List<string>();
originalToModifiedIds[originalAppId] = modifiedIds;
}
modifiedIds.Add(modifiedAppId);
}
localToGlobalMap.AtomicObject.applicationId = modifiedAppId;
@@ -152,14 +159,20 @@ public sealed class RevitHostObjectBuilder(
{
foreach (var proxy in unpackedRoot.RenderMaterialProxies)
{
var updatedObjects = new List<string>();
var objectIdsToUse = new List<string>();
foreach (var objectId in proxy.objects)
{
// Use the modified ID if it exists, otherwise keep the original <- this SUCKS and we need to change
string idToUse = originalToModifiedIds.TryGetValue(objectId, out var modifiedId) ? modifiedId : objectId;
updatedObjects.Add(idToUse);
if (originalToModifiedIds.TryGetValue(objectId, out var modifiedIds))
{
objectIdsToUse.AddRange(modifiedIds);
}
else
{
objectIdsToUse.Add(objectId);
}
}
proxy.objects = updatedObjects;
proxy.objects = objectIdsToUse;
}
}
@@ -11,14 +11,13 @@ namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilter
{
private RevitContext _revitContext;
private Document? _doc;
public string Id { get; set; } = "revitViews";
public string Name { get; set; } = "Views";
public string Type { get; set; } = "Custom";
public string? Summary { get; set; }
public bool IsDefault { get; set; }
public string? SelectedView { get; set; }
public List<string> SelectedObjectIds { get; set; }
public List<string> SelectedObjectIds { get; set; } = new();
public Dictionary<string, string>? IdMap { get; set; } = new();
public List<string>? AvailableViews { get; set; }
@@ -27,12 +26,14 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
public RevitViewsFilter(RevitContext revitContext)
{
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
GetViews();
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc is not null)
{
GetViews(doc);
}
}
public View? GetView()
public View? GetView(Document document)
{
if (SelectedView is null)
{
@@ -42,7 +43,7 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
var viewFamilyString = result[0];
var viewString = result[1];
using var collector = new FilteredElementCollector(_doc);
using var collector = new FilteredElementCollector(document);
return collector
.OfClass(typeof(View))
.Cast<View>()
@@ -56,7 +57,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
/// <exception cref="SpeckleSendFilterException">Whenever no view is found.</exception>
public List<string> RefreshObjectIds()
{
if (SelectedView is null)
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (SelectedView is null || document is null)
{
return [];
}
@@ -66,7 +68,7 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
var viewFamilyString = result[0];
var viewString = result[1];
using var collector = new FilteredElementCollector(_doc);
using var collector = new FilteredElementCollector(document);
View? view = collector
.OfClass(typeof(View))
.Cast<View>()
@@ -78,7 +80,7 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
return [];
}
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
IEnumerable<Element> elementsInView = GetFilteredElementsForView(document, view);
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -94,9 +96,9 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
return objectIds;
}
private void GetViews()
private void GetViews(Document document)
{
using var collector = new FilteredElementCollector(_doc);
using var collector = new FilteredElementCollector(document);
var views = collector
.OfClass(typeof(View))
.Cast<View>()
@@ -125,7 +127,6 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
public void SetContext(RevitContext revitContext)
{
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
@@ -161,9 +162,9 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
private IEnumerable<Element> GetFilteredElementsForView(Document document, View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
using var viewCollector = new FilteredElementCollector(document, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
@@ -183,6 +183,7 @@ public class RevitRootObjectBuilder(
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
converted = value;
@@ -248,6 +249,17 @@ public class RevitRootObjectBuilder(
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 = "revitInstancedObjects"
}
);
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
@@ -1,25 +1,21 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
[GenerateAutoInterface]
public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
public class ToSpeckleSettingsManager(
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
ILogger<ToSpeckleSettingsManager> logger
) : IToSpeckleSettingsManager
{
private readonly RevitContext _revitContext;
private readonly ISendConversionCache _sendConversionCache;
private readonly ElementUnpacker _elementUnpacker;
private readonly ILogger<ToSpeckleSettingsManager> _logger;
// cache invalidation process run with ModelCardId since the settings are model specific
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = [];
private readonly Dictionary<string, Transform?> _referencePointCache = [];
@@ -27,20 +23,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
public ToSpeckleSettingsManager(
RevitContext revitContext,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
ILogger<ToSpeckleSettingsManager> logger
)
{
_revitContext = revitContext;
_elementUnpacker = elementUnpacker;
_sendConversionCache = sendConversionCache;
_logger = logger;
}
public DetailLevelType GetDetailLevelSetting(SenderModelCard modelCard)
public DetailLevelType GetDetailLevelSetting(Document document, SenderModelCard modelCard)
{
var fidelityString =
modelCard.Settings?.FirstOrDefault(s => s.Id == DetailLevelSetting.SETTING_ID)?.Value as string;
@@ -53,7 +36,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
{
if (previousType != fidelity)
{
EvictCacheForModelCard(modelCard);
EvictCacheForModelCard(document, modelCard);
}
}
_detailLevelCache[modelCard.ModelCardId.NotNull()] = fidelity;
@@ -61,7 +44,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
}
// log the issue
_logger.LogWarning(
logger.LogWarning(
"Invalid detail level setting received: '{FidelityString}' for model {ModelCardId}, using default: {DefaultValue}",
fidelityString,
modelCard.ModelCardId,
@@ -74,7 +57,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return defaultValue;
}
public Transform? GetReferencePointSetting(ModelCard modelCard)
public Transform? GetReferencePointSetting(Document document, ModelCard modelCard)
{
var referencePointString =
modelCard.Settings?.FirstOrDefault(s => s.Id == SendReferencePointSetting.SETTING_ID)?.Value as string;
@@ -88,14 +71,14 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
{
// get the current transform from setting first
// we are doing this because we can't track if reference points were changed between send operations.
Transform? currentTransform = GetTransform(referencePoint);
Transform? currentTransform = GetTransform(document, referencePoint);
if (_referencePointCache.TryGetValue(modelCard.ModelCardId.NotNull(), out Transform? previousTransform))
{
// invalidate conversion cache if the transform has changed
if (modelCard is SenderModelCard senderModelCard && previousTransform != currentTransform)
{
EvictCacheForModelCard(senderModelCard);
EvictCacheForModelCard(document, senderModelCard);
}
}
@@ -104,7 +87,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
}
// log the issue
_logger.LogWarning(
logger.LogWarning(
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
referencePointString,
modelCard.ModelCardId,
@@ -116,8 +99,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return null;
}
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard) =>
public bool GetSendParameterNullOrEmptyStringsSetting(Document document, SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
document,
SendParameterNullOrEmptyStringsSetting.SETTING_ID,
SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE,
modelCard,
@@ -127,8 +111,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
public bool GetLinkedModelsSetting(SenderModelCard modelCard) =>
public bool GetLinkedModelsSetting(Document document, SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
document,
LinkedModelsSetting.SETTING_ID,
LinkedModelsSetting.DEFAULT_VALUE,
modelCard,
@@ -136,8 +121,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
"Linked models"
);
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard) =>
public bool GetSendRebarsAsVolumetric(Document document, SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
document,
SendRebarsAsVolumetricSetting.SETTING_ID,
SendRebarsAsVolumetricSetting.DEFAULT_VALUE,
modelCard,
@@ -149,6 +135,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
/// Helper method to handle boolean settings with caching and logging
/// </summary>
private bool GetBooleanSettingWithCache(
Document document,
string settingId,
bool defaultValue,
SenderModelCard modelCard,
@@ -163,7 +150,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
EvictCacheForModelCard(document, modelCard);
}
}
@@ -173,7 +160,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
// respected (linked models disabled but still sent linked models), I think we should note this occurence so we know
if (settingValue == null)
{
_logger.LogWarning(
logger.LogWarning(
"{SettingName} setting was null for model {ModelCardId}, using default: {DefaultValue}",
settingName,
modelCard.ModelCardId,
@@ -184,71 +171,59 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
private void EvictCacheForModelCard(Document document, SenderModelCard modelCard)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
throw new SpeckleException("Unable to retrieve active UI document");
}
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
_sendConversionCache.EvictObjects(unpackedObjectIds);
var objectIds = modelCard.SendFilter?.SelectedObjectIds ?? [];
var unpackedObjectIds = elementUnpacker.GetUnpackedElementIds(objectIds, document);
sendConversionCache.EvictObjects(unpackedObjectIds);
}
private Transform? GetTransform(ReferencePointType referencePointType)
private Transform? GetTransform(Document document, ReferencePointType referencePointType)
{
Transform? referencePointTransform = null;
if (_revitContext.UIApplication is UIApplication uiApplication)
// first get the main doc base points and reference setting transform
using FilteredElementCollector filteredElementCollector = new(document);
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
BasePoint? surveyPoint = points.FirstOrDefault(o => o.IsShared);
switch (referencePointType)
{
// first get the main doc base points and reference setting transform
using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document);
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
BasePoint? surveyPoint = points.FirstOrDefault(o => o.IsShared);
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReferencePointType.ProjectBase:
if (projectPoint is not null)
{
referencePointTransform = Transform.CreateTranslation(projectPoint.Position);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Project Point from document");
}
break;
switch (referencePointType)
{
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReferencePointType.ProjectBase:
if (projectPoint is not null)
{
referencePointTransform = Transform.CreateTranslation(projectPoint.Position);
}
else
{
throw new InvalidOperationException("Couldn't retrieve Project Point from document");
}
break;
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
case ReferencePointType.Survey:
if (surveyPoint is not null && projectPoint is not null)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// 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)
{
// POC: should a null angle resolve to 0?
// retrieve the survey point rotation from the project point
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
// POC: following disposed incorrectly or early or maybe a false negative?
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
}
break;
// POC: following disposed incorrectly or early or maybe a false negative?
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
}
else
{
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
}
break;
case ReferencePointType.InternalOrigin:
break;
}
return referencePointTransform;
case ReferencePointType.InternalOrigin:
break;
}
throw new InvalidOperationException(
"Revit Context UI Application was null when retrieving reference point transform."
);
return referencePointTransform;
}
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.rhino7": {
@@ -347,6 +347,29 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -379,38 +402,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.rhino8": {
@@ -346,6 +346,29 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -378,38 +401,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -64,6 +64,13 @@ public class CreateCollection : VariableParameterComponentBase
}
}
// validate for duplicate application IDs across the entire collection hierarchy
if (HasDuplicateApplicationIds(rootCollection))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The same object(s) cannot appear in multiple collections");
return; // error already added in validation method
}
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
}
@@ -182,6 +189,54 @@ public class CreateCollection : VariableParameterComponentBase
}
}
/// <summary>
/// Validates that all application IDs are unique across the entire collection hierarchy.
/// Shows an error if duplicates are found, indicating objects appear in multiple collections.
/// </summary>
/// <returns>True if duplicates exist, false if all IDs are unique</returns>
private bool HasDuplicateApplicationIds(SpeckleCollectionWrapper rootCollection)
{
// args to CheckForDuplicateApplicationIds passed in since the method can recursively check
var seenIds = new HashSet<string>();
var duplicateIds = new HashSet<string>();
// iterate, create hash set and check all application IDs
ProcessAndCheckForDuplicateApplicationIds(rootCollection, seenIds, duplicateIds);
return duplicateIds.Count > 0;
}
/// <summary>
/// Recursively collects application IDs from all in the collection hierarchy.
/// </summary>
/// <remarks>
/// Only checks the wrapper's ApplicationId, not for example geometries within DataObjects.
/// </remarks>
private void ProcessAndCheckForDuplicateApplicationIds(
SpeckleCollectionWrapper collection,
HashSet<string> seenIds,
HashSet<string> duplicateIds
)
{
foreach (var element in collection.Elements)
{
switch (element)
{
case SpeckleCollectionWrapper childCollection:
// recurse into child collections
ProcessAndCheckForDuplicateApplicationIds(childCollection, seenIds, duplicateIds);
break;
case SpeckleWrapper wrapper:
if (wrapper.ApplicationId != null && !seenIds.Add(wrapper.ApplicationId))
{
duplicateIds.Add(wrapper.ApplicationId);
}
break;
}
}
}
// IGH_VariableParameterComponent implementation
public override bool CanInsertParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Input;
@@ -34,8 +34,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
pManager.AddParameter(param);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
@@ -43,7 +42,6 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
"Properties for Speckle Objects",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
@@ -67,8 +65,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
{
var paramName = Params.Input[i].NickName;
var data = Params.Input[i].VolatileData.AllData(true).ToList();
if (data.Count == 0)
if (Params.Input[i].VolatileDataCount == 0)
{
continue;
}
@@ -165,13 +162,12 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
Params.RegisterInputParam(param);
}
Params.OnParametersChanged();
ExpireSolution(true);
}
protected override void WriteComponentSpecificData(GH_IWriter writer)
{
protected override void WriteComponentSpecificData(GH_IWriter writer) =>
writer.SetBoolean("CreateEmptyProperties", CreateEmptyProperties);
}
protected override void ReadComponentSpecificData(GH_IReader reader)
{
@@ -89,7 +89,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
private List<int>? _outputFilterIndices;
// Caches the list of all objects by geometrybase type
private readonly Dictionary<ObjectType, List<SpeckleGeometryWrapper>> _filterDict = new();
private readonly Dictionary<ObjectType, List<SpeckleGeometryWrapper>> _filterDict = [];
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
@@ -104,6 +104,9 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
string path = "";
dataAccess.GetData(1, ref path);
// ensure fresh data for type-specific outputs
_filterDict.Clear();
// filter by collection path
// Note: the collection paths selector will omit the target collection from the path of nested collections.
// the discard ("_objects") will be used to indicate objects found directly in the target collection.
@@ -127,10 +130,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
// sort geometry objects by filters
var geometryObjects = filteredObjects.OfType<SpeckleGeometryWrapper>().ToList();
if (_filterDict.Count == 0)
{
SortObjectsByGeometryBaseType(geometryObjects);
}
SortObjectsByGeometryBaseType(geometryObjects);
// Set output objects
for (int i = 0; i < Params.Output.Count; i++)
@@ -173,13 +173,13 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
{
if (_filterDict.Count > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Stored input objects are in an invalid state.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Stored input objects are in an invalid state");
return;
}
foreach (ObjectType filter in Filters)
{
_filterDict.Add(filter, new());
_filterDict.Add(filter, []);
}
foreach (var wrapper in objs)
@@ -233,7 +233,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
// repopulate current output params if needed
if (_outputFilterIndices is null)
{
_outputFilterIndices = new();
_outputFilterIndices = [];
foreach (var output in Params.Output)
{
if (Enum.TryParse(output.Name, out ObjectType filter))
@@ -289,8 +289,5 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
base.RemovedFromDocument(document);
}
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args) =>
// an empty filter dict will trigger the SortObjectsByGeometryBaseType method.
// we only want to re-sort objects if an input has changed, not on every trigger of solve instance.
_filterDict.Clear();
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args) { }
}
@@ -103,7 +103,7 @@ public class SpeckleDataObjectPassthrough()
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
if (!da.GetDataList(1, inputGeometry) && result == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle DataObject or Geometries.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle DataObject or Geometries");
return;
}
@@ -111,7 +111,7 @@ public class SpeckleDataObjectPassthrough()
{
if (inputGeo.Value is SpeckleBlockInstanceWrapper)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"DataObjects cannot contain Block Instances.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "DataObjects cannot contain Block Instances");
return;
}
}
@@ -158,6 +158,10 @@ public class SpeckleDataObjectPassthrough()
result.Properties = inputProperties;
}
// generate application ID for new data objects. Unlike SpeckleGeometry, DataObject wrappers aren't created
// through casting (which auto-generates IDs), so we must explicitly ensure an ID exists here
result.ApplicationId ??= Guid.NewGuid().ToString();
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
@@ -138,7 +138,7 @@ public class SpeckleGeometryPassthrough()
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle Geometry or Geometry");
return;
}
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using GH_IO.Serialization;
using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
@@ -297,6 +298,32 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
}
}
public override bool Write(GH_IWriter writer)
{
// call base implementation first
var result = base.Write(writer);
// persist AutoReceive setting
writer.SetBoolean("AutoReceive", AutoReceive);
return result;
}
public override bool Read(GH_IReader reader)
{
// call base implementation first
var result = base.Read(reader);
// restore AutoReceive setting
bool autoReceive = false;
if (reader.TryGetBoolean("AutoReceive", ref autoReceive))
{
AutoReceive = autoReceive;
}
return result;
}
}
public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponent>
@@ -1,5 +1,6 @@
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Analytics;
using Speckle.Connectors.Common.Operations;
@@ -12,6 +13,7 @@ using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Collections;
@@ -31,11 +33,20 @@ public class ReceiveComponentInput
public class ReceiveComponentOutput
{
public SpeckleCollectionWrapperGoo RootObject { get; set; }
/// <remarks>
/// Made nullable as output can be null when Run = false or on error
/// </remarks>
public SpeckleCollectionWrapperGoo? RootObject { get; set; }
}
public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInput, ReceiveComponentOutput>
{
private IClient? _apiClient;
private string? _lastVersionId;
private SpeckleUrlModelResource? _lastResource;
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
protected override Bitmap Icon => Resources.speckle_operations_syncload;
public ReceiveComponent()
: base(
"(Sync) Load",
@@ -45,9 +56,6 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
ComponentCategories.DEVELOPER
) { }
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
protected override Bitmap Icon => Resources.speckle_operations_syncload;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
@@ -77,19 +85,28 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
bool run = false;
da.GetData(1, ref run);
return new(url, run);
if (run)
{
SetupSubscription(url);
}
else
{
CleanupSubscription();
}
return new ReceiveComponentInput(url, run);
}
protected override void SetOutput(IGH_DataAccess da, ReceiveComponentOutput result)
{
if (result.RootObject is null)
{
Message = "Not Loaded";
Message = _apiClient != null ? "Monitoring" : "Not Loaded";
}
else
{
da.SetData(0, result.RootObject);
Message = "Done";
Message = _apiClient != null ? "Loaded" : "Done";
}
}
@@ -107,11 +124,11 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
GH_RuntimeMessageLevel.Error,
"Only one model can be loaded at a time. To load to multiple models, please use different load components."
);
return new();
return new ReceiveComponentOutput();
}
if (!input.Run)
{
return new();
return new ReceiveComponentOutput();
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
@@ -129,6 +146,9 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
using var client = clientFactory.Create(account);
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
// store version id for tracking
_lastVersionId = receiveInfo.SelectedVersionId;
var progress = new Progress<CardProgress>(_ =>
{
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
@@ -195,4 +215,116 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo };
}
private void SetupSubscription(SpeckleUrlModelResource resource)
{
// skip if already subscribed to this resource
if (_apiClient != null && _lastResource != null && _lastResource.Equals(resource))
{
return;
}
// only subscribe for Model URLs (not specific versions)
if (resource is SpeckleUrlModelVersionResource)
{
CleanupSubscription();
_lastResource = resource;
return;
}
try
{
CleanupSubscription(); // clean up old subscription first
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var account = resource.Account.GetAccount(scope);
if (account == null)
{
return;
}
_apiClient = scope.Get<IClientFactory>().Create(account);
_apiClient.Subscription.CreateProjectVersionsUpdatedSubscription(resource.ProjectId).Listeners +=
OnVersionCreated;
_lastResource = resource;
Message = "Monitoring";
}
catch (Exception ex) when (!ex.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Could not setup monitoring: {ex.Message}");
}
}
private void OnVersionCreated(object? sender, ProjectVersionsUpdatedMessage e) =>
// new version detected - trigger reload
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
ExpireSolution(true);
}
);
private void CleanupSubscription()
{
if (_apiClient != null && _lastResource != null)
{
try
{
_apiClient.Subscription.CreateProjectVersionsUpdatedSubscription(_lastResource.ProjectId).Listeners -=
OnVersionCreated;
}
catch (Exception ex) when (!ex.IsFatal())
{
// ignore cleanup errors
}
_apiClient.Dispose();
_apiClient = null;
}
}
// Cleanup on removal
public override void RemovedFromDocument(GH_Document document)
{
CleanupSubscription();
base.RemovedFromDocument(document);
}
// Handle document context changes
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context)
{
if (context == GH_DocumentContext.Unloaded)
{
CleanupSubscription();
}
else if (context == GH_DocumentContext.Loaded && _lastResource != null && _apiClient != null)
{
// Check for version changes when document reopens
Task.Run(async () =>
{
try
{
var receiveInfo = await _lastResource.GetReceiveInfo(_apiClient);
if (receiveInfo.SelectedVersionId != _lastVersionId)
{
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
ExpireSolution(true);
}
);
}
}
catch (Exception ex) when (!ex.IsFatal())
{
// ignore errors during background check
}
});
}
base.DocumentContextChanged(document, context);
}
}
@@ -87,6 +87,7 @@ public class SpeckleSelectModelComponent : GH_Component
string? urlInput = null;
// SCENARIO 1: Component has input wire connected
if (da.GetData(0, ref urlInput))
{
UrlInput = urlInput;
@@ -99,6 +100,11 @@ public class SpeckleSelectModelComponent : GH_Component
return;
}
if (_justPastedIn)
{
RestoreAccountFromStoredState();
}
try
{
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
@@ -132,22 +138,9 @@ public class SpeckleSelectModelComponent : GH_Component
_storedUserId = SpeckleOperationWizard.SelectedAccount?.id;
}
if (_justPastedIn && _storedUserId != null && !string.IsNullOrEmpty(_storedUserId))
if (_justPastedIn)
{
try
{
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
}
catch (SpeckleAccountManagerException e)
{
// Swallow and move onto checking server.
Console.WriteLine(e);
}
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
{
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
}
RestoreAccountFromStoredState();
}
// Validate backing data
@@ -396,4 +389,39 @@ public class SpeckleSelectModelComponent : GH_Component
VersionContextMenuButton.ExpirePreview(redraw);
base.ExpirePreview(redraw);
}
/// <summary>
/// Restores the account from stored state when the component is pasted or loaded from file.
/// </summary>
/// <remarks>
/// Attempts to restore account in two stages:
/// <list type="number">
/// <item>First tries to get account by stored user ID</item>
/// <item>If that fails and server url is available, falls back to getting any account matching the server</item>
/// </list>
/// Only executes when <see cref="_justPastedIn"/> is true and <see cref="_storedUserId"/> is not empty.
/// </remarks>
private void RestoreAccountFromStoredState()
{
if (_storedUserId is null || string.IsNullOrEmpty(_storedUserId))
{
return;
}
try
{
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
}
catch (SpeckleAccountManagerException e)
{
Console.WriteLine(e);
}
// Fallback: if account wasn't found by ID but we have a server URL,
// try to find any account matching that server
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
{
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
}
}
}
@@ -79,22 +79,40 @@ public class SpeckleOperationWizard
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
if (resources.Length == 0)
{
throw new SpeckleException($"Input url string was empty");
throw new SpeckleException("Input url string was empty");
}
if (resources.Length > 1)
{
throw new SpeckleException($"Input multi-model url is not supported");
throw new SpeckleException("Input multi-model url is not supported");
}
var resource = resources.First();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var account = resource.Account.GetAccount(scope);
SetAccount(account, false);
var urlDerivedAccount = resource.Account.GetAccount(scope);
// if no account is selected, happily go through the url derived account approach
if (SelectedAccount == null)
{
throw new SpeckleException("No account found for server URL");
SetAccount(urlDerivedAccount, false);
}
// if we have an account from right-click context-menu, we rely on that and just validate that it's actually applicable to that server
else if (urlDerivedAccount != null && SelectedAccount.serverInfo.url != urlDerivedAccount.serverInfo.url)
{
throw new SpeckleException(
$"Selected account is for '{SelectedAccount.serverInfo.url}' "
+ $"but URL requires '{urlDerivedAccount.serverInfo.url}'"
);
}
// we have both scenarios covered
// Scenario #1 - default account from url
// Scenario #2 - triggered by account switch on right-click context (and validated)
if (SelectedAccount == null)
{
throw new SpeckleException(
$"No appropriate account found for the given '{urlDerivedAccount?.serverInfo.url}' server"
);
}
IClient client = _clientFactory.Create(SelectedAccount);
@@ -11,6 +11,7 @@ namespace Speckle.Connectors.GrasshopperShared.Parameters;
/// </summary>
public class SpeckleOutputParam : Param_GenericObject
{
public override GH_Exposure Exposure => GH_Exposure.hidden;
public override Guid ComponentGuid => new("D2B4713D-FE8B-4EF0-8445-B6096DB15B24");
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
@@ -52,6 +52,7 @@ public class SpeckleVariableParam : Param_GenericObject
}
}
public override GH_Exposure Exposure => GH_Exposure.hidden;
public override Guid ComponentGuid => new("A1B2C3D4-E5F6-7890-ABCD-123456789ABC");
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
@@ -4,6 +4,7 @@ using Rhino;
using Rhino.Display;
using Rhino.DocObjects;
using Rhino.Geometry;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
@@ -27,7 +28,25 @@ public class SpeckleGeometryWrapper : SpeckleWrapper, ISpeckleCollectionObject
// The list of layer/collection names that forms the full path to this object
public List<string> Path { get; set; } = new();
public SpeckleCollectionWrapper? Parent { get; set; }
public SpecklePropertyGroupGoo Properties { get; set; } = new();
/// <summary>
/// Properties that stay synchronized with Base["properties"]
/// </summary>
public SpecklePropertyGroupGoo Properties
{
get
{
// always create from Base["properties"] like SpeckleDataObjectWrapper does
if (Base[Constants.PROPERTIES_PROP] is Dictionary<string, object?> baseProps)
{
return new SpecklePropertyGroupGoo(baseProps);
}
return new SpecklePropertyGroupGoo(); // return empty if no properties
}
set =>
// unwrap and store in Base["properties"] like SpeckleDataObjectWrapper does
Base[Constants.PROPERTIES_PROP] = value.Unwrap();
}
/// <summary>
/// The color of the <see cref="Base"/>
@@ -306,9 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -341,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.rhino7": {
@@ -351,6 +351,29 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"GrasshopperAsyncComponent": {
"type": "CentralTransitive",
"requested": "[2.0.3, )",
@@ -399,38 +422,6 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
"requested": "[9.0.4, )",
@@ -306,9 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -341,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.rhino8": {
@@ -350,6 +350,29 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"GrasshopperAsyncComponent": {
"type": "CentralTransitive",
"requested": "[2.0.3, )",
@@ -398,38 +421,6 @@
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
"requested": "[9.0.4, )",
@@ -235,9 +235,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -261,7 +261,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.converters.rhino8": {
@@ -270,6 +270,27 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -308,36 +329,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -83,7 +83,7 @@ public sealed class RhinoSendBinding : ISendBinding
_sendOperationManagerFactory = sendOperationManagerFactory;
_rhinoLayerHelper = rhinoLayerHelper;
Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory.
PreviousUnitSystem = RhinoDoc.ActiveDoc.ModelUnitSystem;
PreviousUnitSystem = RhinoDoc.ActiveDoc?.ModelUnitSystem ?? UnitSystem.None;
SubscribeToRhinoEvents();
}
@@ -104,10 +104,10 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
attributes
);
// POC: check on defIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
// POC: check on defIndex -1, means we haven't created anything - this is most likely an unrecoverable error at this stage
if (defIndex == -1)
{
throw new ConversionException("Failed to create an instance defintion object.");
throw new ConversionException("Failed to create an instance definition object.");
}
if (definitionProxy.applicationId != null)
@@ -170,9 +170,13 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
public void PurgeInstances(string namePrefix)
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
// clean name prefix to match how block names are created
var cleanedPrefix = RhinoUtils.CleanBlockDefinitionName(namePrefix);
foreach (var definition in currentDoc.InstanceDefinitions)
{
if (!definition.IsDeleted && definition.Name.Contains(namePrefix))
if (!definition.IsDeleted && definition.Name.Contains(cleanedPrefix))
{
currentDoc.InstanceDefinitions.Delete(definition.Index, true, false);
}
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using Rhino;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Objects.Other;
@@ -44,35 +43,43 @@ public class RhinoMaterialBaker
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
string matName = $"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}";
matName = matName.Replace("[", "").Replace("]", ""); // "Material" doesn't like square brackets if we create from here. Once they created from Rhino UI, all good..
Color diffuse = Color.FromArgb(speckleRenderMaterial.diffuse);
Color emissive = Color.FromArgb(speckleRenderMaterial.emissive);
double transparency = 1 - speckleRenderMaterial.opacity;
Material rhinoMaterial =
new()
{
Name = matName,
DiffuseColor = diffuse,
EmissionColor = emissive,
Transparency = transparency
};
// Check if material with this name already exists in the document
int matIndex = doc.Materials.Find(matName, ignoreDeletedMaterials: true);
// try to get additional properties
if (speckleRenderMaterial["ior"] is double ior)
{
rhinoMaterial.IndexOfRefraction = ior;
}
if (speckleRenderMaterial["shine"] is double shine)
{
rhinoMaterial.Shine = shine;
}
int matIndex = doc.Materials.Add(rhinoMaterial);
// POC: check on matIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
// If material doesn't exist, create it
if (matIndex == -1)
{
throw new ConversionException("Failed to add a material to the document.");
Color diffuse = Color.FromArgb(speckleRenderMaterial.diffuse);
Color emissive = Color.FromArgb(speckleRenderMaterial.emissive);
double transparency = 1 - speckleRenderMaterial.opacity;
Material rhinoMaterial =
new()
{
Name = matName,
DiffuseColor = diffuse,
EmissionColor = emissive,
Transparency = transparency
};
// try to get additional properties
if (speckleRenderMaterial["ior"] is double ior)
{
rhinoMaterial.IndexOfRefraction = ior;
}
if (speckleRenderMaterial["shine"] is double shine)
{
rhinoMaterial.Shine = shine;
}
matIndex = doc.Materials.Add(rhinoMaterial);
// POC: check on matIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
if (matIndex == -1)
{
throw new ConversionException($"Failed to add a material to the document: '{matName}' (ID: {materialId})");
}
}
// Create the object <> material index map
@@ -87,27 +94,4 @@ public class RhinoMaterialBaker
}
}
}
/// <summary>
/// Removes all materials with a name starting with <paramref name="namePrefix"/> from the active document
/// </summary>
/// <param name="namePrefix"></param>
public void PurgeMaterials(string namePrefix)
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
foreach (Material material in currentDoc.Materials)
{
try
{
if (!material.IsDeleted && material.Name != null && material.Name.Contains(namePrefix))
{
currentDoc.Materials.Delete(material);
}
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to purge a material from the document");
}
}
}
}
@@ -1,9 +1,11 @@
using System.IO;
using Microsoft.Extensions.Logging;
using Rhino;
using Rhino.DocObjects;
using Rhino.Render;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Models;
using Material = Rhino.DocObjects.Material;
using PhysicallyBasedMaterial = Rhino.DocObjects.PhysicallyBasedMaterial;
using RenderMaterial = Rhino.Render.RenderMaterial;
@@ -19,6 +21,7 @@ public class RhinoMaterialUnpacker
/// For send operations
/// </summary>
private Dictionary<string, RenderMaterialProxy> RenderMaterialProxies { get; } = new();
private Dictionary<string, string> Textures { get; } = new();
public RhinoMaterialUnpacker(ILogger<RhinoMaterialUnpacker> logger)
{
@@ -218,23 +221,51 @@ public class RhinoMaterialUnpacker
? pbRenderMaterial.Material.EmissionColor
: pbRenderMaterial.Emission.AsSystemColor(); // pbRenderMaterial.emission gives wrong color for emission materials, and material.emissioncolor gives the wrong value for most others *shrug*
// NOTE: added after CNX-2661, without having file that caused issue hard to say what the issue is
// api bug / funny model (custom textures) / upgrade from old model (e.g. Rhino 6)? who knows.
// PBR standard is 0-1. Clamping to valid range. This may indicate texture data is in wrong scale.
double roughness = pbRenderMaterial.Roughness;
if (roughness < 0 || roughness > 1)
{
_logger.LogWarning("Material '{Name}' has invalid roughness value of {Value}", renderMaterial.Name, roughness);
roughness = Math.Min(Math.Max(0, roughness), 1); // Math.Clamp() only from C# 8.0
}
SpeckleRenderMaterial speckleRenderMaterial =
new()
{
name = renderMaterialName,
opacity = opacity,
metalness = pbRenderMaterial.Metallic,
roughness = pbRenderMaterial.Roughness,
roughness = roughness,
diffuse = diffuse.ToArgb(),
emissive = emissive.ToArgb(),
applicationId = renderMaterial.Id.ToString()
applicationId = renderMaterial.Id.ToString(),
["diffuseTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_BaseColor),
["metallicTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_Metallic),
["specularTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_Specular),
["shineTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_Sheen),
["roughnessTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_Roughness),
["opacityTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.Opacity),
["bumpTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.Bump),
["emissionTexture"] = GetEncodedTexture(pbRenderMaterial, TextureType.PBR_Emission),
["typeName"] = renderMaterial.TypeName,
["ior"] = pbRenderMaterial.ReflectiveIOR,
["shine"] = pbRenderMaterial.Material.Shine,
["specular"] = pbRenderMaterial.Specular
};
// add additional dynamic props for rhino material receive
speckleRenderMaterial["typeName"] = renderMaterial.TypeName;
speckleRenderMaterial["ior"] = pbRenderMaterial.Material.IndexOfRefraction;
speckleRenderMaterial["shine"] = pbRenderMaterial.Material.Shine;
return speckleRenderMaterial;
}
private static Blob? GetEncodedTexture(PhysicallyBasedMaterial pbRenderMaterial, TextureType kind)
{
var texture = pbRenderMaterial.GetTexture(kind);
string? path = texture?.FileName;
if (string.IsNullOrEmpty(path) || !File.Exists(path))
{
return null;
}
return new Blob(path!);
}
}
@@ -256,7 +256,8 @@ public class RhinoHostObjectBuilder : IHostObjectBuilder
.RunOnMain(() =>
{
_instanceBaker.PurgeInstances(baseLayerName);
_materialBaker.PurgeMaterials(baseLayerName);
// Materials are now reused across receives instead of being purged
// _materialBaker.PurgeMaterials(baseLayerName);
var doc = _converterSettings.Current.Document;
// Cleans up any previously received objects
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -360,9 +360,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"LibTessDotNet": {
"type": "CentralTransitive",
"requested": "[1.1.15, )",
@@ -407,38 +430,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -406,9 +406,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -441,9 +441,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"LibTessDotNet": {
"type": "CentralTransitive",
"requested": "[1.1.15, )",
@@ -488,38 +511,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -406,9 +406,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -441,9 +441,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"LibTessDotNet": {
"type": "CentralTransitive",
"requested": "[1.1.15, )",
@@ -488,38 +511,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -258,9 +258,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -284,38 +307,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -258,9 +258,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -284,38 +307,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -285,9 +285,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -326,38 +349,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -236,9 +236,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -277,36 +298,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )",
"Speckle.Sdk": "[1.0.0, )",
"Speckle.Sdk.Dependencies": "[1.0.0, )"
}
},
"speckle.connectors.dui": {
@@ -236,9 +236,30 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -277,36 +298,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -5,7 +5,7 @@ namespace Speckle.Converters.CSiShared;
[GenerateAutoInterface]
public class CsiConversionSettingsFactory(
IHostToSpeckleUnitConverter<eUnits> unitsConverter,
IHostToSpeckleUnitConverter<eLength> unitsConverter,
IConverterSettingsStore<CsiConversionSettings> settingsStore
) : ICsiConversionSettingsFactory
{
@@ -15,11 +15,21 @@ public class CsiConversionSettingsFactory(
cSapModel document,
List<string>? selectedLoadCasesAndCombinations = null,
List<string>? selectedResultTypes = null
) =>
new(
)
{
// NOTE: only applicable to ETABS. If we bring in SAP2000 then we need to revert to GetPresentUnits
// NOTE: change from GetPresentUnits as this was linked to weird behaviour (see CNX-2621), returning "0" sometimes
// bug in the GetPresentUnits api call ...
eTemperature temperatureUnit = eTemperature.NotApplicable;
eLength lengthUnit = eLength.NotApplicable;
eForce forceUnit = eForce.NotApplicable;
document.GetPresentUnits_2(ref forceUnit, ref lengthUnit, ref temperatureUnit);
return new CsiConversionSettings(
document,
unitsConverter.ConvertOrThrow(document.GetPresentUnits()),
unitsConverter.ConvertOrThrow(lengthUnit),
selectedLoadCasesAndCombinations ?? [],
selectedResultTypes ?? []
);
}
}
@@ -5,37 +5,28 @@ using Speckle.Sdk.Common.Exceptions;
namespace Speckle.Converters.CSiShared;
/// <summary>
/// Convert CSi eUnits enumeration to Speckle units.
/// Convert CSi eLength enumeration to Speckle units.
/// </summary>
/// <remarks>
/// CSi GetPresentUnits() valid for both SAP 2000 and ETABS.
/// CSi GetPresentUnits_2() valid for ONLY ETABS. If we add SAP2000, this needs to be modified
/// Represents units transmitted through API calls and not necessarily those displayed in GUI.
/// </remarks>
public class CsiToSpeckleUnitConverter : IHostToSpeckleUnitConverter<eUnits>
public class CsiToSpeckleUnitConverter : IHostToSpeckleUnitConverter<eLength>
{
private readonly Dictionary<eUnits, string> _unitMapping = new Dictionary<eUnits, string>();
private readonly Dictionary<eLength, string> _unitMapping = [];
public CsiToSpeckleUnitConverter()
{
_unitMapping[eUnits.lb_in_F] = Units.Inches;
_unitMapping[eUnits.lb_ft_F] = Units.Feet;
_unitMapping[eUnits.kip_in_F] = Units.Inches;
_unitMapping[eUnits.kip_ft_F] = Units.Feet;
_unitMapping[eUnits.kN_mm_C] = Units.Millimeters;
_unitMapping[eUnits.kN_m_C] = Units.Meters;
_unitMapping[eUnits.kgf_mm_C] = Units.Millimeters;
_unitMapping[eUnits.kgf_m_C] = Units.Meters;
_unitMapping[eUnits.N_mm_C] = Units.Millimeters;
_unitMapping[eUnits.N_m_C] = Units.Meters;
_unitMapping[eUnits.Ton_mm_C] = Units.Millimeters;
_unitMapping[eUnits.Ton_m_C] = Units.Meters;
_unitMapping[eUnits.kN_cm_C] = Units.Centimeters;
_unitMapping[eUnits.kgf_cm_C] = Units.Centimeters;
_unitMapping[eUnits.N_cm_C] = Units.Centimeters;
_unitMapping[eUnits.Ton_cm_C] = Units.Centimeters;
_unitMapping[eLength.NotApplicable] = Units.None;
_unitMapping[eLength.inch] = Units.Inches;
_unitMapping[eLength.ft] = Units.Feet;
// _unitMapping[eLength.micron] = Units.None;
_unitMapping[eLength.mm] = Units.Millimeters;
_unitMapping[eLength.cm] = Units.Centimeters;
_unitMapping[eLength.m] = Units.Meters;
}
public string ConvertOrThrow(eUnits hostUnit) =>
public string ConvertOrThrow(eLength hostUnit) =>
_unitMapping.TryGetValue(hostUnit, out string? value)
? value
: throw new UnitNotSupportedException($"The Unit System \"{hostUnit}\" is unsupported.");
@@ -39,7 +39,7 @@ public static class ServiceRegistration
serviceCollection.AddScoped<CsiToSpeckleCacheSingleton>();
// Settings and unit conversions
serviceCollection.AddApplicationConverters<CsiToSpeckleUnitConverter, eUnits>(converterAssembly);
serviceCollection.AddApplicationConverters<CsiToSpeckleUnitConverter, eLength>(converterAssembly);
serviceCollection.AddScoped<
IConverterSettingsStore<CsiConversionSettings>,
ConverterSettingsStore<CsiConversionSettings>
@@ -8,25 +8,11 @@ namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers;
/// Extracts properties common to frame elements across CSi products (e.g., Etabs, Sap2000)
/// using the FrameObj API calls.
/// </summary>
/// <remarks>
/// Design Decisions:
/// <list type="bullet">
/// <item>
/// <description>
/// Individual methods preferred over batched calls due to:
/// <list type="bullet">
/// <item><description>Independent API calls with no performance gain from batching (?)</description></item>
/// <item><description>Easier debugging and error tracing</description></item>
/// <item><description>Simpler maintenance as each method maps to one API concept</description></item>
/// </list>
/// </description>
/// </item>
/// </list>
/// </remarks>
public sealed class CsiFramePropertiesExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
private readonly DatabaseTableExtractor _databaseTableExtractor;
private static readonly string[] s_releaseKeys =
[
@@ -36,15 +22,17 @@ public sealed class CsiFramePropertiesExtractor
"Torsion",
"Moment 22 (Minor)",
"Moment 33 (Major)"
]; // Note: caching keys for better performance
];
public CsiFramePropertiesExtractor(
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton,
IConverterSettingsStore<CsiConversionSettings> settingsStore
IConverterSettingsStore<CsiConversionSettings> settingsStore,
DatabaseTableExtractor databaseTableExtractor
)
{
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
_settingsStore = settingsStore;
_databaseTableExtractor = databaseTableExtractor;
}
public void ExtractProperties(CsiFrameWrapper frame, PropertyExtractionResult frameData)
@@ -61,13 +49,29 @@ public sealed class CsiFramePropertiesExtractor
assignments[CommonObjectProperty.PROPERTY_MODIFIERS] = GetModifiers(frame);
assignments["End Releases"] = GetReleases(frame);
// NOTE: sectionId and materialId a "quick-fix" to enable filtering in the viewer etc.
// NOTE: sectionId and materialId a "quick-fix" to enable filtering in the viewer etc. Strings are unique
// Assign sectionId to variable as this will be an argument for the GetMaterialName method
string sectionId = GetSectionName(frame);
string materialId = GetMaterialName(sectionId);
assignments[ObjectPropertyKey.SECTION_ID] = sectionId;
assignments[ObjectPropertyKey.MATERIAL_ID] = materialId;
// CNX-2725 adds more numeric props for dashboard-ing
double length = GetLength(frame);
double area = GetCrossSectionalArea(sectionId);
double volume = double.NaN;
if (!double.IsNaN(length) && !double.IsNaN(area) && length > 0 && area > 0)
{
// I am paranoid about what etabs could throw our way
double computedVolume = length * area;
volume = (!double.IsInfinity(computedVolume) && !double.IsNaN(computedVolume)) ? computedVolume : double.NaN;
}
geometry.AddWithUnits(ObjectPropertyKey.LENGTH, length, _settingsStore.Current.SpeckleUnits);
geometry.AddWithUnits(ObjectPropertyKey.CROSS_SECTIONAL_AREA, area, $"{_settingsStore.Current.SpeckleUnits}²");
geometry.AddWithUnits(ObjectPropertyKey.VOLUME, volume, $"{_settingsStore.Current.SpeckleUnits}³");
// store the object, section, and material id relationships in their corresponding caches to be accessed by the connector
if (!string.IsNullOrEmpty(sectionId))
{
@@ -196,4 +200,56 @@ public sealed class CsiFramePropertiesExtractor
_ = _settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
return materialName;
}
private double GetLength(CsiFrameWrapper frame)
{
// using the DatabaseTableExtractor fetch table with key "Frame Assignments - Summary"
// limit query size to "UniqueName" and "Length" fields
string length = _databaseTableExtractor
.GetTableData("Frame Assignments - Summary", requestedColumns: ["UniqueName", ObjectPropertyKey.LENGTH])
.GetRowValue(frame.Name, ObjectPropertyKey.LENGTH);
// all database data is returned as strings
return double.TryParse(length, out double result) ? result : double.NaN;
}
private double GetCrossSectionalArea(string sectionName)
{
if (_csiToSpeckleCacheSingleton.FrameSectionAreaCache.TryGetValue(sectionName, out double value))
{
return value;
}
double area = 0,
as2 = 0,
as3 = 0,
torsion = 0,
i22 = 0,
i33 = 0,
s22 = 0,
s33 = 0,
z22 = 0,
z33 = 0,
r22 = 0,
r33 = 0;
int result = _settingsStore.Current.SapModel.PropFrame.GetSectProps(
sectionName,
ref area,
ref as2,
ref as3,
ref torsion,
ref i22,
ref i33,
ref s22,
ref s33,
ref z22,
ref z33,
ref r22,
ref r33
);
double validatedArea = result == 0 ? area : double.NaN;
_csiToSpeckleCacheSingleton.FrameSectionAreaCache.Add(sectionName, validatedArea);
return validatedArea;
}
}
@@ -8,28 +8,18 @@ namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers;
/// Extracts properties common to shell elements across CSi products (e.g., Etabs, Sap2000)
/// using the AreaObj API calls.
/// </summary>
/// <remarks>
/// Design Decisions:
/// <list type="bullet">
/// <item>
/// <description>
/// Individual methods preferred over batched calls due to:
/// <list type="bullet">
/// <item><description>Independent API calls with no performance gain from batching (?)</description></item>
/// <item><description>Easier debugging and error tracing</description></item>
/// <item><description>Simpler maintenance as each method maps to one API concept</description></item>
/// </list>
/// </description>
/// </item>
/// </list>
/// </remarks>
public sealed class CsiShellPropertiesExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
public CsiShellPropertiesExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
public CsiShellPropertiesExtractor(
IConverterSettingsStore<CsiConversionSettings> settingsStore,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
)
{
_settingsStore = settingsStore;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
public void ExtractProperties(CsiShellWrapper shell, PropertyExtractionResult shellData)
@@ -37,7 +27,7 @@ public sealed class CsiShellPropertiesExtractor
shellData.ApplicationId = shell.GetSpeckleApplicationId(_settingsStore.Current.SapModel);
var geometry = shellData.Properties.EnsureNested(ObjectPropertyCategory.GEOMETRY);
geometry["Joints"] = GetPointNames(shell); // TODO: 🪲 Viewer shows 4 but only displays 3
geometry["Joints"] = GetPointNames(shell);
var assignments = shellData.Properties.EnsureNested(ObjectPropertyCategory.ASSIGNMENTS);
assignments[CommonObjectProperty.GROUPS] = GetGroupAssigns(shell);
@@ -16,4 +16,16 @@ public class CsiToSpeckleCacheSingleton
/// A map of (section id, shell object id). Assumes the section id is the unique name of the section
/// </summary>
public Dictionary<string, List<string>> ShellSectionCache { get; set; } = [];
/// <summary>
/// A cache of cross-sectional areas used
/// </summary>
public Dictionary<string, double> FrameSectionAreaCache { get; set; } = [];
/// <summary>
/// A cache of resolved shell section properties populated by "EtabsShellPropertiesExtractor"
/// and consumed by "EtabsShellSectionPropertyExtractor".
/// This eliminates redundant section resolution API calls.
/// </summary>
public Dictionary<string, Dictionary<string, object?>> ShellSectionPropertiesCache { get; set; } = [];
}
@@ -39,7 +39,7 @@ public abstract class CsiObjectToSpeckleConverterBase : IToSpeckleTopLevelConver
public Base Convert(object target) => Convert((CsiWrapperBase)target);
public Base Convert(CsiWrapperBase wrapper)
private Base Convert(CsiWrapperBase wrapper)
{
var displayValue = _displayValueExtractor.GetDisplayValue(wrapper).ToList();
var objectData = _applicationPropertiesExtractor.ExtractProperties(wrapper);
@@ -21,8 +21,14 @@ public static class ObjectPropertyCategory
/// </summary>
public static class ObjectPropertyKey
{
public const string AREA = "Area";
public const string CROSS_SECTIONAL_AREA = "Cross-Sectional Area";
public const string DESIGN_PROCEDURE = "Design Procedure";
public const string LENGTH = "Length";
public const string MATERIAL_ID = "Material";
public const string SECTION_ID = "Section Property";
public const string THICKNESS = "Thickness";
public const string VOLUME = "Volume";
}
/// <summary>
@@ -30,11 +36,14 @@ public static class ObjectPropertyKey
/// </summary>
public static class SectionPropertyCategory
{
public const string DESIGN_DATA = "Design Data";
public const string GENERAL_DATA = "General Data";
public const string MECHANICAL_DATA = "Mechanical Data";
public const string MODIFIERS = "Modifiers";
public const string PROPERTY_DATA = "Property Data";
public const string SECTION_PROPERTIES = "Section Properties";
public const string SECTION_DIMENSIONS = "Section Dimensions";
public const string WEIGHT_AND_MASS = "Weight and Mass";
}
/// <summary>
@@ -258,9 +258,32 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[1.0.0, )"
}
},
"speckle.objects": {
"type": "Project",
"dependencies": {
"Speckle.Sdk": "[1.0.0, )"
}
},
"speckle.sdk": {
"type": "Project",
"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": "[1.0.0, )"
}
},
"speckle.sdk.dependencies": {
"type": "Project"
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -284,38 +307,6 @@
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"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.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}

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