Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22c6303167 | |||
| a8cc4cebc7 | |||
| 678ba417d2 | |||
| bc9fbe3cf7 | |||
| b09f085f07 | |||
| 539ae1fc78 | |||
| cc47dfaac6 | |||
| 691235a7ac | |||
| deff607bcb | |||
| cfb8aba55f | |||
| 4bcc0d83a9 | |||
| 605d6faf42 | |||
| 28c5f1276a | |||
| e80fe30acb | |||
| 034e1dcef8 | |||
| dd34625acb | |||
| 3090b5f5bb | |||
| 7c609c93ae | |||
| 36a6572483 | |||
| ce04d2fd55 | |||
| 4dbe4dd9a0 | |||
| 6f72402b76 | |||
| a7b3ae8780 | |||
| 5da534aeb7 | |||
| 93ede98135 | |||
| 03cffcdf4c | |||
| c533d0922a | |||
| c24fb7eaa0 | |||
| 0b246cf95c | |||
| 003e310089 | |||
| 5917530761 | |||
| 349009314b | |||
| 659a29a294 | |||
| 1ffeddbc2c | |||
| 9d9a27d9cb | |||
| b2a885c193 | |||
| 60c1811fa6 | |||
| 5365809172 | |||
| 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 | |||
| 868ca8db66 | |||
| a9360e5fac | |||
| 3414599f72 | |||
| 84e92aa8a8 | |||
| edd842763f | |||
| 867ee0f928 | |||
| 29ee648d7f | |||
| 5b9c610856 | |||
| 769ddf6407 | |||
| dd7205f855 | |||
| 30ee410309 | |||
| 2d0b1a3a24 | |||
| 2cdf036172 | |||
| c14aa28e76 | |||
| 0e7d2554f8 | |||
| cdfc618bab | |||
| 1005edb609 | |||
| 3b4da8de52 |
+16
-30
@@ -1,34 +1,16 @@
|
||||
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:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -38,19 +20,23 @@ jobs:
|
||||
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
|
||||
|
||||
- name: Cache Nuget
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
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"
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
file_version: ${{ steps.set-version.outputs.file_version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -26,16 +26,16 @@ jobs:
|
||||
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
|
||||
|
||||
- name: Cache Nuget
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
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@v6
|
||||
with:
|
||||
name: output-${{ env.SEMVER }}
|
||||
path: output/*.*
|
||||
@@ -48,10 +48,10 @@ jobs:
|
||||
run: |
|
||||
echo "semver=${{ env.SEMVER }}" >> "$Env:GITHUB_OUTPUT"
|
||||
echo "file_version=${{ env.FILE_VERSION }}" >> "$Env:GITHUB_OUTPUT"
|
||||
|
||||
|
||||
deploy-installers:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-windows
|
||||
needs: build-connectors
|
||||
env:
|
||||
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
steps:
|
||||
@@ -63,8 +63,8 @@ jobs:
|
||||
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
|
||||
inputs: '{
|
||||
"run_id": "${{ github.run_id }}",
|
||||
"semver": "${{ needs.build-windows.outputs.semver }}",
|
||||
"file_version": "${{ needs.build-windows.outputs.file_version }}",
|
||||
"semver": "${{ needs.build-connectors.outputs.semver }}",
|
||||
"file_version": "${{ needs.build-connectors.outputs.file_version }}",
|
||||
"repo": "${{ github.repository }}",
|
||||
"is_public_release": ${{ env.IS_PUBLIC_RELEASE }}
|
||||
}'
|
||||
@@ -74,42 +74,8 @@ jobs:
|
||||
wait-for-completion-timeout: 10m
|
||||
display-workflow-run-url: true
|
||||
display-workflow-run-url-interval: 10s
|
||||
|
||||
|
||||
# Allows us to inspect the artifacts of failed builds, since this below step will be skipped if the above step fails
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: output-*
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
|
||||
|
||||
- name: Cache Nuget
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
|
||||
- name: ⚒️ Run tests on Linux
|
||||
run: ./build.sh test-only
|
||||
|
||||
- name: ⚒️ Run Build and Pack on Linux
|
||||
run: ./build.sh build-linux
|
||||
|
||||
- name: Upload coverage reports to Codecov with GitHub Action
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: Converters/**/coverage.xml
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Push to nuget.org
|
||||
if: (github.ref_type == 'tag')
|
||||
run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
|
||||
|
||||
+17
-21
@@ -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",
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +312,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -336,35 +356,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +312,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -336,35 +356,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -293,7 +313,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -210,9 +229,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +262,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -288,33 +306,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -155,6 +155,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -210,9 +229,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +262,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -288,33 +306,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+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" />
|
||||
|
||||
@@ -178,6 +178,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -268,9 +289,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +322,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,35 +366,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -268,9 +289,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +322,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,35 +366,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -268,9 +289,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -302,7 +322,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -346,35 +366,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -219,9 +238,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -254,7 +272,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -298,33 +316,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -164,6 +164,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -219,9 +238,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -254,7 +272,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -298,33 +316,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+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>
|
||||
+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);
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-4
@@ -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;
|
||||
@@ -145,7 +153,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
|
||||
requestedResultTypes,
|
||||
objectSelectionSummary
|
||||
);
|
||||
rootObjectCollection["analysisResults"] = analysisResults;
|
||||
rootObjectCollection[RootKeys.ANALYSIS_RESULTS] = analysisResults;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -217,4 +225,20 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
|
||||
group => group.Key, // ModelObjectType (FRAME, JOINT, etc.)
|
||||
group => group.Select(obj => obj.Name).ToList() // Extract Name from each ICsiWrapper and convert to List<string>
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a Base object and pre-populates it with the models defined force units.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SpeckleException"></exception>
|
||||
private (string, string) GetForceAndTemperatureUnits()
|
||||
{
|
||||
var forceUnit = eForce.NotApplicable;
|
||||
var lengthUnit = eLength.NotApplicable;
|
||||
var temperatureUnit = eTemperature.NotApplicable;
|
||||
|
||||
_converterSettings.Current.SapModel.GetDatabaseUnits_2(ref forceUnit, ref lengthUnit, ref temperatureUnit);
|
||||
|
||||
return (forceUnit.ToString(), temperatureUnit.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public static class ServiceRegistration
|
||||
|
||||
services.AddScoped<CsiMaterialPropertyExtractor>();
|
||||
services.AddScoped<CsiResultsExtractorFactory>();
|
||||
services.AddScoped<MaterialUnpacker>();
|
||||
services.AddScoped<IMaterialPropertyExtractor, CsiMaterialPropertyExtractor>();
|
||||
services.AddScoped<IFrameSectionPropertyExtractor, CsiFrameSectionPropertyExtractor>();
|
||||
services.AddScoped<IShellSectionPropertyExtractor, CsiShellSectionPropertyExtractor>();
|
||||
services.AddScoped<AnalysisResultsExtractor>();
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiResultsExtractorFactory.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IApplicationMaterialPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IMaterialPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\IMaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiFrameSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiMaterialPropertyExtractor.cs" />
|
||||
|
||||
@@ -31,8 +31,8 @@ public class AnalysisResultsExtractor
|
||||
Dictionary<ModelObjectType, List<string>> objectSelectionSummary
|
||||
)
|
||||
{
|
||||
// Step 1: get analysis units
|
||||
var analysisResults = CreateAnalysisResultsWithUnits();
|
||||
// Step 1: create base object that will hold analysis results
|
||||
var analysisResults = new Base();
|
||||
|
||||
// Step 2: configure and validate load cases
|
||||
ConfigureAndValidateSelectedLoadCases(selectedCasesAndCombinations);
|
||||
@@ -43,36 +43,6 @@ public class AnalysisResultsExtractor
|
||||
return analysisResults;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a Base object and pre-populates it with the models defined force units.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SpeckleException"></exception>
|
||||
private Base CreateAnalysisResultsWithUnits()
|
||||
{
|
||||
var forceUnit = eForce.NotApplicable;
|
||||
var lengthUnit = eLength.NotApplicable;
|
||||
var temperatureUnit = eTemperature.NotApplicable;
|
||||
|
||||
int success = _converterSettingsStore.Current.SapModel.GetDatabaseUnits_2(
|
||||
ref forceUnit,
|
||||
ref lengthUnit,
|
||||
ref temperatureUnit
|
||||
);
|
||||
|
||||
if (success != 0)
|
||||
{
|
||||
throw new SpeckleException("Failed to retrieve units for analysis results");
|
||||
}
|
||||
|
||||
return new Base
|
||||
{
|
||||
["forceUnit"] = forceUnit.ToString(),
|
||||
["lengthUnit"] = lengthUnit.ToString(),
|
||||
["temperatureUnit"] = temperatureUnit.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private void ExtractResults(
|
||||
List<string> requestedResultTypes,
|
||||
Dictionary<ModelObjectType, List<string>> objectSelectionSummary,
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs21": {
|
||||
@@ -335,35 +355,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -210,9 +229,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -236,7 +254,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs22": {
|
||||
@@ -286,33 +304,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using Speckle.Connectors.CSiShared.HostApp.Helpers;
|
||||
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
|
||||
using Speckle.Sdk.Models.Proxies;
|
||||
|
||||
namespace Speckle.Connectors.ETABSShared.HostApp;
|
||||
|
||||
public class EtabsMaterialUnpacker : IMaterialUnpacker
|
||||
{
|
||||
private readonly CsiToSpeckleCacheSingleton _csiToSpeckleCacheSingleton;
|
||||
private readonly IMaterialPropertyExtractor _csiMaterialPropertyExtractor;
|
||||
private readonly IApplicationMaterialPropertyExtractor _etabsMaterialPropertyExtractor;
|
||||
|
||||
public EtabsMaterialUnpacker(
|
||||
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton,
|
||||
IMaterialPropertyExtractor csiMaterialPropertyExtractor,
|
||||
IApplicationMaterialPropertyExtractor etabsMaterialPropertyExtractor
|
||||
)
|
||||
{
|
||||
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
|
||||
_csiMaterialPropertyExtractor = csiMaterialPropertyExtractor;
|
||||
_etabsMaterialPropertyExtractor = etabsMaterialPropertyExtractor;
|
||||
}
|
||||
|
||||
public IEnumerable<IProxyCollection> UnpackMaterials()
|
||||
{
|
||||
foreach (var kvp in _csiToSpeckleCacheSingleton.MaterialCache)
|
||||
{
|
||||
string name = kvp.Key;
|
||||
var sectionIds = kvp.Value;
|
||||
|
||||
// get the properties of the material
|
||||
Dictionary<string, object?> properties = [];
|
||||
_csiMaterialPropertyExtractor.ExtractProperties(name, properties);
|
||||
_etabsMaterialPropertyExtractor.ExtractProperties(name, properties);
|
||||
|
||||
// create the material proxy
|
||||
GroupProxy materialProxy =
|
||||
new()
|
||||
{
|
||||
id = name,
|
||||
name = name,
|
||||
applicationId = name,
|
||||
objects = sectionIds,
|
||||
["properties"] = properties
|
||||
};
|
||||
|
||||
yield return materialProxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
+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;
|
||||
}
|
||||
}
|
||||
+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,6 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.CSiShared.HostApp;
|
||||
using Speckle.Connectors.CSiShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABS22.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
|
||||
using Speckle.Connectors.ETABSShared.HostApp.Services;
|
||||
@@ -14,12 +15,17 @@ public static class ServiceRegistration
|
||||
{
|
||||
services.AddEtabsConverters();
|
||||
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
|
||||
services.AddScoped<EtabsSectionPropertyDefinitionService>();
|
||||
services.AddScoped<EtabsSectionPropertyExtractor>();
|
||||
services.AddScoped<EtabsShellSectionResolver>();
|
||||
|
||||
// material services
|
||||
services.AddScoped<IMaterialUnpacker, EtabsMaterialUnpacker>();
|
||||
services.AddScoped<IApplicationMaterialPropertyExtractor, EtabsMaterialPropertyExtractor>();
|
||||
|
||||
// section services
|
||||
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
|
||||
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
|
||||
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
|
||||
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
|
||||
services.AddScoped<EtabsSectionPropertyDefinitionService>();
|
||||
services.AddScoped<EtabsSectionPropertyExtractor>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,13 +9,14 @@
|
||||
<Import_RootNamespace>Speckle.Connectors.ETABSShared</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsMaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSectionUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsFrameSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsMaterialPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyDefinitionService.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionPropertyExtractor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionResolver.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\EtabsPluginBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\EtabsSpeckleFormBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
|
||||
|
||||
@@ -2,33 +2,63 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<UseWpf>true</UseWpf>
|
||||
|
||||
<Description>NextGen Speckle Connector for Autodesk Navisworks Manage</Description>
|
||||
<Authors>$(Authors) jonathon@speckle.systems</Authors>
|
||||
<PackageTags>$(PackageTags) connector nwd nwc nwf navisworks manage</PackageTags>
|
||||
|
||||
<PluginBundleTarget>$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Navisworks.bundle</PluginBundleTarget>
|
||||
<PluginVersionContentTarget>$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Navisworks.bundle\Contents\$(NavisworksVersion)</PluginVersionContentTarget>
|
||||
<PluginVersionContentTarget>$(PluginBundleTarget)\Contents\$(NavisworksVersion)</PluginVersionContentTarget>
|
||||
<RootNamespace>Speckle.Connector.Navisworks</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Post Builds -->
|
||||
<ItemGroup>
|
||||
<RibbonFiles Include="$(OutDir)Plugin\NavisworksRibbon.*"/>
|
||||
<ResourceFiles Include="$(OutDir)Resources\**\*.png"/>
|
||||
<ResourceFiles Include="$(OutDir)Resources\**\*.ico"/>
|
||||
<AllFiles Include="$(OutDir)*"/>
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(OS)' == 'Windows_NT' and '$(NavisworksVersion)' != ''">
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="Build" Condition="'$(NavisworksVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
|
||||
<Message Text="Navisworks Version $(NavisworksVersion)" Importance="high"/>
|
||||
|
||||
<RemoveDir Directories="$(PluginVersionContentTarget)" Condition="Exists('$(PluginVersionContentTarget)')"/>
|
||||
|
||||
<Copy SourceFiles="$(OutDir)Plugin\PackageContents.xml" DestinationFolder="$(PluginBundleTarget)\"/>
|
||||
<Copy SourceFiles="@(RibbonFiles)" DestinationFolder="$(PluginVersionContentTarget)\en-US\"/>
|
||||
<Copy SourceFiles="@(ResourceFiles)" DestinationFolder="$(PluginVersionContentTarget)\Resources\"/>
|
||||
|
||||
<Copy SourceFiles="@(AllFiles)" DestinationFolder="$(PluginVersionContentTarget)\" />
|
||||
|
||||
<MakeDir Directories="
|
||||
$(PluginBundleTarget);
|
||||
$(PluginBundleTarget)\Contents;
|
||||
$(PluginVersionContentTarget);
|
||||
$(PluginVersionContentTarget)\en-US;
|
||||
$(PluginVersionContentTarget)\Resources"/>
|
||||
|
||||
<!-- Re-evaluate outputs at execution time -->
|
||||
<ItemGroup>
|
||||
<PackageXml Include="$(OutDir)Plugin\PackageContents.xml"/>
|
||||
<RibbonFiles Include="$(OutDir)Plugin\NavisworksRibbon.*"/>
|
||||
<ResourceFiles Include="$(OutDir)Resources\**\*.png;$(OutDir)Resources\**\*.ico"/>
|
||||
<AllFiles Include="$(OutDir)**\*.*"/>
|
||||
|
||||
<Message Text="AllFiles count: @(AllFiles->Count())" Importance="high"/>
|
||||
<Warning Condition="'@(AllFiles)' == ''" Text="No files in $(OutDir) at PostBuild time."/>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(PackageXml)"
|
||||
DestinationFolder="$(PluginBundleTarget)\"
|
||||
SkipUnchangedFiles="true"/>
|
||||
|
||||
<Copy SourceFiles="@(RibbonFiles)"
|
||||
DestinationFolder="$(PluginVersionContentTarget)\en-US\"
|
||||
SkipUnchangedFiles="true"/>
|
||||
|
||||
<Copy SourceFiles="@(ResourceFiles)"
|
||||
DestinationFiles="@(ResourceFiles->'$(PluginVersionContentTarget)\Resources\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
SkipUnchangedFiles="true"/>
|
||||
|
||||
<Copy SourceFiles="@(AllFiles)"
|
||||
DestinationFiles="@(AllFiles->'$(PluginVersionContentTarget)\%(RecursiveDir)%(Filename)%(Extension)')"
|
||||
SkipUnchangedFiles="true"/>
|
||||
|
||||
<Message Text="Copied build to $(PluginVersionContentTarget)" Importance="high"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="ValidateNavisworksVersion" BeforeTargets="PostBuild"
|
||||
Condition="'$(NavisworksVersion)' == '' and '$(OS)' == 'Windows_NT'">
|
||||
<Error Text="NavisworksVersion property is required for PostBuild packaging."/>
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2020": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2021": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2022": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2023": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,6 +169,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -259,9 +280,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -285,7 +305,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2024": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -175,6 +175,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -265,9 +286,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -291,7 +311,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2025": {
|
||||
@@ -337,35 +357,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -184,6 +184,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -266,9 +287,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -292,7 +312,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2026": {
|
||||
@@ -339,35 +359,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+4
@@ -15,6 +15,7 @@ using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
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
|
||||
|
||||
+43
-18
@@ -16,7 +16,13 @@ public class NavisworksColorUnpacker(
|
||||
IElementSelectionService selectionService
|
||||
)
|
||||
{
|
||||
private static T Select<T>(RepresentationMode mode, T active, T permanent, T original, T defaultValue) =>
|
||||
private static T SelectByRepresentationMode<T>(
|
||||
RepresentationMode mode,
|
||||
T active,
|
||||
T permanent,
|
||||
T original,
|
||||
T defaultValue
|
||||
) =>
|
||||
mode switch
|
||||
{
|
||||
RepresentationMode.Active => active,
|
||||
@@ -71,14 +77,14 @@ public class NavisworksColorUnpacker(
|
||||
|
||||
using var defaultColor = new NAV.Color(1.0, 1.0, 1.0);
|
||||
|
||||
var representationColor = Select(
|
||||
var representationColor = SelectByRepresentationMode(
|
||||
mode,
|
||||
geometry.ActiveColor,
|
||||
geometry.PermanentColor,
|
||||
geometry.OriginalColor,
|
||||
defaultColor
|
||||
);
|
||||
var colorId = Select(
|
||||
var colorId = SelectByRepresentationMode(
|
||||
mode,
|
||||
$"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(),
|
||||
$"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(),
|
||||
@@ -124,30 +130,49 @@ public class NavisworksColorUnpacker(
|
||||
var comSelection = ComBridge.ToInwOpSelection([modelItem]);
|
||||
try
|
||||
{
|
||||
foreach (ComApi.InwOaPath path in comSelection.Paths())
|
||||
var pathsCollection = comSelection.Paths();
|
||||
try
|
||||
{
|
||||
GC.KeepAlive(path);
|
||||
|
||||
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
|
||||
foreach (ComApi.InwOaPath path in pathsCollection)
|
||||
{
|
||||
GC.KeepAlive(fragment);
|
||||
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
// Exit early if triangles are found
|
||||
if (primitiveChecker.HasTriangles)
|
||||
var fragmentsCollection = path.Fragments();
|
||||
try
|
||||
{
|
||||
return false;
|
||||
foreach (ComApi.InwOaFragment3 fragment in fragmentsCollection.OfType<ComApi.InwOaFragment3>())
|
||||
{
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
if (primitiveChecker.HasTriangles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fragmentsCollection != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragmentsCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if any 2D primitives are found
|
||||
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
|
||||
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pathsCollection != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(pathsCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
|
||||
if (comSelection != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+85
-20
@@ -1,7 +1,11 @@
|
||||
using Autodesk.Navisworks.Api.ComApi;
|
||||
using Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
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,12 +15,17 @@ 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
|
||||
// Selector method for individual properties
|
||||
private static T Select<T>(RepresentationMode mode, T active, T permanent, T original, T defaultValue) =>
|
||||
private static T SelectByRepresentationMode<T>(
|
||||
RepresentationMode mode,
|
||||
T active,
|
||||
T permanent,
|
||||
T original,
|
||||
T defaultValue
|
||||
) =>
|
||||
mode switch
|
||||
{
|
||||
RepresentationMode.Active => active,
|
||||
@@ -64,26 +73,87 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject);
|
||||
var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId;
|
||||
|
||||
string hashId = "";
|
||||
try
|
||||
{
|
||||
var item = selectionService.GetModelItemFromPath(finalId);
|
||||
var comSelection = ComApiBridge.ToInwOpSelection([item]);
|
||||
try
|
||||
{
|
||||
var paths = comSelection.Paths();
|
||||
try
|
||||
{
|
||||
if (paths.Count > 0)
|
||||
{
|
||||
var firstPath = paths.OfType<InwOaPath>().FirstOrDefault();
|
||||
if (firstPath != null)
|
||||
{
|
||||
var fragments = firstPath.Fragments();
|
||||
try
|
||||
{
|
||||
if (fragments.Count > 1)
|
||||
{
|
||||
var fragmentId = converter.GenerateFragmentId(paths);
|
||||
hashId = $"{InstanceConstants.GEOMETRY_ID_PREFIX}{fragmentId}";
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fragments != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (paths != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (comSelection != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{ // If COM interop fails during hash generation, fall back to using finalId
|
||||
logger.LogWarning(
|
||||
ex,
|
||||
"Failed to generate fragment hash ID for item {ItemId}, using finalId as fallback",
|
||||
finalId
|
||||
);
|
||||
hashId = "";
|
||||
}
|
||||
|
||||
var geometry = navisworksObject.Geometry;
|
||||
var mode = converterSettings.Current.User.VisualRepresentationMode;
|
||||
|
||||
using var defaultColor = new NAV.Color(1.0, 1.0, 1.0);
|
||||
|
||||
var renderColor = Select(
|
||||
var renderColor = SelectByRepresentationMode(
|
||||
mode,
|
||||
geometry.ActiveColor,
|
||||
geometry.PermanentColor,
|
||||
geometry.OriginalColor,
|
||||
defaultColor
|
||||
);
|
||||
var renderTransparency = Select(
|
||||
var renderTransparency = SelectByRepresentationMode(
|
||||
mode,
|
||||
geometry.ActiveTransparency,
|
||||
geometry.PermanentTransparency,
|
||||
geometry.OriginalTransparency,
|
||||
0.0
|
||||
);
|
||||
var renderMaterialId = Select(
|
||||
var renderMaterialId = SelectByRepresentationMode(
|
||||
mode,
|
||||
$"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(),
|
||||
$"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(),
|
||||
@@ -92,9 +162,8 @@ public class NavisworksMaterialUnpacker(
|
||||
);
|
||||
|
||||
var materialName =
|
||||
$"NavisworksMaterial_{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}";
|
||||
$"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}";
|
||||
|
||||
// Check Item category for material name
|
||||
var itemCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Item");
|
||||
if (itemCategory != null)
|
||||
{
|
||||
@@ -106,7 +175,6 @@ public class NavisworksMaterialUnpacker(
|
||||
}
|
||||
}
|
||||
|
||||
// Check Material category for material name
|
||||
var materialPropertyCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Material");
|
||||
if (materialPropertyCategory != null)
|
||||
{
|
||||
@@ -120,19 +188,14 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
if (renderMaterialProxies.TryGetValue(renderMaterialId.ToString(), out RenderMaterialProxy? value))
|
||||
{
|
||||
value.objects.Add(finalId);
|
||||
value.objects.Add(!string.IsNullOrEmpty(hashId) ? hashId : finalId);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy()
|
||||
{
|
||||
value = ConvertRenderColorAndTransparencyToSpeckle(
|
||||
materialName,
|
||||
renderTransparency,
|
||||
renderColor,
|
||||
renderMaterialId
|
||||
),
|
||||
objects = [finalId]
|
||||
value = CreateRenderMaterial(materialName, renderTransparency, renderColor, renderMaterialId),
|
||||
objects = [!string.IsNullOrEmpty(hashId) ? hashId : finalId]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -145,7 +208,7 @@ public class NavisworksMaterialUnpacker(
|
||||
return renderMaterialProxies.Values.ToList();
|
||||
}
|
||||
|
||||
private static RenderMaterial ConvertRenderColorAndTransparencyToSpeckle(
|
||||
private static RenderMaterial CreateRenderMaterial(
|
||||
string name,
|
||||
double transparency,
|
||||
NAV.Color navisworksColor,
|
||||
@@ -156,7 +219,9 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var speckleRenderMaterial = new RenderMaterial()
|
||||
{
|
||||
name = !string.IsNullOrEmpty(name) ? name : $"NavisworksMaterial_{Math.Abs(color.ToArgb())}",
|
||||
name = !string.IsNullOrEmpty(name)
|
||||
? name
|
||||
: $"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(color.ToArgb())}",
|
||||
opacity = 1 - transparency,
|
||||
metalness = 0,
|
||||
roughness = 1,
|
||||
|
||||
+43
-44
@@ -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,9 @@ public class NavisworksRootObjectBuilder(
|
||||
ISdkActivityFactory activityFactory,
|
||||
NavisworksMaterialUnpacker materialUnpacker,
|
||||
NavisworksColorUnpacker colorUnpacker,
|
||||
IElementSelectionService elementSelectionService
|
||||
IElementSelectionService elementSelectionService,
|
||||
IUiUnitsCache uiUnitsCache,
|
||||
InstanceStoreManager instanceStoreManager
|
||||
) : IRootObjectBuilder<NAV.ModelItem>
|
||||
{
|
||||
private bool SkipNodeMerging { get; set; }
|
||||
@@ -40,32 +43,45 @@ 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");
|
||||
|
||||
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
|
||||
|
||||
// 2. Initialize root collection
|
||||
var rootCollection = InitializeRootCollection();
|
||||
|
||||
// 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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -137,12 +153,10 @@ public class NavisworksRootObjectBuilder(
|
||||
Dictionary<string, List<NAV.ModelItem>> groupedNodes
|
||||
)
|
||||
{
|
||||
// First build the grouped nodes as before
|
||||
var finalElements = new List<Base>();
|
||||
var processedPaths = new HashSet<string>();
|
||||
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
|
||||
|
||||
// If hierarchy mode is enabled, reorganize into proper nested structure
|
||||
if (converterSettings.Current.User.PreserveModelHierarchy)
|
||||
{
|
||||
var hierarchyBuilder = new NavisworksHierarchyBuilder(
|
||||
@@ -151,12 +165,9 @@ public class NavisworksRootObjectBuilder(
|
||||
elementSelectionService
|
||||
);
|
||||
|
||||
var hierarchy = hierarchyBuilder.BuildHierarchy();
|
||||
|
||||
return hierarchy;
|
||||
return hierarchyBuilder.BuildHierarchy();
|
||||
}
|
||||
|
||||
// Otherwise continue with flat mode
|
||||
AddRemainingElements(finalElements, convertedBases, processedPaths);
|
||||
return finalElements;
|
||||
}
|
||||
@@ -213,24 +224,17 @@ public class NavisworksRootObjectBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
private (string name, string path) GetContext(string applicationId)
|
||||
private (string name, string path) GetElementNameAndPath(string applicationId)
|
||||
{
|
||||
var modelItem = elementSelectionService.GetModelItemFromPath(applicationId);
|
||||
var context = HierarchyHelper.ExtractContext(modelItem);
|
||||
return (context.Name, context.Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes and adds any remaining non-grouped elements.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Handles both Collection and Base type elements differently.
|
||||
/// Only processes elements that weren't handled in grouped processing.
|
||||
/// </remarks>
|
||||
private NavisworksObject CreateNavisworksObject(string groupKey, List<Base> siblingBases)
|
||||
{
|
||||
string cleanParentPath = ElementSelectionHelper.GetCleanPath(groupKey);
|
||||
(string name, string path) = GetContext(cleanParentPath);
|
||||
(string name, string path) = GetElementNameAndPath(cleanParentPath);
|
||||
|
||||
return new NavisworksObject
|
||||
{
|
||||
@@ -238,16 +242,11 @@ public class NavisworksRootObjectBuilder(
|
||||
displayValue = siblingBases.SelectMany(b => b["displayValue"] as List<Base> ?? []).ToList(),
|
||||
properties = siblingBases.First()["properties"] as Dictionary<string, object?> ?? [],
|
||||
units = converterSettings.Current.Derived.SpeckleUnits,
|
||||
applicationId = groupKey, // Use the full composite key as applicationId to preserve uniqueness
|
||||
applicationId = groupKey,
|
||||
["path"] = path
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a NavisworksObject from a single converted base.
|
||||
/// </summary>
|
||||
/// <param name="convertedBase">The converted Speckle Base object.</param>
|
||||
/// <returns>A new NavisworksObject containing the converted data.</returns>
|
||||
private NavisworksObject? CreateNavisworksObject(Base convertedBase)
|
||||
{
|
||||
if (convertedBase.applicationId == null)
|
||||
@@ -255,14 +254,16 @@ public class NavisworksRootObjectBuilder(
|
||||
return null;
|
||||
}
|
||||
|
||||
(string name, string path) = GetContext(convertedBase.applicationId);
|
||||
(string name, string path) = GetElementNameAndPath(convertedBase.applicationId);
|
||||
|
||||
var units = uiUnitsCache.Ensure();
|
||||
|
||||
return new NavisworksObject
|
||||
{
|
||||
name = name,
|
||||
displayValue = convertedBase["displayValue"] as List<Base> ?? [],
|
||||
properties = convertedBase["properties"] as Dictionary<string, object?> ?? [],
|
||||
units = converterSettings.Current.Derived.SpeckleUnits,
|
||||
units = units.ToString(),
|
||||
applicationId = convertedBase.applicationId,
|
||||
["path"] = path
|
||||
};
|
||||
@@ -288,18 +289,16 @@ public class NavisworksRootObjectBuilder(
|
||||
rootCollection[ProxyKeys.COLOR] = colors;
|
||||
}
|
||||
|
||||
var instanceDefinitionProxies = instanceStoreManager.GetInstanceDefinitionProxies();
|
||||
|
||||
if (instanceDefinitionProxies.Count > 0)
|
||||
{
|
||||
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies.ToList();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a single Navisworks item to a Speckle object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Attempts to retrieve from cache first.
|
||||
/// Falls back to fresh conversion if not cached.
|
||||
/// Logs errors but doesn't throw exceptions.
|
||||
/// </remarks>
|
||||
/// <returns>A SendConversionResult indicating success or failure.</returns>
|
||||
private SendConversionResult ConvertNavisworksItem(
|
||||
NAV.ModelItem navisworksItem,
|
||||
Dictionary<string, Base?> convertedBases,
|
||||
|
||||
+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>();
|
||||
|
||||
|
||||
@@ -191,6 +191,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -281,9 +302,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +326,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2022": {
|
||||
@@ -351,11 +371,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -363,29 +383,6 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2022.0.2.1",
|
||||
"contentHash": "IrLN4WyI2ix+g3zCpo7sX8zNB3FrtrdQ3E2RpceGVPNG00v8OfD+Kei7o1bn1u/ML46iBYRAr/JcsLbwfUQsBw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -281,9 +302,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +326,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2023": {
|
||||
@@ -351,11 +371,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -363,29 +383,6 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2023.0.0",
|
||||
"contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -281,9 +302,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -306,7 +326,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2024": {
|
||||
@@ -351,11 +371,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -363,29 +383,6 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2024.0.0",
|
||||
"contentHash": "a4dsvZ00ocvzTgCD6dUdydf0jIZDVcDhs6dUX9cv+y3aTDbU8rmzhYXWt8sThedIG+IPSVa0vHmAH9pKiJL3SQ=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -226,9 +245,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -251,7 +269,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2025": {
|
||||
@@ -296,11 +314,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -308,27 +326,6 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2025.0.0",
|
||||
"contentHash": "Hwf/3Ydc7KxvjgD9pSZKLSJRsFTsxYg95YyTm6f43hcsGjmk49GsLFQt921Z9OcvUVewOggQHcmBgti+P2EPHw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -164,6 +164,25 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -219,9 +238,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -244,7 +262,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2026": {
|
||||
@@ -280,11 +298,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -292,27 +310,6 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2026.0.0",
|
||||
"contentHash": "SiqqKbF1pXyZWXZhAl2JhjYhTt7RiYO5JaQrAjq+OlleAjT4zatwAp/DnTwQspFbP7UZr3b2Ed2kuWNN0ZFelw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
using Autodesk.Revit.UI;
|
||||
@@ -31,6 +32,10 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
}
|
||||
catch (SEHException)
|
||||
{
|
||||
//do nothing as we can't control external components
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
//do nothing, happens when closing Revit while a script is being executed
|
||||
|
||||
+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
|
||||
|
||||
@@ -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;
|
||||
@@ -90,7 +94,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new RevitSelectionFilter() { IsDefault = true },
|
||||
new RevitSelectionFilter { IsDefault = true },
|
||||
new RevitViewsFilter(_revitContext),
|
||||
new RevitCategoriesFilter(_revitContext)
|
||||
];
|
||||
@@ -110,6 +114,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
public async Task Send(string modelCardId)
|
||||
{
|
||||
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
if (document == null)
|
||||
{
|
||||
throw new SpeckleException("No document is active for sending.");
|
||||
}
|
||||
using var manager = _sendOperationManagerFactory.Create();
|
||||
|
||||
await manager.Process<DocumentToConvert>(
|
||||
@@ -120,24 +129,20 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
sp.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
|
||||
.Initialize(
|
||||
_revitConversionSettingsFactory.Create(
|
||||
_toSpeckleSettingsManager.GetDetailLevelSetting(card),
|
||||
_toSpeckleSettingsManager.GetReferencePointSetting(card),
|
||||
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(card),
|
||||
_toSpeckleSettingsManager.GetLinkedModelsSetting(card),
|
||||
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(card)
|
||||
_toSpeckleSettingsManager.GetDetailLevelSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetReferencePointSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetLinkedModelsSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(document, card)
|
||||
)
|
||||
);
|
||||
},
|
||||
async x => await RefreshElementsIdsOnSender(x.NotNull())
|
||||
async x => await RefreshElementsIdsOnSender(document, x.NotNull())
|
||||
);
|
||||
}
|
||||
|
||||
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
|
||||
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
var activeUIDoc =
|
||||
_revitContext.UIApplication?.ActiveUIDocument
|
||||
?? throw new SpeckleException("Unable to retrieve active UI document");
|
||||
|
||||
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
|
||||
{
|
||||
viewFilter.SetContext(_revitContext);
|
||||
@@ -147,10 +152,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
() => Task.FromResult(modelCard.SendFilter.NotNull().RefreshObjectIds())
|
||||
);
|
||||
|
||||
var allElements = selectedObjects
|
||||
.Select(uid => activeUIDoc.Document.GetElement(uid))
|
||||
.Where(el => el is not null)
|
||||
.ToList();
|
||||
var allElements = selectedObjects.Select(uid => document.GetElement(uid)).Where(el => el is not null).ToList();
|
||||
|
||||
// split elements between main model and linked models
|
||||
var elementsOnMainModel = allElements.Where(el => el is not RevitLinkInstance).ToList();
|
||||
@@ -158,14 +160,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
// should ideally reuse the initialized value from the scoped IConverterSettingsStore<RevitConversionSettings>.
|
||||
// but, it's scoped and to avoid bigger scarier changes I'm re-fetching the setting here (inexpensive operation?)
|
||||
Transform? mainModelTransform = _toSpeckleSettingsManager.GetReferencePointSetting(modelCard);
|
||||
List<DocumentToConvert> documentElementContexts =
|
||||
[
|
||||
new(mainModelTransform, activeUIDoc.Document, elementsOnMainModel)
|
||||
];
|
||||
Transform? mainModelTransform = _toSpeckleSettingsManager.GetReferencePointSetting(document, modelCard);
|
||||
List<DocumentToConvert> documentElementContexts = [new(mainModelTransform, document, elementsOnMainModel)];
|
||||
|
||||
// get the linked models setting - this decision belongs at this level
|
||||
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard);
|
||||
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(document, modelCard);
|
||||
|
||||
// ⚠️ process linked models - RevitSendBinding controls the flow based on settings!
|
||||
// If setting not enabled, we won't unpack (see if-else block)
|
||||
@@ -192,7 +191,12 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
if (includeLinkedModels)
|
||||
{
|
||||
// handler is only responsible for element collection mechanics
|
||||
var linkedElements = _linkedModelHandler.GetLinkedModelElements(modelCard.SendFilter, linkedDoc, transform);
|
||||
var linkedElements = _linkedModelHandler.GetLinkedModelElements(
|
||||
document,
|
||||
modelCard.SendFilter,
|
||||
linkedDoc,
|
||||
transform
|
||||
);
|
||||
linkedDocumentContexts.Add(new(transform, linkedDoc, linkedElements));
|
||||
}
|
||||
// ⚠️ when disabled, still adds empty contexts to maintain warning generation in RevitRootObjectBuilder
|
||||
@@ -332,9 +336,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
private async Task PostSetObjectIds()
|
||||
{
|
||||
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
if (document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var sender in _store.GetSenders().ToList())
|
||||
{
|
||||
await RefreshElementsIdsOnSender(sender);
|
||||
await RefreshElementsIdsOnSender(document, sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,6 +453,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private async Task OnDocumentChanged()
|
||||
{
|
||||
_sendConversionCache.ClearCache();
|
||||
_revitToSpeckleCacheSingleton.ClearCache();
|
||||
|
||||
if (_cancellationManager.NumberOfOperations > 0)
|
||||
{
|
||||
|
||||
+1
-1
@@ -58,6 +58,7 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
|
||||
serviceCollection.AddScoped<ElementUnpacker>();
|
||||
serviceCollection.AddScoped<LevelUnpacker>();
|
||||
serviceCollection.AddScoped<ViewUnpacker>();
|
||||
serviceCollection.AddScoped<SendCollectionManager>();
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
|
||||
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
@@ -74,7 +75,6 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddSingleton<RevitUtils>();
|
||||
serviceCollection.AddSingleton<IFailuresPreprocessor, HideWarningsFailuresPreprocessor>();
|
||||
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
|
||||
serviceCollection.AddScoped<LocalToGlobalConverterUtils>();
|
||||
|
||||
// operation progress manager
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.Architecture;
|
||||
using Speckle.Converters.RevitShared.Extensions;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
@@ -23,11 +24,11 @@ public class ElementUnpacker
|
||||
// Step 1: unpack groups
|
||||
var atomicObjects = UnpackElements(selectionElements, doc);
|
||||
|
||||
// Step 2: pack curtain wall elements, once we know the full extent of our flattened item list.
|
||||
// The behaviour we're looking for:
|
||||
// If parent wall is part of selection, does not select individual elements out. Otherwise, selects individual elements (Panels, Mullions) as atomic objects.
|
||||
// NOTE: this also conditionally "packs" stacked wall elements if their parent is present. See detailed note inside the function.
|
||||
return PackCurtainWallElementsAndStackedWalls(atomicObjects, doc);
|
||||
// Step 2: Deduplicate parent-child elements in selection
|
||||
// Removes child elements (mullions, panels, top rails, stacked wall members) when
|
||||
// their parent element is also selected, since parents include children in their conversion.
|
||||
// Children are only converted independently when their parent is NOT in the selection.
|
||||
return RemoveKnownChildElementsWhenParentPresent(atomicObjects, doc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -108,7 +109,7 @@ 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)
|
||||
private List<Element> RemoveKnownChildElementsWhenParentPresent(List<Element> elements, Document doc)
|
||||
{
|
||||
//just used for contains so use ToHashSet
|
||||
var ids = elements.Select(el => el.Id).ToHashSet();
|
||||
@@ -131,64 +132,37 @@ public class ElementUnpacker
|
||||
// If you wonder why revit is driving people to insanity, this is one of those moments.
|
||||
// See [CNX-851: Stacked Wall Duplicate Geometry or Materials not applied](https://linear.app/speckle/issue/CNX-851/stacked-wall-duplicate-geometry-or-materials-not-applied)
|
||||
|| (element is Wall { IsStackedWallMember: true } wall && ids.Contains(wall.StackedWallOwnerId))
|
||||
// Railings: Remove TopRail when parent railing is selected
|
||||
// Prevents duplication since railing converter includes TopRail as a child element
|
||||
// TODO: Consider adding HandRail support (also inherits from ContinuousRail)
|
||||
|| (
|
||||
element is TopRail topRail
|
||||
&& doc.GetElement(topRail.HostRailingId) is Railing railing
|
||||
&& ids.Contains(railing.Id)
|
||||
)
|
||||
);
|
||||
return elements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a set of atomic elements, it will return a list of all their ids as well as their subelements. This currently handles <b>curtain walls</b> and <b>stacked walls</b>.
|
||||
/// This might not be an exhaustive list of valid objects with "subelements" in revit, and will need revisiting.
|
||||
/// Returns element IDs and their known child element IDs for cache tracking.
|
||||
/// Uses <see cref="ElementExtensions.GetKnownChildrenElements"/> to determine which children to include.
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="elements">Elements to process</param>
|
||||
/// <returns>Flattened list of parent and child element IDs</returns>
|
||||
public List<string> GetElementsAndSubelementIdsFromAtomicObjects(List<Element> elements)
|
||||
{
|
||||
var ids = new HashSet<string>();
|
||||
foreach (var element in elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case Wall wall:
|
||||
if (wall.CurtainGrid is { } grid)
|
||||
{
|
||||
foreach (var mullionId in grid.GetMullionIds())
|
||||
{
|
||||
ids.Add(mullionId.ToString());
|
||||
}
|
||||
foreach (var panelId in grid.GetPanelIds())
|
||||
{
|
||||
ids.Add(panelId.ToString());
|
||||
}
|
||||
}
|
||||
else if (wall.IsStackedWall)
|
||||
{
|
||||
foreach (var stackedWallId in wall.GetStackedWallMemberIds())
|
||||
{
|
||||
ids.Add(stackedWallId.ToString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FootPrintRoof footPrintRoof:
|
||||
if (footPrintRoof.CurtainGrids is { } gs)
|
||||
{
|
||||
foreach (CurtainGrid roofGrid in gs)
|
||||
{
|
||||
foreach (var mullionId in roofGrid.GetMullionIds())
|
||||
{
|
||||
ids.Add(mullionId.ToString());
|
||||
}
|
||||
foreach (var panelId in roofGrid.GetPanelIds())
|
||||
{
|
||||
ids.Add(panelId.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// add the element's own ID
|
||||
ids.Add(element.Id.ToString());
|
||||
|
||||
// add all known children IDs using the extension method. trying to consolidate duplication here with converter
|
||||
foreach (var childId in element.GetKnownChildrenElements())
|
||||
{
|
||||
ids.Add(childId.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return ids.ToList();
|
||||
|
||||
@@ -34,18 +34,22 @@ public class LevelUnpacker
|
||||
Dictionary<string, LevelProxy> levelProxies = new();
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (levelProxies.TryGetValue(element.LevelId.ToString(), out LevelProxy? levelProxy))
|
||||
// NOTE: Use level.UniqueId (not element.LevelId) as key
|
||||
// face-based instances don't have a valid element.LevelId, hence all the changes in the LevelExtractor
|
||||
var level = _levelExtractor.GetLevel(element);
|
||||
if (level is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string levelKey = level.UniqueId;
|
||||
|
||||
if (levelProxies.TryGetValue(levelKey, out LevelProxy? levelProxy))
|
||||
{
|
||||
levelProxy.objects.Add(element.UniqueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var level = _levelExtractor.GetLevel(element);
|
||||
if (level is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var levelDataObject = new DataObject()
|
||||
{
|
||||
name = level.Name,
|
||||
@@ -53,11 +57,11 @@ public class LevelUnpacker
|
||||
properties = _propertiesExtractor.GetProperties(level)
|
||||
};
|
||||
var unitSettings = _converterSettings.Current.Document.GetUnits();
|
||||
var lengthUnitType = unitSettings.GetFormatOptions(Autodesk.Revit.DB.SpecTypeId.Length).GetUnitTypeId();
|
||||
var lengthUnitType = unitSettings.GetFormatOptions(SpecTypeId.Length).GetUnitTypeId();
|
||||
levelDataObject["elevation"] = UnitUtils.ConvertFromInternalUnits(level.Elevation, lengthUnitType);
|
||||
levelDataObject["units"] = _converterSettings.Current.SpeckleUnits;
|
||||
|
||||
levelProxies[element.LevelId.ToString()] = new LevelProxy()
|
||||
levelProxies[levelKey] = new LevelProxy()
|
||||
{
|
||||
applicationId = level.UniqueId,
|
||||
objects = [element.UniqueId],
|
||||
|
||||
@@ -3,7 +3,6 @@ using Autodesk.Revit.DB;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.RevitShared;
|
||||
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
@@ -17,20 +16,19 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
/// </summary>
|
||||
public class LinkedModelHandler
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
public Dictionary<string, string> LinkedModelDisplayNames { get; } = new();
|
||||
|
||||
public LinkedModelHandler(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets elements from a linked document based on the provided send filter.
|
||||
/// This method handles the specifics of element collection but doesn't make decisions
|
||||
/// about whether the linked model should be processed - that's the caller's responsibility.
|
||||
/// </summary>
|
||||
public List<Element> GetLinkedModelElements(ISendFilter sendFilter, Document linkedDocument, Transform? transform)
|
||||
public List<Element> GetLinkedModelElements(
|
||||
Document currentDocument,
|
||||
ISendFilter sendFilter,
|
||||
Document linkedDocument,
|
||||
Transform? transform
|
||||
)
|
||||
{
|
||||
// send mode → Categories
|
||||
if (sendFilter is RevitCategoriesFilter categoryFilter && categoryFilter.SelectedCategories is not null)
|
||||
@@ -48,19 +46,15 @@ public class LinkedModelHandler
|
||||
}
|
||||
|
||||
// send mode → Views (taken from the legacy code)
|
||||
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView() != null)
|
||||
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView(currentDocument) != null)
|
||||
{
|
||||
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(
|
||||
linkedDocument.PathName,
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document,
|
||||
transform
|
||||
);
|
||||
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(linkedDocument.PathName, currentDocument, transform);
|
||||
|
||||
#if REVIT2024_OR_GREATER
|
||||
// revit 2024 and 2025 we can use the three-parameter constructor to get only visible elements
|
||||
using var viewCollector = new FilteredElementCollector(
|
||||
_revitContext.UIApplication.ActiveUIDocument.Document,
|
||||
viewFilter.GetView().NotNull().Id,
|
||||
currentDocument,
|
||||
viewFilter.GetView(currentDocument).NotNull().Id,
|
||||
linkInstance.Id
|
||||
);
|
||||
|
||||
@@ -70,7 +64,7 @@ public class LinkedModelHandler
|
||||
// 🚨 LIMITATION: in Revit 2023 and below, we can only check if the entire linked model is visible,
|
||||
// not individual elements within it. If the linked model is visible, all its elements will be included.
|
||||
// constructor overload pertaining to searching and filtering visible elements from a revit link only added 2024.
|
||||
if (linkInstance.IsHidden(viewFilter.GetView().NotNull()))
|
||||
if (linkInstance.IsHidden(viewFilter.GetView(currentDocument).NotNull()))
|
||||
{
|
||||
return new List<Element>(); // if the linked model is hidden, return no elements
|
||||
}
|
||||
|
||||
@@ -97,10 +97,12 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
try
|
||||
{
|
||||
var key = GetKeyForDocument(document);
|
||||
if (key != null)
|
||||
if (key is null)
|
||||
{
|
||||
_jsonCacheManager.UpdateObject(key, modelCardState);
|
||||
LoadFromString(null);
|
||||
return;
|
||||
}
|
||||
_jsonCacheManager.UpdateObject(key, modelCardState);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
@@ -111,11 +113,17 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
|
||||
private string? GetKeyForDocument(Document doc)
|
||||
{
|
||||
string? id = doc?.ProjectInformation?.UniqueId; //ProjectInformation Should only be null for family docs
|
||||
#if REVIT_2024_OR_GREATER
|
||||
id ??= doc.CreationGUID.ToString(); //fallback for family docs
|
||||
#if REVIT2024_OR_GREATER
|
||||
return doc.CreationGUID.ToString();
|
||||
#else
|
||||
//basically, no document state will ever be saved when it's a new document. It must be saved first for path name to be a valid value.
|
||||
var x = doc.PathName;
|
||||
if (string.IsNullOrEmpty(x))
|
||||
{
|
||||
return doc.Title;
|
||||
}
|
||||
return x;
|
||||
#endif
|
||||
return id;
|
||||
}
|
||||
|
||||
protected override void LoadState()
|
||||
@@ -128,14 +136,12 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
}
|
||||
|
||||
var key = GetKeyForDocument(document);
|
||||
if (key != null)
|
||||
if (key is null)
|
||||
{
|
||||
var state = _jsonCacheManager.GetObject(key);
|
||||
if (state == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LoadFromString(state);
|
||||
LoadFromString(null);
|
||||
return;
|
||||
}
|
||||
var state = _jsonCacheManager.GetObject(key);
|
||||
LoadFromString(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,14 @@ public class RevitMaterialBaker
|
||||
|
||||
try
|
||||
{
|
||||
// all values assumed to be on the 0 - 1 scale need to pass through this validation and logging (if assumption wrong)
|
||||
double roughness = ClampToUnitRange(speckleRenderMaterial.roughness, "roughness", speckleRenderMaterial.name);
|
||||
double opacity = ClampToUnitRange(speckleRenderMaterial.opacity, "opacity", speckleRenderMaterial.name);
|
||||
double metalness = ClampToUnitRange(speckleRenderMaterial.metalness, "metalness", speckleRenderMaterial.name);
|
||||
|
||||
var diffuse = System.Drawing.Color.FromArgb(speckleRenderMaterial.diffuse);
|
||||
double transparency = 1 - speckleRenderMaterial.opacity;
|
||||
double smoothness = 1 - speckleRenderMaterial.roughness;
|
||||
double transparency = 1 - opacity;
|
||||
double smoothness = 1 - roughness;
|
||||
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
|
||||
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}");
|
||||
|
||||
@@ -130,7 +135,7 @@ public class RevitMaterialBaker
|
||||
var revitMaterial = (Material)_converterSettings.Current.Document.GetElement(newMaterialId);
|
||||
revitMaterial.Color = new Color(diffuse.R, diffuse.G, diffuse.B);
|
||||
revitMaterial.Transparency = (int)(transparency * 100);
|
||||
revitMaterial.Shininess = (int)(speckleRenderMaterial.metalness * 128);
|
||||
revitMaterial.Shininess = (int)(metalness * 128);
|
||||
revitMaterial.Smoothness = (int)(smoothness * 128);
|
||||
|
||||
foreach (var objectId in proxy.objects)
|
||||
@@ -163,4 +168,30 @@ public class RevitMaterialBaker
|
||||
document.Delete(materialIds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After CNX-2661, we've seen some edge cases contradicting the expected 0 - 1 range for PRB properties.
|
||||
/// Defensively, we'd rather clamp these values than throw.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Created a method so that we can extend the checks to any numerical value potentially leading to a negative value,
|
||||
/// which would throw an exception. Generalised method since Math.Clamp() only available since C# 8.0 and this method
|
||||
/// handles logging (in the hope that we can get a better feel for these "weird" models, e.g. 0 - 100 scale??)
|
||||
/// </remarks>
|
||||
private double ClampToUnitRange(double value, string propertyName, string materialName)
|
||||
{
|
||||
if (value is < 0 or > 1)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Material '{MaterialName}' has an invalid {PropertyName} value of {Value} and was clamped to 0 - 1 range",
|
||||
materialName,
|
||||
propertyName,
|
||||
value
|
||||
);
|
||||
|
||||
value = Math.Min(Math.Max(0, value), 1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks Revit Views for sending
|
||||
/// </summary>
|
||||
|
||||
public class ViewUnpacker
|
||||
{
|
||||
private readonly ILogger<ViewUnpacker> _logger;
|
||||
private readonly Converters.Common.IRootToSpeckleConverter _rootToSpeckleConverter;
|
||||
|
||||
public ViewUnpacker(Converters.Common.IRootToSpeckleConverter rootToSpeckleConverter, ILogger<ViewUnpacker> logger)
|
||||
{
|
||||
_rootToSpeckleConverter = rootToSpeckleConverter;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private Camera? ConvertViewToCamera(View3D view)
|
||||
{
|
||||
try
|
||||
{
|
||||
var converted = (Camera)_rootToSpeckleConverter.Convert(view);
|
||||
if (converted is null)
|
||||
{
|
||||
_logger.LogError("Failed to create a view from {view}", view.Name);
|
||||
return null;
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create a view from {view}", view.Name);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through the 3D views in the provided document to create cameras
|
||||
/// </summary>
|
||||
/// <param name="doc">Document to retrieve 3D views from</param>
|
||||
/// <returns></returns>
|
||||
public List<Camera> Unpack(Document doc)
|
||||
{
|
||||
List<Camera> cameras = new();
|
||||
using FilteredElementCollector collector = new(doc);
|
||||
List<View> views = collector
|
||||
.WhereElementIsNotElementType()
|
||||
.OfCategory(BuiltInCategory.OST_Views)
|
||||
.Cast<View>()
|
||||
.Where(x => x.ViewType == ViewType.ThreeD)
|
||||
.ToList();
|
||||
|
||||
foreach (View view in views)
|
||||
{
|
||||
if (view is not View3D view3D)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// not supporting parallel project yet, since it is too complex to match in the viewer for now
|
||||
try
|
||||
{
|
||||
if (!view3D.IsPerspective)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
|
||||
{
|
||||
continue; // some threed views will throw an exception: returns true if view is not a view template
|
||||
}
|
||||
|
||||
if (ConvertViewToCamera(view3D) is Camera camera)
|
||||
{
|
||||
cameras.Add(camera);
|
||||
}
|
||||
}
|
||||
|
||||
return cameras;
|
||||
}
|
||||
}
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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)
|
||||
|
||||
+50
-3
@@ -24,6 +24,7 @@ public class RevitRootObjectBuilder(
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
LevelUnpacker levelUnpacker,
|
||||
ViewUnpacker viewUnpacker,
|
||||
IThreadContext threadContext,
|
||||
SendCollectionManager sendCollectionManager,
|
||||
ILogger<RevitRootObjectBuilder> logger,
|
||||
@@ -41,7 +42,11 @@ public class RevitRootObjectBuilder(
|
||||
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
|
||||
);
|
||||
|
||||
#pragma warning disable CA1506
|
||||
#pragma warning disable CA1502
|
||||
private RootObjectBuilderResult BuildSync(
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1502
|
||||
IReadOnlyList<DocumentToConvert> documentElementContexts,
|
||||
string projectId,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
@@ -55,6 +60,9 @@ public class RevitRootObjectBuilder(
|
||||
throw new SpeckleException("Family Environment documents are not supported.");
|
||||
}
|
||||
|
||||
// create a new send pipeline
|
||||
using var sendPipeline = new Speckle.Sdk.Pipeline.Send();
|
||||
|
||||
// init the root
|
||||
Collection rootObject =
|
||||
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
|
||||
@@ -183,9 +191,12 @@ 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
|
||||
bool wasCached = false;
|
||||
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
|
||||
{
|
||||
// TODO: cahce hit
|
||||
converted = value;
|
||||
wasCached = true;
|
||||
cacheHitCount++;
|
||||
}
|
||||
// not in cache means we convert
|
||||
@@ -204,6 +215,12 @@ public class RevitRootObjectBuilder(
|
||||
converted.applicationId = applicationId;
|
||||
}
|
||||
|
||||
var reference = sendPipeline.Process(converted).Result; // .Wait(cancellationToken);//.ConfigureAwait(false);
|
||||
if (!wasCached)
|
||||
{
|
||||
sendConversionCache.AppendSendResult(projectId, applicationId, reference);
|
||||
}
|
||||
|
||||
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
|
||||
revitElement,
|
||||
rootObject,
|
||||
@@ -211,7 +228,7 @@ public class RevitRootObjectBuilder(
|
||||
modelDisplayName
|
||||
);
|
||||
|
||||
collection.elements.Add(converted);
|
||||
collection.elements.Add(reference);
|
||||
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
@@ -239,6 +256,7 @@ public class RevitRootObjectBuilder(
|
||||
throw new SpeckleException("Failed to convert all objects.");
|
||||
}
|
||||
|
||||
// STEP 5: Unpack proxies to attach to root collection
|
||||
var flatElements = atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList();
|
||||
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(flatElements);
|
||||
|
||||
@@ -248,6 +266,31 @@ public class RevitRootObjectBuilder(
|
||||
var levelProxies = levelUnpacker.Unpack(flatElements);
|
||||
rootObject[ProxyKeys.LEVEL] = levelProxies;
|
||||
|
||||
rootObject[ProxyKeys.INSTANCE_DEFINITION] = revitToSpeckleCacheSingleton.GetInstanceDefinitionProxiesForObjects(
|
||||
idsAndSubElementIds
|
||||
);
|
||||
// NOTE: i might be overdoing things in here, but tldr:
|
||||
// - all instance objects (meshes) are processed individually
|
||||
// - process their collection individually, and then attach it to the root collection
|
||||
// we could, theoretically, just process the collection as a whole (but it can be big?)
|
||||
// note/ask: do these need to go in the conversion cache? or not?
|
||||
var instanceObjects = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds);
|
||||
var instanceReferences = new Collection("revitInstancedObjects");
|
||||
foreach (var instanceObject in instanceObjects)
|
||||
{
|
||||
var referenceInstanceObject = sendPipeline.Process(instanceObject).Result;
|
||||
instanceReferences.elements.Add(referenceInstanceObject);
|
||||
}
|
||||
var instanceReferenceCollection = sendPipeline.Process(instanceReferences).Result;
|
||||
rootObject.elements.Add(instanceReferenceCollection);
|
||||
|
||||
// STEP 6: Unpack all other objects to attach to root collection
|
||||
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
|
||||
if (views.Count > 0)
|
||||
{
|
||||
rootObject[RootKeys.VIEW] = views;
|
||||
}
|
||||
|
||||
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
|
||||
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
|
||||
|
||||
@@ -255,9 +298,13 @@ 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);
|
||||
// NOTE: could be
|
||||
sendPipeline.Process(rootObject).Wait(cancellationToken);
|
||||
sendPipeline.WaitForUpload().Wait(cancellationToken);
|
||||
|
||||
return new RootObjectBuilderResult(new Collection() { name = "ignore" }, results);
|
||||
}
|
||||
}
|
||||
|
||||
+63
-88
@@ -1,25 +1,21 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.UI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.Revit.HostApp;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
public class ToSpeckleSettingsManager(
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
ILogger<ToSpeckleSettingsManager> logger
|
||||
) : IToSpeckleSettingsManager
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ISendConversionCache _sendConversionCache;
|
||||
private readonly ElementUnpacker _elementUnpacker;
|
||||
private readonly ILogger<ToSpeckleSettingsManager> _logger;
|
||||
|
||||
// cache invalidation process run with ModelCardId since the settings are model specific
|
||||
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = [];
|
||||
private readonly Dictionary<string, Transform?> _referencePointCache = [];
|
||||
@@ -27,20 +23,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
|
||||
|
||||
public ToSpeckleSettingsManager(
|
||||
RevitContext revitContext,
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
ILogger<ToSpeckleSettingsManager> logger
|
||||
)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_elementUnpacker = elementUnpacker;
|
||||
_sendConversionCache = sendConversionCache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public DetailLevelType GetDetailLevelSetting(SenderModelCard modelCard)
|
||||
public DetailLevelType GetDetailLevelSetting(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
var fidelityString =
|
||||
modelCard.Settings?.FirstOrDefault(s => s.Id == DetailLevelSetting.SETTING_ID)?.Value as string;
|
||||
@@ -53,7 +36,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
{
|
||||
if (previousType != fidelity)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
EvictCacheForModelCard(document, modelCard);
|
||||
}
|
||||
}
|
||||
_detailLevelCache[modelCard.ModelCardId.NotNull()] = fidelity;
|
||||
@@ -61,7 +44,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
}
|
||||
|
||||
// log the issue
|
||||
_logger.LogWarning(
|
||||
logger.LogWarning(
|
||||
"Invalid detail level setting received: '{FidelityString}' for model {ModelCardId}, using default: {DefaultValue}",
|
||||
fidelityString,
|
||||
modelCard.ModelCardId,
|
||||
@@ -74,7 +57,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Transform? GetReferencePointSetting(ModelCard modelCard)
|
||||
public Transform? GetReferencePointSetting(Document document, ModelCard modelCard)
|
||||
{
|
||||
var referencePointString =
|
||||
modelCard.Settings?.FirstOrDefault(s => s.Id == SendReferencePointSetting.SETTING_ID)?.Value as string;
|
||||
@@ -88,14 +71,14 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
{
|
||||
// get the current transform from setting first
|
||||
// we are doing this because we can't track if reference points were changed between send operations.
|
||||
Transform? currentTransform = GetTransform(referencePoint);
|
||||
Transform? currentTransform = GetTransform(document, referencePoint);
|
||||
|
||||
if (_referencePointCache.TryGetValue(modelCard.ModelCardId.NotNull(), out Transform? previousTransform))
|
||||
{
|
||||
// invalidate conversion cache if the transform has changed
|
||||
if (modelCard is SenderModelCard senderModelCard && previousTransform != currentTransform)
|
||||
{
|
||||
EvictCacheForModelCard(senderModelCard);
|
||||
EvictCacheForModelCard(document, senderModelCard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +87,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
}
|
||||
|
||||
// log the issue
|
||||
_logger.LogWarning(
|
||||
logger.LogWarning(
|
||||
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
|
||||
referencePointString,
|
||||
modelCard.ModelCardId,
|
||||
@@ -116,8 +99,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard) =>
|
||||
public bool GetSendParameterNullOrEmptyStringsSetting(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
SendParameterNullOrEmptyStringsSetting.SETTING_ID,
|
||||
SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
@@ -127,8 +111,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
|
||||
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
|
||||
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
|
||||
public bool GetLinkedModelsSetting(SenderModelCard modelCard) =>
|
||||
public bool GetLinkedModelsSetting(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
LinkedModelsSetting.SETTING_ID,
|
||||
LinkedModelsSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
@@ -136,8 +121,9 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
"Linked models"
|
||||
);
|
||||
|
||||
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard) =>
|
||||
public bool GetSendRebarsAsVolumetric(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
SendRebarsAsVolumetricSetting.SETTING_ID,
|
||||
SendRebarsAsVolumetricSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
@@ -149,6 +135,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
/// Helper method to handle boolean settings with caching and logging
|
||||
/// </summary>
|
||||
private bool GetBooleanSettingWithCache(
|
||||
Document document,
|
||||
string settingId,
|
||||
bool defaultValue,
|
||||
SenderModelCard modelCard,
|
||||
@@ -163,7 +150,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
EvictCacheForModelCard(document, modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +160,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
// respected (linked models disabled but still sent linked models), I think we should note this occurence so we know
|
||||
if (settingValue == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
logger.LogWarning(
|
||||
"{SettingName} setting was null for model {ModelCardId}, using default: {DefaultValue}",
|
||||
settingName,
|
||||
modelCard.ModelCardId,
|
||||
@@ -184,71 +171,59 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private void EvictCacheForModelCard(SenderModelCard modelCard)
|
||||
private void EvictCacheForModelCard(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
if (doc == null)
|
||||
{
|
||||
throw new SpeckleException("Unable to retrieve active UI document");
|
||||
}
|
||||
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
var objectIds = modelCard.SendFilter?.SelectedObjectIds ?? [];
|
||||
var unpackedObjectIds = elementUnpacker.GetUnpackedElementIds(objectIds, document);
|
||||
sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
private Transform? GetTransform(ReferencePointType referencePointType)
|
||||
private Transform? GetTransform(Document document, ReferencePointType referencePointType)
|
||||
{
|
||||
Transform? referencePointTransform = null;
|
||||
|
||||
if (_revitContext.UIApplication is UIApplication uiApplication)
|
||||
// first get the main doc base points and reference setting transform
|
||||
using FilteredElementCollector filteredElementCollector = new(document);
|
||||
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
|
||||
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
|
||||
BasePoint? surveyPoint = points.FirstOrDefault(o => o.IsShared);
|
||||
|
||||
switch (referencePointType)
|
||||
{
|
||||
// first get the main doc base points and reference setting transform
|
||||
using FilteredElementCollector filteredElementCollector = new(uiApplication.ActiveUIDocument.Document);
|
||||
var points = filteredElementCollector.OfClass(typeof(BasePoint)).Cast<BasePoint>().ToList();
|
||||
BasePoint? projectPoint = points.FirstOrDefault(o => !o.IsShared);
|
||||
BasePoint? surveyPoint = points.FirstOrDefault(o => o.IsShared);
|
||||
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
|
||||
case ReferencePointType.ProjectBase:
|
||||
if (projectPoint is not null)
|
||||
{
|
||||
referencePointTransform = Transform.CreateTranslation(projectPoint.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't retrieve Project Point from document");
|
||||
}
|
||||
break;
|
||||
|
||||
switch (referencePointType)
|
||||
{
|
||||
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
|
||||
case ReferencePointType.ProjectBase:
|
||||
if (projectPoint is not null)
|
||||
{
|
||||
referencePointTransform = Transform.CreateTranslation(projectPoint.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't retrieve Project Point from document");
|
||||
}
|
||||
break;
|
||||
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
|
||||
case ReferencePointType.Survey:
|
||||
if (surveyPoint is not null && projectPoint is not null)
|
||||
{
|
||||
// POC: should a null angle resolve to 0?
|
||||
// retrieve the survey point rotation from the project point
|
||||
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
|
||||
|
||||
// note that the project base (ui) rotation is registered on the survey pt, not on the base point
|
||||
case ReferencePointType.Survey:
|
||||
if (surveyPoint is not null && projectPoint is not null)
|
||||
{
|
||||
// POC: should a null angle resolve to 0?
|
||||
// retrieve the survey point rotation from the project point
|
||||
var angle = projectPoint.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM)?.AsDouble() ?? 0;
|
||||
// POC: following disposed incorrectly or early or maybe a false negative?
|
||||
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
|
||||
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
|
||||
}
|
||||
break;
|
||||
|
||||
// POC: following disposed incorrectly or early or maybe a false negative?
|
||||
using Transform translation = Transform.CreateTranslation(surveyPoint.Position);
|
||||
referencePointTransform = translation.Multiply(Transform.CreateRotation(XYZ.BasisZ, angle));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't retrieve Survey and Project Point from document");
|
||||
}
|
||||
break;
|
||||
|
||||
case ReferencePointType.InternalOrigin:
|
||||
break;
|
||||
}
|
||||
|
||||
return referencePointTransform;
|
||||
case ReferencePointType.InternalOrigin:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Revit Context UI Application was null when retrieving reference point transform."
|
||||
);
|
||||
return referencePointTransform;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ViewUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitViewManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
|
||||
@@ -60,4 +61,4 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -202,6 +202,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -325,9 +346,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -337,7 +357,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino7": {
|
||||
@@ -382,35 +402,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GrasshopperAsyncComponent" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
|
||||
<PackageReference Include="System.Resources.Extensions" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -202,6 +202,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "D04pCdleqLeDxthANCb8+X1xfEYr4+Q3GTuHtqOrMQeGHDAVPc5G3M0D6VYEUYbLYav0NBZ6tNuWO2Y/fqfWSw==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "u8lJ+ECslmVPsn4yOCg3hAzj3zh6r+gp2oQh8RDGn22NihIPOsMhBFvoBruL1QVhXdJcS4rI2J6VEAbdvL9FRg=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -325,9 +346,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )",
|
||||
"Speckle.Sdk": "[3.5.4, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.4, )"
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -337,7 +357,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.4, )"
|
||||
"Speckle.Objects": "[3.11.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
@@ -381,35 +401,12 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
|
||||
"requested": "[3.11.1, )",
|
||||
"resolved": "3.11.1",
|
||||
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.4"
|
||||
"Speckle.Sdk": "3.11.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.4"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.4, )",
|
||||
"resolved": "3.5.4",
|
||||
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -47,6 +47,11 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
|
||||
// note: we are skipping the input collection, to make the output paths more intuitive
|
||||
foreach (var element in wrapper.Elements)
|
||||
{
|
||||
if (element is null)
|
||||
{
|
||||
continue; // skip nulls (CNX-2855)
|
||||
}
|
||||
|
||||
if (element is SpeckleCollectionWrapper childCollectionWrapper)
|
||||
{
|
||||
paths.AddRange(GetPaths(childCollectionWrapper));
|
||||
@@ -70,11 +75,15 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
|
||||
void GetPathsInternal(SpeckleCollectionWrapper w)
|
||||
{
|
||||
currentPath.Add(w.Name);
|
||||
var subCols = w.Elements.OfType<SpeckleCollectionWrapper>().ToList();
|
||||
var subCols = w
|
||||
.Elements.Where(e => e != null) // skip nulls (CNX-2855)
|
||||
.OfType<SpeckleCollectionWrapper>()
|
||||
.ToList();
|
||||
|
||||
// NOTE: here we're basically outputting only paths that correspond to a collection
|
||||
// that has values inside of it.
|
||||
if (subCols.Count != w.Elements.Count)
|
||||
var nonNullElementCount = w.Elements.Count(e => e != null);
|
||||
if (subCols.Count != nonNullElementCount)
|
||||
{
|
||||
allPaths.Add(string.Join(Constants.LAYER_PATH_DELIMITER, currentPath));
|
||||
}
|
||||
|
||||
+107
-6
@@ -48,6 +48,7 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
var rootCollection = CreateRootCollection();
|
||||
bool hasAnyInput = false;
|
||||
|
||||
foreach (var inputParam in Params.Input)
|
||||
{
|
||||
@@ -57,6 +58,7 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
continue;
|
||||
}
|
||||
|
||||
hasAnyInput = true;
|
||||
var childCollection = ProcessInputParameter(inputParam, data, rootCollection.Name);
|
||||
if (childCollection != null)
|
||||
{
|
||||
@@ -64,12 +66,46 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
// Skip validation if no input provided
|
||||
if (!hasAnyInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// validate collection isn't empty (CNX-2855)
|
||||
if (rootCollection.Elements.Count == 0 || !rootCollection.Elements.Any(HasAnyValidContent))
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
"Collection contains no valid geometry. All input objects are unsupported types."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
|
||||
}
|
||||
|
||||
private SpeckleCollectionWrapper CreateRootCollection()
|
||||
{
|
||||
return new SpeckleCollectionWrapper
|
||||
/// <summary>
|
||||
/// Recursively checks if collection or any descendants contain valid geometry/data objects
|
||||
/// </summary>
|
||||
private bool HasAnyValidContent(ISpeckleCollectionObject? element) =>
|
||||
element switch
|
||||
{
|
||||
SpeckleGeometryWrapper => true,
|
||||
SpeckleDataObjectWrapper => true,
|
||||
SpeckleCollectionWrapper collection => collection.Elements.Any(HasAnyValidContent),
|
||||
_ => false
|
||||
};
|
||||
|
||||
private SpeckleCollectionWrapper CreateRootCollection() =>
|
||||
new()
|
||||
{
|
||||
Base = new Collection(),
|
||||
Name = "Unnamed",
|
||||
@@ -78,7 +114,6 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
Material = null,
|
||||
ApplicationId = InstanceGuid.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private SpeckleCollectionWrapper? ProcessInputParameter(IGH_Param inputParam, List<IGH_Goo> data, string rootName)
|
||||
{
|
||||
@@ -133,7 +168,10 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
|
||||
// Check for duplicate names within this collection
|
||||
foreach (
|
||||
var subCollectionName in collectionGoo.Value.Elements.OfType<SpeckleCollectionWrapper>().Select(c => c.Name)
|
||||
var subCollectionName in collectionGoo
|
||||
.Value.Elements.Where(e => e != null) // skip nulls (CNX-2855)
|
||||
.OfType<SpeckleCollectionWrapper>()
|
||||
.Select(c => c.Name)
|
||||
)
|
||||
{
|
||||
if (!duplicateNames.Add(subCollectionName))
|
||||
@@ -156,6 +194,8 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
List<string> childPath
|
||||
)
|
||||
{
|
||||
int skippedCount = 0;
|
||||
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
// handle data objects directly (deep copy to avoid mutations)
|
||||
@@ -177,7 +217,68 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"{obj?.GetType().Name} type cannot be added to collections.");
|
||||
// add null placeholder to preserve topology (CNX-2855)
|
||||
parentCollection.Elements.Add(null);
|
||||
skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// add warning if objects were skipped (CNX-2855)
|
||||
if (skippedCount > 0)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Skipped {skippedCount} unsupported object(s) (Leaders, TextDots, Dimensions, etc.)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 null:
|
||||
break; // skip nulls (CNX-2855)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-14
@@ -51,11 +51,14 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
Name = wrapper.Name;
|
||||
NickName = wrapper.Name;
|
||||
|
||||
// Separate objects and collections
|
||||
// Separate objects and collections (skip nulls for non-topology outputs)
|
||||
// Note: SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper,
|
||||
// so it will be included in objects
|
||||
List<SpeckleWrapper> objects = wrapper.GetAtomicObjects().ToList();
|
||||
List<SpeckleCollectionWrapper> collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
|
||||
List<SpeckleCollectionWrapper> collections = wrapper
|
||||
.Elements.Where(e => e != null)
|
||||
.OfType<SpeckleCollectionWrapper>()
|
||||
.ToList();
|
||||
|
||||
var outputParams = new List<OutputParamWrapper>();
|
||||
if (objects.Count != 0)
|
||||
@@ -69,22 +72,14 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
Access = GH_ParamAccess.list
|
||||
};
|
||||
|
||||
// Create appropriate Goo types for each object (downside of the inheritance refactor)
|
||||
// Don't use topology for _objects output (always list)
|
||||
List<IGH_Goo> atomicObjectGoos = objects.Select(obj => obj.CreateGoo()).ToList();
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, atomicObjectGoos, null));
|
||||
}
|
||||
|
||||
foreach (SpeckleCollectionWrapper childWrapper in collections)
|
||||
{
|
||||
/* POC: we shouldn't skip empty, people would probably expect to see what they see in browser.
|
||||
if (childWrapper.Elements.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
var hasInnerCollections = childWrapper.Elements.Any(el => el is SpeckleCollectionWrapper);
|
||||
var hasInnerCollections = childWrapper.Elements.Where(e => e != null).Any(el => el is SpeckleCollectionWrapper);
|
||||
var topology = childWrapper.Topology;
|
||||
var nickName = childWrapper.Name;
|
||||
if (nickName.Length > 16)
|
||||
@@ -111,8 +106,11 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create appropriate Goo types for child objects
|
||||
List<IGH_Goo> childObjectGoos = childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
|
||||
// If topology exists, include nulls to match topology count (CNX-2855)
|
||||
List<IGH_Goo> childObjectGoos =
|
||||
topology != null
|
||||
? childWrapper.ToGooListWithNulls()
|
||||
: childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
|
||||
outputValue = childObjectGoos;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
|
||||
=> ParseSpeckleWrapper(
|
||||
collectionGoo.Value,
|
||||
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
|
||||
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o!).CreateGoo()).ToList()
|
||||
),
|
||||
|
||||
// get geometries from wrapper to override displayValue prop while parsing
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ public class TokenUrlComponent : GH_Component
|
||||
{
|
||||
public TokenUrlComponent()
|
||||
: base(
|
||||
"Speckle Model URL",
|
||||
"Speckle Model URL with Token",
|
||||
"URL",
|
||||
"Create a Speckle model link using URL and developer token",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
|
||||
+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)
|
||||
{
|
||||
|
||||
+1
-3
@@ -27,8 +27,7 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
|
||||
protected override Bitmap Icon => Resources.speckle_properties_expand;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
@@ -36,7 +35,6 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
|
||||
"Speckle Properties to expand",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
|
||||
|
||||
|
||||
+12
-14
@@ -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++)
|
||||
@@ -155,10 +155,11 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
|
||||
var outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
|
||||
|
||||
// only use topology for the first output when we have a path
|
||||
if (i == 0 && targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
|
||||
{
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoos);
|
||||
// include nulls to match topology count (CNX-2855)
|
||||
var outputGoosWithNulls = targetCollectionWrapper.ToGooListWithNulls();
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoosWithNulls);
|
||||
dataAccess.SetDataTree(i, tree);
|
||||
}
|
||||
else
|
||||
@@ -173,13 +174,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 +234,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 +290,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) { }
|
||||
}
|
||||
|
||||
+6
-2
@@ -103,7 +103,7 @@ public class SpeckleDataObjectPassthrough()
|
||||
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
|
||||
if (!da.GetDataList(1, inputGeometry) && result == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle DataObject or Geometries.");
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle DataObject or Geometries");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class SpeckleDataObjectPassthrough()
|
||||
{
|
||||
if (inputGeo.Value is SpeckleBlockInstanceWrapper)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"DataObjects cannot contain Block Instances.");
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "DataObjects cannot contain Block Instances");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -158,6 +158,10 @@ public class SpeckleDataObjectPassthrough()
|
||||
result.Properties = inputProperties;
|
||||
}
|
||||
|
||||
// generate application ID for new data objects. Unlike SpeckleGeometry, DataObject wrappers aren't created
|
||||
// through casting (which auto-generates IDs), so we must explicitly ensure an ID exists here
|
||||
result.ApplicationId ??= Guid.NewGuid().ToString();
|
||||
|
||||
// get the path
|
||||
string? path =
|
||||
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ public class SpeckleGeometryPassthrough()
|
||||
|
||||
if (result == null && inputGeometry == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle Geometry or Geometry");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+120
-62
@@ -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)
|
||||
@@ -123,7 +130,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
{
|
||||
var autoReceiveMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Load automatically",
|
||||
"Load new versions automatically",
|
||||
(s, e) =>
|
||||
{
|
||||
AutoReceive = !AutoReceive;
|
||||
@@ -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)
|
||||
@@ -412,66 +447,89 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
}
|
||||
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
Root = await scope
|
||||
.Get<GrasshopperReceiveOperation>()
|
||||
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
TraversalContextUnpacker traversalContextUnpacker = new();
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
// initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
try
|
||||
{
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
Root = await scope
|
||||
.Get<GrasshopperReceiveOperation>()
|
||||
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
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...";
|
||||
SpeckleConversionContext.SetupCurrent(scope);
|
||||
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
// initialize unpackers and collection builder (data holders - created with new)
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// get handler from DI and initialize with per-operation data
|
||||
var mapHandler = scope
|
||||
.Get<LocalToGlobalMapHandler>()
|
||||
.Initialize(
|
||||
scope.Get<TraversalContextUnpacker>(),
|
||||
colorUnpacker,
|
||||
materialUnpacker,
|
||||
collectionRebuilder,
|
||||
unpackedRoot.DefinitionProxies
|
||||
);
|
||||
|
||||
// handler deals with two-pass conversion: normal objects first, then DataObjects with InstanceProxies
|
||||
mapHandler.ConvertAtomicObjects(atomicObjects);
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// internally filters out InstanceProxies that belong to registered DataObjects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances);
|
||||
|
||||
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>()
|
||||
{
|
||||
{ "isAsync", true },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
|
||||
{ "auto", Parent.AutoReceive }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add(
|
||||
"isMultiplayer",
|
||||
receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id
|
||||
);
|
||||
}
|
||||
|
||||
await scope
|
||||
.Get<IMixPanelManager>()
|
||||
.TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
finally
|
||||
{
|
||||
{ "isAsync", true },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
|
||||
{ "auto", Parent.AutoReceive }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
SpeckleConversionContext.EndCurrent();
|
||||
}
|
||||
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id);
|
||||
}
|
||||
await scope.Get<IMixPanelManager>().TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+231
-74
@@ -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,92 +135,221 @@ 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();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
// Do the thing 👇🏼
|
||||
|
||||
Account? account = input.Resource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
try
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
// Do the thing 👇🏼
|
||||
|
||||
Account? account = input.Resource.Account.GetAccount(scope);
|
||||
if (account is null)
|
||||
{
|
||||
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.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.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>
|
||||
{
|
||||
{ "isAsync", false },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
}
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
|
||||
}
|
||||
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
|
||||
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
|
||||
|
||||
// Setup conversion context BEFORE unpacking (which triggers DataObjectConverter)
|
||||
SpeckleConversionContext.SetupCurrent(scope);
|
||||
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
// Initialize unpackers and collection builder (data holders - created with new)
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// get handler from DI and initialize with per-operation data
|
||||
var mapHandler = scope
|
||||
.ServiceProvider.GetRequiredService<LocalToGlobalMapHandler>()
|
||||
.Initialize(
|
||||
scope.ServiceProvider.GetRequiredService<TraversalContextUnpacker>(),
|
||||
colorUnpacker,
|
||||
materialUnpacker,
|
||||
collectionRebuilder,
|
||||
unpackedRoot.DefinitionProxies
|
||||
);
|
||||
|
||||
// two-pass conversion: normal objects first, then DataObjects with InstanceProxies
|
||||
mapHandler.ConvertAtomicObjects(atomicObjects);
|
||||
|
||||
// process block instances (internally filters InstanceProxies belonging to registered DataObjects)
|
||||
mapHandler.ConvertBlockInstances(blockInstances);
|
||||
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo, RootProperties = rootPropertiesGoo };
|
||||
}
|
||||
finally
|
||||
{
|
||||
SpeckleConversionContext.EndCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupSubscription(SpeckleUrlModelResource resource)
|
||||
{
|
||||
// skip if already subscribed to this resource
|
||||
if (_apiClient != null && _lastResource != null && _lastResource.Equals(resource))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
// only subscribe for Model URLs (not specific versions)
|
||||
if (resource is SpeckleUrlModelVersionResource)
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "isAsync", false },
|
||||
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
|
||||
};
|
||||
if (receiveInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
|
||||
CleanupSubscription();
|
||||
_lastResource = resource;
|
||||
return;
|
||||
}
|
||||
if (receiveInfo.SelectedVersionUserId != null)
|
||||
|
||||
try
|
||||
{
|
||||
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
|
||||
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";
|
||||
}
|
||||
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
|
||||
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Could not setup monitoring: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// We need to rethink these lovely unpackers, there's a bit too many of 'em
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
var traversalContextUnpacker = new TraversalContextUnpacker();
|
||||
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
private void OnVersionCreated(object? sender, ProjectVersionsUpdatedMessage e) =>
|
||||
// new version detected - trigger reload
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
ExpireSolution(true);
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
private void CleanupSubscription()
|
||||
{
|
||||
if (_apiClient != null && _lastResource != null)
|
||||
{
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo };
|
||||
base.DocumentContextChanged(document, context);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-1
@@ -51,6 +51,7 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
|
||||
public IClient ApiClient { get; set; }
|
||||
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
|
||||
public SpecklePropertyGroupGoo? RootProperties { get; private set; }
|
||||
public SpeckleUrlModelResource? OutputParam { get; set; }
|
||||
public bool HasMultipleInputs { get; set; }
|
||||
|
||||
@@ -58,7 +59,10 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
// speckle model
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
|
||||
// collection
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Collection",
|
||||
@@ -68,6 +72,16 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
|
||||
);
|
||||
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
|
||||
pManager[2].Optional = true;
|
||||
|
||||
// model-wide props (see cnx-2722)
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"properties",
|
||||
"Optional model-wide properties to attach to the root collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
pManager[3].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
@@ -280,6 +294,19 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
|
||||
string? versionMessage = null;
|
||||
da.GetData(2, ref versionMessage);
|
||||
VersionMessage = versionMessage;
|
||||
|
||||
SpecklePropertyGroupGoo? rootPropsGoo = null;
|
||||
da.GetData(3, ref rootPropsGoo);
|
||||
|
||||
// validate single properties group
|
||||
// we can't support a list input here, what does that even mean? grafting the collection to each props entry?? scary.
|
||||
if (Params.Input[3].VolatileData.DataCount > 1)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one Model Properties group is allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
RootProperties = rootPropsGoo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +422,13 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
throw new InvalidOperationException("Root Collection was null");
|
||||
}
|
||||
|
||||
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly - it's only
|
||||
// constructed here from the Collection + Model Properties inputs.
|
||||
// if this changes, then we need to update below!
|
||||
var rootWrapper = new SpeckleRootCollectionWrapper(rootCollectionWrapper.Value, Parent.RootProperties?.Unwrap());
|
||||
|
||||
rootCollectionWrapper = new SpeckleRootCollectionWrapperGoo(rootWrapper);
|
||||
|
||||
// Step 1 - SEND TO SERVER
|
||||
var sendInfo = await urlModelResource.GetSendInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -408,7 +442,7 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
SendOperationResult? result = await sendOperation
|
||||
.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
|
||||
new List<SpeckleCollectionWrapperGoo> { rootCollectionWrapper },
|
||||
sendInfo,
|
||||
Parent.VersionMessage,
|
||||
progress,
|
||||
|
||||
+51
-19
@@ -20,12 +20,19 @@ public class SendComponentInput
|
||||
public SpeckleUrlModelResource Resource { get; }
|
||||
public SpeckleCollectionWrapperGoo Input { get; }
|
||||
public bool Run { get; }
|
||||
public SpecklePropertyGroupGoo? RootProperties { get; }
|
||||
|
||||
public SendComponentInput(SpeckleUrlModelResource resource, SpeckleCollectionWrapperGoo input, bool run)
|
||||
public SendComponentInput(
|
||||
SpeckleUrlModelResource resource,
|
||||
SpeckleCollectionWrapperGoo input,
|
||||
bool run,
|
||||
SpecklePropertyGroupGoo? rootProperties
|
||||
)
|
||||
{
|
||||
Resource = resource;
|
||||
Input = input;
|
||||
Run = run;
|
||||
RootProperties = rootProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +43,11 @@ public class SendComponentOutput(SpeckleUrlModelResource? resource)
|
||||
|
||||
public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, SendComponentOutput>
|
||||
{
|
||||
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
|
||||
public string? Url { get; private set; }
|
||||
public string? VersionMessage { get; private set; }
|
||||
protected override Bitmap Icon => Resources.speckle_operations_syncpublish;
|
||||
|
||||
public SendComponent()
|
||||
: base(
|
||||
"(Sync) Publish",
|
||||
@@ -45,17 +57,12 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
ComponentCategories.DEVELOPER
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
|
||||
|
||||
public string? Url { get; private set; }
|
||||
|
||||
public string? VersionMessage { get; private set; }
|
||||
|
||||
protected override Bitmap Icon => Resources.speckle_operations_syncpublish;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
// speckle model
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
|
||||
// collection
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Collection",
|
||||
@@ -65,13 +72,22 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
);
|
||||
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
|
||||
pManager[2].Optional = true;
|
||||
|
||||
// model-wide props (see cnx-2722)
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"properties",
|
||||
"Optional model-wide properties to attach to the root collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
pManager[3].Optional = true;
|
||||
|
||||
pManager.AddBooleanParameter("Run", "r", "Run the publish operation", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
}
|
||||
|
||||
protected override SendComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
@@ -93,10 +109,20 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
da.GetData(2, ref versionMessage);
|
||||
VersionMessage = versionMessage;
|
||||
|
||||
bool run = false;
|
||||
da.GetData(3, ref run);
|
||||
SpecklePropertyGroupGoo? rootPropsGoo = null;
|
||||
da.GetData(3, ref rootPropsGoo);
|
||||
|
||||
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper, run);
|
||||
// validate single properties group
|
||||
// we can't support a list input here, what does that even mean? grafting the collection to each props entry?? scary.
|
||||
if (Params.Input[3].VolatileData.DataCount > 1)
|
||||
{
|
||||
throw new SpeckleException("Only one Model Properties group is allowed");
|
||||
}
|
||||
|
||||
bool run = false;
|
||||
da.GetData(4, ref run);
|
||||
|
||||
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper, run, rootPropsGoo);
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, SendComponentOutput result)
|
||||
@@ -121,7 +147,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
{
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
Menu_AppendItem(menu, $"View created model online ↗", (s, e) => Open(Url));
|
||||
Menu_AppendItem(menu, "View created model online ↗", (s, e) => Open(Url));
|
||||
}
|
||||
|
||||
static void Open(string url)
|
||||
@@ -166,6 +192,12 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
return new(null);
|
||||
}
|
||||
|
||||
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly - it's only
|
||||
// constructed here from the Collection + Model Properties inputs.
|
||||
// if this changes, then we need to update below!
|
||||
var rootWrapper = new SpeckleRootCollectionWrapper(input.Input.Value, input.RootProperties?.Unwrap());
|
||||
var collectionToSend = new SpeckleRootCollectionWrapperGoo(rootWrapper);
|
||||
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
@@ -173,7 +205,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
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");
|
||||
}
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
@@ -186,7 +218,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
await sendOperation
|
||||
.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo>() { input.Input },
|
||||
new List<SpeckleCollectionWrapperGoo> { collectionToSend },
|
||||
sendInfo,
|
||||
VersionMessage,
|
||||
progress,
|
||||
@@ -195,7 +227,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
var customProperties = new Dictionary<string, object>() { { "isAsync", false } };
|
||||
var customProperties = new Dictionary<string, object> { { "isAsync", false } };
|
||||
if (sendInfo.WorkspaceId != null)
|
||||
{
|
||||
customProperties.Add("workspace_id", sendInfo.WorkspaceId);
|
||||
|
||||
+43
-15
@@ -87,6 +87,7 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
string? urlInput = null;
|
||||
|
||||
// SCENARIO 1: Component has input wire connected
|
||||
|
||||
if (da.GetData(0, ref urlInput))
|
||||
{
|
||||
UrlInput = urlInput;
|
||||
@@ -99,6 +100,11 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
return;
|
||||
}
|
||||
|
||||
if (_justPastedIn)
|
||||
{
|
||||
RestoreAccountFromStoredState();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
|
||||
@@ -132,22 +138,9 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
_storedUserId = SpeckleOperationWizard.SelectedAccount?.id;
|
||||
}
|
||||
|
||||
if (_justPastedIn && _storedUserId != null && !string.IsNullOrEmpty(_storedUserId))
|
||||
if (_justPastedIn)
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
|
||||
}
|
||||
catch (SpeckleAccountManagerException e)
|
||||
{
|
||||
// Swallow and move onto checking server.
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
|
||||
{
|
||||
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
|
||||
}
|
||||
RestoreAccountFromStoredState();
|
||||
}
|
||||
|
||||
// Validate backing data
|
||||
@@ -396,4 +389,39 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
VersionContextMenuButton.ExpirePreview(redraw);
|
||||
base.ExpirePreview(redraw);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the account from stored state when the component is pasted or loaded from file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Attempts to restore account in two stages:
|
||||
/// <list type="number">
|
||||
/// <item>First tries to get account by stored user ID</item>
|
||||
/// <item>If that fails and server url is available, falls back to getting any account matching the server</item>
|
||||
/// </list>
|
||||
/// Only executes when <see cref="_justPastedIn"/> is true and <see cref="_storedUserId"/> is not empty.
|
||||
/// </remarks>
|
||||
private void RestoreAccountFromStoredState()
|
||||
{
|
||||
if (_storedUserId is null || string.IsNullOrEmpty(_storedUserId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
|
||||
}
|
||||
catch (SpeckleAccountManagerException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
// Fallback: if account wasn't found by ID but we have a server URL,
|
||||
// try to find any account matching that server
|
||||
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
|
||||
{
|
||||
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -79,22 +79,40 @@ public class SpeckleOperationWizard
|
||||
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
|
||||
if (resources.Length == 0)
|
||||
{
|
||||
throw new SpeckleException($"Input url string was empty");
|
||||
throw new SpeckleException("Input url string was empty");
|
||||
}
|
||||
|
||||
if (resources.Length > 1)
|
||||
{
|
||||
throw new SpeckleException($"Input multi-model url is not supported");
|
||||
throw new SpeckleException("Input multi-model url is not supported");
|
||||
}
|
||||
|
||||
var resource = resources.First();
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
var account = resource.Account.GetAccount(scope);
|
||||
SetAccount(account, false);
|
||||
var urlDerivedAccount = resource.Account.GetAccount(scope);
|
||||
|
||||
// if no account is selected, happily go through the url derived account approach
|
||||
if (SelectedAccount == null)
|
||||
{
|
||||
throw new SpeckleException("No account found for server URL");
|
||||
SetAccount(urlDerivedAccount, false);
|
||||
}
|
||||
// if we have an account from right-click context-menu, we rely on that and just validate that it's actually applicable to that server
|
||||
else if (urlDerivedAccount != null && SelectedAccount.serverInfo.url != urlDerivedAccount.serverInfo.url)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"Selected account is for '{SelectedAccount.serverInfo.url}' "
|
||||
+ $"but URL requires '{urlDerivedAccount.serverInfo.url}'"
|
||||
);
|
||||
}
|
||||
|
||||
// we have both scenarios covered
|
||||
// Scenario #1 - default account from url
|
||||
// Scenario #2 - triggered by account switch on right-click context (and validated)
|
||||
if (SelectedAccount == null)
|
||||
{
|
||||
throw new SpeckleException(
|
||||
$"No appropriate account found for the given '{urlDerivedAccount?.serverInfo.url}' server"
|
||||
);
|
||||
}
|
||||
|
||||
IClient client = _clientFactory.Create(SelectedAccount);
|
||||
|
||||
@@ -107,17 +107,14 @@ public static class GrasshopperHelpers
|
||||
M12 = rhinoTransform.M01,
|
||||
M13 = rhinoTransform.M02,
|
||||
M14 = rhinoTransform.M03 * conversionFactor,
|
||||
|
||||
M21 = rhinoTransform.M10,
|
||||
M22 = rhinoTransform.M11,
|
||||
M23 = rhinoTransform.M12,
|
||||
M24 = rhinoTransform.M13 * conversionFactor,
|
||||
|
||||
M31 = rhinoTransform.M20,
|
||||
M32 = rhinoTransform.M21,
|
||||
M33 = rhinoTransform.M22,
|
||||
M34 = rhinoTransform.M23 * conversionFactor,
|
||||
|
||||
M41 = rhinoTransform.M30,
|
||||
M42 = rhinoTransform.M31,
|
||||
M43 = rhinoTransform.M32,
|
||||
@@ -139,6 +136,8 @@ public static class GrasshopperHelpers
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case null:
|
||||
break; // skip nulls (CNX-2855)
|
||||
case SpeckleDataObjectWrapper dataObject:
|
||||
yield return dataObject;
|
||||
break;
|
||||
@@ -153,8 +152,7 @@ public static class GrasshopperHelpers
|
||||
yield return subElement;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -173,11 +171,9 @@ public static class GrasshopperHelpers
|
||||
{
|
||||
return instanceGoo.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpeckleGeometryWrapperGoo objGoo = new();
|
||||
return objGoo.CastFrom(goo) ? objGoo.Value : null;
|
||||
}
|
||||
|
||||
SpeckleGeometryWrapperGoo objGoo = new();
|
||||
return objGoo.CastFrom(goo) ? objGoo.Value : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -270,7 +266,9 @@ public static class GrasshopperHelpers
|
||||
|
||||
for (int i = 0; i < elCount; i++)
|
||||
{
|
||||
tree.EnsurePath(myPath).Add(new Grasshopper.Kernel.Types.GH_ObjectWrapper(subset[subsetCount + i]));
|
||||
var item = subset[subsetCount + i];
|
||||
// GH_ObjectWrapper accepts null to preserve tree structure for failed conversions (CNX-2855)
|
||||
tree.EnsurePath(myPath).Add(item != null ? new GH_ObjectWrapper(item) : new GH_ObjectWrapper(null!));
|
||||
}
|
||||
|
||||
subsetCount += elCount;
|
||||
@@ -293,6 +291,13 @@ public static class GrasshopperHelpers
|
||||
{
|
||||
topology += myPath.ToString(false) + "-" + param.VolatileData.get_Branch(myPath).Count + " ";
|
||||
}
|
||||
|
||||
return topology;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Elements to Goo list, converting nulls to GH_ObjectWrapper(null) for topology preservation (CNX-2855)
|
||||
/// </summary>
|
||||
public static List<IGH_Goo> ToGooListWithNulls(this SpeckleCollectionWrapper coll) =>
|
||||
coll.Elements.Select(e => e == null ? new GH_ObjectWrapper(null!) : ((SpeckleWrapper)e).CreateGoo()).ToList();
|
||||
}
|
||||
|
||||
+19
-3
@@ -4,6 +4,7 @@ using Speckle.Connectors.GrasshopperShared.Registration;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
@@ -29,13 +30,13 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupCurrent()
|
||||
public static void SetupCurrent(IServiceScope? scope = null)
|
||||
{
|
||||
if (s_currentContext != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
s_scope = scope ?? PriorityLoader.CreateScopeForActiveDocument();
|
||||
s_currentContext = s_scope.Get<SpeckleConversionContext>();
|
||||
}
|
||||
|
||||
@@ -50,7 +51,21 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
|
||||
s_scope = null;
|
||||
}
|
||||
|
||||
public Base ConvertToSpeckle(object geo) => speckleConverter.Convert(geo);
|
||||
public Base? ConvertToSpeckle(object geo)
|
||||
{
|
||||
try
|
||||
{
|
||||
return speckleConverter.Convert(geo);
|
||||
}
|
||||
catch (ConversionException ex)
|
||||
{
|
||||
// changed as of CNX-2855
|
||||
// log for debugging but don't throw - let caller handle null return
|
||||
// we don't want to throw and fail whole operation, but want a way to signal to component that sumting wong
|
||||
System.Diagnostics.Debug.WriteLine($"Conversion failed for {geo.GetType().Name}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<(object, Base)> ConvertToHost(Base input)
|
||||
{
|
||||
@@ -60,6 +75,7 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
|
||||
{
|
||||
GeometryBase geometry => [(geometry, input)],
|
||||
List<GeometryBase> geometryList => geometryList.Select(o => ((object)o, input)).ToList(),
|
||||
List<(GeometryBase, Base)> pairList when pairList.Count == 0 => [],
|
||||
IEnumerable<(GeometryBase, Base)> fallbackConversionResult
|
||||
=> fallbackConversionResult.Select(o => ((object)o.Item1, o.Item2)).ToList(),
|
||||
object obj => [(obj, input)],
|
||||
|
||||
+346
-118
@@ -1,12 +1,20 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.ToHost;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using DataObject = Speckle.Objects.Data.DataObject;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// Handles conversion of atomic objects from TraversalContexts into Grasshopper wrapper objects.
|
||||
@@ -19,184 +27,404 @@ using Speckle.Sdk.Models.Instances;
|
||||
internal sealed class LocalToGlobalMapHandler
|
||||
{
|
||||
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
|
||||
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
|
||||
|
||||
private readonly TraversalContextUnpacker _traversalContextUnpacker;
|
||||
private readonly GrasshopperColorUnpacker _colorUnpacker;
|
||||
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
|
||||
// injected via constructor (DI-managed)
|
||||
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
|
||||
private readonly ILogger<LocalToGlobalMapHandler> _logger;
|
||||
private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;
|
||||
|
||||
// set via Initialize() method (per-operation data)
|
||||
private TraversalContextUnpacker _traversalContextUnpacker = null!;
|
||||
private GrasshopperColorUnpacker _colorUnpacker = null!;
|
||||
private GrasshopperMaterialUnpacker _materialUnpacker = null!;
|
||||
private IReadOnlyCollection<InstanceDefinitionProxy>? _definitionProxies;
|
||||
|
||||
// auto property (fixes IDE0032)
|
||||
public GrasshopperCollectionRebuilder CollectionRebuilder { get; private set; } = null!;
|
||||
|
||||
public LocalToGlobalMapHandler(
|
||||
IDataObjectInstanceRegistry dataObjectInstanceRegistry,
|
||||
ILogger<LocalToGlobalMapHandler> logger,
|
||||
IConverterSettingsStore<RhinoConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_dataObjectInstanceRegistry = dataObjectInstanceRegistry;
|
||||
_logger = logger;
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the handler with per-operation data.
|
||||
/// Must be called before using ConvertAtomicObjects or ConvertBlockInstances.
|
||||
/// </summary>
|
||||
public LocalToGlobalMapHandler Initialize(
|
||||
TraversalContextUnpacker traversalContextUnpacker,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder,
|
||||
GrasshopperColorUnpacker colorUnpacker,
|
||||
GrasshopperMaterialUnpacker materialUnpacker
|
||||
GrasshopperMaterialUnpacker materialUnpacker,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
)
|
||||
{
|
||||
_traversalContextUnpacker = traversalContextUnpacker;
|
||||
_colorUnpacker = colorUnpacker;
|
||||
_materialUnpacker = materialUnpacker;
|
||||
CollectionRebuilder = collectionRebuilder;
|
||||
_definitionProxies = definitionProxies;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts atomic object from TraversalContext to SpeckleObjectWrapper.
|
||||
/// Converts all atomic objects in two passes:
|
||||
/// Pass 1 - Convert normal objects and populate ConvertedObjectsMap
|
||||
/// Pass 2 - Resolve registered DataObjects with InstanceProxies using the populated map
|
||||
/// </summary>
|
||||
public void ConvertAtomicObject(TraversalContext atomicContext)
|
||||
public void ConvertAtomicObjects(IEnumerable<TraversalContext> atomicContexts)
|
||||
{
|
||||
// Cache to avoid re-iterating for registered check
|
||||
var atomicList = atomicContexts as IList<TraversalContext> ?? atomicContexts.ToList();
|
||||
|
||||
// Pass 1: Convert all non-registered DataObjects to populate ConvertedObjectsMap
|
||||
foreach (var atomicContext in atomicList)
|
||||
{
|
||||
ConvertObjectToCache(atomicContext);
|
||||
}
|
||||
|
||||
// Pass 2: Process registered DataObjects (definitions now available in ConvertedObjectsMap)
|
||||
foreach (var atomicContext in atomicList)
|
||||
{
|
||||
if (atomicContext.Current is DataObject dataObject)
|
||||
{
|
||||
var dataObjectId = dataObject.applicationId ?? dataObject.id;
|
||||
if (dataObjectId is not null && _dataObjectInstanceRegistry.IsRegistered(dataObjectId))
|
||||
{
|
||||
ResolveDataObjectInstanceProxies(atomicContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts and caches an atomic object for later lookup.
|
||||
/// Skips registered DataObjects (displayValue is InstanceProxy) - they are resolved in ResolveDataObjectInstanceProxies.
|
||||
/// </summary>
|
||||
private void ConvertObjectToCache(TraversalContext atomicContext)
|
||||
{
|
||||
var obj = atomicContext.Current;
|
||||
var objId = obj.applicationId ?? obj.id;
|
||||
|
||||
if (objId == null || ConvertedObjectsMap.ContainsKey(objId))
|
||||
if (objId is null || ConvertedObjectsMap.ContainsKey(objId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// skip registered DataObjects - they'll be processed in second pass
|
||||
if (obj is DataObject dataObject)
|
||||
{
|
||||
var id = dataObject.applicationId ?? dataObject.id.NotNull();
|
||||
if (_dataObjectInstanceRegistry.IsRegistered(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
|
||||
|
||||
if (converted.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get path and collection
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
|
||||
|
||||
// Always create collection - consumed objects will be cleaned up later
|
||||
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
if (obj is Speckle.Objects.Data.DataObject dataObject)
|
||||
// nothing converted - nothing to do
|
||||
if (converted.Count == 0)
|
||||
{
|
||||
// get color and mat on dataobject first
|
||||
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjColor
|
||||
)
|
||||
? cachedDataObjColor
|
||||
: null;
|
||||
|
||||
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjMaterial
|
||||
)
|
||||
? cachedDataObjMaterial
|
||||
: null;
|
||||
|
||||
// get geometries
|
||||
List<SpeckleGeometryWrapper> geometries = new();
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = original,
|
||||
GeometryBase = geometryBase,
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: dataObjColor,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: dataObjMat,
|
||||
};
|
||||
|
||||
geometries.Add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(dataObject.properties);
|
||||
|
||||
// remove the displayvalue of the original dataobject since these are now processed and stored on the wrapper
|
||||
// to prevent storing of duplicate Base
|
||||
dataObject.displayValue.Clear();
|
||||
|
||||
var dataObjectWrapper = new SpeckleDataObjectWrapper()
|
||||
{
|
||||
Base = dataObject,
|
||||
Geometries = geometries,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
Name = dataObject.name,
|
||||
Properties = propertyGroup,
|
||||
ApplicationId = dataObject.applicationId,
|
||||
};
|
||||
|
||||
// Add to collections (not to map since these won't be definition objects)
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// handle normal DataObject (has converted geometry)
|
||||
if (obj is DataObject normalDataObject)
|
||||
{
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
var geometries = ConvertToGeometryWrappers(converted);
|
||||
var dataObjectWrapper = CreateDataObjectWrapper(normalDataObject, geometries, path, objectCollection);
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle normal geometry (not DataObject)
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
var wrapper = new SpeckleGeometryWrapper()
|
||||
{
|
||||
var wrapper = new SpeckleGeometryWrapper()
|
||||
{
|
||||
Base = original,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = obj[Constants.NAME_PROP] as string ?? "",
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
ApplicationId = objId
|
||||
};
|
||||
Base = original,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = obj[Constants.NAME_PROP] as string ?? "",
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
ApplicationId = objId
|
||||
};
|
||||
|
||||
// Always add to both map and collections
|
||||
ConvertedObjectsMap[objId] = wrapper;
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
ConvertedObjectsMap[objId] = wrapper;
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// TODO: throw?
|
||||
// don't throw - continue processing other objects
|
||||
_logger.LogError(ex, "Failed to convert object {objectId} of type {objectType}", objId, obj.speckle_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a registered DataObject by transforming its InstanceProxy definition objects.
|
||||
/// Requires definition objects to exist in ConvertedObjectsMap (populated by ConvertObjectToCache).
|
||||
/// </summary>
|
||||
private void ResolveDataObjectInstanceProxies(TraversalContext atomicContext)
|
||||
{
|
||||
var obj = atomicContext.Current;
|
||||
if (obj is not DataObject dataObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dataObjectId = dataObject.applicationId ?? dataObject.id.NotNull();
|
||||
if (!_dataObjectInstanceRegistry.IsRegistered(dataObjectId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
|
||||
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
var entry = _dataObjectInstanceRegistry.GetEntries()[dataObjectId];
|
||||
var resolvedGeometries = ResolveInstanceProxiesToGeometries(entry.InstanceProxies);
|
||||
var dataObjectWrapper = CreateDataObjectWrapper(dataObject, resolvedGeometries, path, objectCollection);
|
||||
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// don't throw - continue processing other DataObjects
|
||||
_logger.LogError(ex, "Failed to resolve DataObject {dataObjectId} with InstanceProxies", dataObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts block instances and definitions from traversal contexts into Grasshopper wrapper objects.
|
||||
/// Automatically filters out InstanceProxies belonging to registered DataObjects.
|
||||
/// Automatically handles cleanup of consumed objects from the collection hierarchy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deliberately handles both block conversion AND consumed object cleanup in a single operation.
|
||||
/// Too much, I know, BUT it ensures the cleanup always occurs immediately after block processing without
|
||||
/// requiring receive components to call a separate cleanup method in the correct order.
|
||||
/// </remarks>
|
||||
public void ConvertBlockInstances(
|
||||
IReadOnlyCollection<TraversalContext> blocks,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
)
|
||||
public void ConvertBlockInstances(IReadOnlyCollection<TraversalContext> blockInstances)
|
||||
{
|
||||
// build set of registered InstanceProxy IDs for fast lookup
|
||||
var registeredProxyIds = new HashSet<string>();
|
||||
foreach (var entry in _dataObjectInstanceRegistry.GetEntries().Values)
|
||||
{
|
||||
foreach (var proxy in entry.InstanceProxies)
|
||||
{
|
||||
var proxyId = proxy.applicationId ?? proxy.id;
|
||||
if (proxyId is not null)
|
||||
{
|
||||
registeredProxyIds.Add(proxyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filter out InstanceProxies that belong to registered DataObjects
|
||||
var filteredBlockInstances = blockInstances
|
||||
.Where(tc =>
|
||||
{
|
||||
if (tc.Current is InstanceProxy proxy)
|
||||
{
|
||||
var proxyId = proxy.applicationId ?? proxy.id;
|
||||
return proxyId is null || !registeredProxyIds.Contains(proxyId);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var blockUnpacker = new GrasshopperBlockUnpacker(_traversalContextUnpacker, _colorUnpacker, _materialUnpacker);
|
||||
|
||||
// Get consumed object IDs from unpacker
|
||||
// get consumed object IDs from unpacker
|
||||
var consumedObjectIds = blockUnpacker.UnpackBlocks(
|
||||
blocks,
|
||||
definitionProxies,
|
||||
filteredBlockInstances,
|
||||
_definitionProxies,
|
||||
ConvertedObjectsMap,
|
||||
CollectionRebuilder
|
||||
);
|
||||
|
||||
// Clean up consumed objects from collections
|
||||
// clean up consumed objects from collections
|
||||
CollectionRebuilder.RemoveConsumedObjects(consumedObjectIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataObjectWrapper from a DataObject and its geometries.
|
||||
/// Handles color/material inheritance and property extraction.
|
||||
/// </summary>
|
||||
private SpeckleDataObjectWrapper CreateDataObjectWrapper(
|
||||
DataObject dataObject,
|
||||
List<SpeckleGeometryWrapper> geometries,
|
||||
List<Collection> path,
|
||||
SpeckleCollectionWrapper objectCollection
|
||||
)
|
||||
{
|
||||
// Get color and material on DataObject
|
||||
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(dataObject.applicationId ?? "", out var cachedDataObjColor)
|
||||
? cachedDataObjColor
|
||||
: null;
|
||||
|
||||
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjMaterial
|
||||
)
|
||||
? cachedDataObjMaterial
|
||||
: null;
|
||||
|
||||
// Apply DataObject color/material to geometries that don't have their own
|
||||
foreach (var geometry in geometries)
|
||||
{
|
||||
geometry.Color ??= dataObjColor;
|
||||
geometry.Material ??= dataObjMat;
|
||||
}
|
||||
|
||||
// Create property group
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(dataObject.properties);
|
||||
|
||||
// Clear the displayValue to prevent storing duplicate Base
|
||||
dataObject.displayValue.Clear();
|
||||
|
||||
return new SpeckleDataObjectWrapper()
|
||||
{
|
||||
Base = dataObject,
|
||||
Geometries = geometries,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
Name = dataObject.name,
|
||||
Properties = propertyGroup,
|
||||
ApplicationId = dataObject.applicationId,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves InstanceProxy displayValues to transformed geometries.
|
||||
/// Returns the list of resolved geometries that can be used as DataObject displayValue replacements.
|
||||
/// </summary>
|
||||
private List<SpeckleGeometryWrapper> ResolveInstanceProxiesToGeometries(List<InstanceProxy> instanceProxies)
|
||||
{
|
||||
var resolvedGeometries = new List<SpeckleGeometryWrapper>();
|
||||
|
||||
// build a lookup of definitionId -> definition objects for quick access
|
||||
var definitionObjectsMap = new Dictionary<string, List<string>>();
|
||||
if (_definitionProxies is not null)
|
||||
{
|
||||
foreach (var defProxy in _definitionProxies)
|
||||
{
|
||||
var defId = defProxy.applicationId ?? defProxy.id;
|
||||
if (defId is not null)
|
||||
{
|
||||
definitionObjectsMap[defId] = defProxy.objects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var instanceProxy in instanceProxies)
|
||||
{
|
||||
// get the definition objects for this instance
|
||||
if (!definitionObjectsMap.TryGetValue(instanceProxy.definitionId, out var definitionObjectIds))
|
||||
{
|
||||
continue; // definition not found, skip this proxy
|
||||
}
|
||||
|
||||
// get transform from the instance proxy
|
||||
var transform = GrasshopperHelpers.MatrixToTransform(instanceProxy.transform, instanceProxy.units);
|
||||
|
||||
// apply transform to each definition object
|
||||
foreach (var objectId in definitionObjectIds)
|
||||
{
|
||||
if (ConvertedObjectsMap.TryGetValue(objectId, out var definitionObject))
|
||||
{
|
||||
// deep copy and transform the geometry
|
||||
var transformedWrapper = definitionObject.DeepCopy();
|
||||
|
||||
// transform the GeometryBase
|
||||
transformedWrapper.GeometryBase.NotNull().Transform(transform);
|
||||
|
||||
// keep Base and GeometryBase in sync (CNX-2847)
|
||||
// Exception shouldn't ever happen for objects in ConvertedObjectsMap
|
||||
transformedWrapper.Base =
|
||||
SpeckleConversionContext.Current.ConvertToSpeckle(transformedWrapper.GeometryBase)
|
||||
?? throw new InvalidOperationException($"Failed to convert transformed geometry for object {objectId}");
|
||||
|
||||
// preserve metadata from original Base
|
||||
transformedWrapper.Base.applicationId = definitionObject.Base.applicationId;
|
||||
transformedWrapper.Base["units"] = _settingsStore.Current.SpeckleUnits;
|
||||
|
||||
resolvedGeometries.Add(transformedWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedGeometries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the raw converted objects to SpeckleGeometryWrappers for DataObject display values.
|
||||
/// Does NOT apply DataObject-level colors/materials - that's handled by CreateDataObjectWrapper.
|
||||
/// </summary>
|
||||
private List<SpeckleGeometryWrapper> ConvertToGeometryWrappers(List<(object, Base)> converted)
|
||||
{
|
||||
var geometries = new List<SpeckleGeometryWrapper>();
|
||||
|
||||
foreach ((object convertedObj, Base original) in converted)
|
||||
{
|
||||
if (convertedObj is GeometryBase geometryBase)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = original,
|
||||
GeometryBase = geometryBase,
|
||||
// try to get color/material from the individual geometry first
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
};
|
||||
|
||||
geometries.Add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
return geometries;
|
||||
}
|
||||
}
|
||||
|
||||
+35
-17
@@ -35,9 +35,15 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
// deep copy input (to not mutate input) and set the input collection name to "Grasshopper Model"
|
||||
var inputCollectionGoo = (SpeckleCollectionWrapperGoo)input[0].Duplicate();
|
||||
inputCollectionGoo.Value.Name = "Grasshopper Model";
|
||||
// create root collection
|
||||
var rootCollectionGoo = (SpeckleRootCollectionWrapperGoo)input[0].Duplicate();
|
||||
rootCollectionGoo.Value.Name = "Grasshopper Model";
|
||||
RootCollection rootCollection =
|
||||
new(rootCollectionGoo.Value.Name)
|
||||
{
|
||||
applicationId = rootCollectionGoo.Value.ApplicationId,
|
||||
properties = rootCollectionGoo.Value.Properties ?? new()
|
||||
};
|
||||
|
||||
// create packers for colors and render materials
|
||||
GrasshopperColorPacker colorPacker = new();
|
||||
@@ -45,15 +51,15 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
GrasshopperBlockPacker blockPacker = new(_instanceObjectsManager);
|
||||
|
||||
// unwrap the input collection to remove all wrappers
|
||||
Collection root = Unwrap(inputCollectionGoo.Value, colorPacker, materialPacker, blockPacker);
|
||||
Unwrap(rootCollectionGoo.Value, rootCollection, colorPacker, materialPacker, blockPacker);
|
||||
|
||||
// add proxies
|
||||
root[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
|
||||
root[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
|
||||
root[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
|
||||
rootCollection[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
|
||||
rootCollection[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
|
||||
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
|
||||
|
||||
// TODO: Not getting any conversion results yet
|
||||
var result = new RootObjectBuilderResult(root, []);
|
||||
var result = new RootObjectBuilderResult(rootCollection, []);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
@@ -62,42 +68,47 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
// Also packs colors, render materials and block definitions into proxies while unwrapping.
|
||||
private Collection Unwrap(
|
||||
SpeckleCollectionWrapper wrapper,
|
||||
Collection targetCollection,
|
||||
GrasshopperColorPacker colorPacker,
|
||||
GrasshopperMaterialPacker materialPacker,
|
||||
GrasshopperBlockPacker blockPacker
|
||||
)
|
||||
{
|
||||
Collection currentColl = wrapper.Collection;
|
||||
|
||||
// unpack color, render material and block definitions
|
||||
colorPacker.ProcessColor(wrapper.ApplicationId, wrapper.Color);
|
||||
materialPacker.ProcessMaterial(wrapper.ApplicationId, wrapper.Material);
|
||||
|
||||
int skippedNulls = 0;
|
||||
|
||||
// iterate through this wrapper's elements to unwrap children
|
||||
// HashSet<string> collObjectIds = new();
|
||||
foreach (ISpeckleCollectionObject element in wrapper.Elements)
|
||||
foreach (ISpeckleCollectionObject? element in wrapper.Elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case null:
|
||||
// CNX-2855: count skipped nulls and obvs don't add to collection (can't send nulls)
|
||||
skippedNulls++;
|
||||
continue;
|
||||
case SpeckleCollectionWrapper collWrapper:
|
||||
// create an application id for this collection if none exists. This will be used for color and render material proxies
|
||||
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
|
||||
|
||||
// add to collection and continue unwrap
|
||||
currentColl.elements.Add(collWrapper.Collection);
|
||||
Unwrap(collWrapper, colorPacker, materialPacker, blockPacker);
|
||||
targetCollection.elements.Add(collWrapper.Collection);
|
||||
Unwrap(collWrapper, collWrapper.Collection, colorPacker, materialPacker, blockPacker);
|
||||
break;
|
||||
|
||||
case SpeckleGeometryWrapper so: // handles both SpeckleObjectWrapper and SpeckleBlockInstanceWrapper (inheritance)
|
||||
// convert wrapper to base and add to collection - common for all object wrappers
|
||||
Base objectBase = UnwrapGeometry(so);
|
||||
string applicationId = objectBase.applicationId!;
|
||||
currentColl.elements.Add(objectBase);
|
||||
targetCollection.elements.Add(objectBase);
|
||||
|
||||
// do block instance specific stuff (if this object wrapper is actually a block instance)
|
||||
if (so is SpeckleBlockInstanceWrapper blockInstance)
|
||||
{
|
||||
ProcessBlockInstanceDefinition(blockInstance, colorPacker, materialPacker, blockPacker, currentColl);
|
||||
ProcessBlockInstanceDefinition(blockInstance, colorPacker, materialPacker, blockPacker, targetCollection);
|
||||
}
|
||||
|
||||
// process color and material for all object wrappers (including block instances)
|
||||
@@ -110,7 +121,7 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
// UnwrapDataObject will unwrap underlying geometry and handle color and material
|
||||
// arguably doing too much, but I'm apprehensive looping twice without good reason
|
||||
DataObject dataObject = UnwrapDataObject(dataObjectWrapper, colorPacker, materialPacker);
|
||||
currentColl.elements.Add(dataObject);
|
||||
targetCollection.elements.Add(dataObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +138,14 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
}
|
||||
*/
|
||||
|
||||
return currentColl;
|
||||
// clear topology when nulls are present - no simple way to preserve tree structure since Collection.elements
|
||||
// doesn't support nulls on publish. Topology is GH-specific and optional, so clearing it is safe. (CNX-2855)
|
||||
if (skippedNulls > 0)
|
||||
{
|
||||
targetCollection[Constants.TOPOLOGY_PROP] = null;
|
||||
}
|
||||
|
||||
return targetCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
/// </summary>
|
||||
public class SpeckleOutputParam : Param_GenericObject
|
||||
{
|
||||
public override GH_Exposure Exposure => GH_Exposure.hidden;
|
||||
public override Guid ComponentGuid => new("D2B4713D-FE8B-4EF0-8445-B6096DB15B24");
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
|
||||
@@ -79,6 +79,9 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
|
||||
case int i:
|
||||
Value = i;
|
||||
return true;
|
||||
case long l:
|
||||
Value = l;
|
||||
return true;
|
||||
case string s:
|
||||
Value = s;
|
||||
return true;
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ public class SpeckleVariableParam : Param_GenericObject
|
||||
}
|
||||
}
|
||||
|
||||
public override GH_Exposure Exposure => GH_Exposure.hidden;
|
||||
public override Guid ComponentGuid => new("A1B2C3D4-E5F6-7890-ABCD-123456789ABC");
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
|
||||
+47
-35
@@ -13,49 +13,61 @@ public partial class SpeckleBlockDefinitionWrapperGoo
|
||||
switch (source)
|
||||
{
|
||||
case InstanceDefinition instanceDefinition:
|
||||
List<SpeckleGeometryWrapper> objects = new();
|
||||
foreach (var defObj in instanceDefinition.GetObjects())
|
||||
{
|
||||
SpeckleGeometryWrapperGoo defObjGoo = new();
|
||||
if (defObjGoo.CastFrom(defObj))
|
||||
{
|
||||
objects.Add(defObjGoo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (objects.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetValueFromDefinitionProps(objects, instanceDefinition.Name, instanceDefinition.Id.ToString());
|
||||
return true;
|
||||
return TryConvertDefinition(
|
||||
instanceDefinition.GetObjects().Cast<object>(),
|
||||
instanceDefinition.Name,
|
||||
instanceDefinition.Id.ToString()
|
||||
);
|
||||
|
||||
case ModelInstanceDefinition modelInstanceDef:
|
||||
List<SpeckleGeometryWrapper> defObjs = new();
|
||||
foreach (var defObj in modelInstanceDef.Objects)
|
||||
{
|
||||
SpeckleGeometryWrapperGoo geoWrapperGoo = new();
|
||||
if (geoWrapperGoo.CastFrom(defObj))
|
||||
{
|
||||
defObjs.Add(geoWrapperGoo.Value);
|
||||
}
|
||||
}
|
||||
return TryConvertDefinition(
|
||||
modelInstanceDef.Objects.Cast<object>(),
|
||||
modelInstanceDef.Name,
|
||||
modelInstanceDef.Id.ToString()
|
||||
);
|
||||
|
||||
if (defObjs.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Block definition '{modelInstanceDef.Name}' did not have any valid geometry."
|
||||
);
|
||||
}
|
||||
|
||||
SetValueFromDefinitionProps(defObjs, modelInstanceDef.Name, modelInstanceDef.Id.ToString());
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to convert block definition objects to SpeckleGeometryWrappers.
|
||||
/// Returns false if all objects are unsupported, true if at least one converts.
|
||||
/// </summary>
|
||||
private bool TryConvertDefinition(IEnumerable<object> definitionObjects, string definitionName, string definitionId)
|
||||
{
|
||||
var objects = definitionObjects as object[] ?? definitionObjects.ToArray();
|
||||
int totalCount = objects.Length;
|
||||
List<SpeckleGeometryWrapper> converted = new();
|
||||
|
||||
foreach (var defObj in objects)
|
||||
{
|
||||
SpeckleGeometryWrapperGoo defObjGoo = new();
|
||||
if (defObjGoo.CastFrom(defObj))
|
||||
{
|
||||
converted.Add(defObjGoo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
int skippedCount = totalCount - converted.Count;
|
||||
|
||||
// return false if nothing converted - Grasshopper handles this as warning (CNX-2855)
|
||||
if (converted.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// show debug info if some objects skipped (CNX-2855)
|
||||
if (skippedCount > 0)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Block '{definitionName}' skipped {skippedCount} unsupported object(s)");
|
||||
}
|
||||
|
||||
SetValueFromDefinitionProps(converted, definitionName, definitionId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetValueFromDefinitionProps(List<SpeckleGeometryWrapper> objs, string name, string id)
|
||||
{
|
||||
string validAppId = string.IsNullOrWhiteSpace(id) ? Guid.NewGuid().ToString() : id;
|
||||
|
||||
+7
-1
@@ -75,7 +75,13 @@ public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanc
|
||||
return false;
|
||||
}
|
||||
|
||||
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(instance);
|
||||
Base? converted = SpeckleConversionContext.Current.ConvertToSpeckle(instance);
|
||||
|
||||
if (converted is null)
|
||||
{
|
||||
return false; // gh deals with false return from casting as warning 😎
|
||||
}
|
||||
|
||||
Value = new SpeckleBlockInstanceWrapper()
|
||||
{
|
||||
GeometryBase = instance,
|
||||
|
||||
+9
-1
@@ -51,7 +51,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
}
|
||||
}
|
||||
|
||||
public List<ISpeckleCollectionObject> Elements { get; set; } = new();
|
||||
public List<ISpeckleCollectionObject?> Elements { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The Grasshopper Topology of this collection. This setter also sets the "topology" prop dynamically on <see cref="Collection"/>
|
||||
@@ -92,6 +92,8 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case null:
|
||||
continue; // skip nulls (CNX-2855)
|
||||
case SpeckleGeometryWrapper o:
|
||||
o.Path = newPath;
|
||||
o.Parent = this;
|
||||
@@ -120,6 +122,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
.Select(e =>
|
||||
e switch
|
||||
{
|
||||
null => null, // preserve nulls (CNX-2855)
|
||||
SpeckleCollectionWrapper c => c.DeepCopy(),
|
||||
SpeckleBlockInstanceWrapper b => b.DeepCopy(),
|
||||
SpeckleGeometryWrapper o => o.DeepCopy(),
|
||||
@@ -156,6 +159,11 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
// then bake elements in this collection
|
||||
foreach (var obj in Elements)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
continue; // skip nulls (CNX-2855)
|
||||
}
|
||||
|
||||
if (obj is SpeckleGeometryWrapper so)
|
||||
{
|
||||
if (bakeObjects)
|
||||
|
||||
+11
-9
@@ -113,16 +113,18 @@ public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH
|
||||
{
|
||||
foreach (var element in collWrapper.Elements)
|
||||
{
|
||||
if (element is SpeckleCollectionWrapper subCollWrapper)
|
||||
switch (element)
|
||||
{
|
||||
FlattenForPreview(subCollWrapper);
|
||||
}
|
||||
|
||||
if (element is SpeckleGeometryWrapper objWrapper)
|
||||
{
|
||||
_previewObjects.Add(objWrapper);
|
||||
var box = objWrapper.GeometryBase is null ? new() : objWrapper.GeometryBase.GetBoundingBox(false);
|
||||
_clippingBox.Union(box);
|
||||
case null:
|
||||
continue; // skip nulls (CNX-2855)
|
||||
case SpeckleCollectionWrapper subCollWrapper:
|
||||
FlattenForPreview(subCollWrapper);
|
||||
break;
|
||||
case SpeckleGeometryWrapper objWrapper:
|
||||
_previewObjects.Add(objWrapper);
|
||||
var box = objWrapper.GeometryBase?.GetBoundingBox(false) ?? new BoundingBox();
|
||||
_clippingBox.Union(box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user