Compare commits

..

21 Commits

Author SHA1 Message Date
oguzhankoral 9e16a6cd85 properties poc 2025-10-14 13:00:17 +03:00
Björn Steinhagen 95070bfc65 fix: material assignment on revit receive (#1146) 2025-10-14 12:59:44 +03:00
oguzhankoral 7c5ddf1553 Clear instance proxies per conversion 2025-10-14 11:54:59 +03:00
Björn Steinhagen e746548c81 refactor(revit): replace tuples with DisplayValueResult record for display values (#1145) 2025-10-10 14:52:14 +08:00
Jedd Morgan e52ed561e2 Merge remote-tracking branch 'origin/dev' into oguzhan/display-value-proxies 2025-10-09 16:24:36 +01:00
Jedd Morgan 361195f1f1 feat(revit): Use Symbol geometry when it's safe to, and fix linked documents (#1144)
* Hash function

* Use v2 style transform

* extra comments

* experiment1

* correct transform logic and disposal

* corrected transform logic

* simplify (maybe) the transform combination
2025-10-09 15:00:50 +01:00
Jedd Morgan dc5467de88 Merge branch 'oguzhan/display-value-proxies' into jrm/experimental-linked-document 2025-10-09 12:21:42 +01:00
Jedd Morgan 48441a3630 simplify (maybe) the transform combination 2025-10-09 12:20:26 +01:00
Jedd Morgan c08a3e6129 corrected transform logic 2025-10-09 11:32:06 +01:00
Jedd Morgan 43bbe4efd0 correct transform logic and disposal 2025-10-09 11:11:36 +01:00
Jedd Morgan 1b94fd9901 experiment1 2025-10-08 17:33:01 +01:00
Jedd Morgan cfed10e10f extra comments 2025-10-08 16:01:43 +01:00
Jedd Morgan 723c3dc25d Use v2 style transform 2025-10-08 15:54:10 +01:00
Jedd Morgan a5ccad03c2 Merge pull request #1142 from specklesystems/jedd/cnx-2657-hashing-the-meshes
feat(revit)!: Use Hash function for mesh geometry instance ids
2025-10-08 17:13:06 +03:00
Jedd Morgan c06067bfc4 Hash function 2025-10-08 12:50:42 +01:00
Björn b22c92919d fix: custom mesh id logic 2025-10-07 15:57:15 +02:00
oguzhankoral 96ba945f51 Fix transform issues 2025-10-02 20:32:21 +03:00
oguzhankoral 90ecf20582 Handle revit receive 2025-10-02 07:31:54 +03:00
oguzhankoral a1d9c0093d Handle autocad, rhino and sketchup receives 2025-10-01 22:30:33 +03:00
oguzhankoral 26c0558a14 some fixes 2025-09-30 11:04:03 +03:00
oguzhankoral 025a99da22 POC 2025-09-29 23:52:21 +03:00
251 changed files with 4099 additions and 7982 deletions
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -20,7 +20,7 @@ jobs:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
+3 -3
View File
@@ -16,7 +16,7 @@ jobs:
file_version: ${{ steps.set-version.outputs.file_version }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -26,7 +26,7 @@ jobs:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
- name: Cache Nuget
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@@ -35,7 +35,7 @@ jobs:
run: ./build.ps1 zip
- name: ⬆️ Upload artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: output-${{ env.SEMVER }}
path: output/*.*
+9 -9
View File
@@ -16,12 +16,12 @@
},
"Microsoft.Build": {
"type": "Direct",
"requested": "[17.11.48, )",
"resolved": "17.11.48",
"contentHash": "g8Kn575mNAKcuFotV3C7xvF+IbxuHennl67LH2shL2au1U6UqwReTDygCHyU04+koc2Yn7fHIbVQaC08HqEWow==",
"requested": "[17.11.4, )",
"resolved": "17.11.4",
"contentHash": "UMC7DfeFEHY2GGHHaghybUuUlLaByFHEFudR2PehMgDBuRuLAUePp1iaa4eFtVzepRzMtIbeSCVJCzzX3NV2Gg==",
"dependencies": {
"Microsoft.Build.Framework": "17.11.48",
"Microsoft.NET.StringTools": "17.11.48",
"Microsoft.Build.Framework": "17.11.4",
"Microsoft.NET.StringTools": "17.11.4",
"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.48",
"contentHash": "C3WIMt2wBl4++NX3jSEpTq5KXBhvAV154R4JrYHkfy9JSBcXWiL0mkgpspk5xSdOj+fS/uz7zluIy6bMM1fkkQ=="
"resolved": "17.11.4",
"contentHash": "u28uDihlqxtt8h2dL1ZJOZ7TRkxBK+HGr+3FgQpILVo7Q7gErkw8mYW9R+RM5PtxvZTdYb/4MWDL66vdIsANBQ=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
@@ -97,8 +97,8 @@
},
"Microsoft.NET.StringTools": {
"type": "Transitive",
"resolved": "17.11.48",
"contentHash": "0IQo089IGBEC4jgtishauZMVr9ZxOWNiGKeDvyzZlvw7p2r253lJh6IJCLLFWXvZnVrVO5mnsYIPamxFPzM08w=="
"resolved": "17.11.4",
"contentHash": "mudqUHhNpeqIdJoUx2YDWZO/I9uEDYVowan89R6wsomfnUJQk6HteoQTlNjZDixhT2B4IXMkMtgZtoceIjLRmA=="
},
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
"type": "Transitive",
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -356,12 +336,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -356,12 +336,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -313,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -155,25 +155,6 @@
"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",
@@ -229,8 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,12 +288,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -155,25 +155,6 @@
"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",
@@ -229,8 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,12 +288,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -1,296 +0,0 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.HostApp.Extensions;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk.Common;
using Speckle.Sdk.Dependencies;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
using AutocadColor = Autodesk.AutoCAD.Colors.Color;
namespace Speckle.Connectors.Autocad.Operations.Receive;
/// <summary>
/// <para>Base class for AutoCAD host object builders. Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public abstract class AutocadHostObjectBaseBuilder : IHostObjectBuilder
{
private readonly IRootToHostConverter _converter;
private readonly AutocadLayerBaker _layerBaker;
private readonly AutocadGroupBaker _groupBaker;
private readonly AutocadInstanceBaker _instanceBaker;
private readonly IAutocadMaterialBaker _materialBaker;
private readonly IAutocadColorBaker _colorBaker;
private readonly AutocadContext _autocadContext;
private readonly RootObjectUnpacker _rootObjectUnpacker;
private readonly IReceiveConversionHandler _conversionHandler;
protected AutocadHostObjectBaseBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
)
{
_converter = converter;
_layerBaker = layerBaker;
_groupBaker = groupBaker;
_instanceBaker = instanceBaker;
_materialBaker = materialBaker;
_colorBaker = colorBaker;
_autocadContext = autocadContext;
_rootObjectUnpacker = rootObjectUnpacker;
_conversionHandler = conversionHandler;
}
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed.Report(new("Converting", null));
// Layer filter for received commit with project and model name
_layerBaker.CreateLayerFilter(projectName, modelName);
// 0 - Clean then Rock n Roll!
string baseLayerPrefix = _autocadContext.RemoveInvalidChars($"SPK-{projectName}-{modelName}-");
PreReceiveDeepClean(baseLayerPrefix);
// 1 - Unpack objects and proxies from root commit object
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
// 2 - Split atomic objects and instance components with their path
var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
var atomicObjectsWithPath = _layerBaker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = _layerBaker.GetInstanceComponentsWithPath(instanceComponents);
// POC: these are not captured by traversal, so we need to re-add them here
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
{
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
(Array.Empty<Collection>(), proxy as IInstanceComponent)
);
instanceComponentsWithPath.AddRange(transformed);
}
// 3 - Parse and bake proxies (materials and colors), as they are used later down the line by layers and objects
if (unpackedRoot.RenderMaterialProxies != null)
{
_materialBaker.ParseAndBakeRenderMaterials(
unpackedRoot.RenderMaterialProxies,
baseLayerPrefix,
onOperationProgressed
);
}
if (unpackedRoot.ColorProxies != null)
{
_colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
}
// 3.5 - Parse and bake additional proxies that are needed for conversion
ParseAndBakeAdditionalProxies(rootObject, baseLayerPrefix);
// 4 - Convert atomic objects
HashSet<ReceiveConversionResult> results = new();
HashSet<string> bakedObjectIds = new();
Dictionary<string, IReadOnlyCollection<Entity>> applicationIdMap = new();
var count = 0;
foreach (var (layerPath, atomicObject) in atomicObjectsWithPath)
{
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
var ex = _conversionHandler.TryConvert(() =>
{
cancellationToken.ThrowIfCancellationRequested();
string objectId = atomicObject.applicationId ?? atomicObject.id.NotNull();
IReadOnlyCollection<Entity> convertedObjects = ConvertObject(atomicObject, layerPath, baseLayerPrefix);
applicationIdMap[objectId] = convertedObjects;
results.UnionWith(
convertedObjects.Select(e => new ReceiveConversionResult(
Status.SUCCESS,
atomicObject,
e.GetSpeckleApplicationId(),
e.GetType().ToString()
))
);
bakedObjectIds.UnionWith(convertedObjects.Select(e => e.GetSpeckleApplicationId()));
});
if (ex != null)
{
results.Add(new(Status.ERROR, atomicObject, null, null, ex));
}
}
// 5 - Convert instances
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = _instanceBaker.BakeInstances(
instanceComponentsWithPath,
applicationIdMap,
baseLayerPrefix,
onOperationProgressed
);
bakedObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id));
bakedObjectIds.UnionWith(createdInstanceIds);
results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
results.UnionWith(instanceConversionResults);
// 6 - Create groups
if (unpackedRoot.GroupProxies != null)
{
IReadOnlyCollection<ReceiveConversionResult> groupResults = _groupBaker.CreateGroups(
unpackedRoot.GroupProxies,
applicationIdMap
);
results.UnionWith(groupResults);
}
return Task.FromResult(new HostObjectBuilderResult(bakedObjectIds, results));
}
protected void PreReceiveDeepClean(string baseLayerPrefix)
{
_layerBaker.DeleteAllLayersByPrefix(baseLayerPrefix);
_instanceBaker.PurgeInstances(baseLayerPrefix);
_materialBaker.PurgeMaterials(baseLayerPrefix);
PreReceiveAdditionalDeepClean(baseLayerPrefix);
}
/// <summary>
/// Method for adding app-specific additional deep clean of the document prior to receiving.
/// </summary>
protected virtual void PreReceiveAdditionalDeepClean(string baseLayerPrefix) { }
/// <summary>
/// Method for parsing and baking additional app-specific proxies on the root prior to converting and baking objects
/// </summary>
protected virtual void ParseAndBakeAdditionalProxies(Base rootObject, string baseLayerPrefix) { }
private IReadOnlyCollection<Entity> ConvertObject(Base obj, Collection[] layerPath, string baseLayerNamePrefix)
{
string layerName = _layerBaker.CreateLayerForReceive(layerPath, baseLayerNamePrefix);
var convertedEntities = new HashSet<Entity>();
using var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction();
// 1: convert
var converted = _converter.Convert(obj);
// 2: handle result
switch (converted)
{
case Entity entity:
var bakedEntity = BakeObject(entity, obj, layerName, tr);
convertedEntities.Add(bakedEntity);
break;
case List<(Entity, Base)> listConversionResult: // this is from fallback conversion for brep/brepx/subdx/extrusionx/polycurve
var bakedFallbackEntities = BakeObjectsAsGroup(listConversionResult, obj, layerName, baseLayerNamePrefix, tr);
convertedEntities.UnionWith(bakedFallbackEntities);
break;
default:
// TODO: capture defualt case with report object here? Same as in Rhino
break;
}
tr.Commit();
return convertedEntities.Freeze();
}
private Entity BakeObject(
Entity entity,
Base originalObject,
string layerName,
Transaction tr,
Base? parentObject = null
)
{
var objId = originalObject.applicationId ?? originalObject.id.NotNull();
if (_colorBaker.ObjectColorsIdMap.TryGetValue(objId, out AutocadColor? color))
{
entity.Color = color;
}
if (_materialBaker.TryGetMaterialId(originalObject, parentObject, out ObjectId matId))
{
entity.MaterialId = matId;
}
entity.AppendToDb(layerName);
// Hook for derived classes to perform additional operations after entity is added to database
PostBakeEntity(entity, originalObject, tr);
return entity;
}
/// <summary>
/// Method for additional app-specific operations on entities after the entity has been added to the document database.
/// Called after the entity is added to the database in an open transaction
/// </summary>
/// <param name="entity"></param>
/// <param name="originalObject"></param>
/// <param name="tr"></param>
protected virtual void PostBakeEntity(Entity entity, Base originalObject, Transaction tr)
{
// Default implementation does nothing - override in derived classes
}
private List<Entity> BakeObjectsAsGroup(
List<(Entity, Base)> fallbackConversionResult,
Base parentObject,
string layerName,
string baseLayerName,
Transaction tr
)
{
var ids = new ObjectIdCollection();
var entities = new List<Entity>();
foreach (var (conversionResult, originalObject) in fallbackConversionResult)
{
BakeObject(conversionResult, originalObject, layerName, tr, parentObject);
ids.Add(conversionResult.ObjectId);
entities.Add(conversionResult);
}
if (entities.Count <= 1) // return if empty list or only one, because we don't want to create empty or single item groups.
{
return entities;
}
var groupDictionary = (DBDictionary)
tr.GetObject(Application.DocumentManager.CurrentDocument.Database.GroupDictionaryId, OpenMode.ForWrite);
var groupName = _autocadContext.RemoveInvalidChars(
$@"{parentObject.speckle_type.Split('.').Last()} - {parentObject.applicationId ?? parentObject.id} ({baseLayerName})"
);
var newGroup = new Group(groupName, true);
newGroup.Append(ids);
groupDictionary.UpgradeOpen();
groupDictionary.SetAt(groupName, newGroup);
tr.AddNewlyCreatedDBObject(newGroup, true);
return entities;
}
}
@@ -1,35 +1,238 @@
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>AutoCAD-specific host object builder. Expects to be a scoped dependency per receive operation.</para>
/// <para>Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public sealed class AutocadHostObjectBuilder : AutocadHostObjectBaseBuilder
public class AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
) : IHostObjectBuilder
{
public AutocadHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler
public Task<HostObjectBuilderResult> Build(
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
: base(
converter,
layerBaker,
groupBaker,
instanceBaker,
materialBaker,
colorBaker,
autocadContext,
rootObjectUnpacker,
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;
}
}
@@ -38,7 +38,6 @@
<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,27 +178,6 @@
"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",
@@ -289,8 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,12 +346,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -178,27 +178,6 @@
"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",
@@ -289,8 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,12 +346,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -178,27 +178,6 @@
"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",
@@ -289,8 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -322,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -366,12 +346,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -164,25 +164,6 @@
"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",
@@ -238,8 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,12 +298,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -164,25 +164,6 @@
"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",
@@ -238,8 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -272,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,12 +298,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -3,8 +3,6 @@ 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;
@@ -26,12 +24,10 @@ 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());
@@ -1,404 +0,0 @@
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;
}
}
}
@@ -1,60 +0,0 @@
using Autodesk.AutoCAD.DatabaseServices;
using Speckle.Connectors.Autocad.HostApp;
using Speckle.Connectors.Autocad.Operations.Receive;
using Speckle.Connectors.Civil3dShared.HostApp;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.Civil3dShared.Operations.Receive;
/// <summary>
/// <para>Civil3D specific host object builder with property set support. Expects to be a scoped dependency per receive operation.</para>
/// </summary>
public sealed class Civil3dHostObjectBuilder : AutocadHostObjectBaseBuilder
{
private readonly PropertySetBaker _propertySetBaker;
public Civil3dHostObjectBuilder(
IRootToHostConverter converter,
AutocadLayerBaker layerBaker,
AutocadGroupBaker groupBaker,
AutocadInstanceBaker instanceBaker,
IAutocadMaterialBaker materialBaker,
IAutocadColorBaker colorBaker,
AutocadContext autocadContext,
RootObjectUnpacker rootObjectUnpacker,
IReceiveConversionHandler conversionHandler,
PropertySetBaker propertySetBaker
)
: base(
converter,
layerBaker,
groupBaker,
instanceBaker,
materialBaker,
colorBaker,
autocadContext,
rootObjectUnpacker,
conversionHandler
)
{
_propertySetBaker = propertySetBaker;
}
protected override void PreReceiveAdditionalDeepClean(string baseLayerPrefix)
{
_propertySetBaker.PurgePropertySets(baseLayerPrefix);
}
protected override void ParseAndBakeAdditionalProxies(Base rootObject, string baseLayerPrefix)
{
_propertySetBaker.ParseAndBakePropertySetDefinitions(rootObject, baseLayerPrefix);
}
protected override void PostBakeEntity(Entity entity, Base originalObject, Transaction tr)
{
_propertySetBaker.TryBakePropertySets(entity, originalObject, tr);
}
}
@@ -11,15 +11,11 @@
<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>
@@ -153,7 +153,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection[RootKeys.ANALYSIS_RESULTS] = analysisResults;
rootObjectCollection["analysisResults"] = analysisResults;
}
catch (Exception ex)
{
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.etabs21": {
@@ -355,12 +335,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -155,25 +155,6 @@
"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",
@@ -229,8 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.etabs22": {
@@ -304,12 +286,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -1,7 +1,8 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Converters.ETABSShared.ToSpeckle.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
@@ -10,55 +11,54 @@ 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,
CsiToSpeckleCacheSingleton csiToSpeckleCacheSingleton
EtabsShellSectionResolver etabsShellSectionResolver
)
{
_settingsStore = settingsStore;
_logger = logger;
_etabsShellSectionResolver = etabsShellSectionResolver;
_csiToSpeckleCacheSingleton = csiToSpeckleCacheSingleton;
}
/// <summary>
/// Extract shell section properties from cache.
/// Extract shell section properties
/// </summary>
/// <remarks>
/// 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.
/// 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.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
var sectionProps = GetSectionProperties(sectionName);
// Step 01: Finding the appropriate api query for the unknown section type (wall, deck or slab)
Dictionary<string, object?> resolvedProperties = _etabsShellSectionResolver.ResolveSection(sectionName);
// shallow copy nested dictionaries into provided properties dict to mutate it (required by interface contract)
foreach (var kvp in sectionProps)
// Step 02: Mutate properties dictionary with resolved properties
foreach (var nestedDictionary in resolvedProperties)
{
properties[kvp.Key] = kvp.Value;
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;
}
}
}
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;
}
}
@@ -2,7 +2,7 @@ using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers;
namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Attempts to resolve the section type and retrieve its properties by trying different section resolvers.
@@ -26,6 +26,7 @@ public static class ServiceRegistration
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<EtabsSectionPropertyDefinitionService>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
return services;
}
@@ -17,6 +17,7 @@
<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" />
+17 -47
View File
@@ -2,63 +2,33 @@
<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>$(PluginBundleTarget)\Contents\$(NavisworksVersion)</PluginVersionContentTarget>
<PluginVersionContentTarget>$(AppData)\Autodesk\ApplicationPlugins\Speckle.Connectors.Navisworks.bundle\Contents\$(NavisworksVersion)</PluginVersionContentTarget>
<RootNamespace>Speckle.Connector.Navisworks</RootNamespace>
</PropertyGroup>
<Target Name="PostBuild"
AfterTargets="Build"
Condition="'$(OS)' == 'Windows_NT' and '$(NavisworksVersion)' != ''">
<!-- 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="'$(NavisworksVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Message Text="Navisworks Version $(NavisworksVersion)" Importance="high"/>
<RemoveDir Directories="$(PluginVersionContentTarget)" Condition="Exists('$(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."/>
<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)\" />
</Target>
</Project>
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2020": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2021": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2022": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2023": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -169,27 +169,6 @@
"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",
@@ -280,8 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -305,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2024": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -175,27 +175,6 @@
"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",
@@ -286,8 +265,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -311,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2025": {
@@ -357,12 +337,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -184,27 +184,6 @@
"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",
@@ -287,8 +266,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -312,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.navisworks2026": {
@@ -359,12 +339,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -15,7 +15,6 @@ using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.WebView;
using Speckle.Converter.Navisworks.Services;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converters.Common;
using Speckle.Sdk.Models.GraphTraversal;
@@ -53,9 +52,6 @@ public static class NavisworksConnectorServiceRegistration
serviceCollection.AddScoped<NavisworksMaterialUnpacker>();
serviceCollection.AddScoped<NavisworksColorUnpacker>();
// Register dual shared geometry stores for instancing pattern
serviceCollection.AddScoped<InstanceStoreManager>();
serviceCollection.AddSingleton<IAppIdleManager, NavisworksIdleManager>();
// Sending operations
@@ -16,13 +16,7 @@ public class NavisworksColorUnpacker(
IElementSelectionService selectionService
)
{
private static T SelectByRepresentationMode<T>(
RepresentationMode mode,
T active,
T permanent,
T original,
T defaultValue
) =>
private static T Select<T>(RepresentationMode mode, T active, T permanent, T original, T defaultValue) =>
mode switch
{
RepresentationMode.Active => active,
@@ -77,14 +71,14 @@ public class NavisworksColorUnpacker(
using var defaultColor = new NAV.Color(1.0, 1.0, 1.0);
var representationColor = SelectByRepresentationMode(
var representationColor = Select(
mode,
geometry.ActiveColor,
geometry.PermanentColor,
geometry.OriginalColor,
defaultColor
);
var colorId = SelectByRepresentationMode(
var colorId = Select(
mode,
$"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(),
$"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(),
@@ -130,49 +124,30 @@ public class NavisworksColorUnpacker(
var comSelection = ComBridge.ToInwOpSelection([modelItem]);
try
{
var pathsCollection = comSelection.Paths();
try
foreach (ComApi.InwOaPath path in comSelection.Paths())
{
foreach (ComApi.InwOaPath path in pathsCollection)
{
var fragmentsCollection = path.Fragments();
try
{
foreach (ComApi.InwOaFragment3 fragment in fragmentsCollection.OfType<ComApi.InwOaFragment3>())
{
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
GC.KeepAlive(path);
if (primitiveChecker.HasTriangles)
{
return false;
}
}
}
finally
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
{
GC.KeepAlive(fragment);
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
// Exit early if triangles are found
if (primitiveChecker.HasTriangles)
{
if (fragmentsCollection != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragmentsCollection);
}
return false;
}
}
}
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
}
finally
{
if (pathsCollection != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(pathsCollection);
}
}
// Return true if any 2D primitives are found
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
}
finally
{
if (comSelection != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
}
}
}
@@ -1,11 +1,7 @@
using Autodesk.Navisworks.Api.ComApi;
using Autodesk.Navisworks.Api.Interop.ComApi;
using Microsoft.Extensions.Logging;
using Speckle.Connector.Navisworks.Services;
using Speckle.Converter.Navisworks.Constants;
using Speckle.Converter.Navisworks.Helpers;
using Speckle.Converter.Navisworks.Settings;
using Speckle.Converter.Navisworks.ToSpeckle;
using Speckle.Converters.Common;
using Speckle.Objects.Other;
using Speckle.Sdk;
@@ -15,17 +11,12 @@ namespace Speckle.Connector.Navisworks.HostApp;
public class NavisworksMaterialUnpacker(
ILogger<NavisworksMaterialUnpacker> logger,
IConverterSettingsStore<NavisworksConversionSettings> converterSettings,
IElementSelectionService selectionService,
GeometryToSpeckleConverter converter
IElementSelectionService selectionService
)
{
private static T SelectByRepresentationMode<T>(
RepresentationMode mode,
T active,
T permanent,
T original,
T defaultValue
) =>
// 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) =>
mode switch
{
RepresentationMode.Active => active,
@@ -73,87 +64,26 @@ public class NavisworksMaterialUnpacker(
var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject);
var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId;
string hashId = "";
try
{
var item = selectionService.GetModelItemFromPath(finalId);
var comSelection = ComApiBridge.ToInwOpSelection([item]);
try
{
var paths = comSelection.Paths();
try
{
if (paths.Count > 0)
{
var firstPath = paths.OfType<InwOaPath>().FirstOrDefault();
if (firstPath != null)
{
var fragments = firstPath.Fragments();
try
{
if (fragments.Count > 1)
{
var fragmentId = converter.GenerateFragmentId(paths);
hashId = $"{InstanceConstants.GEOMETRY_ID_PREFIX}{fragmentId}";
}
}
finally
{
if (fragments != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragments);
}
}
}
}
}
finally
{
if (paths != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(paths);
}
}
}
finally
{
if (comSelection != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(comSelection);
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{ // If COM interop fails during hash generation, fall back to using finalId
logger.LogWarning(
ex,
"Failed to generate fragment hash ID for item {ItemId}, using finalId as fallback",
finalId
);
hashId = "";
}
var geometry = navisworksObject.Geometry;
var mode = converterSettings.Current.User.VisualRepresentationMode;
using var defaultColor = new NAV.Color(1.0, 1.0, 1.0);
var renderColor = SelectByRepresentationMode(
var renderColor = Select(
mode,
geometry.ActiveColor,
geometry.PermanentColor,
geometry.OriginalColor,
defaultColor
);
var renderTransparency = SelectByRepresentationMode(
var renderTransparency = Select(
mode,
geometry.ActiveTransparency,
geometry.PermanentTransparency,
geometry.OriginalTransparency,
0.0
);
var renderMaterialId = SelectByRepresentationMode(
var renderMaterialId = Select(
mode,
$"{geometry.ActiveColor.GetHashCode()}_{geometry.ActiveTransparency}".GetHashCode(),
$"{geometry.PermanentColor.GetHashCode()}_{geometry.PermanentTransparency}".GetHashCode(),
@@ -162,8 +92,9 @@ public class NavisworksMaterialUnpacker(
);
var materialName =
$"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}";
$"NavisworksMaterial_{Math.Abs(ColorConverter.NavisworksColorToColor(renderColor).ToArgb())}";
// Check Item category for material name
var itemCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Item");
if (itemCategory != null)
{
@@ -175,6 +106,7 @@ public class NavisworksMaterialUnpacker(
}
}
// Check Material category for material name
var materialPropertyCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Material");
if (materialPropertyCategory != null)
{
@@ -188,14 +120,19 @@ public class NavisworksMaterialUnpacker(
if (renderMaterialProxies.TryGetValue(renderMaterialId.ToString(), out RenderMaterialProxy? value))
{
value.objects.Add(!string.IsNullOrEmpty(hashId) ? hashId : finalId);
value.objects.Add(finalId);
}
else
{
renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy()
{
value = CreateRenderMaterial(materialName, renderTransparency, renderColor, renderMaterialId),
objects = [!string.IsNullOrEmpty(hashId) ? hashId : finalId]
value = ConvertRenderColorAndTransparencyToSpeckle(
materialName,
renderTransparency,
renderColor,
renderMaterialId
),
objects = [finalId]
};
}
}
@@ -208,7 +145,7 @@ public class NavisworksMaterialUnpacker(
return renderMaterialProxies.Values.ToList();
}
private static RenderMaterial CreateRenderMaterial(
private static RenderMaterial ConvertRenderColorAndTransparencyToSpeckle(
string name,
double transparency,
NAV.Color navisworksColor,
@@ -219,9 +156,7 @@ public class NavisworksMaterialUnpacker(
var speckleRenderMaterial = new RenderMaterial()
{
name = !string.IsNullOrEmpty(name)
? name
: $"{MaterialConstants.DEFAULT_MATERIAL_NAME_PREFIX}{Math.Abs(color.ToArgb())}",
name = !string.IsNullOrEmpty(name) ? name : $"NavisworksMaterial_{Math.Abs(color.ToArgb())}",
opacity = 1 - transparency,
metalness = 0,
roughness = 1,
@@ -6,7 +6,6 @@ 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;
@@ -26,9 +25,7 @@ public class NavisworksRootObjectBuilder(
ISdkActivityFactory activityFactory,
NavisworksMaterialUnpacker materialUnpacker,
NavisworksColorUnpacker colorUnpacker,
IElementSelectionService elementSelectionService,
IUiUnitsCache uiUnitsCache,
InstanceStoreManager instanceStoreManager
IElementSelectionService elementSelectionService
) : IRootObjectBuilder<NAV.ModelItem>
{
private bool SkipNodeMerging { get; set; }
@@ -43,45 +40,32 @@ public class NavisworksRootObjectBuilder(
)
{
#if DEBUG
SkipNodeMerging = true;
// This is a temporary workaround to disable node merging for debugging purposes - false is default, true is for debugging
SkipNodeMerging = false;
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
// 2. Initialize root collection
var rootCollection = InitializeRootCollection();
(Dictionary<string, Base?> convertedElements, List<SendConversionResult> conversionResults) =
await ConvertModelItemsAsync(navisworksModelItems, projectId, onOperationProgressed, cancellationToken);
// 3. Convert all model items and store results
var (convertedElements, conversionResults) = await ConvertModelItemsAsync(
navisworksModelItems,
projectId,
onOperationProgressed,
cancellationToken
);
ValidateConversionResults(conversionResults);
var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems);
var finalElements = BuildFinalElements(convertedElements, groupedNodes);
List<Base> geometryDefinitions = instanceStoreManager.GetGeometryDefinitions();
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
var geometryDefinitionsCollection = new Collection
{
name = "Geometry Definitions",
["units"] = converterSettings.Current.Derived.SpeckleUnits,
elements = geometryDefinitions
};
var mainElementsCollection = new Collection
{
name = rootCollection.name,
["units"] = converterSettings.Current.Derived.SpeckleUnits,
elements = finalElements
};
rootCollection.elements = [mainElementsCollection];
if (geometryDefinitions.Count > 0)
{
rootCollection.elements.Add(geometryDefinitionsCollection);
}
rootCollection.elements = finalElements;
return new RootObjectBuilderResult(rootCollection, conversionResults);
}
@@ -153,10 +137,12 @@ public class NavisworksRootObjectBuilder(
Dictionary<string, List<NAV.ModelItem>> groupedNodes
)
{
// First build the grouped nodes as before
var finalElements = new List<Base>();
var processedPaths = new HashSet<string>();
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
// If hierarchy mode is enabled, reorganize into proper nested structure
if (converterSettings.Current.User.PreserveModelHierarchy)
{
var hierarchyBuilder = new NavisworksHierarchyBuilder(
@@ -165,9 +151,12 @@ public class NavisworksRootObjectBuilder(
elementSelectionService
);
return hierarchyBuilder.BuildHierarchy();
var hierarchy = hierarchyBuilder.BuildHierarchy();
return hierarchy;
}
// Otherwise continue with flat mode
AddRemainingElements(finalElements, convertedBases, processedPaths);
return finalElements;
}
@@ -224,17 +213,24 @@ public class NavisworksRootObjectBuilder(
}
}
private (string name, string path) GetElementNameAndPath(string applicationId)
private (string name, string path) GetContext(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) = GetElementNameAndPath(cleanParentPath);
(string name, string path) = GetContext(cleanParentPath);
return new NavisworksObject
{
@@ -242,11 +238,16 @@ public class NavisworksRootObjectBuilder(
displayValue = siblingBases.SelectMany(b => b["displayValue"] as List<Base> ?? []).ToList(),
properties = siblingBases.First()["properties"] as Dictionary<string, object?> ?? [],
units = converterSettings.Current.Derived.SpeckleUnits,
applicationId = groupKey,
applicationId = groupKey, // Use the full composite key as applicationId to preserve uniqueness
["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)
@@ -254,16 +255,14 @@ public class NavisworksRootObjectBuilder(
return null;
}
(string name, string path) = GetElementNameAndPath(convertedBase.applicationId);
var units = uiUnitsCache.Ensure();
(string name, string path) = GetContext(convertedBase.applicationId);
return new NavisworksObject
{
name = name,
displayValue = convertedBase["displayValue"] as List<Base> ?? [],
properties = convertedBase["properties"] as Dictionary<string, object?> ?? [],
units = units.ToString(),
units = converterSettings.Current.Derived.SpeckleUnits,
applicationId = convertedBase.applicationId,
["path"] = path
};
@@ -289,16 +288,18 @@ public class NavisworksRootObjectBuilder(
rootCollection[ProxyKeys.COLOR] = colors;
}
var instanceDefinitionProxies = instanceStoreManager.GetInstanceDefinitionProxies();
if (instanceDefinitionProxies.Count > 0)
{
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies.ToList();
}
return Task.CompletedTask;
}
/// <summary>
/// Converts a single Navisworks item to a Speckle object.
/// </summary>
/// <remarks>
/// Attempts to retrieve from cache first.
/// Falls back to fresh conversion if not cached.
/// Logs errors but doesn't throw exceptions.
/// </remarks>
/// <returns>A SendConversionResult indicating success or failure.</returns>
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases,
@@ -191,27 +191,6 @@
"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",
@@ -302,8 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -326,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2022": {
@@ -371,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -383,6 +363,29 @@
"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,27 +191,6 @@
"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",
@@ -302,8 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -326,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2023": {
@@ -371,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -383,6 +363,29 @@
"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,27 +191,6 @@
"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",
@@ -302,8 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -326,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2024": {
@@ -371,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -383,6 +363,29 @@
"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,25 +171,6 @@
"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",
@@ -245,8 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -269,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2025": {
@@ -314,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -326,6 +308,27 @@
"requested": "[2023.0.0, )",
"resolved": "2025.0.0",
"contentHash": "Hwf/3Ydc7KxvjgD9pSZKLSJRsFTsxYg95YyTm6f43hcsGjmk49GsLFQt921Z9OcvUVewOggQHcmBgti+P2EPHw=="
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -164,25 +164,6 @@
"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",
@@ -238,8 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -262,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.revit2026": {
@@ -298,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Revit.API": {
@@ -310,6 +292,27 @@
"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": {
@@ -29,11 +29,9 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private readonly DocumentModelStore _store;
private readonly ICancellationManager _cancellationManager;
private readonly ISendConversionCache _sendConversionCache;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
private readonly ElementUnpacker _elementUnpacker;
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
private readonly RevitToSpeckleCacheSingleton _revitToSpeckleCacheSingleton;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly LinkedModelHandler _linkedModelHandler;
private readonly IThreadContext _threadContext;
@@ -57,7 +55,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
ToSpeckleSettingsManager toSpeckleSettingsManager,
ElementUnpacker elementUnpacker,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
ITopLevelExceptionHandler topLevelExceptionHandler,
LinkedModelHandler linkedModelHandler,
IThreadContext threadContext,
@@ -74,7 +71,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_toSpeckleSettingsManager = toSpeckleSettingsManager;
_elementUnpacker = elementUnpacker;
_revitConversionSettingsFactory = revitConversionSettingsFactory;
_revitToSpeckleCacheSingleton = revitToSpeckleCacheSingleton;
_topLevelExceptionHandler = topLevelExceptionHandler;
_linkedModelHandler = linkedModelHandler;
_threadContext = threadContext;
@@ -94,7 +90,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public List<ISendFilter> GetSendFilters() =>
[
new RevitSelectionFilter { IsDefault = true },
new RevitSelectionFilter() { IsDefault = true },
new RevitViewsFilter(_revitContext),
new RevitCategoriesFilter(_revitContext)
];
@@ -453,7 +449,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private async Task OnDocumentChanged()
{
_sendConversionCache.ClearCache();
_revitToSpeckleCacheSingleton.ClearCache();
if (_cancellationManager.NumberOfOperations > 0)
{
@@ -58,7 +58,6 @@ public static class ServiceRegistration
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
serviceCollection.AddScoped<ElementUnpacker>();
serviceCollection.AddScoped<LevelUnpacker>();
serviceCollection.AddScoped<ViewUnpacker>();
serviceCollection.AddScoped<SendCollectionManager>();
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
@@ -75,6 +74,7 @@ public static class ServiceRegistration
serviceCollection.AddSingleton<RevitUtils>();
serviceCollection.AddSingleton<IFailuresPreprocessor, HideWarningsFailuresPreprocessor>();
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
serviceCollection.AddScoped<LocalToGlobalConverterUtils>();
// operation progress manager
@@ -1,6 +1,5 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Speckle.Converters.RevitShared.Extensions;
namespace Speckle.Connectors.Revit.HostApp;
@@ -24,11 +23,11 @@ public class ElementUnpacker
// Step 1: unpack groups
var atomicObjects = UnpackElements(selectionElements, 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);
// 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);
}
/// <summary>
@@ -109,7 +108,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> RemoveKnownChildElementsWhenParentPresent(List<Element> elements, Document doc)
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document doc)
{
//just used for contains so use ToHashSet
var ids = elements.Select(el => el.Id).ToHashSet();
@@ -132,37 +131,64 @@ 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>
/// Returns element IDs and their known child element IDs for cache tracking.
/// Uses <see cref="ElementExtensions.GetKnownChildrenElements"/> to determine which children to include.
/// 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.
/// </summary>
/// <param name="elements">Elements to process</param>
/// <returns>Flattened list of parent and child element IDs</returns>
/// <param name="elements"></param>
/// <returns></returns>
public List<string> GetElementsAndSubelementIdsFromAtomicObjects(List<Element> elements)
{
var ids = new HashSet<string>();
foreach (var element in elements)
{
// 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())
switch (element)
{
ids.Add(childId.ToString());
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;
}
ids.Add(element.Id.ToString());
}
return ids.ToList();
@@ -34,34 +34,30 @@ public class LevelUnpacker
Dictionary<string, LevelProxy> levelProxies = new();
foreach (var element in elements)
{
// 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))
if (levelProxies.TryGetValue(element.LevelId.ToString(), 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,
displayValue = [],
properties = _propertiesExtractor.GetProperties(level)
properties = new Dictionary<string, object?>() { }
};
var unitSettings = _converterSettings.Current.Document.GetUnits();
var lengthUnitType = unitSettings.GetFormatOptions(SpecTypeId.Length).GetUnitTypeId();
var lengthUnitType = unitSettings.GetFormatOptions(Autodesk.Revit.DB.SpecTypeId.Length).GetUnitTypeId();
levelDataObject["elevation"] = UnitUtils.ConvertFromInternalUnits(level.Elevation, lengthUnitType);
levelDataObject["units"] = _converterSettings.Current.SpeckleUnits;
levelProxies[levelKey] = new LevelProxy()
levelProxies[element.LevelId.ToString()] = new LevelProxy()
{
applicationId = level.UniqueId,
objects = [element.UniqueId],
@@ -120,7 +120,7 @@ internal sealed class RevitDocumentStore : DocumentModelStore
var x = doc.PathName;
if (string.IsNullOrEmpty(x))
{
return doc.Title;
return null;
}
return x;
#endif
@@ -120,14 +120,9 @@ 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 - opacity;
double smoothness = 1 - roughness;
double transparency = 1 - speckleRenderMaterial.opacity;
double smoothness = 1 - speckleRenderMaterial.roughness;
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}");
@@ -135,7 +130,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)(metalness * 128);
revitMaterial.Shininess = (int)(speckleRenderMaterial.metalness * 128);
revitMaterial.Smoothness = (int)(smoothness * 128);
foreach (var objectId in proxy.objects)
@@ -168,30 +163,4 @@ 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;
}
}
@@ -1,87 +0,0 @@
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;
}
}
@@ -66,8 +66,8 @@ public sealed class RevitHostObjectBuilder(
// TODO: formalise getting transform info from rootObject. this dict access is gross.
Autodesk.Revit.DB.Transform? referencePointTransformFromRootObject = null;
if (
rootObject.DynamicPropertyKeys.Contains(RootKeys.REFERENCE_POINT_TRANSFORM)
&& rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] is Dictionary<string, object> transformDict
rootObject.DynamicPropertyKeys.Contains(ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY)
&& rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] is Dictionary<string, object> transformDict
&& transformDict.TryGetValue("transform", out var transformValue)
)
{
@@ -1,7 +1,7 @@
using Autodesk.Revit.DB;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Caching;
// using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
@@ -21,10 +21,9 @@ namespace Speckle.Connectors.Revit.Operations.Send;
public class RevitRootObjectBuilder(
IRootToSpeckleConverter converter,
IConverterSettingsStore<RevitConversionSettings> converterSettings,
ISendConversionCache sendConversionCache,
//ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
LevelUnpacker levelUnpacker,
ViewUnpacker viewUnpacker,
IThreadContext threadContext,
SendCollectionManager sendCollectionManager,
ILogger<RevitRootObjectBuilder> logger,
@@ -42,17 +41,14 @@ public class RevitRootObjectBuilder(
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
);
#pragma warning disable CA1506
#pragma warning disable CA1502
private RootObjectBuilderResult BuildSync(
#pragma warning restore CA1506
#pragma warning restore CA1502
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
Console.WriteLine(projectId);
var doc = converterSettings.Current.Document;
if (doc.IsFamilyDocument)
@@ -60,9 +56,6 @@ public class RevitRootObjectBuilder(
throw new SpeckleException("Family Environment documents are not supported.");
}
// create a new send pipeline
using var sendPipeline = new Speckle.Sdk.Pipeline.Send();
// init the root
Collection rootObject =
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
@@ -138,7 +131,7 @@ public class RevitRootObjectBuilder(
}
var countProgress = 0;
var cacheHitCount = 0;
// var cacheHitCount = 0;
var skippedObjectCount = 0;
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
@@ -191,35 +184,17 @@ public class RevitRootObjectBuilder(
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
bool wasCached = false;
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
{
// TODO: cahce hit
converted = value;
wasCached = true;
cacheHitCount++;
}
// not in cache means we convert
else
{
// if it has a transform we append transform hash to the applicationId to distinguish the elements from other instances
if (hasTransform)
{
string transformHash = linkedModelHandler.GetTransformHash(
atomicObjectByDocumentAndTransform.Transform.NotNull()
);
applicationId = $"{applicationId}_t{transformHash}";
}
// normal conversions
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
}
var reference = sendPipeline.Process(converted).Result; // .Wait(cancellationToken);//.ConfigureAwait(false);
if (!wasCached)
if (hasTransform)
{
sendConversionCache.AppendSendResult(projectId, applicationId, reference);
string transformHash = linkedModelHandler.GetTransformHash(
atomicObjectByDocumentAndTransform.Transform.NotNull()
);
applicationId = $"{applicationId}_t{transformHash}";
}
// normal conversions
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
revitElement,
@@ -228,7 +203,7 @@ public class RevitRootObjectBuilder(
modelDisplayName
);
collection.elements.Add(reference);
collection.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
}
catch (Exception ex) when (!ex.IsFatal())
@@ -256,7 +231,6 @@ 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);
@@ -266,30 +240,17 @@ public class RevitRootObjectBuilder(
var levelProxies = levelUnpacker.Unpack(flatElements);
rootObject[ProxyKeys.LEVEL] = levelProxies;
rootObject[ProxyKeys.INSTANCE_DEFINITION] = revitToSpeckleCacheSingleton.GetInstanceDefinitionProxiesForObjects(
idsAndSubElementIds
rootObject[ProxyKeys.INSTANCE_DEFINITION] =
revitToSpeckleCacheSingleton.InstanceDefinitionProxiesMap.Values.ToList();
rootObject.elements.Add(
new Collection()
{
elements = revitToSpeckleCacheSingleton.InstancedObjects.Values.ToList(),
name = "revitInstancedObjects"
}
);
// NOTE: i might be overdoing things in here, but tldr:
// - all instance objects (meshes) are processed individually
// - process their collection individually, and then attach it to the root collection
// we could, theoretically, just process the collection as a whole (but it can be big?)
// note/ask: do these need to go in the conversion cache? or not?
var instanceObjects = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds);
var instanceReferences = new Collection("revitInstancedObjects");
foreach (var instanceObject in instanceObjects)
{
var referenceInstanceObject = sendPipeline.Process(instanceObject).Result;
instanceReferences.elements.Add(referenceInstanceObject);
}
var instanceReferenceCollection = sendPipeline.Process(instanceReferences).Result;
rootObject.elements.Add(instanceReferenceCollection);
// STEP 6: Unpack all other objects to attach to root collection
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
if (views.Count > 0)
{
rootObject[RootKeys.VIEW] = views;
}
revitToSpeckleCacheSingleton.ClearInstanceProxies();
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
@@ -298,13 +259,9 @@ public class RevitRootObjectBuilder(
if (converterSettings.Current.ReferencePointTransform is Transform transform)
{
var transformMatrix = ReferencePointHelper.CreateTransformDataForRootObject(transform);
rootObject[RootKeys.REFERENCE_POINT_TRANSFORM] = transformMatrix;
rootObject[ReferencePointHelper.REFERENCE_POINT_TRANSFORM_KEY] = transformMatrix;
}
// NOTE: could be
sendPipeline.Process(rootObject).Wait(cancellationToken);
sendPipeline.WaitForUpload().Wait(cancellationToken);
return new RootObjectBuilderResult(new Collection() { name = "ignore" }, results);
return new RootObjectBuilderResult(rootObject, results);
}
}
@@ -24,7 +24,6 @@
<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" />
@@ -61,4 +60,4 @@
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
</ItemGroup>
</Project>
</Project>
@@ -202,27 +202,6 @@
"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",
@@ -346,8 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.logging": {
@@ -357,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino7": {
@@ -402,12 +382,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -29,8 +29,8 @@
<ItemGroup>
<PackageReference Include="GrasshopperAsyncComponent" />
<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="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" />
<PackageReference Include="System.Resources.Extensions" />
</ItemGroup>
@@ -202,27 +202,6 @@
"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",
@@ -346,8 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.logging": {
@@ -357,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino8": {
@@ -401,12 +381,35 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
}
}
@@ -47,11 +47,6 @@ 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));
@@ -75,15 +70,11 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
void GetPathsInternal(SpeckleCollectionWrapper w)
{
currentPath.Add(w.Name);
var subCols = w
.Elements.Where(e => e != null) // skip nulls (CNX-2855)
.OfType<SpeckleCollectionWrapper>()
.ToList();
var subCols = w.Elements.OfType<SpeckleCollectionWrapper>().ToList();
// NOTE: here we're basically outputting only paths that correspond to a collection
// that has values inside of it.
var nonNullElementCount = w.Elements.Count(e => e != null);
if (subCols.Count != nonNullElementCount)
if (subCols.Count != w.Elements.Count)
{
allPaths.Add(string.Join(Constants.LAYER_PATH_DELIMITER, currentPath));
}
@@ -48,7 +48,6 @@ public class CreateCollection : VariableParameterComponentBase
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
var rootCollection = CreateRootCollection();
bool hasAnyInput = false;
foreach (var inputParam in Params.Input)
{
@@ -58,7 +57,6 @@ public class CreateCollection : VariableParameterComponentBase
continue;
}
hasAnyInput = true;
var childCollection = ProcessInputParameter(inputParam, data, rootCollection.Name);
if (childCollection != null)
{
@@ -66,46 +64,12 @@ public class CreateCollection : VariableParameterComponentBase
}
}
// Skip validation if no input provided
if (!hasAnyInput)
{
return;
}
// validate for duplicate application IDs across the entire collection hierarchy
if (HasDuplicateApplicationIds(rootCollection))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The same object(s) cannot appear in multiple collections");
return;
}
// validate collection isn't empty (CNX-2855)
if (rootCollection.Elements.Count == 0 || !rootCollection.Elements.Any(HasAnyValidContent))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
"Collection contains no valid geometry. All input objects are unsupported types."
);
return;
}
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
}
/// <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()
private SpeckleCollectionWrapper CreateRootCollection()
{
return new SpeckleCollectionWrapper
{
Base = new Collection(),
Name = "Unnamed",
@@ -114,6 +78,7 @@ public class CreateCollection : VariableParameterComponentBase
Material = null,
ApplicationId = InstanceGuid.ToString()
};
}
private SpeckleCollectionWrapper? ProcessInputParameter(IGH_Param inputParam, List<IGH_Goo> data, string rootName)
{
@@ -168,10 +133,7 @@ public class CreateCollection : VariableParameterComponentBase
// Check for duplicate names within this collection
foreach (
var subCollectionName in collectionGoo
.Value.Elements.Where(e => e != null) // skip nulls (CNX-2855)
.OfType<SpeckleCollectionWrapper>()
.Select(c => c.Name)
var subCollectionName in collectionGoo.Value.Elements.OfType<SpeckleCollectionWrapper>().Select(c => c.Name)
)
{
if (!duplicateNames.Add(subCollectionName))
@@ -194,8 +156,6 @@ public class CreateCollection : VariableParameterComponentBase
List<string> childPath
)
{
int skippedCount = 0;
foreach (var obj in objects)
{
// handle data objects directly (deep copy to avoid mutations)
@@ -217,68 +177,7 @@ public class CreateCollection : VariableParameterComponentBase
}
else
{
// add null placeholder to preserve topology (CNX-2855)
parentCollection.Elements.Add(null);
skippedCount++;
}
}
// add warning if objects were skipped (CNX-2855)
if (skippedCount > 0)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Warning,
$"Skipped {skippedCount} unsupported object(s) (Leaders, TextDots, Dimensions, etc.)"
);
}
}
/// <summary>
/// Validates that all application IDs are unique across the entire collection hierarchy.
/// Shows an error if duplicates are found, indicating objects appear in multiple collections.
/// </summary>
/// <returns>True if duplicates exist, false if all IDs are unique</returns>
private bool HasDuplicateApplicationIds(SpeckleCollectionWrapper rootCollection)
{
// args to CheckForDuplicateApplicationIds passed in since the method can recursively check
var seenIds = new HashSet<string>();
var duplicateIds = new HashSet<string>();
// iterate, create hash set and check all application IDs
ProcessAndCheckForDuplicateApplicationIds(rootCollection, seenIds, duplicateIds);
return duplicateIds.Count > 0;
}
/// <summary>
/// Recursively collects application IDs from all in the collection hierarchy.
/// </summary>
/// <remarks>
/// Only checks the wrapper's ApplicationId, not for example geometries within DataObjects.
/// </remarks>
private void ProcessAndCheckForDuplicateApplicationIds(
SpeckleCollectionWrapper collection,
HashSet<string> seenIds,
HashSet<string> duplicateIds
)
{
foreach (var element in collection.Elements)
{
switch (element)
{
case null:
break; // skip nulls (CNX-2855)
case SpeckleCollectionWrapper childCollection:
// recurse into child collections
ProcessAndCheckForDuplicateApplicationIds(childCollection, seenIds, duplicateIds);
break;
case SpeckleWrapper wrapper:
if (wrapper.ApplicationId != null && !seenIds.Add(wrapper.ApplicationId))
{
duplicateIds.Add(wrapper.ApplicationId);
}
break;
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"{obj?.GetType().Name} type cannot be added to collections.");
}
}
}
@@ -51,14 +51,11 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
Name = wrapper.Name;
NickName = wrapper.Name;
// Separate objects and collections (skip nulls for non-topology outputs)
// Separate objects and collections
// Note: SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper,
// so it will be included in objects
List<SpeckleWrapper> objects = wrapper.GetAtomicObjects().ToList();
List<SpeckleCollectionWrapper> collections = wrapper
.Elements.Where(e => e != null)
.OfType<SpeckleCollectionWrapper>()
.ToList();
List<SpeckleCollectionWrapper> collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
@@ -72,14 +69,22 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
Access = GH_ParamAccess.list
};
// Don't use topology for _objects output (always list)
// Create appropriate Goo types for each object (downside of the inheritance refactor)
List<IGH_Goo> atomicObjectGoos = objects.Select(obj => obj.CreateGoo()).ToList();
outputParams.Add(new OutputParamWrapper(param, atomicObjectGoos, null));
}
foreach (SpeckleCollectionWrapper childWrapper in collections)
{
var hasInnerCollections = childWrapper.Elements.Where(e => e != null).Any(el => el is SpeckleCollectionWrapper);
/* 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 topology = childWrapper.Topology;
var nickName = childWrapper.Name;
if (nickName.Length > 16)
@@ -106,11 +111,8 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
}
else
{
// 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();
// Create appropriate Goo types for child objects
List<IGH_Goo> childObjectGoos = childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
outputValue = childObjectGoos;
}
@@ -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
@@ -15,7 +15,7 @@ public class TokenUrlComponent : GH_Component
{
public TokenUrlComponent()
: base(
"Speckle Model URL with Token",
"Speckle Model URL",
"URL",
"Create a Speckle model link using URL and developer token",
ComponentCategories.PRIMARY_RIBBON,
@@ -162,7 +162,6 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
Params.RegisterInputParam(param);
}
Params.OnParametersChanged();
ExpireSolution(true);
}
@@ -27,7 +27,8 @@ 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",
@@ -35,6 +36,7 @@ public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterCompon
"Speckle Properties to expand",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
@@ -155,11 +155,10 @@ 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))
{
// include nulls to match topology count (CNX-2855)
var outputGoosWithNulls = targetCollectionWrapper.ToGooListWithNulls();
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoosWithNulls);
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoos);
dataAccess.SetDataTree(i, tree);
}
else
@@ -103,7 +103,7 @@ public class SpeckleDataObjectPassthrough()
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
if (!da.GetDataList(1, inputGeometry) && result == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle DataObject or Geometries");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle DataObject or Geometries.");
return;
}
@@ -111,7 +111,7 @@ public class SpeckleDataObjectPassthrough()
{
if (inputGeo.Value is SpeckleBlockInstanceWrapper)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "DataObjects cannot contain Block Instances");
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"DataObjects cannot contain Block Instances.");
return;
}
}
@@ -158,10 +158,6 @@ public class SpeckleDataObjectPassthrough()
result.Properties = inputProperties;
}
// generate application ID for new data objects. Unlike SpeckleGeometry, DataObject wrappers aren't created
// through casting (which auto-generates IDs), so we must explicitly ensure an ID exists here
result.ApplicationId ??= Guid.NewGuid().ToString();
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
@@ -138,7 +138,7 @@ public class SpeckleGeometryPassthrough()
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Pass in a Speckle Geometry or Geometry");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
return;
}
@@ -47,13 +47,15 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
public bool JustPastedIn { get; set; }
public string LastVersionDate { get; set; }
public string LastInfoMessage { get; set; }
public SpeckleUrlModelResource? UrlModelResource { get; set; }
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
// DI props
public IClient ApiClient { get; private set; }
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
@@ -64,14 +66,6 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
"The model collection of the loaded version",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"properties",
"Model-wide properties from the root collection",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
@@ -130,7 +124,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
{
var autoReceiveMi = Menu_AppendItem(
menu,
"Load new versions automatically",
"Load automatically",
(s, e) =>
{
AutoReceive = !AutoReceive;
@@ -291,7 +285,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
Account? account = urlResource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException("No default account was found");
throw new SpeckleAccountManagerException($"No default account was found");
}
ApiClient?.Dispose();
@@ -344,7 +338,6 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
public Base Root { get; set; }
public SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo Result { get; set; }
public SpecklePropertyGroupGoo? RootProperties { get; private set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance<ReceiveAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
@@ -381,7 +374,6 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
}
da.SetData(0, Result);
da.SetData(1, RootProperties);
}
public override async Task DoWork(Action<string, double> reportProgress, Action done)
@@ -447,89 +439,66 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
try
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
CancellationToken.ThrowIfCancellationRequested();
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
TraversalContextUnpacker traversalContextUnpacker = new();
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// separate atomic objects from block instances
var (atomicObjects, blockInstances) = scope
.Get<RootObjectUnpacker>()
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
// initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(Root as Collection) ?? new Collection { name = "unnamed" }
);
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
foreach (var atomicContext in atomicObjects)
{
Root = await scope
.Get<GrasshopperReceiveOperation>()
.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
CancellationToken.ThrowIfCancellationRequested();
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
if (Root is RootCollection rootCollection && rootCollection.properties.Count > 0)
{
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
}
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
SpeckleConversionContext.SetupCurrent(scope);
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
// separate atomic objects from block instances
var (atomicObjects, blockInstances) = scope
.Get<RootObjectUnpacker>()
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
// initialize unpackers and collection builder (data holders - created with new)
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(Root as Collection) ?? new Collection { name = "unnamed" }
);
// get handler from DI and initialize with per-operation data
var mapHandler = scope
.Get<LocalToGlobalMapHandler>()
.Initialize(
scope.Get<TraversalContextUnpacker>(),
colorUnpacker,
materialUnpacker,
collectionRebuilder,
unpackedRoot.DefinitionProxies
);
// handler deals with two-pass conversion: normal objects first, then DataObjects with InstanceProxies
mapHandler.ConvertAtomicObjects(atomicObjects);
// process block instances using converted atomic objects
// internally filters out InstanceProxies that belong to registered DataObjects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances);
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
RootProperties = rootPropertiesGoo;
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", Parent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add(
"isMultiplayer",
receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id
);
}
await scope
.Get<IMixPanelManager>()
.TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
mapHandler.ConvertAtomicObject(atomicContext);
}
finally
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
SpeckleConversionContext.EndCurrent();
{ "isAsync", true },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) },
{ "auto", Parent.AutoReceive }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != Parent.ApiClient.Account.userInfo.id);
}
await scope.Get<IMixPanelManager>().TrackEvent(MixPanelEvents.Receive, Parent.ApiClient.Account, customProperties);
}
}
@@ -37,7 +37,6 @@ public class ReceiveComponentOutput
/// Made nullable as output can be null when Run = false or on error
/// </remarks>
public SpeckleCollectionWrapperGoo? RootObject { get; set; }
public SpecklePropertyGroupGoo? RootProperties { get; set; }
}
public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInput, ReceiveComponentOutput>
@@ -72,14 +71,6 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
"The model collection of the loaded version",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"properties",
"Model-wide properties from the root collection",
GH_ParamAccess.item
);
}
protected override ReceiveComponentInput GetInput(IGH_DataAccess da)
@@ -115,8 +106,6 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
else
{
da.SetData(0, result.RootObject);
da.SetData(1, result.RootProperties);
Message = _apiClient != null ? "Loaded" : "Done";
}
}
@@ -143,102 +132,88 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
}
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
try
// Do the thing 👇🏼
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
// Do the thing 👇🏼
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException("No default account was found");
}
using var client = clientFactory.Create(account);
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
// store version id for tracking
_lastVersionId = receiveInfo.SelectedVersionId;
var progress = new Progress<CardProgress>(_ =>
{
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
// Message = $"{progress.Status}: {progress.Progress}";
});
var root = await receiveOperation
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
.ConfigureAwait(false);
// extract model-wide root properties (see cnx-2722)
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
if (root is RootCollection rootCollection && rootCollection.properties.Count > 0)
{
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
}
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>
{
{ "isAsync", false },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
}
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
// Setup conversion context BEFORE unpacking (which triggers DataObjectConverter)
SpeckleConversionContext.SetupCurrent(scope);
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
var unpackedRoot = rootObjectUnpacker.Unpack(root);
// split atomic objects from block components before conversion
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
// Initialize unpackers and collection builder (data holders - created with new)
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(root as Collection) ?? new Collection { name = "unnamed" }
);
// get handler from DI and initialize with per-operation data
var mapHandler = scope
.ServiceProvider.GetRequiredService<LocalToGlobalMapHandler>()
.Initialize(
scope.ServiceProvider.GetRequiredService<TraversalContextUnpacker>(),
colorUnpacker,
materialUnpacker,
collectionRebuilder,
unpackedRoot.DefinitionProxies
);
// two-pass conversion: normal objects first, then DataObjects with InstanceProxies
mapHandler.ConvertAtomicObjects(atomicObjects);
// process block instances (internally filters InstanceProxies belonging to registered DataObjects)
mapHandler.ConvertBlockInstances(blockInstances);
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo, RootProperties = rootPropertiesGoo };
throw new SpeckleAccountManagerException($"No default account was found");
}
finally
using var client = clientFactory.Create(account);
var receiveInfo = await input.Resource.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
// store version id for tracking
_lastVersionId = receiveInfo.SelectedVersionId;
var progress = new Progress<CardProgress>(_ =>
{
SpeckleConversionContext.EndCurrent();
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
// Message = $"{progress.Status}: {progress.Progress}";
});
var root = await receiveOperation
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
.ConfigureAwait(false);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object>()
{
{ "isAsync", false },
{ "sourceHostApp", HostApplications.GetSlugFromHostAppNameAndVersion(receiveInfo.SourceApplication) }
};
if (receiveInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", receiveInfo.WorkspaceId);
}
if (receiveInfo.SelectedVersionUserId != null)
{
customProperties.Add("isMultiplayer", receiveInfo.SelectedVersionUserId != client.Account.userInfo.id);
}
var mixpanel = PriorityLoader.Container.GetRequiredService<IMixPanelManager>();
await mixpanel.TrackEvent(MixPanelEvents.Receive, account, customProperties);
// We need to rethink these lovely unpackers, there's a bit too many of 'em
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
var traversalContextUnpacker = new TraversalContextUnpacker();
var unpackedRoot = rootObjectUnpacker.Unpack(root);
// split atomic objects from block components before conversion
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
// Initialize unpackers and collection builder
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(root as Collection) ?? new Collection { name = "unnamed" }
);
// convert atomic objects directly
var mapHandler = new LocalToGlobalMapHandler(
traversalContextUnpacker,
collectionRebuilder,
colorUnpacker,
materialUnpacker
);
foreach (var atomicContext in atomicObjects)
{
mapHandler.ConvertAtomicObject(atomicContext);
}
// process block instances using converted atomic objects
// block processing needs converted objects, but object filtering needs block definitions.
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo };
}
private void SetupSubscription(SpeckleUrlModelResource resource)
@@ -51,7 +51,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
public IClient ApiClient { get; set; }
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
public SpecklePropertyGroupGoo? RootProperties { get; private set; }
public SpeckleUrlModelResource? OutputParam { get; set; }
public bool HasMultipleInputs { get; set; }
@@ -59,10 +58,7 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
// speckle model
pManager.AddParameter(new SpeckleUrlModelResourceParam());
// collection
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Collection",
@@ -72,16 +68,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
);
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
// model-wide props (see cnx-2722)
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"properties",
"Optional model-wide properties to attach to the root collection",
GH_ParamAccess.item
);
pManager[3].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
@@ -294,19 +280,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
string? versionMessage = null;
da.GetData(2, ref versionMessage);
VersionMessage = versionMessage;
SpecklePropertyGroupGoo? rootPropsGoo = null;
da.GetData(3, ref rootPropsGoo);
// validate single properties group
// we can't support a list input here, what does that even mean? grafting the collection to each props entry?? scary.
if (Params.Input[3].VolatileData.DataCount > 1)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one Model Properties group is allowed");
return;
}
RootProperties = rootPropsGoo;
}
}
@@ -422,13 +395,6 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
throw new InvalidOperationException("Root Collection was null");
}
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly - it's only
// constructed here from the Collection + Model Properties inputs.
// if this changes, then we need to update below!
var rootWrapper = new SpeckleRootCollectionWrapper(rootCollectionWrapper.Value, Parent.RootProperties?.Unwrap());
rootCollectionWrapper = new SpeckleRootCollectionWrapperGoo(rootWrapper);
// Step 1 - SEND TO SERVER
var sendInfo = await urlModelResource.GetSendInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
@@ -442,7 +408,7 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
SendOperationResult? result = await sendOperation
.Execute(
new List<SpeckleCollectionWrapperGoo> { rootCollectionWrapper },
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
sendInfo,
Parent.VersionMessage,
progress,
@@ -20,19 +20,12 @@ public class SendComponentInput
public SpeckleUrlModelResource Resource { get; }
public SpeckleCollectionWrapperGoo Input { get; }
public bool Run { get; }
public SpecklePropertyGroupGoo? RootProperties { get; }
public SendComponentInput(
SpeckleUrlModelResource resource,
SpeckleCollectionWrapperGoo input,
bool run,
SpecklePropertyGroupGoo? rootProperties
)
public SendComponentInput(SpeckleUrlModelResource resource, SpeckleCollectionWrapperGoo input, bool run)
{
Resource = resource;
Input = input;
Run = run;
RootProperties = rootProperties;
}
}
@@ -43,11 +36,6 @@ public class SendComponentOutput(SpeckleUrlModelResource? resource)
public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, SendComponentOutput>
{
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
public string? Url { get; private set; }
public string? VersionMessage { get; private set; }
protected override Bitmap Icon => Resources.speckle_operations_syncpublish;
public SendComponent()
: base(
"(Sync) Publish",
@@ -57,12 +45,17 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
ComponentCategories.DEVELOPER
) { }
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
public string? Url { get; private set; }
public string? VersionMessage { get; private set; }
protected override Bitmap Icon => Resources.speckle_operations_syncpublish;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
// speckle model
pManager.AddParameter(new SpeckleUrlModelResourceParam());
// collection
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Collection",
@@ -72,22 +65,13 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
);
pManager.AddTextParameter("Version Message", "versionMessage", "The version message", GH_ParamAccess.item);
pManager[2].Optional = true;
// model-wide props (see cnx-2722)
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"properties",
"Optional model-wide properties to attach to the root collection",
GH_ParamAccess.item
);
pManager[3].Optional = true;
pManager.AddBooleanParameter("Run", "r", "Run the publish operation", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
protected override SendComponentInput GetInput(IGH_DataAccess da)
{
@@ -109,20 +93,10 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
da.GetData(2, ref versionMessage);
VersionMessage = versionMessage;
SpecklePropertyGroupGoo? rootPropsGoo = null;
da.GetData(3, ref rootPropsGoo);
// validate single properties group
// we can't support a list input here, what does that even mean? grafting the collection to each props entry?? scary.
if (Params.Input[3].VolatileData.DataCount > 1)
{
throw new SpeckleException("Only one Model Properties group is allowed");
}
bool run = false;
da.GetData(4, ref run);
da.GetData(3, ref run);
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper, run, rootPropsGoo);
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper, run);
}
protected override void SetOutput(IGH_DataAccess da, SendComponentOutput result)
@@ -147,7 +121,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
{
Menu_AppendSeparator(menu);
Menu_AppendItem(menu, "View created model online ↗", (s, e) => Open(Url));
Menu_AppendItem(menu, $"View created model online ↗", (s, e) => Open(Url));
}
static void Open(string url)
@@ -192,12 +166,6 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
return new(null);
}
// safe to always create new wrapper since users cannot create SpeckleRootCollectionWrapper directly - it's only
// constructed here from the Collection + Model Properties inputs.
// if this changes, then we need to update below!
var rootWrapper = new SpeckleRootCollectionWrapper(input.Input.Value, input.RootProperties?.Unwrap());
var collectionToSend = new SpeckleRootCollectionWrapperGoo(rootWrapper);
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
@@ -205,7 +173,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
Account? account = input.Resource.Account.GetAccount(scope);
if (account is null)
{
throw new SpeckleAccountManagerException("No default account was found");
throw new SpeckleAccountManagerException($"No default account was found");
}
var progress = new Progress<CardProgress>(_ =>
@@ -218,7 +186,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
await sendOperation
.Execute(
new List<SpeckleCollectionWrapperGoo> { collectionToSend },
new List<SpeckleCollectionWrapperGoo>() { input.Input },
sendInfo,
VersionMessage,
progress,
@@ -227,7 +195,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
.ConfigureAwait(false);
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
var customProperties = new Dictionary<string, object> { { "isAsync", false } };
var customProperties = new Dictionary<string, object>() { { "isAsync", false } };
if (sendInfo.WorkspaceId != null)
{
customProperties.Add("workspace_id", sendInfo.WorkspaceId);
@@ -87,7 +87,6 @@ public class SpeckleSelectModelComponent : GH_Component
string? urlInput = null;
// SCENARIO 1: Component has input wire connected
if (da.GetData(0, ref urlInput))
{
UrlInput = urlInput;
@@ -100,11 +99,6 @@ public class SpeckleSelectModelComponent : GH_Component
return;
}
if (_justPastedIn)
{
RestoreAccountFromStoredState();
}
try
{
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
@@ -138,9 +132,22 @@ public class SpeckleSelectModelComponent : GH_Component
_storedUserId = SpeckleOperationWizard.SelectedAccount?.id;
}
if (_justPastedIn)
if (_justPastedIn && _storedUserId != null && !string.IsNullOrEmpty(_storedUserId))
{
RestoreAccountFromStoredState();
try
{
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
}
catch (SpeckleAccountManagerException e)
{
// Swallow and move onto checking server.
Console.WriteLine(e);
}
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
{
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
}
}
// Validate backing data
@@ -389,39 +396,4 @@ public class SpeckleSelectModelComponent : GH_Component
VersionContextMenuButton.ExpirePreview(redraw);
base.ExpirePreview(redraw);
}
/// <summary>
/// Restores the account from stored state when the component is pasted or loaded from file.
/// </summary>
/// <remarks>
/// Attempts to restore account in two stages:
/// <list type="number">
/// <item>First tries to get account by stored user ID</item>
/// <item>If that fails and server url is available, falls back to getting any account matching the server</item>
/// </list>
/// Only executes when <see cref="_justPastedIn"/> is true and <see cref="_storedUserId"/> is not empty.
/// </remarks>
private void RestoreAccountFromStoredState()
{
if (_storedUserId is null || string.IsNullOrEmpty(_storedUserId))
{
return;
}
try
{
SpeckleOperationWizard.SetAccountFromId(_storedUserId);
}
catch (SpeckleAccountManagerException e)
{
Console.WriteLine(e);
}
// Fallback: if account wasn't found by ID but we have a server URL,
// try to find any account matching that server
if (_storedServer != null && SpeckleOperationWizard.SelectedAccount == null)
{
SpeckleOperationWizard.SetAccountFromIdAndUrl(_storedUserId, _storedServer);
}
}
}
@@ -79,40 +79,22 @@ public class SpeckleOperationWizard
var resources = SpeckleResourceBuilder.FromUrlString(input, token);
if (resources.Length == 0)
{
throw new SpeckleException("Input url string was empty");
throw new SpeckleException($"Input url string was empty");
}
if (resources.Length > 1)
{
throw new SpeckleException("Input multi-model url is not supported");
throw new SpeckleException($"Input multi-model url is not supported");
}
var resource = resources.First();
using var scope = PriorityLoader.CreateScopeForActiveDocument();
var urlDerivedAccount = resource.Account.GetAccount(scope);
var account = resource.Account.GetAccount(scope);
SetAccount(account, false);
// if no account is selected, happily go through the url derived account approach
if (SelectedAccount == null)
{
SetAccount(urlDerivedAccount, false);
}
// if we have an account from right-click context-menu, we rely on that and just validate that it's actually applicable to that server
else if (urlDerivedAccount != null && SelectedAccount.serverInfo.url != urlDerivedAccount.serverInfo.url)
{
throw new SpeckleException(
$"Selected account is for '{SelectedAccount.serverInfo.url}' "
+ $"but URL requires '{urlDerivedAccount.serverInfo.url}'"
);
}
// we have both scenarios covered
// Scenario #1 - default account from url
// Scenario #2 - triggered by account switch on right-click context (and validated)
if (SelectedAccount == null)
{
throw new SpeckleException(
$"No appropriate account found for the given '{urlDerivedAccount?.serverInfo.url}' server"
);
throw new SpeckleException("No account found for server URL");
}
IClient client = _clientFactory.Create(SelectedAccount);
@@ -107,14 +107,17 @@ public static class GrasshopperHelpers
M12 = rhinoTransform.M01,
M13 = rhinoTransform.M02,
M14 = rhinoTransform.M03 * conversionFactor,
M21 = rhinoTransform.M10,
M22 = rhinoTransform.M11,
M23 = rhinoTransform.M12,
M24 = rhinoTransform.M13 * conversionFactor,
M31 = rhinoTransform.M20,
M32 = rhinoTransform.M21,
M33 = rhinoTransform.M22,
M34 = rhinoTransform.M23 * conversionFactor,
M41 = rhinoTransform.M30,
M42 = rhinoTransform.M31,
M43 = rhinoTransform.M32,
@@ -136,8 +139,6 @@ public static class GrasshopperHelpers
{
switch (element)
{
case null:
break; // skip nulls (CNX-2855)
case SpeckleDataObjectWrapper dataObject:
yield return dataObject;
break;
@@ -152,7 +153,8 @@ public static class GrasshopperHelpers
yield return subElement;
}
}
break;
default:
break;
}
}
@@ -171,9 +173,11 @@ public static class GrasshopperHelpers
{
return instanceGoo.Value;
}
SpeckleGeometryWrapperGoo objGoo = new();
return objGoo.CastFrom(goo) ? objGoo.Value : null;
else
{
SpeckleGeometryWrapperGoo objGoo = new();
return objGoo.CastFrom(goo) ? objGoo.Value : null;
}
}
/// <summary>
@@ -266,9 +270,7 @@ public static class GrasshopperHelpers
for (int i = 0; i < elCount; i++)
{
var item = subset[subsetCount + i];
// GH_ObjectWrapper accepts null to preserve tree structure for failed conversions (CNX-2855)
tree.EnsurePath(myPath).Add(item != null ? new GH_ObjectWrapper(item) : new GH_ObjectWrapper(null!));
tree.EnsurePath(myPath).Add(new Grasshopper.Kernel.Types.GH_ObjectWrapper(subset[subsetCount + i]));
}
subsetCount += elCount;
@@ -291,13 +293,6 @@ public static class GrasshopperHelpers
{
topology += myPath.ToString(false) + "-" + param.VolatileData.get_Branch(myPath).Count + " ";
}
return topology;
}
/// <summary>
/// Converts Elements to Goo list, converting nulls to GH_ObjectWrapper(null) for topology preservation (CNX-2855)
/// </summary>
public static List<IGH_Goo> ToGooListWithNulls(this SpeckleCollectionWrapper coll) =>
coll.Elements.Select(e => e == null ? new GH_ObjectWrapper(null!) : ((SpeckleWrapper)e).CreateGoo()).ToList();
}
@@ -4,7 +4,6 @@ using Speckle.Connectors.GrasshopperShared.Registration;
using Speckle.Converters.Common;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.GrasshopperShared.HostApp;
@@ -30,13 +29,13 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
}
}
public static void SetupCurrent(IServiceScope? scope = null)
public static void SetupCurrent()
{
if (s_currentContext != null)
{
return;
}
s_scope = scope ?? PriorityLoader.CreateScopeForActiveDocument();
s_scope = PriorityLoader.CreateScopeForActiveDocument();
s_currentContext = s_scope.Get<SpeckleConversionContext>();
}
@@ -51,21 +50,7 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
s_scope = null;
}
public Base? ConvertToSpeckle(object geo)
{
try
{
return speckleConverter.Convert(geo);
}
catch (ConversionException ex)
{
// changed as of CNX-2855
// log for debugging but don't throw - let caller handle null return
// we don't want to throw and fail whole operation, but want a way to signal to component that sumting wong
System.Diagnostics.Debug.WriteLine($"Conversion failed for {geo.GetType().Name}: {ex.Message}");
return null;
}
}
public Base ConvertToSpeckle(object geo) => speckleConverter.Convert(geo);
public List<(object, Base)> ConvertToHost(Base input)
{
@@ -75,7 +60,6 @@ public class SpeckleConversionContext(IRootToSpeckleConverter speckleConverter,
{
GeometryBase geometry => [(geometry, input)],
List<GeometryBase> geometryList => geometryList.Select(o => ((object)o, input)).ToList(),
List<(GeometryBase, Base)> pairList when pairList.Count == 0 => [],
IEnumerable<(GeometryBase, Base)> fallbackConversionResult
=> fallbackConversionResult.Select(o => ((object)o.Item1, o.Item2)).ToList(),
object obj => [(obj, input)],
@@ -1,20 +1,12 @@
using Microsoft.Extensions.Logging;
using Rhino.Geometry;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Converters.Common;
using Speckle.Converters.Common.ToHost;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.GraphTraversal;
using Speckle.Sdk.Models.Instances;
using DataObject = Speckle.Objects.Data.DataObject;
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
/// <summary>
/// Handles conversion of atomic objects from TraversalContexts into Grasshopper wrapper objects.
@@ -27,404 +19,184 @@ namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
internal sealed class LocalToGlobalMapHandler
{
public Dictionary<string, SpeckleGeometryWrapper> ConvertedObjectsMap { get; } = new();
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
// injected via constructor (DI-managed)
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
private readonly ILogger<LocalToGlobalMapHandler> _logger;
private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;
// set via Initialize() method (per-operation data)
private TraversalContextUnpacker _traversalContextUnpacker = null!;
private GrasshopperColorUnpacker _colorUnpacker = null!;
private GrasshopperMaterialUnpacker _materialUnpacker = null!;
private IReadOnlyCollection<InstanceDefinitionProxy>? _definitionProxies;
// auto property (fixes IDE0032)
public GrasshopperCollectionRebuilder CollectionRebuilder { get; private set; } = null!;
private readonly TraversalContextUnpacker _traversalContextUnpacker;
private readonly GrasshopperColorUnpacker _colorUnpacker;
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
public LocalToGlobalMapHandler(
IDataObjectInstanceRegistry dataObjectInstanceRegistry,
ILogger<LocalToGlobalMapHandler> logger,
IConverterSettingsStore<RhinoConversionSettings> settingsStore
)
{
_dataObjectInstanceRegistry = dataObjectInstanceRegistry;
_logger = logger;
_settingsStore = settingsStore;
}
/// <summary>
/// Initializes the handler with per-operation data.
/// Must be called before using ConvertAtomicObjects or ConvertBlockInstances.
/// </summary>
public LocalToGlobalMapHandler Initialize(
TraversalContextUnpacker traversalContextUnpacker,
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker,
GrasshopperCollectionRebuilder collectionRebuilder,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
GrasshopperColorUnpacker colorUnpacker,
GrasshopperMaterialUnpacker materialUnpacker
)
{
_traversalContextUnpacker = traversalContextUnpacker;
_colorUnpacker = colorUnpacker;
_materialUnpacker = materialUnpacker;
CollectionRebuilder = collectionRebuilder;
_definitionProxies = definitionProxies;
return this;
}
/// <summary>
/// Converts all atomic objects in two passes:
/// Pass 1 - Convert normal objects and populate ConvertedObjectsMap
/// Pass 2 - Resolve registered DataObjects with InstanceProxies using the populated map
/// Converts atomic object from TraversalContext to SpeckleObjectWrapper.
/// </summary>
public void ConvertAtomicObjects(IEnumerable<TraversalContext> atomicContexts)
{
// Cache to avoid re-iterating for registered check
var atomicList = atomicContexts as IList<TraversalContext> ?? atomicContexts.ToList();
// Pass 1: Convert all non-registered DataObjects to populate ConvertedObjectsMap
foreach (var atomicContext in atomicList)
{
ConvertObjectToCache(atomicContext);
}
// Pass 2: Process registered DataObjects (definitions now available in ConvertedObjectsMap)
foreach (var atomicContext in atomicList)
{
if (atomicContext.Current is DataObject dataObject)
{
var dataObjectId = dataObject.applicationId ?? dataObject.id;
if (dataObjectId is not null && _dataObjectInstanceRegistry.IsRegistered(dataObjectId))
{
ResolveDataObjectInstanceProxies(atomicContext);
}
}
}
}
/// <summary>
/// Converts and caches an atomic object for later lookup.
/// Skips registered DataObjects (displayValue is InstanceProxy) - they are resolved in ResolveDataObjectInstanceProxies.
/// </summary>
private void ConvertObjectToCache(TraversalContext atomicContext)
public void ConvertAtomicObject(TraversalContext atomicContext)
{
var obj = atomicContext.Current;
var objId = obj.applicationId ?? obj.id;
if (objId is null || ConvertedObjectsMap.ContainsKey(objId))
if (objId == null || ConvertedObjectsMap.ContainsKey(objId))
{
return;
}
// skip registered DataObjects - they'll be processed in second pass
if (obj is DataObject dataObject)
{
var id = dataObject.applicationId ?? dataObject.id.NotNull();
if (_dataObjectInstanceRegistry.IsRegistered(id))
{
return;
}
}
try
{
List<(object, Base)> converted = SpeckleConversionContext.Current.ConvertToHost(obj);
// get path and collection
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
path,
_colorUnpacker,
_materialUnpacker
);
// nothing converted - nothing to do
if (converted.Count == 0)
{
return;
}
// handle normal DataObject (has converted geometry)
if (obj is DataObject normalDataObject)
{
var geometries = ConvertToGeometryWrappers(converted);
var dataObjectWrapper = CreateDataObjectWrapper(normalDataObject, geometries, path, objectCollection);
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
return;
}
// handle normal geometry (not DataObject)
SpecklePropertyGroupGoo propertyGroup = new();
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
foreach ((object convertedObj, Base original) in converted)
{
if (convertedObj is GeometryBase geometryBase)
{
var wrapper = new SpeckleGeometryWrapper()
{
Base = original,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = obj[Constants.NAME_PROP] as string ?? "",
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: null,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: null,
ApplicationId = objId
};
ConvertedObjectsMap[objId] = wrapper;
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{
// don't throw - continue processing other objects
_logger.LogError(ex, "Failed to convert object {objectId} of type {objectType}", objId, obj.speckle_type);
}
}
/// <summary>
/// Resolves a registered DataObject by transforming its InstanceProxy definition objects.
/// Requires definition objects to exist in ConvertedObjectsMap (populated by ConvertObjectToCache).
/// </summary>
private void ResolveDataObjectInstanceProxies(TraversalContext atomicContext)
{
var obj = atomicContext.Current;
if (obj is not DataObject dataObject)
{
return;
}
var dataObjectId = dataObject.applicationId ?? dataObject.id.NotNull();
if (!_dataObjectInstanceRegistry.IsRegistered(dataObjectId))
{
return;
}
try
{
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
// Always create collection - consumed objects will be cleaned up later
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
path,
_colorUnpacker,
_materialUnpacker
);
var entry = _dataObjectInstanceRegistry.GetEntries()[dataObjectId];
var resolvedGeometries = ResolveInstanceProxiesToGeometries(entry.InstanceProxies);
var dataObjectWrapper = CreateDataObjectWrapper(dataObject, resolvedGeometries, path, objectCollection);
if (obj is Speckle.Objects.Data.DataObject dataObject)
{
// get color and mat on dataobject first
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(
dataObject.applicationId ?? "",
out var cachedDataObjColor
)
? cachedDataObjColor
: null;
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
dataObject.applicationId ?? "",
out var cachedDataObjMaterial
)
? cachedDataObjMaterial
: null;
// get geometries
List<SpeckleGeometryWrapper> geometries = new();
foreach ((object convertedObj, Base original) in converted)
{
if (convertedObj is GeometryBase geometryBase)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = original,
GeometryBase = geometryBase,
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: dataObjColor,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: dataObjMat,
};
geometries.Add(wrapper);
}
}
SpecklePropertyGroupGoo propertyGroup = new();
propertyGroup.CastFrom(dataObject.properties);
// remove the displayvalue of the original dataobject since these are now processed and stored on the wrapper
// to prevent storing of duplicate Base
dataObject.displayValue.Clear();
var dataObjectWrapper = new SpeckleDataObjectWrapper()
{
Base = dataObject,
Geometries = geometries,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
Name = dataObject.name,
Properties = propertyGroup,
ApplicationId = dataObject.applicationId,
};
// Add to collections (not to map since these won't be definition objects)
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
}
else
{
SpecklePropertyGroupGoo propertyGroup = new();
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
foreach ((object convertedObj, Base original) in converted)
{
if (convertedObj is GeometryBase geometryBase)
{
var wrapper = new SpeckleGeometryWrapper()
{
Base = original,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = obj[Constants.NAME_PROP] as string ?? "",
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: null,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: null,
ApplicationId = objId
};
// Always add to both map and collections
ConvertedObjectsMap[objId] = wrapper;
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
}
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{
// don't throw - continue processing other DataObjects
_logger.LogError(ex, "Failed to resolve DataObject {dataObjectId} with InstanceProxies", dataObjectId);
// TODO: throw?
}
}
/// <summary>
/// Converts block instances and definitions from traversal contexts into Grasshopper wrapper objects.
/// Automatically filters out InstanceProxies belonging to registered DataObjects.
/// Automatically handles cleanup of consumed objects from the collection hierarchy.
/// </summary>
public void ConvertBlockInstances(IReadOnlyCollection<TraversalContext> blockInstances)
/// <remarks>
/// Deliberately handles both block conversion AND consumed object cleanup in a single operation.
/// Too much, I know, BUT it ensures the cleanup always occurs immediately after block processing without
/// requiring receive components to call a separate cleanup method in the correct order.
/// </remarks>
public void ConvertBlockInstances(
IReadOnlyCollection<TraversalContext> blocks,
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
)
{
// build set of registered InstanceProxy IDs for fast lookup
var registeredProxyIds = new HashSet<string>();
foreach (var entry in _dataObjectInstanceRegistry.GetEntries().Values)
{
foreach (var proxy in entry.InstanceProxies)
{
var proxyId = proxy.applicationId ?? proxy.id;
if (proxyId is not null)
{
registeredProxyIds.Add(proxyId);
}
}
}
// filter out InstanceProxies that belong to registered DataObjects
var filteredBlockInstances = blockInstances
.Where(tc =>
{
if (tc.Current is InstanceProxy proxy)
{
var proxyId = proxy.applicationId ?? proxy.id;
return proxyId is null || !registeredProxyIds.Contains(proxyId);
}
return true;
})
.ToList();
var blockUnpacker = new GrasshopperBlockUnpacker(_traversalContextUnpacker, _colorUnpacker, _materialUnpacker);
// get consumed object IDs from unpacker
// Get consumed object IDs from unpacker
var consumedObjectIds = blockUnpacker.UnpackBlocks(
filteredBlockInstances,
_definitionProxies,
blocks,
definitionProxies,
ConvertedObjectsMap,
CollectionRebuilder
);
// clean up consumed objects from collections
// Clean up consumed objects from collections
CollectionRebuilder.RemoveConsumedObjects(consumedObjectIds);
}
/// <summary>
/// Creates a DataObjectWrapper from a DataObject and its geometries.
/// Handles color/material inheritance and property extraction.
/// </summary>
private SpeckleDataObjectWrapper CreateDataObjectWrapper(
DataObject dataObject,
List<SpeckleGeometryWrapper> geometries,
List<Collection> path,
SpeckleCollectionWrapper objectCollection
)
{
// Get color and material on DataObject
Color? dataObjColor = _colorUnpacker.Cache.TryGetValue(dataObject.applicationId ?? "", out var cachedDataObjColor)
? cachedDataObjColor
: null;
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
dataObject.applicationId ?? "",
out var cachedDataObjMaterial
)
? cachedDataObjMaterial
: null;
// Apply DataObject color/material to geometries that don't have their own
foreach (var geometry in geometries)
{
geometry.Color ??= dataObjColor;
geometry.Material ??= dataObjMat;
}
// Create property group
SpecklePropertyGroupGoo propertyGroup = new();
propertyGroup.CastFrom(dataObject.properties);
// Clear the displayValue to prevent storing duplicate Base
dataObject.displayValue.Clear();
return new SpeckleDataObjectWrapper()
{
Base = dataObject,
Geometries = geometries,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
Name = dataObject.name,
Properties = propertyGroup,
ApplicationId = dataObject.applicationId,
};
}
/// <summary>
/// Resolves InstanceProxy displayValues to transformed geometries.
/// Returns the list of resolved geometries that can be used as DataObject displayValue replacements.
/// </summary>
private List<SpeckleGeometryWrapper> ResolveInstanceProxiesToGeometries(List<InstanceProxy> instanceProxies)
{
var resolvedGeometries = new List<SpeckleGeometryWrapper>();
// build a lookup of definitionId -> definition objects for quick access
var definitionObjectsMap = new Dictionary<string, List<string>>();
if (_definitionProxies is not null)
{
foreach (var defProxy in _definitionProxies)
{
var defId = defProxy.applicationId ?? defProxy.id;
if (defId is not null)
{
definitionObjectsMap[defId] = defProxy.objects;
}
}
}
foreach (var instanceProxy in instanceProxies)
{
// get the definition objects for this instance
if (!definitionObjectsMap.TryGetValue(instanceProxy.definitionId, out var definitionObjectIds))
{
continue; // definition not found, skip this proxy
}
// get transform from the instance proxy
var transform = GrasshopperHelpers.MatrixToTransform(instanceProxy.transform, instanceProxy.units);
// apply transform to each definition object
foreach (var objectId in definitionObjectIds)
{
if (ConvertedObjectsMap.TryGetValue(objectId, out var definitionObject))
{
// deep copy and transform the geometry
var transformedWrapper = definitionObject.DeepCopy();
// transform the GeometryBase
transformedWrapper.GeometryBase.NotNull().Transform(transform);
// keep Base and GeometryBase in sync (CNX-2847)
// Exception shouldn't ever happen for objects in ConvertedObjectsMap
transformedWrapper.Base =
SpeckleConversionContext.Current.ConvertToSpeckle(transformedWrapper.GeometryBase)
?? throw new InvalidOperationException($"Failed to convert transformed geometry for object {objectId}");
// preserve metadata from original Base
transformedWrapper.Base.applicationId = definitionObject.Base.applicationId;
transformedWrapper.Base["units"] = _settingsStore.Current.SpeckleUnits;
resolvedGeometries.Add(transformedWrapper);
}
}
}
return resolvedGeometries;
}
/// <summary>
/// Converts the raw converted objects to SpeckleGeometryWrappers for DataObject display values.
/// Does NOT apply DataObject-level colors/materials - that's handled by CreateDataObjectWrapper.
/// </summary>
private List<SpeckleGeometryWrapper> ConvertToGeometryWrappers(List<(object, Base)> converted)
{
var geometries = new List<SpeckleGeometryWrapper>();
foreach ((object convertedObj, Base original) in converted)
{
if (convertedObj is GeometryBase geometryBase)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = original,
GeometryBase = geometryBase,
// try to get color/material from the individual geometry first
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
? cachedObjColor
: null,
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
? cachedObjMaterial
: null,
};
geometries.Add(wrapper);
}
}
return geometries;
}
}
@@ -35,15 +35,9 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
CancellationToken ct = default
)
{
// create root collection
var rootCollectionGoo = (SpeckleRootCollectionWrapperGoo)input[0].Duplicate();
rootCollectionGoo.Value.Name = "Grasshopper Model";
RootCollection rootCollection =
new(rootCollectionGoo.Value.Name)
{
applicationId = rootCollectionGoo.Value.ApplicationId,
properties = rootCollectionGoo.Value.Properties ?? new()
};
// deep copy input (to not mutate input) and set the input collection name to "Grasshopper Model"
var inputCollectionGoo = (SpeckleCollectionWrapperGoo)input[0].Duplicate();
inputCollectionGoo.Value.Name = "Grasshopper Model";
// create packers for colors and render materials
GrasshopperColorPacker colorPacker = new();
@@ -51,15 +45,15 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
GrasshopperBlockPacker blockPacker = new(_instanceObjectsManager);
// unwrap the input collection to remove all wrappers
Unwrap(rootCollectionGoo.Value, rootCollection, colorPacker, materialPacker, blockPacker);
Collection root = Unwrap(inputCollectionGoo.Value, colorPacker, materialPacker, blockPacker);
// add proxies
rootCollection[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
rootCollection[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
root[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
root[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
root[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
// TODO: Not getting any conversion results yet
var result = new RootObjectBuilderResult(rootCollection, []);
var result = new RootObjectBuilderResult(root, []);
return Task.FromResult(result);
}
@@ -68,47 +62,42 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
// Also packs colors, render materials and block definitions into proxies while unwrapping.
private Collection Unwrap(
SpeckleCollectionWrapper wrapper,
Collection targetCollection,
GrasshopperColorPacker colorPacker,
GrasshopperMaterialPacker materialPacker,
GrasshopperBlockPacker blockPacker
)
{
Collection currentColl = wrapper.Collection;
// unpack color, render material and block definitions
colorPacker.ProcessColor(wrapper.ApplicationId, wrapper.Color);
materialPacker.ProcessMaterial(wrapper.ApplicationId, wrapper.Material);
int skippedNulls = 0;
// iterate through this wrapper's elements to unwrap children
// HashSet<string> collObjectIds = new();
foreach (ISpeckleCollectionObject? element in wrapper.Elements)
foreach (ISpeckleCollectionObject element in wrapper.Elements)
{
switch (element)
{
case null:
// CNX-2855: count skipped nulls and obvs don't add to collection (can't send nulls)
skippedNulls++;
continue;
case SpeckleCollectionWrapper collWrapper:
// create an application id for this collection if none exists. This will be used for color and render material proxies
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
// add to collection and continue unwrap
targetCollection.elements.Add(collWrapper.Collection);
Unwrap(collWrapper, collWrapper.Collection, colorPacker, materialPacker, blockPacker);
currentColl.elements.Add(collWrapper.Collection);
Unwrap(collWrapper, colorPacker, materialPacker, blockPacker);
break;
case SpeckleGeometryWrapper so: // handles both SpeckleObjectWrapper and SpeckleBlockInstanceWrapper (inheritance)
// convert wrapper to base and add to collection - common for all object wrappers
Base objectBase = UnwrapGeometry(so);
string applicationId = objectBase.applicationId!;
targetCollection.elements.Add(objectBase);
currentColl.elements.Add(objectBase);
// do block instance specific stuff (if this object wrapper is actually a block instance)
if (so is SpeckleBlockInstanceWrapper blockInstance)
{
ProcessBlockInstanceDefinition(blockInstance, colorPacker, materialPacker, blockPacker, targetCollection);
ProcessBlockInstanceDefinition(blockInstance, colorPacker, materialPacker, blockPacker, currentColl);
}
// process color and material for all object wrappers (including block instances)
@@ -121,7 +110,7 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
// UnwrapDataObject will unwrap underlying geometry and handle color and material
// arguably doing too much, but I'm apprehensive looping twice without good reason
DataObject dataObject = UnwrapDataObject(dataObjectWrapper, colorPacker, materialPacker);
targetCollection.elements.Add(dataObject);
currentColl.elements.Add(dataObject);
break;
}
}
@@ -138,14 +127,7 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
}
*/
// clear topology when nulls are present - no simple way to preserve tree structure since Collection.elements
// doesn't support nulls on publish. Topology is GH-specific and optional, so clearing it is safe. (CNX-2855)
if (skippedNulls > 0)
{
targetCollection[Constants.TOPOLOGY_PROP] = null;
}
return targetCollection;
return currentColl;
}
/// <summary>
@@ -79,9 +79,6 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
case int i:
Value = i;
return true;
case long l:
Value = l;
return true;
case string s:
Value = s;
return true;
@@ -13,61 +13,49 @@ public partial class SpeckleBlockDefinitionWrapperGoo
switch (source)
{
case InstanceDefinition instanceDefinition:
return TryConvertDefinition(
instanceDefinition.GetObjects().Cast<object>(),
instanceDefinition.Name,
instanceDefinition.Id.ToString()
);
List<SpeckleGeometryWrapper> objects = new();
foreach (var defObj in instanceDefinition.GetObjects())
{
SpeckleGeometryWrapperGoo defObjGoo = new();
if (defObjGoo.CastFrom(defObj))
{
objects.Add(defObjGoo.Value);
}
}
if (objects.Count == 0)
{
return false;
}
SetValueFromDefinitionProps(objects, instanceDefinition.Name, instanceDefinition.Id.ToString());
return true;
case ModelInstanceDefinition modelInstanceDef:
return TryConvertDefinition(
modelInstanceDef.Objects.Cast<object>(),
modelInstanceDef.Name,
modelInstanceDef.Id.ToString()
);
List<SpeckleGeometryWrapper> defObjs = new();
foreach (var defObj in modelInstanceDef.Objects)
{
SpeckleGeometryWrapperGoo geoWrapperGoo = new();
if (geoWrapperGoo.CastFrom(defObj))
{
defObjs.Add(geoWrapperGoo.Value);
}
}
if (defObjs.Count == 0)
{
throw new InvalidOperationException(
$"Block definition '{modelInstanceDef.Name}' did not have any valid geometry."
);
}
SetValueFromDefinitionProps(defObjs, modelInstanceDef.Name, modelInstanceDef.Id.ToString());
return true;
default:
return false;
}
}
/// <summary>
/// Attempts to convert block definition objects to SpeckleGeometryWrappers.
/// Returns false if all objects are unsupported, true if at least one converts.
/// </summary>
private bool TryConvertDefinition(IEnumerable<object> definitionObjects, string definitionName, string definitionId)
{
var objects = definitionObjects as object[] ?? definitionObjects.ToArray();
int totalCount = objects.Length;
List<SpeckleGeometryWrapper> converted = new();
foreach (var defObj in objects)
{
SpeckleGeometryWrapperGoo defObjGoo = new();
if (defObjGoo.CastFrom(defObj))
{
converted.Add(defObjGoo.Value);
}
}
int skippedCount = totalCount - converted.Count;
// return false if nothing converted - Grasshopper handles this as warning (CNX-2855)
if (converted.Count == 0)
{
return false;
}
// show debug info if some objects skipped (CNX-2855)
if (skippedCount > 0)
{
System.Diagnostics.Debug.WriteLine($"Block '{definitionName}' skipped {skippedCount} unsupported object(s)");
}
SetValueFromDefinitionProps(converted, definitionName, definitionId);
return true;
}
private void SetValueFromDefinitionProps(List<SpeckleGeometryWrapper> objs, string name, string id)
{
string validAppId = string.IsNullOrWhiteSpace(id) ? Guid.NewGuid().ToString() : id;
@@ -75,13 +75,7 @@ public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanc
return false;
}
Base? converted = SpeckleConversionContext.Current.ConvertToSpeckle(instance);
if (converted is null)
{
return false; // gh deals with false return from casting as warning 😎
}
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(instance);
Value = new SpeckleBlockInstanceWrapper()
{
GeometryBase = instance,
@@ -51,7 +51,7 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
}
}
public List<ISpeckleCollectionObject?> Elements { get; set; } = new();
public List<ISpeckleCollectionObject> Elements { get; set; } = new();
/// <summary>
/// The Grasshopper Topology of this collection. This setter also sets the "topology" prop dynamically on <see cref="Collection"/>
@@ -92,8 +92,6 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
{
switch (element)
{
case null:
continue; // skip nulls (CNX-2855)
case SpeckleGeometryWrapper o:
o.Path = newPath;
o.Parent = this;
@@ -122,7 +120,6 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
.Select(e =>
e switch
{
null => null, // preserve nulls (CNX-2855)
SpeckleCollectionWrapper c => c.DeepCopy(),
SpeckleBlockInstanceWrapper b => b.DeepCopy(),
SpeckleGeometryWrapper o => o.DeepCopy(),
@@ -159,11 +156,6 @@ public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
// then bake elements in this collection
foreach (var obj in Elements)
{
if (obj is null)
{
continue; // skip nulls (CNX-2855)
}
if (obj is SpeckleGeometryWrapper so)
{
if (bakeObjects)
@@ -113,18 +113,16 @@ public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH
{
foreach (var element in collWrapper.Elements)
{
switch (element)
if (element is SpeckleCollectionWrapper subCollWrapper)
{
case null:
continue; // skip nulls (CNX-2855)
case SpeckleCollectionWrapper subCollWrapper:
FlattenForPreview(subCollWrapper);
break;
case SpeckleGeometryWrapper objWrapper:
_previewObjects.Add(objWrapper);
var box = objWrapper.GeometryBase?.GetBoundingBox(false) ?? new BoundingBox();
_clippingBox.Union(box);
break;
FlattenForPreview(subCollWrapper);
}
if (element is SpeckleGeometryWrapper objWrapper)
{
_previewObjects.Add(objWrapper);
var box = objWrapper.GeometryBase is null ? new() : objWrapper.GeometryBase.GetBoundingBox(false);
_clippingBox.Union(box);
}
}
}
@@ -19,8 +19,8 @@ public partial class SpeckleDataObjectWrapperGoo : GH_Goo<SpeckleDataObjectWrapp
switch (source)
{
case ModelObject modelObject:
var geometryWrapperGoo = new SpeckleGeometryWrapperGoo();
if (geometryWrapperGoo.CastFrom(modelObject))
var geometryWrapperGoo = new SpeckleGeometryWrapperGoo(modelObject);
if (geometryWrapperGoo.IsValid)
{
return CastFromSpeckleGeometryWrapper(geometryWrapperGoo.Value);
}
@@ -192,11 +192,6 @@ public partial class SpeckleDataObjectWrapperGoo : GH_Goo<SpeckleDataObjectWrapp
SpeckleGeometryWrapperGoo geoGoo = new();
if (geoGoo.CastFrom(geometricGoo))
{
// check if the geometry wrapper is valid before using it (CNX-2855)
if (!geoGoo.IsValid)
{
return false;
}
return CastFromSpeckleGeometryWrapper(geoGoo.Value);
}
return false;
@@ -42,12 +42,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
}
GeometryBase geometryBase = geometryGoo.ToGeometryBase();
Base? converted = SpeckleConversionContext.Current.ConvertToSpeckle(geometryBase);
if (converted is null)
{
return false; // gh deals with false return from casting as warning 😎
}
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(geometryBase);
// get layer, props, color, and mat
SpeckleCollectionWrapper? collection = GetLayerCollectionFromModelObject(modelObject);
@@ -60,13 +60,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
return CastFromDataObject(dataObjectWrapper);
case IGH_GeometricGoo geometricGoo:
GeometryBase gb = geometricGoo.ToGeometryBase();
Base? converted = SpeckleConversionContext.Current.ConvertToSpeckle(gb);
if (converted is null)
{
return false; // gh deals with false return from casting as warning 😎
}
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(gb);
string appId = Guid.NewGuid().ToString();
Value = gb is InstanceReferenceGeometry instance
? new SpeckleBlockInstanceWrapper()
@@ -1,42 +0,0 @@
using System.Diagnostics.CodeAnalysis;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public class SpeckleRootCollectionWrapper : SpeckleCollectionWrapper
{
public Dictionary<string, object?>? Properties { get; set; }
public SpeckleRootCollectionWrapper() { }
[SetsRequiredMembers]
public SpeckleRootCollectionWrapper(SpeckleCollectionWrapper wrapper, Dictionary<string, object?>? properties = null)
{
Base = wrapper.Base;
Color = wrapper.Color;
Material = wrapper.Material;
ApplicationId = wrapper.ApplicationId;
Name = wrapper.Name;
Path = wrapper.Path;
Topology = wrapper.Topology;
Elements = wrapper.Elements;
Properties = properties;
}
public new SpeckleRootCollectionWrapper DeepCopy()
{
// delegate most to SpeckleCollectionWrapper and we just copy result
SpeckleCollectionWrapper baseCopy = base.DeepCopy();
return new SpeckleRootCollectionWrapper
{
Base = baseCopy.Base,
Color = baseCopy.Color,
Material = baseCopy.Material,
ApplicationId = baseCopy.ApplicationId,
Name = baseCopy.Name,
Path = baseCopy.Path,
Topology = baseCopy.Topology,
Elements = baseCopy.Elements,
Properties = Properties != null ? new Dictionary<string, object?>(Properties) : null
};
}
}
@@ -1,20 +0,0 @@
using Grasshopper.Kernel.Types;
namespace Speckle.Connectors.GrasshopperShared.Parameters;
public class SpeckleRootCollectionWrapperGoo : SpeckleCollectionWrapperGoo
{
public new SpeckleRootCollectionWrapper Value { get; set; }
public SpeckleRootCollectionWrapperGoo() { }
public SpeckleRootCollectionWrapperGoo(SpeckleRootCollectionWrapper value)
: base(value)
{
Value = value;
}
public override IGH_Goo Duplicate() => new SpeckleRootCollectionWrapperGoo(Value.DeepCopy());
public override string ToString() => Value?.ToString() ?? "Invalid Root Collection";
}
@@ -15,7 +15,6 @@ using Speckle.Connectors.GrasshopperShared.Operations.Send;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Converters.Common;
using Speckle.Converters.Common.ToHost;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Models.GraphTraversal;
@@ -59,8 +58,6 @@ public class PriorityLoader : GH_AssemblyPriority
services.AddTransient<GrasshopperReceiveOperation>();
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
services.AddTransient<TraversalContextUnpacker>();
services.AddScoped<IDataObjectInstanceRegistry, DataObjectInstanceRegistry>();
services.AddTransient<LocalToGlobalMapHandler>();
// send
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
@@ -90,8 +90,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleDataObjectWrapperParam.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperGoo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperParam.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleRootCollectionWrapper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleRootCollectionWrapperGoo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleWrapper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperGoo.ModelObjects.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapper.cs" />
@@ -8,7 +8,6 @@
<StartProgram>$(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe</StartProgram>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWpf>true</UseWpf>
<PlatformTarget>x64</PlatformTarget>
<UseWindowsForms>true</UseWindowsForms>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
</PropertyGroup>
@@ -183,27 +183,6 @@
"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",
@@ -327,8 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -361,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino7": {
@@ -421,13 +401,36 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
"requested": "[9.0.4, )",
@@ -7,15 +7,14 @@
<TargetExt>.rhp</TargetExt>
<StartProgram>$(ProgramFiles)\Rhino $(RhinoVersion)\System\Rhino.exe</StartProgram>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<PlatformTarget>x64</PlatformTarget>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.9.24194.18121"/>
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
@@ -183,27 +183,6 @@
"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",
@@ -327,8 +306,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -361,7 +341,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino8": {
@@ -420,13 +400,36 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
},
"System.Resources.Extensions": {
"type": "CentralTransitive",
"requested": "[9.0.4, )",
@@ -19,8 +19,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all"/>
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all"/>
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
</ItemGroup>
<ItemGroup>
@@ -29,20 +29,20 @@
},
"RhinoCommon": {
"type": "Direct",
"requested": "[8.25.25328.11001, )",
"resolved": "8.25.25328.11001",
"contentHash": "PDKR9GwqyUXUkTulV4J0dzDIf/aWqSJkL7nkS8ReAx8xhnt/+RQpE8gTjOSCmkSU2tjG6WzclowbTxwMTU7VAA==",
"requested": "[8.21.25188.17001, )",
"resolved": "8.21.25188.17001",
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
"dependencies": {
"System.Drawing.Common": "7.0.0"
}
},
"RhinoWindows": {
"type": "Direct",
"requested": "[8.25.25328.11001, )",
"resolved": "8.25.25328.11001",
"contentHash": "I/+++piwtYTue+iAAQqcMF5QlontqwNnC7Leyhiv2FiF8JpAl6K44ZsJqB7ZEUC6ns0LDfa3mbFzQwUfHwYumQ==",
"requested": "[8.21.25188.17001, )",
"resolved": "8.21.25188.17001",
"contentHash": "9zqCorcLRBeiW/j1RTwUS4E7bnZetAdA9WDdtd/AQccjOpxdtw76wdN+ciyQ6qslseWkwZ9qSBeh7QaM800Ntw==",
"dependencies": {
"RhinoCommon": "[8.25.25328.11001]"
"RhinoCommon": "[8.21.25188.17001]"
}
},
"Speckle.InterfaceGenerator": {
@@ -172,25 +172,6 @@
"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",
@@ -254,8 +235,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
}
},
"speckle.connectors.dui": {
@@ -279,7 +261,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.11.1, )"
"Speckle.Objects": "[3.5.4, )"
}
},
"speckle.converters.rhino8": {
@@ -329,12 +311,33 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.11.1, )",
"resolved": "3.11.1",
"contentHash": "JUCY3bA6Pa+fa6wZV9uQ9mhLRihvICkF58nIr28Yi94j0th7wSg4l8WeThl3ubKVnHDQE5mdVffVlY1e5ZUkuQ==",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"dependencies": {
"Speckle.Sdk": "3.11.1"
"Speckle.Sdk": "3.5.4"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -83,7 +83,7 @@ public sealed class RhinoSendBinding : ISendBinding
_sendOperationManagerFactory = sendOperationManagerFactory;
_rhinoLayerHelper = rhinoLayerHelper;
Commands = new SendBindingUICommands(parent); // POC: Commands are tightly coupled with their bindings, at least for now, saves us injecting a factory.
PreviousUnitSystem = RhinoDoc.ActiveDoc?.ModelUnitSystem ?? UnitSystem.None;
PreviousUnitSystem = RhinoDoc.ActiveDoc.ModelUnitSystem;
SubscribeToRhinoEvents();
}
@@ -1,87 +0,0 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Rhino.Extensions;
using Speckle.Converters.Common;
using Speckle.Converters.Common.ToHost;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
namespace Speckle.Connectors.Rhino.HostApp;
/// <summary>
/// Groups block instances created from DataObject with InstanceProxies as display values and applies DataObject metadata.
/// </summary>
public class DataObjectInstanceGrouper
{
private readonly IConverterSettingsStore<RhinoConversionSettings> _converterSettings;
private readonly ILogger<DataObjectInstanceGrouper> _logger;
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
public DataObjectInstanceGrouper(
IConverterSettingsStore<RhinoConversionSettings> converterSettings,
ILogger<DataObjectInstanceGrouper> logger,
IDataObjectInstanceRegistry dataObjectInstanceRegistry
)
{
_converterSettings = converterSettings;
_logger = logger;
_dataObjectInstanceRegistry = dataObjectInstanceRegistry;
}
/// <summary>
/// After all instances have been created, we then run through the data object instance registry to see which instances
/// belonged to a data object. The method then groups all instances to "re-assemble" the original data object and
/// applies the properties of the data object on to the instances.
/// </summary>
/// <remarks>
/// This is a deferred action and can only occur once the RhinoInstanceBaker has done its thing.
/// </remarks>
public void GroupAndApplyProperties()
{
var doc = _converterSettings.Current.Document;
var entries = _dataObjectInstanceRegistry.GetEntries(); // see docstring
foreach (var kvp in entries)
{
var dataObjectId = kvp.Key;
var entry = kvp.Value;
try
{
var instanceIds = _dataObjectInstanceRegistry.GetInstanceIdsForDataObject(dataObjectId);
if (instanceIds.Count == 0)
{
continue;
}
// create group, name the group and apply properties
using var dataObjectAtts = entry.DataObject.GetAttributes();
var groupName = dataObjectAtts.Name;
var groupIndex = doc.Groups.Add(groupName, instanceIds.Select(id => new Guid(id)));
if (groupIndex >= 0)
{
// apply properties to each instance (doing this on an instance level because setting to group doesn't work)
foreach (var instanceId in instanceIds)
{
var rhinoObj = doc.Objects.FindId(new Guid(instanceId));
if (rhinoObj != null)
{
// set the name from DataObject
rhinoObj.Attributes.Name = dataObjectAtts.Name;
// copy all user strings
foreach (var key in dataObjectAtts.GetUserStrings().AllKeys)
{
rhinoObj.Attributes.SetUserString(key, dataObjectAtts.GetUserString(key));
}
rhinoObj.CommitChanges();
}
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to group DataObject instances {dataObjectId}", dataObjectId);
}
}
}
}
@@ -6,7 +6,6 @@ using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Rhino.Extensions;
using Speckle.Converters.Common.ToHost;
using Speckle.DoubleNumerics;
using Speckle.Sdk;
using Speckle.Sdk.Common;
@@ -23,21 +22,18 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
private readonly RhinoLayerBaker _layerBaker;
private readonly RhinoColorBaker _colorBaker;
private readonly ILogger<RhinoInstanceBaker> _logger;
private readonly IDataObjectInstanceRegistry _dataObjectInstanceRegistry;
public RhinoInstanceBaker(
RhinoLayerBaker layerBaker,
RhinoMaterialBaker rhinoMaterialBaker,
RhinoColorBaker colorBaker,
ILogger<RhinoInstanceBaker> logger,
IDataObjectInstanceRegistry dataObjectInstanceRegistry
ILogger<RhinoInstanceBaker> logger
)
{
_layerBaker = layerBaker;
_materialBaker = rhinoMaterialBaker;
_colorBaker = colorBaker;
_logger = logger;
_dataObjectInstanceRegistry = dataObjectInstanceRegistry;
}
/// <summary>
@@ -108,10 +104,10 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
attributes
);
// POC: check on defIndex -1, means we haven't created anything - this is most likely an unrecoverable error at this stage
// POC: check on defIndex -1, means we haven't created anything - this is most likely an recoverable error at this stage
if (defIndex == -1)
{
throw new ConversionException("Failed to create an instance definition object.");
throw new ConversionException("Failed to create an instance defintion object.");
}
if (definitionProxy.applicationId != null)
@@ -159,9 +155,6 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
applicationIdMap[instanceProxyId] = new List<string>() { id.ToString() };
createdObjectIds.Add(id.ToString());
conversionResults.Add(new(Status.SUCCESS, instanceProxy, id.ToString(), "Instance (Block)"));
// link this baked instance back to its DataObject if it came from one (the method handles the check)
_dataObjectInstanceRegistry.LinkInstanceToDataObject(instanceProxyId, id.ToString());
}
}
catch (Exception ex) when (!ex.IsFatal())
@@ -177,13 +170,9 @@ public class RhinoInstanceBaker : IInstanceBaker<IReadOnlyCollection<string>>
public void PurgeInstances(string namePrefix)
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
// clean name prefix to match how block names are created
var cleanedPrefix = RhinoUtils.CleanBlockDefinitionName(namePrefix);
foreach (var definition in currentDoc.InstanceDefinitions)
{
if (!definition.IsDeleted && definition.Name.Contains(cleanedPrefix))
if (!definition.IsDeleted && definition.Name.Contains(namePrefix))
{
currentDoc.InstanceDefinitions.Delete(definition.Index, true, false);
}
@@ -123,12 +123,6 @@ public class RhinoLayerBaker : TraversalContextUnpacker
}
var cleanNewLayerName = RhinoUtils.CleanLayerName(collection.name);
if (!ModelComponent.IsValidComponentName(cleanNewLayerName))
{
throw new SpeckleException($"Layer name '{currentLayerName}' is not valid");
}
Layer newLayer = new() { Name = cleanNewLayerName, ParentLayerId = previousLayer?.Id ?? Guid.Empty };
// set material
@@ -156,7 +150,7 @@ public class RhinoLayerBaker : TraversalContextUnpacker
int index = currentDocument.Layers.Add(newLayer);
if (index == -1)
{
throw new SpeckleException($"Could not create layer '{currentLayerName}'");
throw new SpeckleException($"Could not create layer '{currentLayerName}'.");
}
_hostLayerCache.Add(currentLayerName, index);

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