Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 637ffbfc54 | |||
| c41c57544a | |||
| 6687383ce4 | |||
| f08d52e080 | |||
| 4dcf9910a5 | |||
| 9a61ded43e | |||
| 5acb0b80ab | |||
| ba41ceca2f | |||
| 8474088e5b | |||
| fe042d7a1c | |||
| d681c63a05 | |||
| bdc0e2b5bd | |||
| 6dc1726536 | |||
| 46198934ec | |||
| b4191d1d65 | |||
| f007e28565 | |||
| d05667dac8 | |||
| c922976bcd | |||
| c3aa44dfc2 | |||
| e1a64189c8 | |||
| b0e0669cab | |||
| b2a14e055c | |||
| 74f4525ff2 | |||
| bb57b31ae4 | |||
| d33a6ca358 | |||
| 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 | |||
| 656ed709f3 | |||
| 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": {
|
||||
|
||||
+15
-25
@@ -6,32 +6,22 @@ using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Bindings;
|
||||
|
||||
public class NavisworksBasicConnectorBinding : IBasicConnectorBinding
|
||||
public class NavisworksBasicConnectorBinding(
|
||||
IBrowserBridge parent,
|
||||
DocumentModelStore store,
|
||||
ISpeckleApplication speckleApplication
|
||||
) : IBasicConnectorBinding
|
||||
{
|
||||
public string Name => "baseBinding";
|
||||
public IBrowserBridge Parent { get; }
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
public IBrowserBridge Parent { get; } = parent;
|
||||
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
public BasicConnectorBindingCommands Commands { get; } = new(parent);
|
||||
|
||||
public NavisworksBasicConnectorBinding(
|
||||
IBrowserBridge parent,
|
||||
DocumentModelStore store,
|
||||
ISpeckleApplication speckleApplication
|
||||
)
|
||||
{
|
||||
Parent = parent;
|
||||
_store = store;
|
||||
_speckleApplication = speckleApplication;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
}
|
||||
public string GetSourceApplicationName() => speckleApplication.Slug;
|
||||
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
public string GetSourceApplicationVersion() => speckleApplication.HostApplicationVersion;
|
||||
|
||||
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
public string GetConnectorVersion() => speckleApplication.SpeckleVersion;
|
||||
|
||||
public DocumentInfo? GetDocumentInfo() =>
|
||||
NavisworksApp.ActiveDocument is null || NavisworksApp.ActiveDocument.Models.Count == 0
|
||||
@@ -42,15 +32,15 @@ public class NavisworksBasicConnectorBinding : IBasicConnectorBinding
|
||||
NavisworksApp.ActiveDocument.GetHashCode().ToString()
|
||||
);
|
||||
|
||||
public DocumentModelStore GetDocumentState() => _store;
|
||||
public DocumentModelStore GetDocumentState() => store;
|
||||
|
||||
public void AddModel(ModelCard model) => _store.AddModel(model);
|
||||
public void AddModel(ModelCard model) => store.AddModel(model);
|
||||
|
||||
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
|
||||
public void UpdateModel(ModelCard model) => store.UpdateModel(model);
|
||||
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
public void RemoveModel(ModelCard model) => store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
public void RemoveModels(List<ModelCard> models) => store.RemoveModels(models);
|
||||
|
||||
public Task HighlightModel(string modelCardId) => Task.CompletedTask;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Bindings;
|
||||
|
||||
|
||||
+61
-11
@@ -12,6 +12,7 @@ using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -58,12 +59,12 @@ public class NavisworksSendBinding : ISendBinding
|
||||
|
||||
private static void SubscribeToNavisworksEvents() { }
|
||||
|
||||
// Do not change the behavior/scope of this class on send binding unless make sure the behavior is same. Otherwise, we might not be able to update list of saved sets.
|
||||
// WARNING: Changes to filter behavior here must match everywhere filters are used, or saved sets won't update correctly
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new NavisworksSelectionFilter() { IsDefault = true },
|
||||
new NavisworksSavedSetsFilter(new ElementSelectionService()),
|
||||
new NavisworksSavedViewsFilter(new ElementSelectionService())
|
||||
new NavisworksSavedSetsFilter(new ConnectorElementSelectionService()),
|
||||
new NavisworksSavedViewsFilter(new ConnectorElementSelectionService())
|
||||
];
|
||||
|
||||
public List<ICardSetting> GetSendSettings() =>
|
||||
@@ -105,6 +106,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
)
|
||||
{
|
||||
var selectedPaths = modelCard.SendFilter.NotNull().RefreshObjectIds();
|
||||
|
||||
var convertHiddenElementsSetting =
|
||||
modelCard.Settings!.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool? ?? false;
|
||||
var message = convertHiddenElementsSetting
|
||||
@@ -115,30 +117,78 @@ public class NavisworksSendBinding : ISendBinding
|
||||
{
|
||||
throw new SpeckleSendFilterException(message);
|
||||
}
|
||||
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", null));
|
||||
await Task.CompletedTask;
|
||||
|
||||
var modelItems = new List<NAV.ModelItem>();
|
||||
int estimatedCapacity = selectedPaths.Count * 10;
|
||||
var modelItems = new List<NAV.ModelItem>(estimatedCapacity);
|
||||
double count = 0;
|
||||
|
||||
foreach (var path in selectedPaths)
|
||||
{
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", count / selectedPaths.Count));
|
||||
await Task.CompletedTask;
|
||||
|
||||
var modelItem = _selectionService.GetModelItemFromPath(path);
|
||||
modelItems.AddRange(_selectionService.GetGeometryNodes(modelItem).Where(_selectionService.IsVisible));
|
||||
var hasChildren = modelItem.Children.Any();
|
||||
|
||||
if (hasChildren)
|
||||
{
|
||||
int nodesVisited = 0;
|
||||
int hiddenBranchesPruned = 0;
|
||||
const int REPORT_INTERVAL = 1000;
|
||||
|
||||
void TraverseWithProgress(NAV.ModelItem node)
|
||||
{
|
||||
nodesVisited++;
|
||||
|
||||
if (nodesVisited % REPORT_INTERVAL == 0)
|
||||
{
|
||||
onOperationProgressed.Report(
|
||||
new CardProgress(
|
||||
$"Expanding tree: {nodesVisited} visited, {modelItems.Count} with geometry, {hiddenBranchesPruned} hidden",
|
||||
null
|
||||
)
|
||||
);
|
||||
Task.Delay(1).Wait();
|
||||
}
|
||||
|
||||
if (!_selectionService.IsVisible(node))
|
||||
{
|
||||
hiddenBranchesPruned++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.HasGeometry)
|
||||
{
|
||||
modelItems.Add(node);
|
||||
}
|
||||
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
TraverseWithProgress(child);
|
||||
}
|
||||
}
|
||||
|
||||
TraverseWithProgress(modelItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (modelItem.HasGeometry && _selectionService.IsVisible(modelItem))
|
||||
{
|
||||
modelItems.Add(modelItem);
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems;
|
||||
}
|
||||
|
||||
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels all outstanding send operations for the current document.
|
||||
/// This method is called when the active document changes, to ensure
|
||||
/// that any in-progress send operations are properly canceled before
|
||||
/// the new document is loaded.
|
||||
/// </summary>
|
||||
public void CancelAllSendOperations()
|
||||
{
|
||||
foreach (var modelCardId in _store.GetSenders().Select(m => m.ModelCardId))
|
||||
|
||||
+8
-1
@@ -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.Constants.Registers;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
@@ -60,6 +61,9 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
|
||||
|
||||
// Registers and caches
|
||||
serviceCollection.AddScoped<IInstanceFragmentRegistry, InstanceFragmentRegistry>();
|
||||
|
||||
// Register Intercom/interop
|
||||
serviceCollection.AddSingleton<NavisworksDocumentModelStore>();
|
||||
serviceCollection.AddSingleton<DocumentModelStore>(sp => sp.GetRequiredService<NavisworksDocumentModelStore>());
|
||||
@@ -69,6 +73,9 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSelectionFilter>();
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSavedSetsFilter>();
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSavedViewsFilter>();
|
||||
serviceCollection.AddScoped<IElementSelectionService, ElementSelectionService>();
|
||||
serviceCollection.AddScoped<
|
||||
Converter.Navisworks.Services.IElementSelectionService,
|
||||
ConnectorElementSelectionService
|
||||
>();
|
||||
}
|
||||
}
|
||||
|
||||
+9
-28
@@ -1,31 +1,12 @@
|
||||
using Speckle.InterfaceGenerator;
|
||||
using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper;
|
||||
namespace Speckle.Connector.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Services;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ElementSelectionService : IElementSelectionService
|
||||
/// <summary>
|
||||
/// Connector-specific element selection service that extends the converter's base implementation.
|
||||
/// Inherits the cached visibility checking and path resolution from the converter layer.
|
||||
/// </summary>
|
||||
public class ConnectorElementSelectionService : Converter.Navisworks.Services.ElementSelectionService
|
||||
{
|
||||
private readonly Dictionary<Guid, bool> _visibleCache = new();
|
||||
|
||||
public string GetModelItemPath(NAV.ModelItem modelItem) => ResolveModelItemToIndexPath(modelItem);
|
||||
|
||||
public NAV.ModelItem GetModelItemFromPath(string path) => ResolveIndexPathToModelItem(path);
|
||||
|
||||
public bool IsVisible(NAV.ModelItem modelItem)
|
||||
{
|
||||
var key = modelItem.InstanceGuid;
|
||||
if (_visibleCache.TryGetValue(key, out var isVisible))
|
||||
{
|
||||
return isVisible;
|
||||
}
|
||||
//same as ElementSelectionHelper.IsElementVisible
|
||||
foreach (var item in modelItem.AncestorsAndSelf)
|
||||
{
|
||||
_visibleCache[item.InstanceGuid] = !item.IsHidden;
|
||||
}
|
||||
return _visibleCache[key];
|
||||
}
|
||||
|
||||
public IEnumerable<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem) => ResolveGeometryLeafNodes(modelItem);
|
||||
// This inherits all functionality from the converter's ElementSelectionService
|
||||
// including cached IsVisible, GetModelItemPath, GetModelItemFromPath, and GetGeometryNodes
|
||||
// Connector-specific extensions can be added here if needed in the future
|
||||
}
|
||||
|
||||
+47
-18
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk;
|
||||
@@ -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,53 @@ public class NavisworksColorUnpacker(
|
||||
var comSelection = ComBridge.ToInwOpSelection([modelItem]);
|
||||
try
|
||||
{
|
||||
foreach (ComApi.InwOaPath path in comSelection.Paths())
|
||||
var paths = comSelection.Paths();
|
||||
try
|
||||
{
|
||||
GC.KeepAlive(path);
|
||||
|
||||
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
|
||||
foreach (ComApi.InwOaPath path in paths)
|
||||
{
|
||||
GC.KeepAlive(fragment);
|
||||
GC.KeepAlive(path);
|
||||
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
// Exit early if triangles are found
|
||||
if (primitiveChecker.HasTriangles)
|
||||
var fragments = path.Fragments();
|
||||
try
|
||||
{
|
||||
return false;
|
||||
foreach (ComApi.InwOaFragment3 fragment in fragments)
|
||||
{
|
||||
GC.KeepAlive(fragment);
|
||||
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
if (primitiveChecker.HasTriangles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fragments != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if any 2D primitives are found
|
||||
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
|
||||
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (paths != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
|
||||
if (comSelection != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-11
@@ -120,17 +120,6 @@ public sealed class NavisworksDocumentEvents
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromDocumentModelEvents(object _)
|
||||
{
|
||||
var activeDocument = NavisworksApp.ActiveDocument;
|
||||
if (activeDocument != null)
|
||||
{
|
||||
UnsubscribeFromModelEvents(activeDocument);
|
||||
}
|
||||
|
||||
_isSubscribed = false;
|
||||
}
|
||||
|
||||
private void UnsubscribeFromModelEvents(NAV.Document document)
|
||||
{
|
||||
document.Models.CollectionChanged -= HandleDocumentModelCountChanged;
|
||||
|
||||
+17
-18
@@ -1,10 +1,11 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
using static Speckle.Converter.Navisworks.Constants.MaterialConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.HostApp;
|
||||
|
||||
@@ -14,9 +15,13 @@ public class NavisworksMaterialUnpacker(
|
||||
IElementSelectionService selectionService
|
||||
)
|
||||
{
|
||||
// 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 +69,27 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject);
|
||||
var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId;
|
||||
|
||||
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 +98,8 @@ public class NavisworksMaterialUnpacker(
|
||||
);
|
||||
|
||||
var materialName =
|
||||
$"NavisworksMaterial_{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}";
|
||||
$"{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 +111,6 @@ public class NavisworksMaterialUnpacker(
|
||||
}
|
||||
}
|
||||
|
||||
// Check Material category for material name
|
||||
var materialPropertyCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Material");
|
||||
if (materialPropertyCategory != null)
|
||||
{
|
||||
@@ -126,12 +130,7 @@ public class NavisworksMaterialUnpacker(
|
||||
{
|
||||
renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy()
|
||||
{
|
||||
value = ConvertRenderColorAndTransparencyToSpeckle(
|
||||
materialName,
|
||||
renderTransparency,
|
||||
renderColor,
|
||||
renderMaterialId
|
||||
),
|
||||
value = CreateRenderMaterial(materialName, renderTransparency, renderColor, renderMaterialId),
|
||||
objects = [finalId]
|
||||
};
|
||||
}
|
||||
@@ -145,7 +144,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 +155,7 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var speckleRenderMaterial = new RenderMaterial()
|
||||
{
|
||||
name = !string.IsNullOrEmpty(name) ? name : $"NavisworksMaterial_{Math.Abs(color.ToArgb())}",
|
||||
name = !string.IsNullOrEmpty(name) ? name : $"{DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(color.ToArgb())}",
|
||||
opacity = 1 - transparency,
|
||||
metalness = 0,
|
||||
roughness = 1,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
|
||||
+5
-7
@@ -1,7 +1,7 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
@@ -48,8 +48,6 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
return objectIds;
|
||||
}
|
||||
|
||||
var savedViews = NavisworksApp.ActiveDocument.SavedViewpoints;
|
||||
|
||||
foreach (var savedViewItem in SelectedItems.Select(item => ResolveSavedView(item.Id)))
|
||||
{
|
||||
// Get the visible elements in the saved view.
|
||||
@@ -82,12 +80,12 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
{
|
||||
var objectIds = new List<string>();
|
||||
|
||||
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
|
||||
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOR - DISCUSSION REQUIRED
|
||||
// if (!savedView.ContainsVisibilityOverrides)
|
||||
// {
|
||||
// // We check this again as the view settings may have changed in the saved card.
|
||||
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
|
||||
// // This will need to be the documented behaviour.
|
||||
// // This will need to be the documented behavior.
|
||||
// throw new SpeckleSendFilterException(
|
||||
// "Saved view does not contain visibility overrides. This would effectively publish everything in the model."
|
||||
// );
|
||||
@@ -154,7 +152,7 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
switch (item)
|
||||
{
|
||||
// case NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
|
||||
// Legacy defensive behaviour: skip viewpoints without visibility overrides.
|
||||
// Legacy defensive behavior: skip viewpoints without visibility overrides.
|
||||
// Essentially, send everything, or whatever the current view state for hidden elements is.
|
||||
// break;
|
||||
case NAV.SavedViewpointAnimationCut:
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
@@ -15,6 +15,6 @@ public static class SavedItemHelpers
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return string.Join(PathConstants.SET_SEPARATOR, pathParts);
|
||||
return string.Join(SET_SEPARATOR, pathParts);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -10,29 +10,29 @@ public static class GeometryNodeMerger
|
||||
{
|
||||
/// <summary>
|
||||
/// Groups sibling geometry nodes based on material properties for merging.
|
||||
/// Only merges nodes that share the same parent and have identical material properties.
|
||||
/// This only merges nodes that share the same parent and have identical material properties.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The collection of ModelItems to process</param>
|
||||
/// <returns>Dictionary mapping parent paths (with material signature suffix) to their mergeable child nodes</returns>
|
||||
public static Dictionary<string, List<NAV.ModelItem>> GroupSiblingGeometryNodes(IReadOnlyList<NAV.ModelItem> nodes)
|
||||
{
|
||||
var selectionService = new ElementSelectionService();
|
||||
var selectionService = new ConnectorElementSelectionService();
|
||||
|
||||
// Group nameless geometry nodes by parent path and material signature
|
||||
var mergeableGroups = nodes
|
||||
.Where(node => node.HasGeometry && string.IsNullOrEmpty(node.DisplayName)) // Only anonymous geometry nodes
|
||||
.GroupBy(node =>
|
||||
{
|
||||
// Get parent path
|
||||
// Get the parent path
|
||||
var path = selectionService.GetModelItemPath(node);
|
||||
var lastSeparatorIndex = path.LastIndexOf(PathConstants.SEPARATOR);
|
||||
var lastSeparatorIndex = path.LastIndexOf(SEPARATOR);
|
||||
var parentPath = lastSeparatorIndex == -1 ? path : path[..lastSeparatorIndex];
|
||||
|
||||
// Generate material signature
|
||||
string signature = GenerateSignature(node);
|
||||
|
||||
// Combine parent path with signature
|
||||
return $"{parentPath}{PathConstants.MATERIAL_SEPARATOR}{signature}";
|
||||
return $"{parentPath}{MATERIAL_SEPARATOR}{signature}";
|
||||
})
|
||||
.Where(group => group.Count() > 1) // Only include groups with multiple children
|
||||
.ToDictionary(group => group.Key, group => group.ToList());
|
||||
@@ -95,7 +95,7 @@ public static class GeometryNodeMerger
|
||||
// Build a consistent string representation of all properties
|
||||
var hashInput = new System.Text.StringBuilder();
|
||||
|
||||
// Sort keys to ensure consistent order
|
||||
// Sort keys to ensure a consistent order
|
||||
var sortedKeys = properties.Keys.OrderBy(k => k).ToList();
|
||||
|
||||
foreach (var key in sortedKeys)
|
||||
@@ -139,7 +139,7 @@ public static class GeometryNodeMerger
|
||||
/// </summary>
|
||||
private static string GetMaterialName(NAV.ModelItem node)
|
||||
{
|
||||
// Check Item category for material name
|
||||
// Check the Item category for material name
|
||||
var itemCategory = node.PropertyCategories.FindCategoryByDisplayName("Item");
|
||||
if (itemCategory != null)
|
||||
{
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -59,8 +59,8 @@ public class NavisworksHierarchyBuilder
|
||||
allPaths.Sort(
|
||||
(a, b) =>
|
||||
{
|
||||
var depthA = a.Count(c => c == PathConstants.SEPARATOR);
|
||||
var depthB = b.Count(c => c == PathConstants.SEPARATOR);
|
||||
var depthA = a.Count(c => c == SEPARATOR);
|
||||
var depthB = b.Count(c => c == SEPARATOR);
|
||||
return depthB.CompareTo(depthA); // <- Sort in ascending order of path length
|
||||
}
|
||||
);
|
||||
@@ -126,7 +126,7 @@ public class NavisworksHierarchyBuilder
|
||||
|
||||
private static string GetParentPath(string path)
|
||||
{
|
||||
var idx = path.LastIndexOf(PathConstants.SEPARATOR);
|
||||
var idx = path.LastIndexOf(SEPARATOR);
|
||||
return idx == -1 ? string.Empty : path[..idx];
|
||||
}
|
||||
|
||||
|
||||
+167
-51
@@ -1,11 +1,11 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.HostApp;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Conversion;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Data;
|
||||
@@ -13,7 +13,10 @@ using Speckle.Sdk;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
|
||||
using static Speckle.Connectors.Common.Operations.ProxyKeys;
|
||||
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -25,12 +28,15 @@ public class NavisworksRootObjectBuilder(
|
||||
ISdkActivityFactory activityFactory,
|
||||
NavisworksMaterialUnpacker materialUnpacker,
|
||||
NavisworksColorUnpacker colorUnpacker,
|
||||
IElementSelectionService elementSelectionService
|
||||
Speckle.Converter.Navisworks.Constants.Registers.IInstanceFragmentRegistry instanceRegistry,
|
||||
IElementSelectionService elementSelectionService,
|
||||
IUiUnitsCache uiUnitsCache
|
||||
) : IRootObjectBuilder<NAV.ModelItem>
|
||||
{
|
||||
#pragma warning disable CA1823
|
||||
#pragma warning restore CA1823
|
||||
private bool SkipNodeMerging { get; set; }
|
||||
|
||||
internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current;
|
||||
private bool DisableGroupingForInstanceTesting { get; set; }
|
||||
|
||||
public async Task<RootObjectBuilderResult> Build(
|
||||
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
|
||||
@@ -40,23 +46,16 @@ 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;
|
||||
DisableGroupingForInstanceTesting = false;
|
||||
#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);
|
||||
|
||||
@@ -65,6 +64,13 @@ public class NavisworksRootObjectBuilder(
|
||||
|
||||
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
|
||||
|
||||
AddInstanceDefinitionsToCollection(rootCollection, ref finalElements);
|
||||
int finalInstanceProxyCount = CountInstanceProxiesRecursive(finalElements);
|
||||
logger.LogInformation(
|
||||
"Final output contains {count} InstanceProxy objects in displayValues",
|
||||
finalInstanceProxyCount
|
||||
);
|
||||
|
||||
rootCollection.elements = finalElements;
|
||||
return new RootObjectBuilderResult(rootCollection, conversionResults);
|
||||
}
|
||||
@@ -111,16 +117,32 @@ public class NavisworksRootObjectBuilder(
|
||||
var convertedBases = new Dictionary<string, Base?>();
|
||||
int processedCount = 0;
|
||||
int totalCount = navisworksModelItems.Count;
|
||||
int instanceProxyCount = 0;
|
||||
|
||||
foreach (var item in navisworksModelItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
|
||||
results.Add(converted);
|
||||
|
||||
if (
|
||||
converted.Status == Status.SUCCESS
|
||||
&& convertedBases.TryGetValue(elementSelectionService.GetModelItemPath(item), out var convertedBase)
|
||||
&& convertedBase?["displayValue"] is List<Base> displayValues
|
||||
)
|
||||
{
|
||||
instanceProxyCount += displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
"Converted {total} items, found {instanceProxies} InstanceProxy objects",
|
||||
totalCount,
|
||||
instanceProxyCount
|
||||
);
|
||||
return Task.FromResult((convertedBases, results));
|
||||
}
|
||||
|
||||
@@ -137,27 +159,39 @@ 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 (!DisableGroupingForInstanceTesting)
|
||||
{
|
||||
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
|
||||
logger.LogInformation(
|
||||
"After grouping: {grouped} paths processed, {elements} elements in collection",
|
||||
processedPaths.Count,
|
||||
finalElements.Count
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Grouping disabled for instance testing");
|
||||
}
|
||||
|
||||
if (converterSettings.Current.User.PreserveModelHierarchy)
|
||||
{
|
||||
logger.LogInformation("Building hierarchy (PreserveModelHierarchy=true)");
|
||||
var hierarchyBuilder = new NavisworksHierarchyBuilder(
|
||||
convertedBases,
|
||||
rootToSpeckleConverter,
|
||||
elementSelectionService
|
||||
);
|
||||
|
||||
var hierarchy = hierarchyBuilder.BuildHierarchy();
|
||||
|
||||
return hierarchy;
|
||||
return hierarchyBuilder.BuildHierarchy();
|
||||
}
|
||||
|
||||
// Otherwise continue with flat mode
|
||||
logger.LogInformation("Adding remaining elements (flat mode)");
|
||||
AddRemainingElements(finalElements, convertedBases, processedPaths);
|
||||
|
||||
logger.LogInformation("Final elements count: {count}", finalElements.Count);
|
||||
return finalElements;
|
||||
}
|
||||
|
||||
@@ -170,7 +204,7 @@ public class NavisworksRootObjectBuilder(
|
||||
{
|
||||
foreach (var group in groupedNodes)
|
||||
{
|
||||
var siblingBases = new List<Base>();
|
||||
var siblingBases = new List<Base>(group.Value.Count);
|
||||
foreach (var itemPath in group.Value.Select(elementSelectionService.GetModelItemPath))
|
||||
{
|
||||
processedPaths.Add(itemPath);
|
||||
@@ -213,41 +247,48 @@ 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);
|
||||
|
||||
int estimatedCapacity = siblingBases.Sum(b => (b["displayValue"] as List<Base>)?.Count ?? 0);
|
||||
var displayValues = new List<Base>(estimatedCapacity);
|
||||
displayValues.AddRange(
|
||||
siblingBases
|
||||
.Where(sibling => sibling["displayValue"] is List<Base>)
|
||||
.SelectMany(sibling => (List<Base>)sibling["displayValue"]!)
|
||||
);
|
||||
|
||||
var instanceProxyCount = displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
if (instanceProxyCount > 0)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Group {groupKey} merging {siblings} siblings with {proxies} InstanceProxy objects",
|
||||
groupKey,
|
||||
siblingBases.Count,
|
||||
instanceProxyCount
|
||||
);
|
||||
}
|
||||
|
||||
return new NavisworksObject
|
||||
{
|
||||
name = name,
|
||||
displayValue = siblingBases.SelectMany(b => b["displayValue"] as List<Base> ?? []).ToList(),
|
||||
displayValue = displayValues,
|
||||
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 +296,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
|
||||
};
|
||||
@@ -279,27 +322,100 @@ public class NavisworksRootObjectBuilder(
|
||||
var renderMaterials = materialUnpacker.UnpackRenderMaterial(navisworksModelItems, groupedNodes);
|
||||
if (renderMaterials.Count > 0)
|
||||
{
|
||||
rootCollection[ProxyKeys.RENDER_MATERIAL] = renderMaterials;
|
||||
rootCollection[RENDER_MATERIAL] = renderMaterials;
|
||||
}
|
||||
|
||||
var colors = colorUnpacker.UnpackColor(navisworksModelItems, groupedNodes);
|
||||
if (colors.Count > 0)
|
||||
{
|
||||
rootCollection[ProxyKeys.COLOR] = colors;
|
||||
rootCollection[COLOR] = colors;
|
||||
}
|
||||
|
||||
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 void AddInstanceDefinitionsToCollection(Collection rootCollection, ref List<Base> finalElements)
|
||||
{
|
||||
using var _ = activityFactory.Start("BuildInstanceDefinitions");
|
||||
|
||||
// Get all definition geometries from the registry
|
||||
var allDefinitions = instanceRegistry.GetAllDefinitionGeometries();
|
||||
|
||||
if (allDefinitions.Count == 0)
|
||||
{
|
||||
logger.LogInformation("No instance definitions found - instancing may be disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation("Building instance structure for {count} definition groups", allDefinitions.Count);
|
||||
|
||||
if (allDefinitions.Count > 100)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Large number of definition groups ({count}) detected - this may indicate instance grouping is not working effectively",
|
||||
allDefinitions.Count
|
||||
);
|
||||
}
|
||||
|
||||
var instanceDefinitionProxies = new List<InstanceDefinitionProxy>(allDefinitions.Count);
|
||||
|
||||
int estimatedGeometryCount = allDefinitions.Sum(kvp => kvp.Value.Count);
|
||||
var allDefinitionGeometries = new List<Base>(estimatedGeometryCount);
|
||||
|
||||
foreach (var kvp in allDefinitions)
|
||||
{
|
||||
var groupKey = kvp.Key;
|
||||
var geometries = kvp.Value;
|
||||
var groupKeyHash = groupKey.ToHashString();
|
||||
|
||||
var defProxy = new InstanceDefinitionProxy
|
||||
{
|
||||
name = $"Shared Geometry {groupKeyHash}",
|
||||
objects = geometries.Select(g => g.applicationId ?? "").Where(id => !string.IsNullOrEmpty(id)).ToList(),
|
||||
applicationId = $"{DEFINITION_ID_PREFIX}{groupKeyHash}",
|
||||
maxDepth = 0
|
||||
};
|
||||
|
||||
instanceDefinitionProxies.Add(defProxy);
|
||||
allDefinitionGeometries.AddRange(geometries);
|
||||
}
|
||||
|
||||
rootCollection[INSTANCE_DEFINITION] = instanceDefinitionProxies;
|
||||
var geometryDefinitionsCollection = new Collection
|
||||
{
|
||||
name = "Geometry Definitions",
|
||||
elements = allDefinitionGeometries
|
||||
};
|
||||
|
||||
var objectCollection = new Collection { name = "", elements = finalElements };
|
||||
|
||||
finalElements = [geometryDefinitionsCollection, objectCollection];
|
||||
|
||||
logger.LogInformation(
|
||||
"Added {proxyCount} instance definition proxies and {geomCount} definition geometries",
|
||||
instanceDefinitionProxies.Count,
|
||||
allDefinitionGeometries.Count
|
||||
);
|
||||
}
|
||||
|
||||
private int CountInstanceProxiesRecursive(List<Base> elements)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (element["displayValue"] is List<Base> displayValues)
|
||||
{
|
||||
count += displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
}
|
||||
|
||||
if (element is Collection { elements: not null } collection)
|
||||
{
|
||||
count += CountInstanceProxiesRecursive(collection.elements);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private SendConversionResult ConvertNavisworksItem(
|
||||
NAV.ModelItem navisworksItem,
|
||||
Dictionary<string, Base?> convertedBases,
|
||||
|
||||
+71
-124
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.InterfaceGenerator;
|
||||
@@ -8,158 +7,106 @@ using Speckle.Sdk.Common;
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Settings;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavisworks
|
||||
public class ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
|
||||
: IToSpeckleSettingsManagerNavisworks
|
||||
{
|
||||
private readonly ISendConversionCache _sendConversionCache;
|
||||
|
||||
// cache invalidation process run with ModelCardId since the settings are model specific
|
||||
// cache invalidation process run with ModelCardId since the settings are model-specific
|
||||
private readonly Dictionary<string, RepresentationMode> _visualRepresentationCache = [];
|
||||
private readonly Dictionary<string, OriginMode> _originModeCache = [];
|
||||
private readonly Dictionary<string, bool?> _convertHiddenElementsCache = [];
|
||||
private readonly Dictionary<string, bool?> _includeInternalPropertiesCache = [];
|
||||
private readonly Dictionary<string, bool?> _preserveModelHierarchyCache = [];
|
||||
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
|
||||
private readonly Dictionary<string, bool> _convertHiddenElementsCache = [];
|
||||
private readonly Dictionary<string, bool> _includeInternalPropertiesCache = [];
|
||||
private readonly Dictionary<string, bool> _preserveModelHierarchyCache = [];
|
||||
private readonly Dictionary<string, bool> _revitCategoryMappingCache = [];
|
||||
|
||||
public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
|
||||
{
|
||||
_sendConversionCache = sendConversionCache;
|
||||
}
|
||||
|
||||
public RepresentationMode GetVisualRepresentationMode(SenderModelCard modelCard)
|
||||
/// <summary>
|
||||
/// Generic helper to get a setting value with caching and cache invalidation.
|
||||
/// </summary>
|
||||
private T GetCachedSetting<T>(
|
||||
SenderModelCard modelCard,
|
||||
string settingId,
|
||||
Dictionary<string, T> cache,
|
||||
Func<object?, T> valueExtractor,
|
||||
T defaultValue
|
||||
)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var representationString = modelCard.Settings?.First(s => s.Id == "visualRepresentation").Value as string;
|
||||
var settingValue = modelCard.Settings?.FirstOrDefault(s => s.Id == settingId)?.Value;
|
||||
var returnValue = settingValue != null ? valueExtractor(settingValue) : defaultValue;
|
||||
|
||||
if (
|
||||
representationString is not null
|
||||
&& VisualRepresentationSetting.VisualRepresentationMap.TryGetValue(
|
||||
representationString,
|
||||
out RepresentationMode representation
|
||||
)
|
||||
cache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue)
|
||||
&& !EqualityComparer<T>.Default.Equals(previousValue, returnValue)
|
||||
)
|
||||
{
|
||||
if (_visualRepresentationCache.TryGetValue(modelCard.ModelCardId.NotNull(), out RepresentationMode previousType))
|
||||
{
|
||||
if (previousType != representation)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
_visualRepresentationCache[modelCard.ModelCardId.NotNull()] = representation;
|
||||
return representation;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid visual representation value: {representationString}");
|
||||
}
|
||||
|
||||
public OriginMode GetOriginMode(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var originString = modelCard.Settings?.FirstOrDefault(s => s.Id == "originMode")?.Value as string;
|
||||
if (!OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
|
||||
{
|
||||
return OriginMode.ModelOrigin; // Default to ModelOrigin if not specified or invalid
|
||||
}
|
||||
|
||||
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousType) && previousType != origin)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
|
||||
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
|
||||
return origin;
|
||||
}
|
||||
|
||||
public bool GetMappingToRevitCategories(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
|
||||
cache[modelCard.ModelCardId.NotNull()] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public bool GetConvertHiddenElements(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_convertHiddenElementsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
public RepresentationMode GetVisualRepresentationMode(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"visualRepresentation",
|
||||
_visualRepresentationCache,
|
||||
value =>
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
var representationString = value as string;
|
||||
return
|
||||
representationString is not null
|
||||
&& VisualRepresentationSetting.VisualRepresentationMap.TryGetValue(
|
||||
representationString,
|
||||
out RepresentationMode representation
|
||||
)
|
||||
? representation
|
||||
: throw new ArgumentException($"Invalid visual representation value: {representationString}");
|
||||
},
|
||||
RepresentationMode.Active // default value if setting not found
|
||||
);
|
||||
|
||||
_convertHiddenElementsCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public bool GetIncludeInternalProperties([NotNull] SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "includeInternalProperties")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_includeInternalPropertiesCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
public OriginMode GetOriginMode(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"originMode",
|
||||
_originModeCache,
|
||||
value =>
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
var originString = value as string;
|
||||
if (OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
|
||||
{
|
||||
return origin;
|
||||
}
|
||||
return OriginMode.ModelOrigin;
|
||||
},
|
||||
OriginMode.ModelOrigin
|
||||
);
|
||||
|
||||
_includeInternalPropertiesCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
public bool GetMappingToRevitCategories(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "mappingToRevitCategories", _revitCategoryMappingCache, value => value is true, false);
|
||||
|
||||
public bool GetPreserveModelHierarchy([NotNull] SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "preserveModelHierarchy")?.Value as bool?;
|
||||
public bool GetConvertHiddenElements(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "convertHiddenElements", _convertHiddenElementsCache, value => value is true, false);
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_preserveModelHierarchyCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
public bool GetIncludeInternalProperties(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"includeInternalProperties",
|
||||
_includeInternalPropertiesCache,
|
||||
value => value is true,
|
||||
false
|
||||
);
|
||||
|
||||
_preserveModelHierarchyCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
public bool GetPreserveModelHierarchy(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "preserveModelHierarchy", _preserveModelHierarchyCache, value => value is true, false);
|
||||
|
||||
private void EvictCacheForModelCard(SenderModelCard modelCard)
|
||||
{
|
||||
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
|
||||
_sendConversionCache.EvictObjects(objectIds);
|
||||
sendConversionCache.EvictObjects(objectIds);
|
||||
}
|
||||
}
|
||||
|
||||
+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>();
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public static class SpeckleV3Tool
|
||||
public const string RIBBON_STRINGS = "NavisworksRibbon.name";
|
||||
public const string PLUGIN_SUFFIX = ".Speckle";
|
||||
|
||||
public static Speckle.Sdk.Application App =>
|
||||
public static Sdk.Application App =>
|
||||
#if NAVIS
|
||||
HostApplications.Navisworks;
|
||||
#else
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -3,17 +3,15 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
|
||||
xmlns:dui="clr-namespace:Speckle.Connectors.DUI;assembly=Speckle.Connectors.DUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<wv2:CoreWebView2CreationProperties x:Key="EvergreenWebView2CreationProperties" UserDataFolder="C:\temp" />
|
||||
</UserControl.Resources>
|
||||
<DockPanel>
|
||||
<wv2:WebView2
|
||||
CreationProperties="{StaticResource EvergreenWebView2CreationProperties}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Name="Browser" Grid.Row="0" Source="{x:Static dui:Url.Netlify}" />
|
||||
</DockPanel>
|
||||
<!-- WebView2 is created lazily -->
|
||||
<Border Name="BrowserContainer" Background="White">
|
||||
<TextBlock Name="LoadingText"
|
||||
Text="Loading Speckle..."
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="Gray" />
|
||||
</Border>
|
||||
</UserControl>
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
@@ -12,6 +14,10 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IRevitTask _revitTask;
|
||||
#pragma warning disable CA2213
|
||||
private WebView2? _browser;
|
||||
#pragma warning restore CA2213
|
||||
private bool _isInitializing;
|
||||
|
||||
public RevitControlWebView(IServiceProvider serviceProvider, IRevitTask revitTask)
|
||||
{
|
||||
@@ -19,35 +25,61 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
_revitTask = revitTask;
|
||||
InitializeComponent();
|
||||
|
||||
Browser.CoreWebView2InitializationCompleted += (sender, args) =>
|
||||
// Delay WebView2 creation until the panel is actually visible
|
||||
// This avoids conflicts with other plugins (like pyRevit) during startup
|
||||
IsVisibleChanged += OnIsVisibleChanged;
|
||||
}
|
||||
|
||||
private void OnIsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is true && _browser == null && !_isInitializing)
|
||||
{
|
||||
_isInitializing = true;
|
||||
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, CreateWebView2);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateWebView2()
|
||||
{
|
||||
_browser = new WebView2
|
||||
{
|
||||
CreationProperties = new CoreWebView2CreationProperties { UserDataFolder = "C:\\temp" },
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
|
||||
Source = Url.Netlify
|
||||
};
|
||||
|
||||
_browser.CoreWebView2InitializationCompleted += (sender, args) =>
|
||||
_serviceProvider
|
||||
.GetRequiredService<ITopLevelExceptionHandler>()
|
||||
.CatchUnhandled(() => OnInitialized(sender, args));
|
||||
|
||||
BrowserContainer.Child = _browser;
|
||||
}
|
||||
|
||||
public bool IsBrowserInitialized => Browser.IsInitialized;
|
||||
public bool IsBrowserInitialized => _browser?.IsInitialized ?? false;
|
||||
|
||||
public object BrowserElement => Browser;
|
||||
public object BrowserElement => _browser!;
|
||||
|
||||
public void ExecuteScript(string script)
|
||||
{
|
||||
if (!Browser.IsInitialized)
|
||||
if (_browser == null || !_browser.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
|
||||
}
|
||||
_revitTask.Run(() => Browser.ExecuteScriptAsync(script));
|
||||
_revitTask.Run(() => _browser.ExecuteScriptAsync(script));
|
||||
}
|
||||
|
||||
public void SendProgress(string script)
|
||||
{
|
||||
if (!Browser.IsInitialized)
|
||||
if (_browser == null || !_browser.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
|
||||
}
|
||||
//always invoke even on the main thread because it's better somehow
|
||||
Browser.Dispatcher.Invoke(
|
||||
_browser.Dispatcher.Invoke(
|
||||
//fire and forget
|
||||
() => Browser.ExecuteScriptAsync(script),
|
||||
() => _browser.ExecuteScriptAsync(script),
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
}
|
||||
@@ -74,11 +106,18 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
private void SetupBinding(IBinding binding)
|
||||
{
|
||||
binding.Parent.AssociateWithBinding(binding);
|
||||
Browser.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent);
|
||||
_browser!.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent);
|
||||
}
|
||||
|
||||
public void ShowDevTools() => Browser.CoreWebView2.OpenDevToolsWindow();
|
||||
public void ShowDevTools() => _browser?.CoreWebView2?.OpenDevToolsWindow();
|
||||
|
||||
//https://github.com/MicrosoftEdge/WebView2Feedback/issues/2161
|
||||
public void Dispose() => Browser.Dispatcher.Invoke(() => Browser.Dispose(), DispatcherPriority.Send);
|
||||
public void Dispose()
|
||||
{
|
||||
if (_browser != null)
|
||||
{
|
||||
_browser.Dispatcher.Invoke(() => _browser.Dispose(), DispatcherPriority.Send);
|
||||
_browser = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,18 +24,22 @@ namespace Speckle.Connectors.Revit.Bindings;
|
||||
|
||||
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
{
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitIdleManager _revitIdleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
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;
|
||||
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
|
||||
private bool _isDocChangedSubscribed;
|
||||
private EventHandler<Autodesk.Revit.DB.Events.DocumentChangedEventArgs>? _documentChangedHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
|
||||
@@ -46,7 +50,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private ConcurrentHashSet<ElementId> ChangedObjectIds { get; set; } = new();
|
||||
|
||||
public RevitSendBinding(
|
||||
IAppIdleManager idleManager,
|
||||
RevitIdleManager revitIdleManager,
|
||||
RevitContext revitContext,
|
||||
DocumentModelStore store,
|
||||
ICancellationManager cancellationManager,
|
||||
@@ -55,6 +59,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
ToSpeckleSettingsManager toSpeckleSettingsManager,
|
||||
ElementUnpacker elementUnpacker,
|
||||
IRevitConversionSettingsFactory revitConversionSettingsFactory,
|
||||
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
LinkedModelHandler linkedModelHandler,
|
||||
IThreadContext threadContext,
|
||||
@@ -63,7 +68,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
)
|
||||
: base("sendBinding", bridge)
|
||||
{
|
||||
_idleManager = idleManager;
|
||||
_revitIdleManager = revitIdleManager;
|
||||
_revitContext = revitContext;
|
||||
_store = store;
|
||||
_cancellationManager = cancellationManager;
|
||||
@@ -71,6 +76,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_toSpeckleSettingsManager = toSpeckleSettingsManager;
|
||||
_elementUnpacker = elementUnpacker;
|
||||
_revitConversionSettingsFactory = revitConversionSettingsFactory;
|
||||
_revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_linkedModelHandler = linkedModelHandler;
|
||||
_threadContext = threadContext;
|
||||
@@ -82,15 +88,57 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
revitTask.Run(() =>
|
||||
{
|
||||
revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
// revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
|
||||
// _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_documentChangedHandler = (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_store.ModelCardsChanged += (_, e) => OnModelCardsChanged(e);
|
||||
_store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged());
|
||||
});
|
||||
}
|
||||
|
||||
private void OnModelCardsChanged(ModelCardsChangedEventArgs e)
|
||||
{
|
||||
if (e.ModelCards.Count > 0 && e.ModelCards.Any(m => m.TypeDiscriminator == nameof(SenderModelCard)))
|
||||
{
|
||||
SubscribeDocChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnsubscribeDocChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeDocChanged()
|
||||
{
|
||||
if (_documentChangedHandler == null || _isDocChangedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_threadContext.RunOnMain(() =>
|
||||
{
|
||||
_revitContext.UIApplication.NotNull().Application.DocumentChanged += _documentChangedHandler;
|
||||
});
|
||||
_isDocChangedSubscribed = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeDocChanged()
|
||||
{
|
||||
if (_documentChangedHandler == null || !_isDocChangedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_threadContext.RunOnMain(() =>
|
||||
{
|
||||
_revitContext.UIApplication.NotNull().Application.DocumentChanged -= _documentChangedHandler;
|
||||
});
|
||||
_isDocChangedSubscribed = false;
|
||||
}
|
||||
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new RevitSelectionFilter() { IsDefault = true },
|
||||
new RevitSelectionFilter { IsDefault = true },
|
||||
new RevitViewsFilter(_revitContext),
|
||||
new RevitCategoriesFilter(_revitContext)
|
||||
];
|
||||
@@ -110,6 +158,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 +173,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 +196,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 +204,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 +235,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
|
||||
@@ -272,7 +320,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
if (addedElementIds.Count > 0)
|
||||
{
|
||||
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
_revitIdleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
}
|
||||
|
||||
if (HaveUnitsChanged(doc))
|
||||
@@ -292,8 +340,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
_idleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
_revitIdleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_revitIdleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
}
|
||||
|
||||
// Keeps track of doc and current units
|
||||
@@ -332,9 +380,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 +497,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private async Task OnDocumentChanged()
|
||||
{
|
||||
_sendConversionCache.ClearCache();
|
||||
_revitToSpeckleCacheSingleton.ClearCache();
|
||||
|
||||
if (_cancellationManager.NumberOfOperations > 0)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, ID
|
||||
public SelectionBinding(
|
||||
RevitContext revitContext,
|
||||
IBrowserBridge parent,
|
||||
IAppIdleManager idleManager,
|
||||
RevitIdleManager idleManager,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IRevitTask revitTask
|
||||
)
|
||||
|
||||
+3
-2
@@ -48,16 +48,18 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddSingleton<IBinding, SelectionBinding>();
|
||||
serviceCollection.AddSingleton<IBinding, RevitSendBinding>();
|
||||
serviceCollection.AddSingleton<IBinding, RevitReceiveBinding>();
|
||||
serviceCollection.AddSingleton<RevitIdleManager>();
|
||||
|
||||
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
|
||||
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
// serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
|
||||
// send operation and dependencies
|
||||
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 +76,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
|
||||
}
|
||||
|
||||
@@ -17,13 +17,16 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
{
|
||||
private readonly ILogger<RevitDocumentStore> _logger;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
|
||||
//private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
|
||||
public RevitDocumentStore(
|
||||
IAppIdleManager idleManager,
|
||||
//IAppIdleManager idleManager,
|
||||
RevitIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
IJsonSerializer jsonSerializer,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
@@ -34,6 +37,7 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
: base(logger, jsonSerializer)
|
||||
{
|
||||
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
|
||||
//_idleManager = idleManager;
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
@@ -97,10 +101,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 +117,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 +140,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ public static class SupportedCategoriesUtils
|
||||
return
|
||||
#if REVIT2023_OR_GREATER
|
||||
category.BuiltInCategory != BuiltInCategory.OST_AreaSchemes
|
||||
&& category.BuiltInCategory != BuiltInCategory.OST_AreaSchemeLines;
|
||||
&& category.BuiltInCategory != BuiltInCategory.OST_AreaSchemeLines
|
||||
&& category.BuiltInCategory != BuiltInCategory.INVALID;
|
||||
#else
|
||||
category.Name != "OST_AreaSchemeLines" && category.Name != "OST_AreaSchemes";
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+123
-10
@@ -21,6 +21,7 @@ using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using Transform = Speckle.Objects.Other.Transform;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Receive;
|
||||
@@ -38,12 +39,15 @@ public sealed class RevitHostObjectBuilder(
|
||||
IThreadContext threadContext,
|
||||
RevitToHostCacheSingleton revitToHostCacheSingleton,
|
||||
ITypedConverter<
|
||||
(Base atomicObject, IReadOnlyCollection<Matrix4x4> matrix),
|
||||
(Base atomicObject, IReadOnlyCollection<Matrix4x4> matrix, DataObject? parentDataObject),
|
||||
DirectShape
|
||||
> localToGlobalDirectShapeConverter,
|
||||
IReceiveConversionHandler conversionHandler
|
||||
) : IHostObjectBuilder, IDisposable
|
||||
{
|
||||
// Maps atomic object applicationId -> parent DataObject
|
||||
private readonly Dictionary<string, DataObject> _atomicObjectToParentDataObject = new();
|
||||
|
||||
public Task<HostObjectBuilderResult> Build(
|
||||
Base rootObject,
|
||||
string projectName,
|
||||
@@ -66,8 +70,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)
|
||||
)
|
||||
{
|
||||
@@ -102,6 +106,9 @@ public sealed class RevitHostObjectBuilder(
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
// Register DataObjects with InstanceProxy displayValues
|
||||
RegisterDataObjectsWithInstanceProxies(unpackedRoot);
|
||||
|
||||
// NOTE: below is 💩... https://github.com/specklesystems/speckle-sharp-connectors/pull/813 broke sketchup to revit workflow
|
||||
// ids were modified to fix receiving instances [CNX-1707](https://linear.app/speckle/issue/CNX-1707/revit-curves-and-meshes-in-blocks-come-as-duplicated)
|
||||
// but we then broke sketchup to revit because applicationIds in proxies didn't match modified application ids which cam from #813 hack
|
||||
@@ -110,7 +117,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 +147,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,17 +166,26 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Update DataObject lookup IDs
|
||||
UpdateAtomicObjectLookupWithModifiedIds(originalToModifiedIds);
|
||||
|
||||
// 2 - Bake materials (now with the updated IDs)
|
||||
if (unpackedRoot.RenderMaterialProxies != null)
|
||||
{
|
||||
@@ -221,6 +244,87 @@ public sealed class RevitHostObjectBuilder(
|
||||
return conversionResults.builderResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers DataObjects that have InstanceProxy displayValues and builds the lookup.
|
||||
/// </summary>
|
||||
private void RegisterDataObjectsWithInstanceProxies(RootObjectUnpackerResult unpackedRoot)
|
||||
{
|
||||
var definitionToDataObject = new Dictionary<string, DataObject>();
|
||||
|
||||
foreach (var tc in unpackedRoot.ObjectsToConvert)
|
||||
{
|
||||
if (tc.Current is DataObject dataObject)
|
||||
{
|
||||
var instanceProxies = dataObject.displayValue.OfType<InstanceProxy>().ToList();
|
||||
if (instanceProxies.Count > 0)
|
||||
{
|
||||
foreach (var ip in instanceProxies)
|
||||
{
|
||||
definitionToDataObject[ip.definitionId] = dataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build lookup: definition object applicationId -> parent DataObject
|
||||
_atomicObjectToParentDataObject.Clear();
|
||||
if (unpackedRoot.DefinitionProxies is not null)
|
||||
{
|
||||
foreach (var defProxy in unpackedRoot.DefinitionProxies)
|
||||
{
|
||||
if (
|
||||
defProxy.applicationId is not null
|
||||
&& definitionToDataObject.TryGetValue(defProxy.applicationId, out var parentDataObject)
|
||||
)
|
||||
{
|
||||
foreach (var objectId in defProxy.objects)
|
||||
{
|
||||
_atomicObjectToParentDataObject[objectId] = parentDataObject;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogError(
|
||||
"Could not find parent DataObject for DefinitionProxy {ApplicationId}",
|
||||
defProxy.applicationId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the atomic object lookup with modified IDs
|
||||
/// </summary>
|
||||
private void UpdateAtomicObjectLookupWithModifiedIds(Dictionary<string, List<string>> originalToModifiedIds)
|
||||
{
|
||||
// Build updated entries first to avoid modifying collection during iteration
|
||||
var entriesToAdd = new List<KeyValuePair<string, DataObject>>();
|
||||
var keysToRemove = new List<string>();
|
||||
|
||||
foreach (var kvp in _atomicObjectToParentDataObject)
|
||||
{
|
||||
if (originalToModifiedIds.TryGetValue(kvp.Key, out var modifiedIds))
|
||||
{
|
||||
keysToRemove.Add(kvp.Key);
|
||||
foreach (var modifiedId in modifiedIds)
|
||||
{
|
||||
entriesToAdd.Add(new(modifiedId, kvp.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
_atomicObjectToParentDataObject.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var entry in entriesToAdd)
|
||||
{
|
||||
_atomicObjectToParentDataObject[entry.Key] = entry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private Autodesk.Revit.DB.Transform? CalculateNewTransform(
|
||||
Autodesk.Revit.DB.Transform? receiveTransform,
|
||||
Autodesk.Revit.DB.Transform? rootTransform
|
||||
@@ -265,9 +369,17 @@ public sealed class RevitHostObjectBuilder(
|
||||
onOperationProgressed.Report(new("Converting", (double)++count / localToGlobalMaps.Count));
|
||||
if (result is DirectShapeDefinitionWrapper)
|
||||
{
|
||||
// Look up parent DataObject for this atomic object (handles InstanceProxy displayValue)
|
||||
var atomicId = localToGlobalMap.AtomicObject.applicationId;
|
||||
DataObject? parentDataObject = null;
|
||||
if (atomicId is not null)
|
||||
{
|
||||
_atomicObjectToParentDataObject.TryGetValue(atomicId, out parentDataObject);
|
||||
}
|
||||
|
||||
// direct shape creation happens here
|
||||
DirectShape directShapes = localToGlobalDirectShapeConverter.Convert(
|
||||
(localToGlobalMap.AtomicObject, localToGlobalMap.Matrix)
|
||||
(localToGlobalMap.AtomicObject, localToGlobalMap.Matrix, parentDataObject)
|
||||
);
|
||||
|
||||
bakedObjectIds.Add(directShapes.UniqueId);
|
||||
@@ -338,6 +450,7 @@ public sealed class RevitHostObjectBuilder(
|
||||
DirectShapeLibrary.GetDirectShapeLibrary(converterSettings.Current.Document).Reset(); // Note: this needs to be cleared, as it is being used in the converter
|
||||
|
||||
revitToHostCacheSingleton.MaterialsByObjectId.Clear(); // Massive hack!
|
||||
_atomicObjectToParentDataObject.Clear();
|
||||
groupManager.PurgeGroups(baseGroupName);
|
||||
materialBaker.PurgeMaterials(baseGroupName);
|
||||
}
|
||||
|
||||
+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)
|
||||
|
||||
+22
-1
@@ -24,6 +24,7 @@ public class RevitRootObjectBuilder(
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
LevelUnpacker levelUnpacker,
|
||||
ViewUnpacker viewUnpacker,
|
||||
IThreadContext threadContext,
|
||||
SendCollectionManager sendCollectionManager,
|
||||
ILogger<RevitRootObjectBuilder> logger,
|
||||
@@ -183,6 +184,7 @@ public class RevitRootObjectBuilder(
|
||||
// non-transformed elements can safely rely on cache
|
||||
// TODO: Potential here to transform cached objects and NOT reconvert,
|
||||
// TODO: we wont do !hasTransform here, and re-set application id before this
|
||||
|
||||
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
|
||||
{
|
||||
converted = value;
|
||||
@@ -239,6 +241,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 +251,24 @@ public class RevitRootObjectBuilder(
|
||||
var levelProxies = levelUnpacker.Unpack(flatElements);
|
||||
rootObject[ProxyKeys.LEVEL] = levelProxies;
|
||||
|
||||
rootObject[ProxyKeys.INSTANCE_DEFINITION] = revitToSpeckleCacheSingleton.GetInstanceDefinitionProxiesForObjects(
|
||||
idsAndSubElementIds
|
||||
);
|
||||
rootObject.elements.Add(
|
||||
new Collection()
|
||||
{
|
||||
elements = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds),
|
||||
name = "revitInstancedObjects"
|
||||
}
|
||||
);
|
||||
|
||||
// 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,7 +276,7 @@ public class RevitRootObjectBuilder(
|
||||
if (converterSettings.Current.ReferencePointTransform is Transform transform)
|
||||
{
|
||||
var transformMatrix = ReferencePointHelper.CreateTransformDataForRootObject(transform);
|
||||
rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] = transformMatrix;
|
||||
rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] = transformMatrix;
|
||||
}
|
||||
|
||||
return new RootObjectBuilderResult(rootObject, results);
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,38 +6,77 @@ using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Plugin;
|
||||
|
||||
public sealed class RevitIdleManager : AppIdleManager
|
||||
/// <remarks>
|
||||
/// Please do NOT try and refactor this class.
|
||||
/// Whether it's to try and generalize with the <see cref="IdleCallManager"/> class
|
||||
/// or to unnecessary try and make this class thread safe.
|
||||
/// This class is a simple singleton, targeted to a Revit's host app requirements
|
||||
/// where everything happens on the main thread, and we can avoid overly complex threading/thread-safty.
|
||||
///
|
||||
/// Previous good refactors with good intention have lead to poor debugging experiences, over-engineered threading,
|
||||
/// and low confidence in the reliability.
|
||||
/// </remarks>
|
||||
/// should be registered as singleton
|
||||
public class RevitIdleManager(RevitContext revitContext, ITopLevelExceptionHandler topLevelExceptionHandler)
|
||||
{
|
||||
private readonly UIApplication _uiApplication;
|
||||
private readonly IIdleCallManager _idleCallManager;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly UIApplication _uiApplication = revitContext.UIApplication.NotNull();
|
||||
|
||||
private event EventHandler<IdlingEventArgs>? OnIdle;
|
||||
private readonly Dictionary<string, Func<Task>> _calls = new();
|
||||
private bool _hasSubscribed;
|
||||
|
||||
public RevitIdleManager(
|
||||
RevitContext revitContext,
|
||||
IIdleCallManager idleCallManager,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IRevitTask revitTask
|
||||
)
|
||||
: base(idleCallManager)
|
||||
/// <summary>
|
||||
/// Defers the invocation of an <paramref name="action"/> until next Revit idle tick (deduped by name).
|
||||
/// The <paramref name="action"/> will be called only once.
|
||||
/// </summary>
|
||||
/// <param name="name">A key that prevents enqueuing duplicate events</param>
|
||||
/// <param name="action">The action to be invoked</param>
|
||||
/// <example>
|
||||
/// Some events in host app are triggered many times, we might get 10x per object
|
||||
/// Making this more like a deferred action, so we don't update the UI many times
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// This function must be called on the main thread
|
||||
/// </remarks>
|
||||
public void SubscribeToIdle(string name, Action action)
|
||||
{
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_uiApplication = revitContext.UIApplication.NotNull();
|
||||
_idleCallManager = idleCallManager;
|
||||
revitTask.Run(
|
||||
() => _uiApplication.Idling += (s, e) => OnIdle?.Invoke(s, e) // will be called on the main thread always and fixing the Revit exceptions on subscribing/unsubscribing Idle events
|
||||
SubscribeToIdle(
|
||||
name,
|
||||
() =>
|
||||
{
|
||||
action.Invoke();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected override void AddEvent()
|
||||
/// <inheritdoc cref="SubscribeToIdle(string, Action)"/>
|
||||
public void SubscribeToIdle(string name, Func<Task> action)
|
||||
{
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
_calls[name] = action;
|
||||
|
||||
if (_hasSubscribed)
|
||||
{
|
||||
OnIdle += RevitAppOnIdle;
|
||||
});
|
||||
return;
|
||||
}
|
||||
_hasSubscribed = true;
|
||||
|
||||
_uiApplication.Idling += RevitAppOnIdle;
|
||||
}
|
||||
|
||||
private void RevitAppOnIdle(object? sender, IdlingEventArgs e) =>
|
||||
_idleCallManager.AppOnIdle(() => OnIdle -= RevitAppOnIdle);
|
||||
private void RevitAppOnIdle(object? sender, IdlingEventArgs e)
|
||||
{
|
||||
topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
foreach (KeyValuePair<string, Func<Task>> kvp in _calls)
|
||||
{
|
||||
topLevelExceptionHandler.FireAndForget(kvp.Value.Invoke);
|
||||
}
|
||||
|
||||
_calls.Clear();
|
||||
_uiApplication.Idling -= RevitAppOnIdle;
|
||||
|
||||
// setting last will delay entering re-subscription
|
||||
_hasSubscribed = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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" />
|
||||
@@ -53,11 +54,11 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitTask.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitExternalApplication.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitThreadContext.cs" />
|
||||
<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));
|
||||
}
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Creates collections by matching name tree structure to elements tree structure.
|
||||
/// Each branch in the names tree corresponds to the same-path branch in the elements tree.
|
||||
/// </summary>
|
||||
[Guid("7E8F9A1B-2C3D-4E5F-6A7B-8C9D0E1F2A3B")]
|
||||
public class CollectionsByName : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_collections_create; // TODO: Update to specific icon if available
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
public CollectionsByName()
|
||||
: base(
|
||||
"Collections by Name",
|
||||
"CbN",
|
||||
"Creates collections by matching name tree structure to objects tree structure. Each branch in the names tree corresponds to the same-path branch in the objects tree.",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.COLLECTIONS
|
||||
) { }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter(
|
||||
"Names",
|
||||
"N",
|
||||
"Collection names (tree structure must match Objects tree structure)",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
"Objects",
|
||||
"O",
|
||||
"Objects to group into collections (tree structure must match Names tree structure)",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(),
|
||||
"Collection",
|
||||
"C",
|
||||
"Root collection containing named sub-collections",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
// access the tree data directly from parameters
|
||||
var namesParam = Params.Input[0];
|
||||
var elementsParam = Params.Input[1];
|
||||
|
||||
var namesTree = namesParam.VolatileData;
|
||||
var elementsTree = elementsParam.VolatileData;
|
||||
|
||||
// validate that both inputs have data
|
||||
if (namesTree.IsEmpty)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Names tree is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (elementsTree.IsEmpty)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Objects tree is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// validate tree structures match exactly
|
||||
if (!TreeStructuresMatch(namesTree, elementsTree))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Tree structures and topologies must match exactly");
|
||||
return;
|
||||
}
|
||||
|
||||
// create root collection
|
||||
var rootCollection = CollectionHelpers.CreateRootCollection(InstanceGuid.ToString());
|
||||
|
||||
// process each path
|
||||
foreach (var path in namesTree.Paths)
|
||||
{
|
||||
var nameBranch = namesTree.get_Branch(path);
|
||||
var elementsBranch = elementsTree.get_Branch(path);
|
||||
|
||||
// validate name branch - throw if empty
|
||||
if (nameBranch.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Name branch at path {path} is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// validate name branch - just warn if multiple, use first
|
||||
if (nameBranch.Count > 1)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Name branch at path {path} has {nameBranch.Count} names - using first name only"
|
||||
);
|
||||
}
|
||||
|
||||
// get the collection name
|
||||
string collectionName = GetCollectionName(nameBranch[0]);
|
||||
if (string.IsNullOrWhiteSpace(collectionName))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Invalid collection name at path {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
// skip empty element branches with warning
|
||||
if (elementsBranch.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Skipping empty elements branch at path {path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse nested collection path (e.g. parent::child)
|
||||
var collectionNames = collectionName.Split(
|
||||
new[] { Constants.LAYER_PATH_DELIMITER },
|
||||
StringSplitOptions.RemoveEmptyEntries
|
||||
);
|
||||
|
||||
// create or get nested collection structure
|
||||
var targetCollection = GetOrCreateNestedCollection(rootCollection, collectionNames, elementsBranch, path);
|
||||
|
||||
// add elements to deepest collection
|
||||
AddElementsToCollection(targetCollection, elementsBranch, path);
|
||||
}
|
||||
|
||||
// validate collection has content
|
||||
if (rootCollection.Elements.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
"Collection contains no valid geometry. All branches were empty or contained unsupported types."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate for duplicate application IDs (following CreateCollection pattern)
|
||||
if (CollectionHelpers.HasDuplicateApplicationIds(rootCollection))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The same object(s) cannot appear in multiple collections");
|
||||
return;
|
||||
}
|
||||
|
||||
da.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that two tree structures have exactly matching paths
|
||||
/// </summary>
|
||||
private bool TreeStructuresMatch(
|
||||
Grasshopper.Kernel.Data.IGH_Structure namesTree,
|
||||
Grasshopper.Kernel.Data.IGH_Structure elementsTree
|
||||
)
|
||||
{
|
||||
if (namesTree.PathCount != elementsTree.PathCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that all paths match exactly
|
||||
var namePaths = namesTree.Paths.ToList();
|
||||
var elementPaths = elementsTree.Paths.ToList();
|
||||
|
||||
for (int i = 0; i < namePaths.Count; i++)
|
||||
{
|
||||
if (namePaths[i].CompareTo(elementPaths[i]) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts collection name, handling GH_String and other text types
|
||||
/// </summary>
|
||||
private string GetCollectionName(object nameObj) =>
|
||||
nameObj switch
|
||||
{
|
||||
GH_String ghString => ghString.Value,
|
||||
IGH_Goo goo => goo.ToString(),
|
||||
_ => nameObj.ToString()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a nested collection structure based on the collection names.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Handles paths like "parent::child::grandchild" by creating intermediate collections.
|
||||
/// </remarks>
|
||||
private SpeckleCollectionWrapper GetOrCreateNestedCollection(
|
||||
SpeckleCollectionWrapper rootCollection,
|
||||
string[] collectionNames,
|
||||
System.Collections.IList elementsBranch,
|
||||
Grasshopper.Kernel.Data.GH_Path path
|
||||
)
|
||||
{
|
||||
SpeckleCollectionWrapper currentCollection = rootCollection;
|
||||
var currentPath = new List<string>(rootCollection.Path);
|
||||
|
||||
foreach (var collectionName in collectionNames)
|
||||
{
|
||||
// build path for this level
|
||||
currentPath.Add(collectionName);
|
||||
|
||||
// check if child collection already exists
|
||||
var existingChild = currentCollection
|
||||
.Elements.OfType<SpeckleCollectionWrapper>()
|
||||
.FirstOrDefault(c => c.Name == collectionName);
|
||||
|
||||
if (existingChild != null)
|
||||
{
|
||||
// use existing collection
|
||||
currentCollection = existingChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new child collection
|
||||
var newChild = new SpeckleCollectionWrapper
|
||||
{
|
||||
Base = new Collection(),
|
||||
Name = collectionName,
|
||||
Path = currentPath.ToList(),
|
||||
Color = null,
|
||||
Material = null,
|
||||
Topology = null, // only set topology on leaf collections
|
||||
ApplicationId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
currentCollection.Elements.Add(newChild);
|
||||
currentCollection = newChild;
|
||||
}
|
||||
}
|
||||
|
||||
// set topology on the final (leaf) collection
|
||||
currentCollection.Topology = GetBranchTopology(path, elementsBranch.Count);
|
||||
|
||||
return currentCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds elements from a branch to the target collection
|
||||
/// </summary>
|
||||
private void AddElementsToCollection(
|
||||
SpeckleCollectionWrapper targetCollection,
|
||||
System.Collections.IList elementsBranch,
|
||||
Grasshopper.Kernel.Data.GH_Path path
|
||||
)
|
||||
{
|
||||
foreach (var item in elementsBranch)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
// preserve nulls for topology (CNX-2855 pattern)
|
||||
targetCollection.Elements.Add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert to SpeckleWrapper if possible - cast to IGH_Goo first
|
||||
if (item is not IGH_Goo goo)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Unsupported object type in branch {path}: {item.GetType().Name}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
var wrapper = goo.ToSpeckleObjectWrapper();
|
||||
if (wrapper == null)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Unsupported object type in branch {path}: {item.GetType().Name}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wrapper is ISpeckleCollectionObject collectionObject)
|
||||
{
|
||||
targetCollection.Elements.Add(collectionObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Object type {wrapper.GetType().Name} is not a valid collection element"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates topology string for a single branch (following GrasshopperHelpers.GetParamTopology pattern)
|
||||
/// </summary>
|
||||
private string GetBranchTopology(Grasshopper.Kernel.Data.GH_Path path, int count) =>
|
||||
$"{path.ToString(false)}-{count}";
|
||||
}
|
||||
+58
-7
@@ -47,7 +47,8 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
var rootCollection = CreateRootCollection();
|
||||
var rootCollection = CollectionHelpers.CreateRootCollection(InstanceGuid.ToString());
|
||||
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 (CollectionHelpers.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(CollectionHelpers.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,9 +217,20 @@ 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.)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// IGH_VariableParameterComponent implementation
|
||||
|
||||
+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) { }
|
||||
|
||||
|
||||
+209
-95
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
@@ -12,7 +13,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
/// Given a list of objects, this component will filter the list for objects that match the queries.
|
||||
/// </summary>
|
||||
[Guid("26AEA046-4DD4-4F61-8251-E92A6D2AC880")]
|
||||
public class FilterSpeckleObjects : GH_Component
|
||||
public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
{
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_objects_filter;
|
||||
@@ -49,17 +50,6 @@ public class FilterSpeckleObjects : GH_Component
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[3].Optional = true;
|
||||
|
||||
pManager.AddTextParameter(
|
||||
"Application Id",
|
||||
"aID",
|
||||
"Find objects with a matching applicationId",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[4].Optional = true;
|
||||
|
||||
pManager.AddTextParameter("Speckle Id", "sID", "Find objects with a matching Speckle id", GH_ParamAccess.item);
|
||||
Params.Input[5].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
@@ -103,101 +93,39 @@ public class FilterSpeckleObjects : GH_Component
|
||||
dataAccess.GetData(2, ref property);
|
||||
string material = "";
|
||||
dataAccess.GetData(3, ref material);
|
||||
|
||||
// optional parameters - only read if they've been added via ⊕
|
||||
string appId = "";
|
||||
dataAccess.GetData(4, ref appId);
|
||||
string speckleId = "";
|
||||
dataAccess.GetData(5, ref speckleId);
|
||||
int? appIdIndex = FindInputIndexByName("Application Id");
|
||||
int? speckleIdIndex = FindInputIndexByName("Speckle Id");
|
||||
|
||||
if (appIdIndex.HasValue)
|
||||
{
|
||||
dataAccess.GetData(appIdIndex.Value, ref appId);
|
||||
}
|
||||
|
||||
if (speckleIdIndex.HasValue)
|
||||
{
|
||||
dataAccess.GetData(speckleIdIndex.Value, ref speckleId);
|
||||
}
|
||||
|
||||
bool filterByAppId = appIdIndex.HasValue;
|
||||
bool filterBySpeckleId = speckleIdIndex.HasValue;
|
||||
|
||||
List<SpeckleWrapper> matchedObjects = new();
|
||||
List<SpeckleWrapper> removedObjects = new();
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
|
||||
foreach (SpeckleWrapper wrapper in objects.Cast<SpeckleWrapper>())
|
||||
{
|
||||
SpeckleWrapper wrapper = objects[i]!;
|
||||
|
||||
// filter by name
|
||||
if (!MatchesSearchPattern(name, wrapper.Name))
|
||||
if (MatchesAllFilters(wrapper, name, property, material, appId, filterByAppId, speckleId, filterBySpeckleId))
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by property
|
||||
bool foundProperty = false;
|
||||
if (string.IsNullOrEmpty(property))
|
||||
{
|
||||
foundProperty = true;
|
||||
matchedObjects.Add(wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
|
||||
? dataObjPropWrapper.Properties
|
||||
: wrapper is SpeckleGeometryWrapper geoPropWrapper
|
||||
? geoPropWrapper.Properties
|
||||
: null;
|
||||
|
||||
if (properties is not null)
|
||||
{
|
||||
// use flattened properties to search ALL nested property keys
|
||||
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
|
||||
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
|
||||
foreach (string key in flattenedProps.Keys)
|
||||
{
|
||||
if (MatchesSearchPattern(property, key))
|
||||
{
|
||||
foundProperty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundProperty)
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by material name
|
||||
bool materialMatches = true;
|
||||
if (!string.IsNullOrEmpty(material))
|
||||
{
|
||||
materialMatches = false;
|
||||
|
||||
if (wrapper is SpeckleGeometryWrapper geoWrapper)
|
||||
{
|
||||
materialMatches = MatchesSearchPattern(material, geoWrapper.Material?.Name ?? "");
|
||||
}
|
||||
else if (wrapper is SpeckleDataObjectWrapper dataObjWrapper)
|
||||
{
|
||||
// check if ANY geometry in the data object has a matching material (not sure about this...)
|
||||
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
|
||||
materialMatches = dataObjWrapper.Geometries.Any(geo =>
|
||||
MatchesSearchPattern(material, geo.Material?.Name ?? "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!materialMatches)
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by application id
|
||||
if (!MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by speckle id
|
||||
if (!MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedObjects.Add(wrapper);
|
||||
}
|
||||
|
||||
// Set output objects
|
||||
@@ -214,4 +142,190 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
return Operator.IsSymbolNameLike(target, searchPattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a wrapper matches all active filter criteria.
|
||||
/// </summary>
|
||||
private bool MatchesAllFilters(
|
||||
SpeckleWrapper wrapper,
|
||||
string name,
|
||||
string property,
|
||||
string material,
|
||||
string appId,
|
||||
bool filterByAppId,
|
||||
string speckleId,
|
||||
bool filterBySpeckleId
|
||||
)
|
||||
{
|
||||
// filter by name
|
||||
if (!MatchesSearchPattern(name, wrapper.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by property
|
||||
if (!MatchesPropertyFilter(wrapper, property))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by material name
|
||||
if (!MatchesMaterialFilter(wrapper, material))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by application id (only if parameter was added)
|
||||
if (filterByAppId && !MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by speckle id (only if parameter was added)
|
||||
if (filterBySpeckleId && !MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool MatchesPropertyFilter(SpeckleWrapper wrapper, string property)
|
||||
{
|
||||
if (string.IsNullOrEmpty(property))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
|
||||
? dataObjPropWrapper.Properties
|
||||
: wrapper is SpeckleGeometryWrapper geoPropWrapper
|
||||
? geoPropWrapper.Properties
|
||||
: null;
|
||||
|
||||
if (properties is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// use flattened properties to search ALL nested property keys
|
||||
return properties.Flatten().Keys.Any(key => MatchesSearchPattern(property, key));
|
||||
}
|
||||
|
||||
private bool MatchesMaterialFilter(SpeckleWrapper wrapper, string material)
|
||||
{
|
||||
if (string.IsNullOrEmpty(material))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wrapper is SpeckleGeometryWrapper geoWrapper)
|
||||
{
|
||||
return MatchesSearchPattern(material, geoWrapper.Material?.Name ?? "");
|
||||
}
|
||||
|
||||
if (wrapper is SpeckleDataObjectWrapper dataObjWrapper)
|
||||
{
|
||||
// check if ANY geometry in the data object has a matching material
|
||||
return dataObjWrapper.Geometries.Any(geo => MatchesSearchPattern(material, geo.Material?.Name ?? ""));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of an input parameter by its Name.
|
||||
/// Returns null if the parameter doesn't exist.
|
||||
/// </summary>
|
||||
private int? FindInputIndexByName(string paramName)
|
||||
{
|
||||
for (int i = 0; i < Params.Input.Count; i++)
|
||||
{
|
||||
if (Params.Input[i].Name == paramName)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#region IGH_VariableParameterComponent
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
if (side != GH_ParameterSide.Input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow inserting after the fixed parameters (index 4+)
|
||||
if (index < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check how many optional params are already added (total inputs - 4 fixed)
|
||||
int addedOptionalCount = Params.Input.Count - 4;
|
||||
|
||||
// we have 2 optional parameters available
|
||||
return addedOptionalCount < 2;
|
||||
}
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index) =>
|
||||
// only allow removing optional input parameters (index 4+)
|
||||
side == GH_ParameterSide.Input
|
||||
&& index >= 4;
|
||||
|
||||
/// <remarks>
|
||||
/// The ternary operator for NickName is needed due to a Grasshopper quirk where
|
||||
/// dynamically created parameters don't respect the "Draw Full Names" setting automatically.
|
||||
/// We check CanvasFullNames at creation time to set the appropriate NickName.
|
||||
/// This does not handle the case where the user toggles "Draw Full Names" while the
|
||||
/// component is already on the canvas. Handling that would require subscribing to
|
||||
/// Grasshopper.CentralSettings.CanvasFullNamesChanged event, which is overkill for now.
|
||||
/// </remarks>
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
bool hasAppId = FindInputIndexByName("Application Id").HasValue;
|
||||
bool hasSpeckleId = FindInputIndexByName("Speckle Id").HasValue;
|
||||
|
||||
if (!hasAppId)
|
||||
{
|
||||
return new Param_String
|
||||
{
|
||||
Name = "Application Id",
|
||||
NickName = Grasshopper.CentralSettings.CanvasFullNames ? "Application Id" : "aID", // see remarks
|
||||
Description = "Find objects with a matching applicationId",
|
||||
Access = GH_ParamAccess.item,
|
||||
Optional = true
|
||||
};
|
||||
}
|
||||
|
||||
if (!hasSpeckleId)
|
||||
{
|
||||
return new Param_String
|
||||
{
|
||||
Name = "Speckle Id",
|
||||
NickName = Grasshopper.CentralSettings.CanvasFullNames ? "Speckle Id" : "sID", // see remarks
|
||||
Description = "Find objects with a matching Speckle id",
|
||||
Access = GH_ParamAccess.item,
|
||||
Optional = true
|
||||
};
|
||||
}
|
||||
|
||||
return new Param_String();
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Input && index >= 4;
|
||||
|
||||
public void VariableParameterMaintenance()
|
||||
{
|
||||
// ensure all optional parameters stay marked as optional
|
||||
for (int i = 4; i < Params.Input.Count; i++)
|
||||
{
|
||||
Params.Input[i].Optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
+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) { }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user