Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79a6c42cc7 | |||
| e2318df87a | |||
| 78d7814351 | |||
| 17a320ee53 | |||
| 396ef981ee | |||
| 763c413871 | |||
| 57a5b41ec1 | |||
| 8385532b96 | |||
| e109515852 | |||
| fdf2425ec6 | |||
| c067cf6f91 | |||
| 60e26d85c6 | |||
| 3876f7d220 | |||
| d4ee1f2a55 | |||
| 4f960cc670 | |||
| 1f63c1f8b3 | |||
| 2ed9ffbca7 | |||
| d87b862e2b | |||
| 3ad3ad2f01 | |||
| 6db7e46401 | |||
| 13fc24c7c7 | |||
| cf86158b83 | |||
| 25eb955636 | |||
| 7862a858ae | |||
| bc18d3b494 | |||
| fd34f22028 | |||
| 958c9e5e94 | |||
| 7c7260c603 | |||
| bae9e3e0f1 | |||
| 26b0394613 | |||
| 689ef0bcbe | |||
| 461585b782 | |||
| ea33f35a7d | |||
| 7427f1a2f3 | |||
| b7984bf97e | |||
| 9b24a45b6e | |||
| 4ace81a422 | |||
| a60790c92c | |||
| fd0d00cac3 | |||
| 498396e611 | |||
| 5444377398 | |||
| 9d981f9800 | |||
| 14e17fb67d | |||
| 0ffa7685fd | |||
| dc7d4671e4 | |||
| 10cb5cd66f | |||
| cb15d9f77a | |||
| da74faef9b | |||
| 4368833c7e | |||
| a20df41316 | |||
| ccf48dbad1 | |||
| 6700aa27bc | |||
| df525eab63 | |||
| 275901626f | |||
| fac0dc31b2 | |||
| 8696eca1f0 | |||
| d647c71cf5 | |||
| 9b218dd808 | |||
| 9f39dc521d | |||
| 112093f914 | |||
| d174597770 | |||
| d9289787b7 | |||
| 77c1c3b511 | |||
| 5a1c542832 | |||
| 091d7cc897 | |||
| 21f4fb52a8 | |||
| 656ed709f3 | |||
| 868ca8db66 | |||
| a9360e5fac | |||
| 3414599f72 | |||
| 84e92aa8a8 | |||
| edd842763f | |||
| 867ee0f928 | |||
| 29ee648d7f | |||
| 5b9c610856 | |||
| 769ddf6407 | |||
| dd7205f855 | |||
| 30ee410309 | |||
| 2d0b1a3a24 | |||
| 2cdf036172 | |||
| c14aa28e76 | |||
| 0e7d2554f8 | |||
| cdfc618bab | |||
| 1005edb609 | |||
| 3b4da8de52 | |||
| 58c6370cda | |||
| 0e72adba36 | |||
| d5084dc334 | |||
| 0aeecfd00a | |||
| 9ceb5621bc | |||
| 94c1d4921e | |||
| a4ff20106c | |||
| 289e25be6c | |||
| edebc8e98f | |||
| 8c21e2362b | |||
| 5d40645aef | |||
| b5ad4ac32f | |||
| 2a2801eced | |||
| 6289565f66 | |||
| 8ed6eebc2c | |||
| 58afaecce2 | |||
| 93c6df41fd | |||
| f3bcb55d8a | |||
| 0f32cb3c6d | |||
| f948417e31 | |||
| 2ddd96ebea | |||
| 06fd46a7e3 | |||
| 333ef4bb71 |
+14
-28
@@ -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 }}
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-1
@@ -69,7 +69,8 @@ public static class Consts
|
||||
new("Connectors/CSi/Speckle.Connectors.ETABS21", "net48"),
|
||||
new("Connectors/CSi/Speckle.Connectors.ETABS22", "net8.0-windows"),
|
||||
]
|
||||
)
|
||||
),
|
||||
new("rhino-importer", [new("Importers/Rhino/Speckle.Importers.JobProcessor", "net8.0-windows")]),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+17
-21
@@ -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);
|
||||
|
||||
@@ -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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -336,18 +336,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -357,14 +357,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,9 +259,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -336,18 +336,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -357,14 +357,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,9 +259,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -293,7 +293,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,9 +210,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +244,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -288,18 +288,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -307,14 +307,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +244,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -288,18 +288,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -307,14 +307,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+296
@@ -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;
|
||||
}
|
||||
}
|
||||
+23
-226
@@ -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
|
||||
) { }
|
||||
}
|
||||
|
||||
+1
@@ -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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,18 +346,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -367,14 +367,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,9 +268,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,18 +346,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -367,14 +367,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,9 +268,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,18 +346,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -367,14 +367,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,9 +219,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -254,7 +254,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -298,18 +298,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -317,14 +317,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -254,7 +254,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -298,18 +298,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -317,14 +317,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+4
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
@@ -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>
|
||||
@@ -89,7 +89,11 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
|
||||
_speckleApplication.Slug
|
||||
);
|
||||
DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json");
|
||||
_logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}");
|
||||
_logger.LogDebug(
|
||||
"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}",
|
||||
ModelPathHash,
|
||||
DocumentStateFile
|
||||
);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
|
||||
+13
-55
@@ -20,23 +20,8 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
GetMaterialName(sectionName, properties);
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties) =>
|
||||
GetSectionProperties(sectionName, properties);
|
||||
GetPropertyModifiers(sectionName, properties);
|
||||
}
|
||||
|
||||
private void GetMaterialName(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
// get material name
|
||||
string materialName = string.Empty;
|
||||
_settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
|
||||
|
||||
// append to General Data of properties dictionary
|
||||
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Material"] = materialName;
|
||||
}
|
||||
|
||||
private void GetSectionProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
@@ -69,47 +54,20 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
|
||||
ref radiusOfGyrationAboutMinorAxis
|
||||
);
|
||||
|
||||
string distanceUnit = _settingsStore.Current.SpeckleUnits;
|
||||
string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better
|
||||
string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better
|
||||
string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better
|
||||
|
||||
Dictionary<string, object?> mechanicalProperties = properties.EnsureNested(
|
||||
SectionPropertyCategory.SECTION_PROPERTIES
|
||||
);
|
||||
mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit);
|
||||
mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit);
|
||||
mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit);
|
||||
mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit);
|
||||
mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit);
|
||||
}
|
||||
|
||||
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
double[] stiffnessModifiersArray = [];
|
||||
_settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray);
|
||||
|
||||
Dictionary<string, object?> modifiers =
|
||||
new()
|
||||
{
|
||||
["Cross-section (Axial) Area"] = stiffnessModifiersArray[0],
|
||||
["Shear Area in 2 Direction"] = stiffnessModifiersArray[1],
|
||||
["Shear Area in 3 Direction"] = stiffnessModifiersArray[2],
|
||||
["Torsional Constant"] = stiffnessModifiersArray[3],
|
||||
["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4],
|
||||
["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5],
|
||||
["Mass"] = stiffnessModifiersArray[6],
|
||||
["Weight"] = stiffnessModifiersArray[7],
|
||||
};
|
||||
|
||||
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Modifiers"] = modifiers;
|
||||
mechanicalProperties.Add("Area", crossSectionalArea);
|
||||
mechanicalProperties.Add("As2", shearAreaInMajorAxisDirection);
|
||||
mechanicalProperties.Add("As3", shearAreaInMinorAxisDirection);
|
||||
mechanicalProperties.Add("J", torsionalConstant);
|
||||
mechanicalProperties.Add("I22", momentOfInertiaAboutMajorAxis);
|
||||
mechanicalProperties.Add("I33", momentOfInertiaAboutMinorAxis);
|
||||
mechanicalProperties.Add("S22", sectionModulusAboutMajorAxis);
|
||||
mechanicalProperties.Add("S33", sectionModulusAboutMinorAxis);
|
||||
mechanicalProperties.Add("Z22", plasticModulusAboutMajorAxis);
|
||||
mechanicalProperties.Add("Z33", plasticModulusAboutMinorAxis);
|
||||
mechanicalProperties.Add("R22", radiusOfGyrationAboutMajorAxis);
|
||||
mechanicalProperties.Add("R33", radiusOfGyrationAboutMinorAxis);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -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);
|
||||
|
||||
+2
@@ -19,9 +19,11 @@ public class CsiResultsExtractorFactory
|
||||
ResultsKey.BASE_REACT => _serviceProvider.GetRequiredService<CsiBaseReactResultsExtractor>(),
|
||||
ResultsKey.FRAME_FORCES => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
|
||||
ResultsKey.JOINT_REACT => _serviceProvider.GetRequiredService<CsiJointReactResultsExtractor>(),
|
||||
ResultsKey.MODAL_PERIOD => _serviceProvider.GetRequiredService<CsiModalPeriodExtractor>(),
|
||||
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
|
||||
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
|
||||
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
|
||||
ResultsKey.STORY_FORCES => _serviceProvider.GetRequiredService<CsiStoryForceResultsExtractor>(),
|
||||
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
|
||||
};
|
||||
}
|
||||
|
||||
+13
@@ -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);
|
||||
}
|
||||
+9
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-14
@@ -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;
|
||||
@@ -105,7 +113,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
|
||||
|
||||
if (results.All(x => x.Status == Status.ERROR))
|
||||
{
|
||||
throw new SpeckleException("Failed to convert all objects.");
|
||||
throw new SpeckleException("Failed to convert all objects");
|
||||
}
|
||||
|
||||
using (var _ = _activityFactory.Start("Process Proxies"))
|
||||
@@ -129,22 +137,30 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
|
||||
if (requestedResultTypes == null || requestedResultTypes.Count == 0)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
"No result type input for the requested load cases and combinations. Adjust publish settings."
|
||||
"Adjust publish settings - no result type input for the requested load cases and combinations"
|
||||
);
|
||||
}
|
||||
|
||||
if (!_csiApplicationService.SapModel.GetModelIsLocked())
|
||||
{
|
||||
throw new SpeckleException("Model unlocked. No access to analysis results.");
|
||||
throw new SpeckleException("Model unlocked, no access to analysis results");
|
||||
}
|
||||
|
||||
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
|
||||
selectedCasesAndCombinations,
|
||||
requestedResultTypes,
|
||||
objectSelectionSummary
|
||||
);
|
||||
rootObjectCollection["analysisResults"] = analysisResults;
|
||||
try
|
||||
{
|
||||
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
|
||||
selectedCasesAndCombinations,
|
||||
requestedResultTypes,
|
||||
objectSelectionSummary
|
||||
);
|
||||
rootObjectCollection[RootKeys.ANALYSIS_RESULTS] = analysisResults;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new SpeckleException("Analysis result extraction failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return new RootObjectBuilderResult(rootObjectCollection, results);
|
||||
}
|
||||
|
||||
@@ -182,12 +198,12 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
|
||||
// NOTE: CsiTendonWrapper - not typically modelled in ETABS, rather SAFE
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
_logger.LogError(ex, sourceType);
|
||||
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
|
||||
return new(Status.WARNING, applicationId, sourceType, null, ex);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, sourceType);
|
||||
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
|
||||
return new(Status.ERROR, applicationId, sourceType, null, ex);
|
||||
}
|
||||
}
|
||||
@@ -209,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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs21": {
|
||||
@@ -335,18 +335,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -356,14 +356,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,9 +210,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -236,7 +236,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs22": {
|
||||
@@ -286,18 +286,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -305,14 +305,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,18 +47,17 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
string sectionName = entry.Key;
|
||||
List<string> frameIds = entry.Value;
|
||||
|
||||
// Initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = new Dictionary<string, object?>();
|
||||
// initialize properties
|
||||
Dictionary<string, object?> properties = [];
|
||||
|
||||
// get the properties of the section
|
||||
// openings will have objects assigned to them, but won't have properties
|
||||
// sectionName is initialized with string.Empty, but api ref returns string "None"
|
||||
if (sectionName != "None")
|
||||
// Extract properties if valid section name
|
||||
// "None" is weird but api returns that string if an opening, null element etc.
|
||||
if (sectionName != "None" && !string.IsNullOrEmpty(sectionName))
|
||||
{
|
||||
properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName);
|
||||
}
|
||||
|
||||
// create the section proxy
|
||||
// create section proxy
|
||||
GroupProxy sectionProxy =
|
||||
new()
|
||||
{
|
||||
@@ -66,8 +65,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
name = sectionName,
|
||||
applicationId = sectionName,
|
||||
objects = frameIds,
|
||||
["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell
|
||||
["properties"] = properties // openings will just have an empty dict here
|
||||
["type"] = "Frame Section",
|
||||
["properties"] = properties
|
||||
};
|
||||
|
||||
yield return sectionProxy;
|
||||
@@ -81,8 +80,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
string sectionName = entry.Key;
|
||||
List<string> frameIds = entry.Value;
|
||||
|
||||
// Initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = new Dictionary<string, object?>();
|
||||
// initialize properties outside the if statement
|
||||
Dictionary<string, object?> properties = [];
|
||||
|
||||
// get the properties of the section
|
||||
// openings will have objects assigned to them, but won't have properties
|
||||
@@ -92,7 +91,7 @@ public class EtabsSectionUnpacker : ISectionUnpacker
|
||||
properties = _propertyExtractor.ExtractShellSectionProperties(sectionName);
|
||||
}
|
||||
|
||||
// create the section proxy
|
||||
// create section proxy
|
||||
GroupProxy sectionProxy =
|
||||
new()
|
||||
{
|
||||
|
||||
+75
-47
@@ -1,4 +1,5 @@
|
||||
using Speckle.Connectors.CSiShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.CSiShared;
|
||||
using Speckle.Converters.CSiShared.Utils;
|
||||
@@ -8,69 +9,96 @@ namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
|
||||
/// <summary>
|
||||
/// Extracts ETABS-specific frame section properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The bulk loading strategy is necessary here because we can't know which database table contains which section
|
||||
/// beforehand - there are multiple tables like "Frame Section Property Definitions - Steel",
|
||||
/// "Frame Section Property Definitions - Concrete", etc.
|
||||
/// </remarks>
|
||||
public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor
|
||||
{
|
||||
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
|
||||
private readonly EtabsSectionPropertyDefinitionService _definitionService;
|
||||
|
||||
public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
|
||||
public EtabsFrameSectionPropertyExtractor(
|
||||
IConverterSettingsStore<CsiConversionSettings> settingsStore,
|
||||
EtabsSectionPropertyDefinitionService definitionService
|
||||
)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
_definitionService = definitionService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets generalised frame section properties
|
||||
/// Gets frame section properties from preloaded database table data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sap2000 doesn't support this method, unfortunately
|
||||
/// Alternative is to account for extraction according to section type - we're talking over 40 section types!
|
||||
/// This way, we get basic information with minimal computational costs.
|
||||
/// Property categorization is done heuristically - order matters in the parsing logic.
|
||||
/// </remarks>
|
||||
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
|
||||
{
|
||||
// Get all frame properties
|
||||
int numberOfNames = 0;
|
||||
string[] names = [];
|
||||
eFramePropType[] propTypes = [];
|
||||
double[] t3 = [],
|
||||
t2 = [],
|
||||
tf = [],
|
||||
tw = [],
|
||||
t2b = [],
|
||||
tfb = [],
|
||||
area = [];
|
||||
|
||||
_settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2(
|
||||
ref numberOfNames,
|
||||
ref names,
|
||||
ref propTypes,
|
||||
ref t3,
|
||||
ref t2,
|
||||
ref tf,
|
||||
ref tw,
|
||||
ref t2b,
|
||||
ref tfb,
|
||||
ref area
|
||||
);
|
||||
|
||||
// Find the index of the current section
|
||||
int sectionIndex = Array.IndexOf(names, sectionName);
|
||||
|
||||
if (sectionIndex != -1)
|
||||
// get frame definitions from the service (which uses database table extraction)
|
||||
// this is a fast dictionary lookup since all data is preloaded
|
||||
if (!_definitionService.FrameDefinitions.TryGetValue(sectionName, out var rawDatabaseTableProperties))
|
||||
{
|
||||
// General Data
|
||||
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalData["Section Shape"] = propTypes[sectionIndex].ToString();
|
||||
return; // no definitions found for this section
|
||||
}
|
||||
|
||||
// Section Dimensions
|
||||
string unit = _settingsStore.Current.SpeckleUnits;
|
||||
var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS);
|
||||
sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit);
|
||||
sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²");
|
||||
// define table keys that we don't want to include in the section proxy properties
|
||||
var keysToExclude = new HashSet<string>
|
||||
{
|
||||
"GUID",
|
||||
"Name",
|
||||
"Color",
|
||||
"Notes",
|
||||
"FileName",
|
||||
"FromFile",
|
||||
"SectInFile",
|
||||
"NotAutoFact"
|
||||
};
|
||||
|
||||
// get the section type / shape using the dedicated api query (exception to the database approach)
|
||||
// this specific property isn't available in the database table extraction
|
||||
eFramePropType framePropType = 0;
|
||||
_settingsStore.Current.SapModel.PropFrame.GetTypeOAPI(sectionName, ref framePropType);
|
||||
Dictionary<string, object?> generalProperties = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
|
||||
generalProperties.Add("Section Shape", framePropType.ToString());
|
||||
|
||||
// heuristic property categorization based on key patterns and parse-ability
|
||||
// NOTE: this is gross and quite dangerous 🤨 but beats specific frame prop sect. property extractions imo
|
||||
// order matters here! we check for known string props first, then modifiers, then assume doubles are dimensions
|
||||
foreach (KeyValuePair<string, string> rawDatabaseTableProperty in rawDatabaseTableProperties)
|
||||
{
|
||||
string key = rawDatabaseTableProperty.Key;
|
||||
string value = rawDatabaseTableProperty.Value;
|
||||
|
||||
// skip metadata fields we don't care about
|
||||
if (!keysToExclude.Contains(key))
|
||||
{
|
||||
// material is always a string, grab it first
|
||||
if (key == "Material")
|
||||
{
|
||||
generalProperties.Add(key, value);
|
||||
}
|
||||
// modifier properties end with "Mod" and should be numeric
|
||||
else if (key.EndsWith("Mod") && double.TryParse(value, out double parsedModValue))
|
||||
{
|
||||
Dictionary<string, object?> modificationProperties = properties.EnsureNested(
|
||||
SectionPropertyCategory.MODIFIERS
|
||||
);
|
||||
modificationProperties.Add(key, parsedModValue);
|
||||
}
|
||||
// anything else that parses as a double is assumed to be a section dimension
|
||||
// this covers things like t3, t2, tf, tw, area, etc. without having to enumerate them all
|
||||
else if (double.TryParse(value, out double parsedDimensionValue))
|
||||
{
|
||||
Dictionary<string, object?> sectionDimensions = properties.EnsureNested(
|
||||
SectionPropertyCategory.SECTION_DIMENSIONS
|
||||
);
|
||||
sectionDimensions.Add(key, parsedDimensionValue);
|
||||
}
|
||||
// if it doesn't parse as double and isn't a known string property, we skip it
|
||||
// this is acceptable - we'd rather miss some edge case properties than crash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+219
@@ -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;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.CSiShared;
|
||||
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
|
||||
|
||||
namespace Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Loads and caches section property definitions from database tables for both frame and shell sections.
|
||||
/// </summary>
|
||||
public class EtabsSectionPropertyDefinitionService
|
||||
{
|
||||
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> FrameDefinitions { get; }
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> ShellDefinitions { get; }
|
||||
|
||||
public EtabsSectionPropertyDefinitionService(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
IConverterSettingsStore<CsiConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
|
||||
var availableTableKeys = GetAvailableTableKeys();
|
||||
|
||||
FrameDefinitions = LoadFrameDefinitions(databaseTableExtractor, availableTableKeys);
|
||||
ShellDefinitions = LoadShellDefinitions(databaseTableExtractor, availableTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadFrameDefinitions(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
string[] availableTableKeys
|
||||
)
|
||||
{
|
||||
var frameTableKeys = GetFrameSectionPropertyDefinitionTableKeys(availableTableKeys);
|
||||
return LoadDefinitionsFromTables(databaseTableExtractor, frameTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadShellDefinitions(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
string[] availableTableKeys
|
||||
)
|
||||
{
|
||||
var shellTableKeys = GetShellSectionPropertyDefinitionTableKeys(availableTableKeys);
|
||||
return LoadDefinitionsFromTables(databaseTableExtractor, shellTableKeys);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadDefinitionsFromTables(
|
||||
DatabaseTableExtractor databaseTableExtractor,
|
||||
IEnumerable<string> tableKeys
|
||||
)
|
||||
{
|
||||
var definitions = new Dictionary<string, IReadOnlyDictionary<string, string>>();
|
||||
|
||||
foreach (string tableKey in tableKeys)
|
||||
{
|
||||
var tableData = databaseTableExtractor.GetTableData(tableKey, "Name");
|
||||
foreach (var row in tableData.Rows)
|
||||
{
|
||||
definitions[row.Key] = row.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetFrameSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
|
||||
{
|
||||
var keysToExclude = new HashSet<string>
|
||||
{
|
||||
"Frame Section Property Definitions - Summary",
|
||||
"Frame Section Property Definitions - Concrete Beam Reinforcing",
|
||||
"Frame Section Property Definitions - Concrete Column Reinforcing"
|
||||
};
|
||||
|
||||
return availableTableKeys.Where(key =>
|
||||
key.StartsWith("Frame Section Property Definitions") && !keysToExclude.Contains(key)
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetShellSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
|
||||
{
|
||||
var keysToExclude = new HashSet<string> { "Area Section Property Definitions - Summary" };
|
||||
|
||||
return availableTableKeys.Where(key =>
|
||||
key.StartsWith("Area Section Property Definitions") && !keysToExclude.Contains(key)
|
||||
);
|
||||
}
|
||||
|
||||
private string[] GetAvailableTableKeys()
|
||||
{
|
||||
int numberTables = 0;
|
||||
string[] tableKey = [],
|
||||
tableName = [];
|
||||
int[] importType = [];
|
||||
|
||||
_ = _settingsStore.Current.SapModel.DatabaseTables.GetAvailableTables(
|
||||
ref numberTables,
|
||||
ref tableKey,
|
||||
ref tableName,
|
||||
ref importType
|
||||
);
|
||||
|
||||
return tableKey;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -34,7 +34,7 @@ public class EtabsSectionPropertyExtractor
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ExtractFrameSectionProperties(string sectionName)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
Dictionary<string, object?> properties = [];
|
||||
_csiFrameExtractor.ExtractProperties(sectionName, properties);
|
||||
_etabsFrameExtractor.ExtractProperties(sectionName, properties);
|
||||
return properties;
|
||||
@@ -45,7 +45,7 @@ public class EtabsSectionPropertyExtractor
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ExtractShellSectionProperties(string sectionName)
|
||||
{
|
||||
Dictionary<string, object?> properties = new();
|
||||
Dictionary<string, object?> properties = [];
|
||||
_csiShellExtractor.ExtractProperties(sectionName, properties);
|
||||
_etabsShellExtractor.ExtractProperties(sectionName, properties);
|
||||
return properties;
|
||||
|
||||
+32
-32
@@ -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,8 +1,10 @@
|
||||
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;
|
||||
using Speckle.Converters.ETABSShared;
|
||||
|
||||
namespace Speckle.Connectors.ETABSShared;
|
||||
@@ -12,12 +14,18 @@ public static class ServiceRegistration
|
||||
public static IServiceCollection AddEtabs(this IServiceCollection services)
|
||||
{
|
||||
services.AddEtabsConverters();
|
||||
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
|
||||
|
||||
// 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<EtabsSectionPropertyDefinitionService>();
|
||||
services.AddScoped<EtabsSectionPropertyExtractor>();
|
||||
services.AddScoped<EtabsShellSectionResolver>();
|
||||
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
|
||||
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
+3
-1
@@ -9,12 +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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2020": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2021": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2022": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2023": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2024": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -291,7 +291,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2025": {
|
||||
@@ -337,18 +337,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +358,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2026": {
|
||||
@@ -339,18 +339,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +360,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+4
@@ -16,6 +16,7 @@ using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
@@ -52,6 +53,9 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddScoped<NavisworksMaterialUnpacker>();
|
||||
serviceCollection.AddScoped<NavisworksColorUnpacker>();
|
||||
|
||||
// Register dual shared geometry stores for instancing pattern
|
||||
serviceCollection.AddScoped<InstanceStoreManager>();
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, NavisworksIdleManager>();
|
||||
|
||||
// Sending operations
|
||||
|
||||
+20
-3
@@ -1,7 +1,10 @@
|
||||
using Autodesk.Navisworks.Api.ComApi;
|
||||
using Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converter.Navisworks.ToSpeckle;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
@@ -11,7 +14,8 @@ namespace Speckle.Connector.Navisworks.HostApp;
|
||||
public class NavisworksMaterialUnpacker(
|
||||
ILogger<NavisworksMaterialUnpacker> logger,
|
||||
IConverterSettingsStore<NavisworksConversionSettings> converterSettings,
|
||||
IElementSelectionService selectionService
|
||||
IElementSelectionService selectionService,
|
||||
GeometryToSpeckleConverter converter
|
||||
)
|
||||
{
|
||||
// Helper function to select a property based on the representation mode
|
||||
@@ -64,6 +68,19 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject);
|
||||
var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId;
|
||||
|
||||
var item = selectionService.GetModelItemFromPath(finalId);
|
||||
string hashId = "";
|
||||
var comSelection = ComApiBridge.ToInwOpSelection([item]);
|
||||
var paths = comSelection.Paths();
|
||||
var path = paths.OfType<InwOaPath>().First();
|
||||
var fragments = path.Fragments();
|
||||
if (fragments.Count > 1)
|
||||
{
|
||||
var fragmentId = converter.GenerateFragmentId(paths);
|
||||
hashId = $"geom_{fragmentId}";
|
||||
}
|
||||
|
||||
var geometry = navisworksObject.Geometry;
|
||||
var mode = converterSettings.Current.User.VisualRepresentationMode;
|
||||
|
||||
@@ -120,7 +137,7 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
if (renderMaterialProxies.TryGetValue(renderMaterialId.ToString(), out RenderMaterialProxy? value))
|
||||
{
|
||||
value.objects.Add(finalId);
|
||||
value.objects.Add(!string.IsNullOrEmpty(hashId) ? hashId : finalId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,7 +149,7 @@ public class NavisworksMaterialUnpacker(
|
||||
renderColor,
|
||||
renderMaterialId
|
||||
),
|
||||
objects = [finalId]
|
||||
objects = [!string.IsNullOrEmpty(hashId) ? hashId : finalId]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+49
-9
@@ -6,6 +6,7 @@ using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Conversion;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Data;
|
||||
@@ -25,7 +26,8 @@ public class NavisworksRootObjectBuilder(
|
||||
ISdkActivityFactory activityFactory,
|
||||
NavisworksMaterialUnpacker materialUnpacker,
|
||||
NavisworksColorUnpacker colorUnpacker,
|
||||
IElementSelectionService elementSelectionService
|
||||
IElementSelectionService elementSelectionService,
|
||||
InstanceStoreManager instanceStoreManager
|
||||
) : IRootObjectBuilder<NAV.ModelItem>
|
||||
{
|
||||
private bool SkipNodeMerging { get; set; }
|
||||
@@ -41,7 +43,7 @@ public class NavisworksRootObjectBuilder(
|
||||
{
|
||||
#if DEBUG
|
||||
// This is a temporary workaround to disable node merging for debugging purposes - false is default, true is for debugging
|
||||
SkipNodeMerging = false;
|
||||
SkipNodeMerging = true;
|
||||
#endif
|
||||
using var activity = activityFactory.Start("Build");
|
||||
|
||||
@@ -50,22 +52,42 @@ public class NavisworksRootObjectBuilder(
|
||||
// 2. Initialize root collection
|
||||
var rootCollection = InitializeRootCollection();
|
||||
|
||||
// InstanceStoreManager is scoped - starts fresh for each conversion session
|
||||
|
||||
// 3. Convert all model items and store results
|
||||
var (convertedElements, conversionResults) = await ConvertModelItemsAsync(
|
||||
navisworksModelItems,
|
||||
projectId,
|
||||
onOperationProgressed,
|
||||
cancellationToken
|
||||
);
|
||||
(Dictionary<string, Base?> convertedElements, List<SendConversionResult> conversionResults) =
|
||||
await ConvertModelItemsAsync(navisworksModelItems, projectId, onOperationProgressed, cancellationToken);
|
||||
|
||||
ValidateConversionResults(conversionResults);
|
||||
|
||||
var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems);
|
||||
var finalElements = BuildFinalElements(convertedElements, groupedNodes);
|
||||
List<Base> geometryDefinitions = instanceStoreManager.GetGeometryDefinitions();
|
||||
|
||||
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
|
||||
|
||||
rootCollection.elements = finalElements;
|
||||
// rootCollection.elements will contain two Collections: one for geometry definitions and one for the main elements
|
||||
|
||||
var geometryDefinitionsCollection = new Collection
|
||||
{
|
||||
name = "Geometry Definitions",
|
||||
["units"] = converterSettings.Current.Derived.SpeckleUnits,
|
||||
elements = geometryDefinitions
|
||||
};
|
||||
|
||||
var mainElementsCollection = new Collection
|
||||
{
|
||||
name = rootCollection.name,
|
||||
["units"] = converterSettings.Current.Derived.SpeckleUnits,
|
||||
elements = finalElements
|
||||
};
|
||||
|
||||
rootCollection.elements = [mainElementsCollection];
|
||||
if (geometryDefinitions.Count > 0)
|
||||
{
|
||||
rootCollection.elements.Add(geometryDefinitionsCollection);
|
||||
}
|
||||
|
||||
return new RootObjectBuilderResult(rootCollection, conversionResults);
|
||||
}
|
||||
|
||||
@@ -288,6 +310,24 @@ public class NavisworksRootObjectBuilder(
|
||||
rootCollection[ProxyKeys.COLOR] = colors;
|
||||
}
|
||||
|
||||
// Add instance definition proxies from dual store
|
||||
var instanceDefinitionProxies = instanceStoreManager.GetInstanceDefinitionProxies();
|
||||
logger.LogDebug("Retrieved {Count} instance definition proxies from store", instanceDefinitionProxies.Count);
|
||||
|
||||
if (instanceDefinitionProxies.Count > 0)
|
||||
{
|
||||
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies.ToList();
|
||||
logger.LogDebug(
|
||||
"Added {Count} instance definition proxies to root collection under key '{Key}'",
|
||||
instanceDefinitionProxies.Count,
|
||||
ProxyKeys.INSTANCE_DEFINITION
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogDebug("No instance definition proxies to add to root collection");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +306,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2022": {
|
||||
@@ -351,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -366,9 +366,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -378,14 +378,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,9 +281,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +306,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2023": {
|
||||
@@ -351,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -366,9 +366,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -378,14 +378,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,9 +281,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +306,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2024": {
|
||||
@@ -351,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -366,9 +366,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -378,14 +378,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,9 +226,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -251,7 +251,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2025": {
|
||||
@@ -296,11 +296,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -311,9 +311,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -321,14 +321,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"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.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +244,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2026": {
|
||||
@@ -280,11 +280,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -295,9 +295,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -305,14 +305,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"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;
|
||||
@@ -15,18 +16,30 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
|
||||
|
||||
public void ExecuteScript(string script)
|
||||
{
|
||||
Browser.Dispatcher.Invoke(
|
||||
() =>
|
||||
{
|
||||
//avoid exceptions by checking if IBrowser is there
|
||||
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
|
||||
try
|
||||
{
|
||||
Browser.Dispatcher.Invoke(
|
||||
() =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
Browser.ExecuteScriptAsync(script);
|
||||
},
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
//avoid exceptions by checking if IBrowser is there
|
||||
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.ExecuteScriptAsync(script);
|
||||
},
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
}
|
||||
catch (SEHException)
|
||||
{
|
||||
//do nothing as we can't control external components
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
//do nothing, happens when closing Revit while a script is being executed
|
||||
}
|
||||
}
|
||||
|
||||
public void SendProgress(string script) => ExecuteScript(script);
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class RevitReceiveBinding(
|
||||
ICancellationManager cancellationManager,
|
||||
IBrowserBridge parent,
|
||||
ILogger<RevitReceiveBinding> logger,
|
||||
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
|
||||
Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
|
||||
IRevitConversionSettingsFactory revitConversionSettingsFactory,
|
||||
IReceiveOperationManagerFactory receiveOperationManagerFactory
|
||||
) : IReceiveBinding
|
||||
@@ -24,8 +24,7 @@ public sealed class RevitReceiveBinding(
|
||||
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
|
||||
|
||||
#pragma warning disable CA1024
|
||||
public List<ICardSetting> GetReceiveSettings() =>
|
||||
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
|
||||
public List<ICardSetting> GetReceiveSettings() => [new Operations.Receive.Settings.ReceiveReferencePointSetting()];
|
||||
#pragma warning restore CA1024
|
||||
|
||||
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
|
||||
|
||||
@@ -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;
|
||||
@@ -97,11 +101,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
public List<ICardSetting> GetSendSettings() =>
|
||||
[
|
||||
new DetailLevelSetting(DetailLevelType.Medium),
|
||||
new ReferencePointSetting(ReferencePointType.InternalOrigin),
|
||||
new SendParameterNullOrEmptyStringsSetting(false),
|
||||
new LinkedModelsSetting(true),
|
||||
new SendRebarsAsVolumetricSetting(false)
|
||||
new DetailLevelSetting(),
|
||||
new SendReferencePointSetting(),
|
||||
new SendParameterNullOrEmptyStringsSetting(),
|
||||
new LinkedModelsSetting(),
|
||||
new SendRebarsAsVolumetricSetting()
|
||||
];
|
||||
|
||||
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
@@ -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.NotNull().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
|
||||
@@ -236,13 +240,16 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
|
||||
{
|
||||
ICollection<ElementId> modifiedElementIds = e.GetModifiedElementIds();
|
||||
|
||||
var doc = e.GetDocument();
|
||||
if (doc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// NOTE: Whenever we save data into file this event also trigger changes on its DataStorage.
|
||||
// On every add/remove/update model attempt triggers this handler and was causing unnecessary calls on `RunExpirationChecks`
|
||||
// Re-check it once we implement Linked Documents
|
||||
if (modifiedElementIds.Count == 1)
|
||||
{
|
||||
var doc = e.GetDocument();
|
||||
if (modifiedElementIds.All(el => doc.GetElement(el) is DataStorage))
|
||||
{
|
||||
return;
|
||||
@@ -272,7 +279,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
}
|
||||
|
||||
if (HaveUnitsChanged(e.GetDocument()))
|
||||
if (HaveUnitsChanged(doc))
|
||||
{
|
||||
var objectIds = new List<string>();
|
||||
foreach (var sender in _store.GetSenders().ToList())
|
||||
@@ -285,7 +292,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
var selectedObjects = sender.SendFilter.NotNull().SelectedObjectIds;
|
||||
objectIds.AddRange(selectedObjects);
|
||||
}
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
@@ -329,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,10 +360,13 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
// {
|
||||
// await Commands.RefreshSendFilters();
|
||||
// }
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
if (doc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
ChangedObjectIds.Any(e => _revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View)
|
||||
)
|
||||
if (ChangedObjectIds.Any(e => doc.GetElement(e) is View))
|
||||
{
|
||||
await Commands.RefreshSendFilters();
|
||||
}
|
||||
@@ -361,7 +376,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
{
|
||||
var senders = _store.GetSenders().ToList();
|
||||
// string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
|
||||
var doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
|
||||
if (doc == null)
|
||||
{
|
||||
@@ -403,7 +418,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
}
|
||||
}
|
||||
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds);
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds, doc);
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
|
||||
// Note: we're doing object selection and card expiry management by old school ids
|
||||
@@ -438,6 +453,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private async Task OnDocumentChanged()
|
||||
{
|
||||
_sendConversionCache.ClearCache();
|
||||
_revitToSpeckleCacheSingleton.ClearCache();
|
||||
|
||||
if (_cancellationManager.NumberOfOperations > 0)
|
||||
{
|
||||
|
||||
-1
@@ -37,7 +37,6 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
|
||||
|
||||
// Storage Schema
|
||||
serviceCollection.AddScoped<DocumentModelStorageSchema>();
|
||||
serviceCollection.AddScoped<IdStorageSchema>();
|
||||
|
||||
// POC: we need to review the scopes and create a document on what the policy is
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
public class DocumentModelStorageSchema : IStorageSchema
|
||||
{
|
||||
private readonly Guid _schemaGuid = new("D690F2B4-BDB0-4CB4-8657-17844ADF42AA");
|
||||
|
||||
public Schema GetSchema()
|
||||
{
|
||||
Schema schema = Schema.Lookup(_schemaGuid);
|
||||
if (schema != null)
|
||||
{
|
||||
return schema;
|
||||
}
|
||||
|
||||
using SchemaBuilder builder = new(_schemaGuid);
|
||||
builder.SetSchemaName("DUI3State");
|
||||
builder.AddSimpleField("contents", typeof(string));
|
||||
return builder.Finish();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.Architecture;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
@@ -11,15 +8,6 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
/// </summary>
|
||||
public class ElementUnpacker
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
|
||||
public ElementUnpacker(RevitContext revitContext, IConverterSettingsStore<RevitConversionSettings> converterSettings)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a random set of revit objects into atomic objects. It currently unpacks groups recurisvely, nested families into atomic family instances.
|
||||
/// This method will also "pack" curtain walls if necessary (ie, if mullions or panels are selected without their parent curtain wall, they are sent independently; if the parent curtain wall is selected, they will be removed out as the curtain wall will include all its children).
|
||||
@@ -29,7 +17,7 @@ public class ElementUnpacker
|
||||
/// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.<br/>
|
||||
/// 2- Evicting the cache while introducing the settings</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document? doc = null)
|
||||
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document doc)
|
||||
{
|
||||
// Note: steps kept separate on purpose.
|
||||
// Step 1: unpack groups
|
||||
@@ -50,24 +38,19 @@ public class ElementUnpacker
|
||||
/// <remarks>
|
||||
/// This is used to invalidate object ids in the send conversion cache when the selected object id is only the parent element id
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds)
|
||||
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds, Document doc)
|
||||
{
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
var docElements = doc.GetElements(objectIds);
|
||||
return UnpackSelectionForConversion(docElements).Select(o => o.UniqueId).ToList();
|
||||
|
||||
return UnpackSelectionForConversion(docElements, doc).Select(o => o.UniqueId).ToList();
|
||||
}
|
||||
|
||||
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
|
||||
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
|
||||
// 2- Evicting the cache while introducing the settings
|
||||
private List<Element> UnpackElements(IEnumerable<Element> elements, Document? doc = null)
|
||||
private List<Element> UnpackElements(IEnumerable<Element> elements, Document doc)
|
||||
{
|
||||
var unpackedElements = new List<Element>(); // note: could be a hashset/map so we prevent duplicates (?)
|
||||
if (doc == null)
|
||||
{
|
||||
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
}
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
// UNPACK: Groups
|
||||
@@ -77,7 +60,7 @@ public class ElementUnpacker
|
||||
// We add null checks to handle cases where elements can't be properly resolved
|
||||
// POC: this might screw up generating hosting rel generation here, because nested families in groups get flattened out by GetMemberIds().
|
||||
var groupElements = g.GetMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(groupElements));
|
||||
unpackedElements.AddRange(UnpackElements(groupElements, doc));
|
||||
}
|
||||
else if (element is BaseArray baseArray)
|
||||
{
|
||||
@@ -85,8 +68,8 @@ public class ElementUnpacker
|
||||
// This handles cases where some elements might not resolve in linked contexts
|
||||
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(arrayElements));
|
||||
unpackedElements.AddRange(UnpackElements(originalElements));
|
||||
unpackedElements.AddRange(UnpackElements(arrayElements, doc));
|
||||
unpackedElements.AddRange(UnpackElements(originalElements, doc));
|
||||
}
|
||||
// UNPACK: Family instances (as they potentially have nested families inside)
|
||||
else if (element is FamilyInstance familyInstance)
|
||||
@@ -99,7 +82,7 @@ public class ElementUnpacker
|
||||
|
||||
if (familyElements.Length != 0)
|
||||
{
|
||||
unpackedElements.AddRange(UnpackElements(familyElements));
|
||||
unpackedElements.AddRange(UnpackElements(familyElements, doc));
|
||||
}
|
||||
|
||||
unpackedElements.Add(familyInstance);
|
||||
@@ -107,7 +90,7 @@ public class ElementUnpacker
|
||||
else if (element is MultistoryStairs multistoryStairs)
|
||||
{
|
||||
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(stairs));
|
||||
unpackedElements.AddRange(UnpackElements(stairs, doc));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -125,13 +108,11 @@ public class ElementUnpacker
|
||||
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
|
||||
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
|
||||
// 2- Evicting the cache while introducing the settings
|
||||
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document? doc = null)
|
||||
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document doc)
|
||||
{
|
||||
var ids = elements.Select(el => el.Id).ToArray();
|
||||
if (doc == null)
|
||||
{
|
||||
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
}
|
||||
//just used for contains so use ToHashSet
|
||||
var ids = elements.Select(el => el.Id).ToHashSet();
|
||||
|
||||
elements.RemoveAll(element =>
|
||||
(element is Mullion { Host: not null } m && ids.Contains(m.Host.Id))
|
||||
|| (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
using Autodesk.Revit.UI;
|
||||
using Autodesk.Revit.UI.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -20,7 +19,6 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
private readonly ILogger<RevitDocumentStore> _logger;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
|
||||
@@ -28,7 +26,6 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
IJsonSerializer jsonSerializer,
|
||||
DocumentModelStorageSchema documentModelStorageSchema,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IRevitTask revitTask,
|
||||
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
|
||||
@@ -39,7 +36,6 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_documentModelStorageSchema = documentModelStorageSchema;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_logger = logger;
|
||||
|
||||
@@ -101,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())
|
||||
{
|
||||
@@ -115,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()
|
||||
@@ -131,42 +135,13 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
return;
|
||||
}
|
||||
|
||||
var stateEntity = GetSpeckleEntity(document);
|
||||
if (stateEntity == null || !stateEntity.IsValid())
|
||||
var key = GetKeyForDocument(document);
|
||||
if (key is null)
|
||||
{
|
||||
ClearAndSave();
|
||||
LoadFromString(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var key = GetKeyForDocument(document);
|
||||
if (key != null)
|
||||
{
|
||||
var state = _jsonCacheManager.GetObject(key);
|
||||
LoadFromString(state);
|
||||
}
|
||||
}
|
||||
|
||||
private Entity? GetSpeckleEntity(Document? doc)
|
||||
{
|
||||
if (doc is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
using FilteredElementCollector collector = new(doc);
|
||||
|
||||
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
|
||||
foreach (Element element in dataStorages)
|
||||
{
|
||||
DataStorage dataStorage = (DataStorage)element;
|
||||
Entity settingEntity = dataStorage.GetEntity(_documentModelStorageSchema.GetSchema());
|
||||
if (!settingEntity.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return settingEntity;
|
||||
}
|
||||
|
||||
return null;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -3,9 +3,13 @@ using Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
|
||||
|
||||
public class ReferencePointSetting(ReceiveReferencePointType value) : ICardSetting
|
||||
public class ReceiveReferencePointSetting(ReceiveReferencePointType value = ReceiveReferencePointSetting.DEFAULT_VALUE)
|
||||
: ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "referencePoint";
|
||||
public const string SETTING_ID = "referencePoint";
|
||||
public const ReceiveReferencePointType DEFAULT_VALUE = ReceiveReferencePointType.Source;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Reference Point";
|
||||
public string? Type { get; set; } = "string";
|
||||
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReceiveReferencePointType)).ToList();
|
||||
+21
-8
@@ -66,8 +66,8 @@ public sealed class RevitHostObjectBuilder(
|
||||
// TODO: formalise getting transform info from rootObject. this dict access is gross.
|
||||
Autodesk.Revit.DB.Transform? referencePointTransformFromRootObject = null;
|
||||
if (
|
||||
rootObject.DynamicPropertyKeys.Contains(ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY)
|
||||
&& rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] is Dictionary<string, object> transformDict
|
||||
rootObject.DynamicPropertyKeys.Contains(RootKeys.REFERENCE_POINT_TRANSFORM)
|
||||
&& rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] is Dictionary<string, object> transformDict
|
||||
&& transformDict.TryGetValue("transform", out var transformValue)
|
||||
)
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-5
@@ -1,5 +1,6 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.UI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
@@ -11,18 +12,21 @@ namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
|
||||
public class ToHostSettingsManager : IToHostSettingsManager
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ILogger<ToHostSettingsManager> _logger;
|
||||
|
||||
public ToHostSettingsManager(RevitContext revitContext)
|
||||
public ToHostSettingsManager(RevitContext revitContext, ILogger<ToHostSettingsManager> logger)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Transform? GetReferencePointSetting(ModelCard modelCard)
|
||||
{
|
||||
var referencePointString = modelCard.Settings?.FirstOrDefault(s => s.Id == "referencePoint")?.Value as string;
|
||||
var referencePointString =
|
||||
modelCard.Settings?.FirstOrDefault(s => s.Id == ReceiveReferencePointSetting.SETTING_ID)?.Value as string;
|
||||
if (
|
||||
referencePointString is not null
|
||||
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
|
||||
&& ReceiveReferencePointSetting.ReferencePointMap.TryGetValue(
|
||||
referencePointString,
|
||||
out ReceiveReferencePointType referencePoint
|
||||
)
|
||||
@@ -34,7 +38,16 @@ public class ToHostSettingsManager : IToHostSettingsManager
|
||||
return currentTransform;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
|
||||
// log the issue
|
||||
_logger.LogWarning(
|
||||
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
|
||||
referencePointString,
|
||||
modelCard.ModelCardId,
|
||||
ReceiveReferencePointSetting.DEFAULT_VALUE
|
||||
);
|
||||
|
||||
// return default (null for Source means no transform)
|
||||
return null;
|
||||
}
|
||||
|
||||
private Transform? GetTransform(ReceiveReferencePointType referencePointType)
|
||||
@@ -91,7 +104,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Revit Context UI Application was null when retrieving reference point transform."
|
||||
"Revit Context UI Application was null when retrieving reference point transform"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-15
@@ -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)
|
||||
|
||||
+13
-1
@@ -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;
|
||||
|
||||
@@ -255,7 +267,7 @@ public class RevitRootObjectBuilder(
|
||||
if (converterSettings.Current.ReferencePointTransform is Transform transform)
|
||||
{
|
||||
var transformMatrix = ReferencePointHelper.CreateTransformDataForRootObject(transform);
|
||||
rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] = transformMatrix;
|
||||
rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] = transformMatrix;
|
||||
}
|
||||
|
||||
return new RootObjectBuilderResult(rootObject, results);
|
||||
|
||||
+5
-2
@@ -3,9 +3,12 @@ using Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class DetailLevelSetting(DetailLevelType value) : ICardSetting
|
||||
public class DetailLevelSetting(DetailLevelType value = DetailLevelSetting.DEFAULT_VALUE) : ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "detailLevel";
|
||||
public const string SETTING_ID = "detailLevel";
|
||||
public const DetailLevelType DEFAULT_VALUE = DetailLevelType.Medium;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Detail Level";
|
||||
public string? Type { get; set; } = "string";
|
||||
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(DetailLevelType)).ToList();
|
||||
|
||||
+5
-2
@@ -2,9 +2,12 @@ using Speckle.Connectors.DUI.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class LinkedModelsSetting(bool value) : ICardSetting
|
||||
public class LinkedModelsSetting(bool value = LinkedModelsSetting.DEFAULT_VALUE) : ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "includeLinkedModels";
|
||||
public const string SETTING_ID = "includeLinkedModels";
|
||||
public const bool DEFAULT_VALUE = true;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Include Linked Models";
|
||||
public string? Type { get; set; } = "boolean";
|
||||
public object? Value { get; set; } = value;
|
||||
|
||||
+6
-2
@@ -2,9 +2,13 @@ using Speckle.Connectors.DUI.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class SendParameterNullOrEmptyStringsSetting(bool value) : ICardSetting
|
||||
public class SendParameterNullOrEmptyStringsSetting(bool value = SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE)
|
||||
: ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "nullemptyparams";
|
||||
public const string SETTING_ID = "nullemptyparams";
|
||||
public const bool DEFAULT_VALUE = false;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Send null/empty parameters";
|
||||
public string? Type { get; set; } = "boolean";
|
||||
public List<string>? Enum { get; set; }
|
||||
|
||||
+5
-2
@@ -2,9 +2,12 @@ using Speckle.Connectors.DUI.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class SendRebarsAsVolumetricSetting(bool value) : ICardSetting
|
||||
public class SendRebarsAsVolumetricSetting(bool value = SendRebarsAsVolumetricSetting.DEFAULT_VALUE) : ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "sendRebarsAsVolumetric";
|
||||
public const string SETTING_ID = "sendRebarsAsVolumetric";
|
||||
public const bool DEFAULT_VALUE = false;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Send Rebars As Volumetric (disable for better performance)";
|
||||
public string? Type { get; set; } = "boolean";
|
||||
public object? Value { get; set; } = value;
|
||||
|
||||
+6
-2
@@ -3,9 +3,13 @@ using Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class ReferencePointSetting(ReferencePointType value) : ICardSetting
|
||||
public class SendReferencePointSetting(ReferencePointType value = SendReferencePointSetting.DEFAULT_VALUE)
|
||||
: ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "referencePoint";
|
||||
public const string SETTING_ID = "referencePoint";
|
||||
public const ReferencePointType DEFAULT_VALUE = ReferencePointType.InternalOrigin;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Reference Point";
|
||||
public string? Type { get; set; } = "string";
|
||||
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReferencePointType)).ToList();
|
||||
+143
-116
@@ -1,9 +1,8 @@
|
||||
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.Common;
|
||||
@@ -11,33 +10,23 @@ 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;
|
||||
|
||||
// cache invalidation process run with ModelCardId since the settings are model specific
|
||||
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = new();
|
||||
private readonly Dictionary<string, Transform?> _referencePointCache = new();
|
||||
private readonly Dictionary<string, bool?> _sendNullParamsCache = new();
|
||||
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = new();
|
||||
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = new();
|
||||
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = [];
|
||||
private readonly Dictionary<string, Transform?> _referencePointCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendNullParamsCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
|
||||
|
||||
public ToSpeckleSettingsManager(
|
||||
RevitContext revitContext,
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker
|
||||
)
|
||||
public DetailLevelType GetDetailLevelSetting(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_elementUnpacker = elementUnpacker;
|
||||
_sendConversionCache = sendConversionCache;
|
||||
}
|
||||
|
||||
public DetailLevelType GetDetailLevelSetting(SenderModelCard modelCard)
|
||||
{
|
||||
var fidelityString = modelCard.Settings?.FirstOrDefault(s => s.Id == "detailLevel")?.Value as string;
|
||||
var fidelityString =
|
||||
modelCard.Settings?.FirstOrDefault(s => s.Id == DetailLevelSetting.SETTING_ID)?.Value as string;
|
||||
if (
|
||||
fidelityString is not null
|
||||
&& DetailLevelSetting.GeometryFidelityMap.TryGetValue(fidelityString, out DetailLevelType fidelity)
|
||||
@@ -47,22 +36,34 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
{
|
||||
if (previousType != fidelity)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
EvictCacheForModelCard(document, modelCard);
|
||||
}
|
||||
}
|
||||
_detailLevelCache[modelCard.ModelCardId.NotNull()] = fidelity;
|
||||
return fidelity;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid geometry fidelity value: {fidelityString}");
|
||||
// log the issue
|
||||
logger.LogWarning(
|
||||
"Invalid detail level setting received: '{FidelityString}' for model {ModelCardId}, using default: {DefaultValue}",
|
||||
fidelityString,
|
||||
modelCard.ModelCardId,
|
||||
DetailLevelSetting.DEFAULT_VALUE
|
||||
);
|
||||
|
||||
// return sensible default
|
||||
DetailLevelType defaultValue = DetailLevelSetting.DEFAULT_VALUE;
|
||||
_detailLevelCache[modelCard.ModelCardId.NotNull()] = defaultValue;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Transform? GetReferencePointSetting(ModelCard modelCard)
|
||||
public Transform? GetReferencePointSetting(Document document, ModelCard modelCard)
|
||||
{
|
||||
var referencePointString = modelCard.Settings?.FirstOrDefault(s => s.Id == "referencePoint")?.Value as string;
|
||||
var referencePointString =
|
||||
modelCard.Settings?.FirstOrDefault(s => s.Id == SendReferencePointSetting.SETTING_ID)?.Value as string;
|
||||
if (
|
||||
referencePointString is not null
|
||||
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
|
||||
&& SendReferencePointSetting.ReferencePointMap.TryGetValue(
|
||||
referencePointString,
|
||||
out ReferencePointType referencePoint
|
||||
)
|
||||
@@ -70,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,118 +86,144 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
return currentTransform;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
|
||||
// log the issue
|
||||
logger.LogWarning(
|
||||
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
|
||||
referencePointString,
|
||||
modelCard.ModelCardId,
|
||||
SendReferencePointSetting.DEFAULT_VALUE
|
||||
);
|
||||
|
||||
// return default (null for InternalOrigin means no transform)
|
||||
_referencePointCache[modelCard.ModelCardId.NotNull()] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "nullemptyparams")?.Value as bool?;
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_sendNullParamsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
_sendNullParamsCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
public bool GetSendParameterNullOrEmptyStringsSetting(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
SendParameterNullOrEmptyStringsSetting.SETTING_ID,
|
||||
SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
_sendNullParamsCache,
|
||||
"Send null/empty parameters"
|
||||
);
|
||||
|
||||
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
|
||||
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
|
||||
public bool GetLinkedModelsSetting(SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "includeLinkedModels")?.Value as bool?;
|
||||
var returnValue = value != null && value.NotNull();
|
||||
public bool GetLinkedModelsSetting(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
LinkedModelsSetting.SETTING_ID,
|
||||
LinkedModelsSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
_sendLinkedModelsCache,
|
||||
"Linked models"
|
||||
);
|
||||
|
||||
if (_sendLinkedModelsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
|
||||
public bool GetSendRebarsAsVolumetric(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
SendRebarsAsVolumetricSetting.SETTING_ID,
|
||||
SendRebarsAsVolumetricSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
_sendRebarsAsVolumetricCache,
|
||||
"Send rebars as volumetric"
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to handle boolean settings with caching and logging
|
||||
/// </summary>
|
||||
private bool GetBooleanSettingWithCache(
|
||||
Document document,
|
||||
string settingId,
|
||||
bool defaultValue,
|
||||
SenderModelCard modelCard,
|
||||
Dictionary<string, bool?> cache,
|
||||
string settingName
|
||||
)
|
||||
{
|
||||
var settingValue = modelCard.Settings?.FirstOrDefault(s => s.Id == settingId)?.Value as bool?;
|
||||
bool returnValue = settingValue ?? defaultValue;
|
||||
|
||||
if (cache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
EvictCacheForModelCard(document, modelCard);
|
||||
}
|
||||
}
|
||||
_sendLinkedModelsCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "sendRebarsAsVolumetric")?.Value as bool?;
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_sendRebarsAsVolumetricCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
|
||||
cache[modelCard.ModelCardId] = returnValue;
|
||||
|
||||
// NOTE: we probably don't need to log here BUT considering users might complain that a setting might not have been
|
||||
// respected (linked models disabled but still sent linked models), I think we should note this occurence so we know
|
||||
if (settingValue == null)
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
logger.LogWarning(
|
||||
"{SettingName} setting was null for model {ModelCardId}, using default: {DefaultValue}",
|
||||
settingName,
|
||||
modelCard.ModelCardId,
|
||||
defaultValue
|
||||
);
|
||||
}
|
||||
_sendRebarsAsVolumetricCache[modelCard.ModelCardId] = returnValue;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private void EvictCacheForModelCard(SenderModelCard modelCard)
|
||||
private void EvictCacheForModelCard(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
|
||||
_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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -19,7 +19,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\SelectionBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ElementIdHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
|
||||
@@ -37,7 +36,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReceiveReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\RevitHostObjectBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ToHostSettingsManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\TransactionManager.cs" />
|
||||
@@ -50,7 +49,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsVolumetricSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
|
||||
|
||||
@@ -325,9 +325,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -337,7 +337,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino7": {
|
||||
@@ -382,18 +382,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -403,14 +403,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,9 +325,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )",
|
||||
"Speckle.Sdk": "[3.5.3, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -337,7 +337,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.3, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
@@ -381,18 +381,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "T3xwwoALVGmhIuEjlDrTdDXZ9haFILT32r8OACWrRUItU3xMkOWGyob51Ca1MHPmo8B5gvbk2Gnm8AgReGnxWg==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.3"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "o+HefwtPZBqyuUHEnKF+qb/ctCAlNc2BYIw3ULEsZ93zweHt5wOMvOeuPxIXR0Gvj3fg6yNlY2nUcdFEduIXYA==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -402,14 +402,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.3"
|
||||
"Speckle.Sdk.Dependencies": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.3, )",
|
||||
"resolved": "3.5.3",
|
||||
"contentHash": "kC15SE4yZoVTasgywCm0SpY9yjBsQeUIDt4qoscYAgbn9pe0jj3uM0hZeJUCxn9Fdoj64OfQBpCKzR7VqhkwHQ=="
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+80
-19
@@ -399,6 +399,33 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
|
||||
public string SearchPattern { get; set; } = string.Empty;
|
||||
|
||||
private bool _autoSelectAllItemsItems;
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, all available items will be selected automatically and persistently.
|
||||
/// </summary>
|
||||
public bool AutoSelectAllItems
|
||||
{
|
||||
get => _autoSelectAllItemsItems;
|
||||
set
|
||||
{
|
||||
if (_autoSelectAllItemsItems == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_autoSelectAllItemsItems = value;
|
||||
|
||||
if (value && _listItems.Count > 0)
|
||||
{
|
||||
SelectAllItems();
|
||||
ResetPersistentData(_listItems.Select(x => x.Value), "Enable auto-select all items");
|
||||
}
|
||||
|
||||
OnDisplayExpired(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected internal int LayoutLevel { get; set; } = 1;
|
||||
|
||||
sealed class ListItem
|
||||
@@ -437,16 +464,14 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
public RectangleF BoxName;
|
||||
}
|
||||
|
||||
private List<ListItem> _listItems = new List<ListItem>();
|
||||
private List<ListItem> _listItems = [];
|
||||
IEnumerable<ListItem> SelectedItems => _listItems.Where(x => x.Selected);
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
if (Kind == GH_ParamKind.floating || Kind == GH_ParamKind.input)
|
||||
{
|
||||
Menu_AppendDestroyPersistent(menu);
|
||||
|
||||
if (Exposure != GH_Exposure.hidden) { }
|
||||
Menu_AppendInternaliseData(menu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,6 +574,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
|
||||
protected override GH_GetterResult Prompt_Singular(ref T value) => GH_GetterResult.cancel;
|
||||
|
||||
// NOTE: removed from AppendAdditionalMenuItems as clearing selection simple enough. Keeping here just in case
|
||||
// we want to bring it back
|
||||
protected override void Menu_AppendDestroyPersistent(ToolStripDropDown menu) =>
|
||||
Menu_AppendItem(menu, "Clear selection", Menu_DestroyPersistentData, PersistentDataCount > 0);
|
||||
|
||||
@@ -569,11 +596,36 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
|
||||
protected override void Menu_AppendInternaliseData(ToolStripDropDown menu)
|
||||
{
|
||||
Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
|
||||
Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
|
||||
// Disabled Invert selection and one-off select all according to Discord chat. These are easy enough
|
||||
// Select all also enabled through ctrl+a
|
||||
//Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
|
||||
//Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
|
||||
|
||||
var alwaysSelectAllItem = Menu_AppendItem(
|
||||
menu,
|
||||
"Auto-select all items",
|
||||
Menu_AlwaysSelectAllClicked,
|
||||
true,
|
||||
_autoSelectAllItemsItems
|
||||
);
|
||||
alwaysSelectAllItem.ToolTipText = _autoSelectAllItemsItems
|
||||
? "Currently auto-selecting all available items. Click to disable."
|
||||
: "Enable automatic selection of all available items. Will persist when new data is input.";
|
||||
|
||||
Menu_AppendItem(menu, "Internalise selection", Menu_InternaliseDataClicked, SourceCount > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that reduces code duplication to select all items
|
||||
/// </summary>
|
||||
private void SelectAllItems()
|
||||
{
|
||||
foreach (var item in _listItems)
|
||||
{
|
||||
item.Selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Menu_InternaliseDataClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (SourceCount == 0)
|
||||
@@ -610,14 +662,16 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
|
||||
protected void Menu_SelectAllClicked(object sender, EventArgs e)
|
||||
{
|
||||
foreach (var item in _listItems)
|
||||
{
|
||||
item.Selected = true;
|
||||
}
|
||||
|
||||
SelectAllItems();
|
||||
ResetPersistentData(_listItems.Select(x => x.Value), "Select all");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for auto-select all items menu item
|
||||
/// </summary>
|
||||
private void Menu_AlwaysSelectAllClicked(object sender, EventArgs e) =>
|
||||
AutoSelectAllItems = !_autoSelectAllItemsItems;
|
||||
|
||||
sealed class ResizableAttributes : GH_ResizableAttributes<ValueSet<T>>
|
||||
{
|
||||
public override bool HasInputGrip => true;
|
||||
@@ -1336,11 +1390,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
return GH_ObjectResponse.Ignore;
|
||||
}
|
||||
|
||||
public override GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
|
||||
{
|
||||
return base.RespondToKeyDown(sender, e);
|
||||
}
|
||||
|
||||
private sealed class SearchInputBox : Grasshopper.GUI.Base.GH_TextBoxInputBase
|
||||
{
|
||||
private readonly ResizableAttributes _parentAttributes;
|
||||
@@ -1398,6 +1447,12 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
reader.TryGetInt32("LayoutLevel", ref layoutLevel);
|
||||
LayoutLevel = Rhino.RhinoMath.Clamp(layoutLevel, 1, 2);
|
||||
|
||||
bool alwaysSelectAll = false;
|
||||
if (reader.TryGetBoolean("AutoSelectAllItems", ref alwaysSelectAll))
|
||||
{
|
||||
_autoSelectAllItemsItems = alwaysSelectAll;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1423,6 +1478,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
writer.SetInt32("LayoutLevel", LayoutLevel);
|
||||
}
|
||||
|
||||
writer.SetBoolean("AutoSelectAllItems", _autoSelectAllItemsItems);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1684,11 +1741,8 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
public sealed override void PostProcessData()
|
||||
{
|
||||
LoadVolatileData();
|
||||
|
||||
PreProcessVolatileData();
|
||||
|
||||
ProcessVolatileData();
|
||||
|
||||
SortItems();
|
||||
|
||||
// Order by fuzzy token if suits.
|
||||
@@ -1701,6 +1755,13 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
|
||||
}
|
||||
|
||||
PostProcessVolatileData();
|
||||
|
||||
// auto-select if enabled
|
||||
if (_autoSelectAllItemsItems && _listItems.Count > 0 && _listItems.Any(item => !item.Selected))
|
||||
{
|
||||
SelectAllItems();
|
||||
ResetPersistentData(_listItems.Select(x => x.Value), null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RegisterRemoteIDs(GH_GuidTable id_list)
|
||||
|
||||
+10
-2
@@ -1,3 +1,4 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
@@ -12,7 +13,7 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
|
||||
: base(
|
||||
"Collection Selector",
|
||||
"cSelect",
|
||||
"Allows you to select a set of collection paths for querying",
|
||||
"Allows you to select a set of collection paths for querying. Right-click for 'Auto-select all items' option.",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.COLLECTIONS
|
||||
) { }
|
||||
@@ -22,11 +23,18 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
|
||||
|
||||
protected override void LoadVolatileData()
|
||||
{
|
||||
var collections = VolatileData
|
||||
List<SpeckleCollectionWrapper> collections = VolatileData
|
||||
.AllData(true)
|
||||
.OfType<SpeckleCollectionWrapperGoo>()
|
||||
.Select(goo => goo.Value)
|
||||
.ToList();
|
||||
|
||||
if (VolatileDataCount > collections.Count)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Collections are accepted as inputs.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (collections.Count == 0)
|
||||
{
|
||||
return;
|
||||
|
||||
+55
@@ -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;
|
||||
|
||||
|
||||
+11
-1
@@ -28,7 +28,12 @@ public class TokenUrlComponent : GH_Component
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
|
||||
pManager.AddTextParameter("Speckle Token", "Token", "Speckle Authorization Token", GH_ParamAccess.item);
|
||||
pManager.AddTextParameter(
|
||||
"Speckle Token",
|
||||
"Token",
|
||||
"Speckle Authorization Token. Requires profile:read, profile:email, stream:read, and workspace:read (unless on a non-workspace enable server), as well as any other write scopes needed",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
@@ -99,6 +104,11 @@ public class TokenUrlComponent : GH_Component
|
||||
throw new SpeckleException("No account found for server URL");
|
||||
}
|
||||
|
||||
if (account.userInfo.id is null || account.userInfo.email is null)
|
||||
{
|
||||
throw new SpeckleException("Token requires profile:read and profile:email scopes");
|
||||
}
|
||||
|
||||
IClient client = scope.Get<IClientFactory>().Create(account);
|
||||
|
||||
var project = client.Project.Get(resource.ProjectId).Result;
|
||||
|
||||
+4
-8
@@ -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)
|
||||
{
|
||||
|
||||
+9
-10
@@ -13,7 +13,7 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
|
||||
: base(
|
||||
"Property Selector",
|
||||
"pSelect",
|
||||
"Allows you to select a set of property keys for querying",
|
||||
"Allows you to select a set of property keys for querying. Right-click for 'Auto-select all items' option.",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OBJECTS
|
||||
) { }
|
||||
@@ -24,18 +24,17 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
|
||||
|
||||
protected override void LoadVolatileData()
|
||||
{
|
||||
var propertyGroups = VolatileData
|
||||
List<SpecklePropertyGroupGoo> propertyGroups = VolatileData
|
||||
.AllData(true)
|
||||
.Where(goo => goo is SpecklePropertyGroupGoo)
|
||||
.Select(goo =>
|
||||
goo switch
|
||||
{
|
||||
SpecklePropertyGroupGoo geometryGoo => geometryGoo,
|
||||
_ => throw new InvalidOperationException("Unexpected goo type")
|
||||
}
|
||||
)
|
||||
.OfType<SpecklePropertyGroupGoo>()
|
||||
.ToList();
|
||||
|
||||
if (VolatileDataCount > propertyGroups.Count)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Properties are accepted as inputs.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyGroups.Count == 0)
|
||||
{
|
||||
return;
|
||||
|
||||
+17
-6
@@ -1,3 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
@@ -44,14 +45,14 @@ public class QueryProperties : GH_Component
|
||||
SpecklePropertyGroupGoo? properties = null;
|
||||
if (!da.GetData(0, ref properties))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a Speckle Properties item");
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a Speckle Properties item");
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> keys = new();
|
||||
List<string> keys = [];
|
||||
if (!da.GetDataList(1, keys))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Input a key");
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a key");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,11 +61,21 @@ public class QueryProperties : GH_Component
|
||||
return;
|
||||
}
|
||||
|
||||
List<object?> values = new();
|
||||
List<object?> values = [];
|
||||
foreach (string key in keys)
|
||||
{
|
||||
ISpecklePropertyGoo? value = GetValueByPath(properties, key);
|
||||
values.Add(value);
|
||||
var value = GetValueByPath(properties, key);
|
||||
var extractedValue = (value as SpecklePropertyGoo)?.Value ?? value;
|
||||
|
||||
// NOTE: if property is a list, flatten into individual items for native gh list access
|
||||
if (extractedValue is IList itemList)
|
||||
{
|
||||
values.AddRange(itemList.Cast<object?>());
|
||||
}
|
||||
else
|
||||
{
|
||||
values.Add(extractedValue);
|
||||
}
|
||||
}
|
||||
|
||||
da.SetDataList(0, values);
|
||||
|
||||
+9
-12
@@ -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) { }
|
||||
}
|
||||
|
||||
-1
@@ -88,7 +88,6 @@ public class SpeckleBlockDefinitionPassthrough()
|
||||
if (inputDefinition != null)
|
||||
{
|
||||
result = new SpeckleBlockDefinitionWrapperGoo(inputDefinition.Value.DeepCopy());
|
||||
result.Value.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
|
||||
}
|
||||
|
||||
// process geometry
|
||||
|
||||
-1
@@ -155,7 +155,6 @@ public class SpeckleBlockInstancePassthrough()
|
||||
if (inputInstance != null)
|
||||
{
|
||||
result = new SpeckleBlockInstanceWrapperGoo((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy());
|
||||
result.Value.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
|
||||
}
|
||||
|
||||
// process definition
|
||||
|
||||
+6
-3
@@ -98,13 +98,12 @@ public class SpeckleDataObjectPassthrough()
|
||||
if (da.GetData(0, ref inputObject))
|
||||
{
|
||||
result = inputObject.Value.DeepCopy();
|
||||
result.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
|
||||
}
|
||||
|
||||
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -112,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;
|
||||
}
|
||||
}
|
||||
@@ -159,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();
|
||||
|
||||
+1
-2
@@ -125,7 +125,6 @@ public class SpeckleGeometryPassthrough()
|
||||
if (inputObject?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
|
||||
{
|
||||
result = gooWrapper.DeepCopy();
|
||||
result.Base.id = null; // ⚠️ TODO: Co-ordinate with SDK. We're having to do this otherwise the serializer won't recompute mutated objects
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,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;
|
||||
}
|
||||
|
||||
|
||||
+47
-5
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using GH_IO.Serialization;
|
||||
using Grasshopper.GUI;
|
||||
using Grasshopper.GUI.Canvas;
|
||||
using Grasshopper.Kernel;
|
||||
@@ -46,15 +47,13 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
public bool JustPastedIn { get; set; }
|
||||
public string LastVersionDate { get; set; }
|
||||
public string LastInfoMessage { get; set; }
|
||||
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
|
||||
// DI props
|
||||
public IClient ApiClient { get; private set; }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
@@ -65,6 +64,14 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
"The model collection of the loaded version",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"properties",
|
||||
"Model-wide properties from the root collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
@@ -284,7 +291,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
Account? account = urlResource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
throw new SpeckleAccountManagerException("No default account was found");
|
||||
}
|
||||
|
||||
ApiClient?.Dispose();
|
||||
@@ -297,6 +304,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>
|
||||
@@ -311,6 +344,7 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
public Base Root { get; set; }
|
||||
public SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleCollectionWrapperGoo Result { get; set; }
|
||||
public SpecklePropertyGroupGoo? RootProperties { get; private set; }
|
||||
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
|
||||
|
||||
public override WorkerInstance<ReceiveAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
|
||||
@@ -347,6 +381,7 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
}
|
||||
|
||||
da.SetData(0, Result);
|
||||
da.SetData(1, RootProperties);
|
||||
}
|
||||
|
||||
public override async Task DoWork(Action<string, double> reportProgress, Action done)
|
||||
@@ -419,6 +454,12 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (Root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
TraversalContextUnpacker traversalContextUnpacker = new();
|
||||
@@ -454,6 +495,7 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
RootProperties = rootPropertiesGoo;
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
|
||||
+162
-12
@@ -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,21 @@ 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 SpecklePropertyGroupGoo? RootProperties { 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 +57,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));
|
||||
@@ -63,6 +72,14 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
"The model collection of the loaded version",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"properties",
|
||||
"Model-wide properties from the root collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override ReceiveComponentInput GetInput(IGH_DataAccess da)
|
||||
@@ -77,19 +94,30 @@ 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";
|
||||
da.SetData(1, result.RootProperties);
|
||||
|
||||
Message = _apiClient != null ? "Loaded" : "Done";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,11 +135,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();
|
||||
@@ -123,12 +151,15 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
Account? account = input.Resource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
throw new SpeckleAccountManagerException("No default account was found");
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -139,8 +170,15 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// extract model-wide root properties (see cnx-2722)
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
var customProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "isAsync", false },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
|
||||
@@ -193,6 +231,118 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo };
|
||||
return new ReceiveComponentOutput { RootObject = goo, RootProperties = rootPropertiesGoo };
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user