Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79a6c42cc7 | |||
| e2318df87a | |||
| 78d7814351 | |||
| 17a320ee53 | |||
| 396ef981ee | |||
| 763c413871 | |||
| 57a5b41ec1 | |||
| 8385532b96 | |||
| e109515852 | |||
| fdf2425ec6 | |||
| c067cf6f91 | |||
| 60e26d85c6 |
@@ -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') }}
|
||||
|
||||
@@ -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@v5
|
||||
with:
|
||||
name: output-${{ env.SEMVER }}
|
||||
path: output/*.*
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -312,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -356,12 +336,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -312,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -356,12 +336,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -313,7 +293,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,25 +155,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -262,7 +244,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -306,12 +288,33 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -155,25 +155,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -262,7 +244,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -306,12 +288,33 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+1
-3
@@ -145,9 +145,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
|
||||
Commands,
|
||||
modelCardId,
|
||||
(sp, card) => InitializeSettings(sp),
|
||||
card => Application.DocumentManager.CurrentDocument.GetObjects(card.SendFilter.NotNull().RefreshObjectIds()),
|
||||
Application.DocumentManager.CurrentDocument.Name,
|
||||
null
|
||||
card => Application.DocumentManager.CurrentDocument.GetObjects(card.SendFilter.NotNull().RefreshObjectIds())
|
||||
);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -178,27 +178,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -322,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -366,12 +346,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,27 +178,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -322,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -366,12 +346,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,27 +178,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -322,7 +302,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -366,12 +346,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,25 +164,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -272,7 +254,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -316,12 +298,33 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -164,25 +164,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -272,7 +254,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -316,12 +298,33 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.CSiShared.HostApp;
|
||||
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
|
||||
@@ -56,7 +55,6 @@ public sealed class CsiSharedSendBinding : ISendBinding
|
||||
public async Task Send(string modelCardId)
|
||||
{
|
||||
using var manager = _sendOperationManagerFactory.Create();
|
||||
var (fileName, fileSizeBytes) = GetFileInfo();
|
||||
await manager.Process(
|
||||
Commands,
|
||||
modelCardId,
|
||||
@@ -71,24 +69,10 @@ public sealed class CsiSharedSendBinding : ISendBinding
|
||||
)
|
||||
);
|
||||
},
|
||||
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList(),
|
||||
fileName,
|
||||
fileSizeBytes
|
||||
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList()
|
||||
);
|
||||
}
|
||||
|
||||
private (string? fileName, long? fileBytes) GetFileInfo()
|
||||
{
|
||||
string fullPath = _csiApplicationService.SapModel.GetModelFilename(true);
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(fullPath);
|
||||
return (fileInfo.Name, fileInfo.Length);
|
||||
}
|
||||
|
||||
private ICsiWrapper DecodeObjectIdentifier(string encodedId)
|
||||
{
|
||||
var (type, name) = ObjectIdentifier.Decode(encodedId);
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs21": {
|
||||
@@ -355,12 +335,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,25 +155,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -254,7 +236,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs22": {
|
||||
@@ -304,12 +286,33 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2020": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2021": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2022": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2023": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -169,27 +169,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -305,7 +285,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2024": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -175,27 +175,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -311,7 +291,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2025": {
|
||||
@@ -357,12 +337,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
@@ -184,27 +184,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -312,7 +292,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2026": {
|
||||
@@ -359,12 +339,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+25
-15
@@ -6,22 +6,32 @@ using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Bindings;
|
||||
|
||||
public class NavisworksBasicConnectorBinding(
|
||||
IBrowserBridge parent,
|
||||
DocumentModelStore store,
|
||||
ISpeckleApplication speckleApplication
|
||||
) : IBasicConnectorBinding
|
||||
public class NavisworksBasicConnectorBinding : IBasicConnectorBinding
|
||||
{
|
||||
public string Name => "baseBinding";
|
||||
public IBrowserBridge Parent { get; } = parent;
|
||||
public IBrowserBridge Parent { get; }
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
|
||||
public BasicConnectorBindingCommands Commands { get; } = new(parent);
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
|
||||
public string GetSourceApplicationName() => speckleApplication.Slug;
|
||||
public NavisworksBasicConnectorBinding(
|
||||
IBrowserBridge parent,
|
||||
DocumentModelStore store,
|
||||
ISpeckleApplication speckleApplication
|
||||
)
|
||||
{
|
||||
Parent = parent;
|
||||
_store = store;
|
||||
_speckleApplication = speckleApplication;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
}
|
||||
|
||||
public string GetSourceApplicationVersion() => speckleApplication.HostApplicationVersion;
|
||||
public string GetSourceApplicationName() => _speckleApplication.Slug;
|
||||
|
||||
public string GetConnectorVersion() => speckleApplication.SpeckleVersion;
|
||||
public string GetSourceApplicationVersion() => _speckleApplication.HostApplicationVersion;
|
||||
|
||||
public string GetConnectorVersion() => _speckleApplication.SpeckleVersion;
|
||||
|
||||
public DocumentInfo? GetDocumentInfo() =>
|
||||
NavisworksApp.ActiveDocument is null || NavisworksApp.ActiveDocument.Models.Count == 0
|
||||
@@ -32,15 +42,15 @@ public class NavisworksBasicConnectorBinding(
|
||||
NavisworksApp.ActiveDocument.GetHashCode().ToString()
|
||||
);
|
||||
|
||||
public DocumentModelStore GetDocumentState() => store;
|
||||
public DocumentModelStore GetDocumentState() => _store;
|
||||
|
||||
public void AddModel(ModelCard model) => store.AddModel(model);
|
||||
public void AddModel(ModelCard model) => _store.AddModel(model);
|
||||
|
||||
public void UpdateModel(ModelCard model) => store.UpdateModel(model);
|
||||
public void UpdateModel(ModelCard model) => _store.UpdateModel(model);
|
||||
|
||||
public void RemoveModel(ModelCard model) => store.RemoveModel(model);
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => store.RemoveModels(models);
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
|
||||
public Task HighlightModel(string modelCardId) => Task.CompletedTask;
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Bindings;
|
||||
|
||||
|
||||
+12
-84
@@ -1,5 +1,3 @@
|
||||
using System.IO;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
using Speckle.Connector.Navisworks.Operations.Send.Settings;
|
||||
@@ -14,7 +12,6 @@ using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -61,12 +58,12 @@ public class NavisworksSendBinding : ISendBinding
|
||||
|
||||
private static void SubscribeToNavisworksEvents() { }
|
||||
|
||||
// WARNING: Changes to filter behavior here must match everywhere filters are used, or saved sets won't update correctly
|
||||
// Do not change the behavior/scope of this class on send binding unless make sure the behavior is same. Otherwise, we might not be able to update list of saved sets.
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new NavisworksSelectionFilter() { IsDefault = true },
|
||||
new NavisworksSavedSetsFilter(new ConnectorElementSelectionService()),
|
||||
new NavisworksSavedViewsFilter(new ConnectorElementSelectionService())
|
||||
new NavisworksSavedSetsFilter(new ElementSelectionService()),
|
||||
new NavisworksSavedViewsFilter(new ElementSelectionService())
|
||||
];
|
||||
|
||||
public List<ICardSetting> GetSendSettings() =>
|
||||
@@ -85,27 +82,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
private async Task SendInternal(string modelCardId)
|
||||
{
|
||||
using var manager = _sendOperationManagerFactory.Create();
|
||||
var (fileName, fileSizeBytes) = GetFileInfo();
|
||||
await manager.Process(
|
||||
Commands,
|
||||
modelCardId,
|
||||
InitializeConverterSettings,
|
||||
GetNavisworksModelItems,
|
||||
fileName,
|
||||
fileSizeBytes
|
||||
);
|
||||
}
|
||||
|
||||
private (string? fileName, long? fileSizeBytes) GetFileInfo()
|
||||
{
|
||||
Document? activeDoc = NavisworksApp.ActiveDocument;
|
||||
if (activeDoc is null || !File.Exists(activeDoc.FileName))
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
FileInfo fileInfo = new(activeDoc.FileName);
|
||||
return (fileInfo.Name, fileInfo.Length);
|
||||
await manager.Process(Commands, modelCardId, InitializeConverterSettings, GetNavisworksModelItems);
|
||||
}
|
||||
|
||||
private void InitializeConverterSettings(IServiceProvider serviceProvider, SenderModelCard modelCard) =>
|
||||
@@ -128,7 +105,6 @@ public class NavisworksSendBinding : ISendBinding
|
||||
)
|
||||
{
|
||||
var selectedPaths = modelCard.SendFilter.NotNull().RefreshObjectIds();
|
||||
|
||||
var convertHiddenElementsSetting =
|
||||
modelCard.Settings!.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool? ?? false;
|
||||
var message = convertHiddenElementsSetting
|
||||
@@ -139,78 +115,30 @@ public class NavisworksSendBinding : ISendBinding
|
||||
{
|
||||
throw new SpeckleSendFilterException(message);
|
||||
}
|
||||
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", null));
|
||||
await Task.CompletedTask;
|
||||
|
||||
int estimatedCapacity = selectedPaths.Count * 10;
|
||||
var modelItems = new List<NAV.ModelItem>(estimatedCapacity);
|
||||
var modelItems = new List<NAV.ModelItem>();
|
||||
double count = 0;
|
||||
|
||||
foreach (var path in selectedPaths)
|
||||
{
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", count / selectedPaths.Count));
|
||||
await Task.CompletedTask;
|
||||
|
||||
var modelItem = _selectionService.GetModelItemFromPath(path);
|
||||
var hasChildren = modelItem.Children.Any();
|
||||
|
||||
if (hasChildren)
|
||||
{
|
||||
int nodesVisited = 0;
|
||||
int hiddenBranchesPruned = 0;
|
||||
const int REPORT_INTERVAL = 1000;
|
||||
|
||||
void TraverseWithProgress(NAV.ModelItem node)
|
||||
{
|
||||
nodesVisited++;
|
||||
|
||||
if (nodesVisited % REPORT_INTERVAL == 0)
|
||||
{
|
||||
onOperationProgressed.Report(
|
||||
new CardProgress(
|
||||
$"Expanding tree: {nodesVisited} visited, {modelItems.Count} with geometry, {hiddenBranchesPruned} hidden",
|
||||
null
|
||||
)
|
||||
);
|
||||
Task.Delay(1).Wait();
|
||||
}
|
||||
|
||||
if (!_selectionService.IsVisible(node))
|
||||
{
|
||||
hiddenBranchesPruned++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.HasGeometry)
|
||||
{
|
||||
modelItems.Add(node);
|
||||
}
|
||||
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
TraverseWithProgress(child);
|
||||
}
|
||||
}
|
||||
|
||||
TraverseWithProgress(modelItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (modelItem.HasGeometry && _selectionService.IsVisible(modelItem))
|
||||
{
|
||||
modelItems.Add(modelItem);
|
||||
}
|
||||
}
|
||||
|
||||
modelItems.AddRange(_selectionService.GetGeometryNodes(modelItem).Where(_selectionService.IsVisible));
|
||||
count++;
|
||||
}
|
||||
|
||||
return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems;
|
||||
}
|
||||
|
||||
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
|
||||
/// <summary>
|
||||
/// Cancels all outstanding send operations for the current document.
|
||||
/// This method is called when the active document changes, to ensure
|
||||
/// that any in-progress send operations are properly canceled before
|
||||
/// the new document is loaded.
|
||||
/// </summary>
|
||||
public void CancelAllSendOperations()
|
||||
{
|
||||
foreach (var modelCardId in _store.GetSenders().Select(m => m.ModelCardId))
|
||||
|
||||
+5
-8
@@ -15,8 +15,8 @@ using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Converter.Navisworks.Constants.Registers;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
@@ -53,6 +53,9 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddScoped<NavisworksMaterialUnpacker>();
|
||||
serviceCollection.AddScoped<NavisworksColorUnpacker>();
|
||||
|
||||
// Register dual shared geometry stores for instancing pattern
|
||||
serviceCollection.AddScoped<InstanceStoreManager>();
|
||||
|
||||
serviceCollection.AddSingleton<IAppIdleManager, NavisworksIdleManager>();
|
||||
|
||||
// Sending operations
|
||||
@@ -61,9 +64,6 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
serviceCollection.AddSingleton<IOperationProgressManager, OperationProgressManager>();
|
||||
|
||||
// Registers and caches
|
||||
serviceCollection.AddScoped<IInstanceFragmentRegistry, InstanceFragmentRegistry>();
|
||||
|
||||
// Register Intercom/interop
|
||||
serviceCollection.AddSingleton<NavisworksDocumentModelStore>();
|
||||
serviceCollection.AddSingleton<DocumentModelStore>(sp => sp.GetRequiredService<NavisworksDocumentModelStore>());
|
||||
@@ -73,9 +73,6 @@ public static class NavisworksConnectorServiceRegistration
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSelectionFilter>();
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSavedSetsFilter>();
|
||||
serviceCollection.AddScoped<ISendFilter, NavisworksSavedViewsFilter>();
|
||||
serviceCollection.AddScoped<
|
||||
Converter.Navisworks.Services.IElementSelectionService,
|
||||
ConnectorElementSelectionService
|
||||
>();
|
||||
serviceCollection.AddScoped<IElementSelectionService, ElementSelectionService>();
|
||||
}
|
||||
}
|
||||
|
||||
+28
-9
@@ -1,12 +1,31 @@
|
||||
namespace Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Connector-specific element selection service that extends the converter's base implementation.
|
||||
/// Inherits the cached visibility checking and path resolution from the converter layer.
|
||||
/// </summary>
|
||||
public class ConnectorElementSelectionService : Converter.Navisworks.Services.ElementSelectionService
|
||||
namespace Speckle.Connector.Navisworks.Services;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ElementSelectionService : IElementSelectionService
|
||||
{
|
||||
// This inherits all functionality from the converter's ElementSelectionService
|
||||
// including cached IsVisible, GetModelItemPath, GetModelItemFromPath, and GetGeometryNodes
|
||||
// Connector-specific extensions can be added here if needed in the future
|
||||
private readonly Dictionary<Guid, bool> _visibleCache = new();
|
||||
|
||||
public string GetModelItemPath(NAV.ModelItem modelItem) => ResolveModelItemToIndexPath(modelItem);
|
||||
|
||||
public NAV.ModelItem GetModelItemFromPath(string path) => ResolveIndexPathToModelItem(path);
|
||||
|
||||
public bool IsVisible(NAV.ModelItem modelItem)
|
||||
{
|
||||
var key = modelItem.InstanceGuid;
|
||||
if (_visibleCache.TryGetValue(key, out var isVisible))
|
||||
{
|
||||
return isVisible;
|
||||
}
|
||||
//same as ElementSelectionHelper.IsElementVisible
|
||||
foreach (var item in modelItem.AncestorsAndSelf)
|
||||
{
|
||||
_visibleCache[item.InstanceGuid] = !item.IsHidden;
|
||||
}
|
||||
return _visibleCache[key];
|
||||
}
|
||||
|
||||
public IEnumerable<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem) => ResolveGeometryLeafNodes(modelItem);
|
||||
}
|
||||
|
||||
+18
-47
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk;
|
||||
@@ -16,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,53 +124,30 @@ public class NavisworksColorUnpacker(
|
||||
var comSelection = ComBridge.ToInwOpSelection([modelItem]);
|
||||
try
|
||||
{
|
||||
var paths = comSelection.Paths();
|
||||
try
|
||||
foreach (ComApi.InwOaPath path in comSelection.Paths())
|
||||
{
|
||||
foreach (ComApi.InwOaPath path in paths)
|
||||
GC.KeepAlive(path);
|
||||
|
||||
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
|
||||
{
|
||||
GC.KeepAlive(path);
|
||||
GC.KeepAlive(fragment);
|
||||
|
||||
var fragments = path.Fragments();
|
||||
try
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
// Exit early if triangles are found
|
||||
if (primitiveChecker.HasTriangles)
|
||||
{
|
||||
foreach (ComApi.InwOaFragment3 fragment in fragments)
|
||||
{
|
||||
GC.KeepAlive(fragment);
|
||||
|
||||
fragment.GenerateSimplePrimitives(ComApi.nwEVertexProperty.eNORMAL, primitiveChecker);
|
||||
|
||||
if (primitiveChecker.HasTriangles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fragments != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(fragments);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return primitiveChecker.HasLines || primitiveChecker.HasPoints || primitiveChecker.HasSnapPoints;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (paths != null)
|
||||
{
|
||||
System.Runtime.InteropServices.Marshal.ReleaseComObject(paths);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -120,6 +120,17 @@ public sealed class NavisworksDocumentEvents
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromDocumentModelEvents(object _)
|
||||
{
|
||||
var activeDocument = NavisworksApp.ActiveDocument;
|
||||
if (activeDocument != null)
|
||||
{
|
||||
UnsubscribeFromModelEvents(activeDocument);
|
||||
}
|
||||
|
||||
_isSubscribed = false;
|
||||
}
|
||||
|
||||
private void UnsubscribeFromModelEvents(NAV.Document document)
|
||||
{
|
||||
document.Models.CollectionChanged -= HandleDocumentModelCountChanged;
|
||||
|
||||
+37
-19
@@ -1,27 +1,26 @@
|
||||
using Autodesk.Navisworks.Api.ComApi;
|
||||
using Autodesk.Navisworks.Api.Interop.ComApi;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Helpers;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.Converter.Navisworks.ToSpeckle;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
using static Speckle.Converter.Navisworks.Constants.MaterialConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.HostApp;
|
||||
|
||||
public class NavisworksMaterialUnpacker(
|
||||
ILogger<NavisworksMaterialUnpacker> logger,
|
||||
IConverterSettingsStore<NavisworksConversionSettings> converterSettings,
|
||||
IElementSelectionService selectionService
|
||||
IElementSelectionService selectionService,
|
||||
GeometryToSpeckleConverter converter
|
||||
)
|
||||
{
|
||||
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,
|
||||
@@ -70,26 +69,38 @@ public class NavisworksMaterialUnpacker(
|
||||
var navisworksObjectId = selectionService.GetModelItemPath(navisworksObject);
|
||||
var finalId = mergedIds.TryGetValue(navisworksObjectId, out var mergedId) ? mergedId : navisworksObjectId;
|
||||
|
||||
var item = selectionService.GetModelItemFromPath(finalId);
|
||||
string hashId = "";
|
||||
var comSelection = ComApiBridge.ToInwOpSelection([item]);
|
||||
var paths = comSelection.Paths();
|
||||
var path = paths.OfType<InwOaPath>().First();
|
||||
var fragments = path.Fragments();
|
||||
if (fragments.Count > 1)
|
||||
{
|
||||
var fragmentId = converter.GenerateFragmentId(paths);
|
||||
hashId = $"geom_{fragmentId}";
|
||||
}
|
||||
|
||||
var geometry = navisworksObject.Geometry;
|
||||
var mode = converterSettings.Current.User.VisualRepresentationMode;
|
||||
|
||||
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(),
|
||||
@@ -98,8 +109,9 @@ public class NavisworksMaterialUnpacker(
|
||||
);
|
||||
|
||||
var materialName =
|
||||
$"{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)
|
||||
{
|
||||
@@ -111,6 +123,7 @@ public class NavisworksMaterialUnpacker(
|
||||
}
|
||||
}
|
||||
|
||||
// Check Material category for material name
|
||||
var materialPropertyCategory = navisworksObject.PropertyCategories.FindCategoryByDisplayName("Material");
|
||||
if (materialPropertyCategory != null)
|
||||
{
|
||||
@@ -124,14 +137,19 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
if (renderMaterialProxies.TryGetValue(renderMaterialId.ToString(), out RenderMaterialProxy? value))
|
||||
{
|
||||
value.objects.Add(finalId);
|
||||
value.objects.Add(!string.IsNullOrEmpty(hashId) ? hashId : finalId);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderMaterialProxies[renderMaterialId.ToString()] = new RenderMaterialProxy()
|
||||
{
|
||||
value = CreateRenderMaterial(materialName, renderTransparency, renderColor, renderMaterialId),
|
||||
objects = [finalId]
|
||||
value = ConvertRenderColorAndTransparencyToSpeckle(
|
||||
materialName,
|
||||
renderTransparency,
|
||||
renderColor,
|
||||
renderMaterialId
|
||||
),
|
||||
objects = [!string.IsNullOrEmpty(hashId) ? hashId : finalId]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -144,7 +162,7 @@ public class NavisworksMaterialUnpacker(
|
||||
return renderMaterialProxies.Values.ToList();
|
||||
}
|
||||
|
||||
private static RenderMaterial CreateRenderMaterial(
|
||||
private static RenderMaterial ConvertRenderColorAndTransparencyToSpeckle(
|
||||
string name,
|
||||
double transparency,
|
||||
NAV.Color navisworksColor,
|
||||
@@ -155,7 +173,7 @@ public class NavisworksMaterialUnpacker(
|
||||
|
||||
var speckleRenderMaterial = new RenderMaterial()
|
||||
{
|
||||
name = !string.IsNullOrEmpty(name) ? name : $"{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,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
|
||||
+7
-5
@@ -1,7 +1,7 @@
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
@@ -48,6 +48,8 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
return objectIds;
|
||||
}
|
||||
|
||||
var savedViews = NavisworksApp.ActiveDocument.SavedViewpoints;
|
||||
|
||||
foreach (var savedViewItem in SelectedItems.Select(item => ResolveSavedView(item.Id)))
|
||||
{
|
||||
// Get the visible elements in the saved view.
|
||||
@@ -80,12 +82,12 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
{
|
||||
var objectIds = new List<string>();
|
||||
|
||||
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOR - DISCUSSION REQUIRED
|
||||
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
|
||||
// if (!savedView.ContainsVisibilityOverrides)
|
||||
// {
|
||||
// // We check this again as the view settings may have changed in the saved card.
|
||||
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
|
||||
// // This will need to be the documented behavior.
|
||||
// // This will need to be the documented behaviour.
|
||||
// throw new SpeckleSendFilterException(
|
||||
// "Saved view does not contain visibility overrides. This would effectively publish everything in the model."
|
||||
// );
|
||||
@@ -152,7 +154,7 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
|
||||
switch (item)
|
||||
{
|
||||
// case NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
|
||||
// Legacy defensive behavior: skip viewpoints without visibility overrides.
|
||||
// Legacy defensive behaviour: skip viewpoints without visibility overrides.
|
||||
// Essentially, send everything, or whatever the current view state for hidden elements is.
|
||||
// break;
|
||||
case NAV.SavedViewpointAnimationCut:
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Filters;
|
||||
|
||||
@@ -15,6 +15,6 @@ public static class SavedItemHelpers
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return string.Join(SET_SEPARATOR, pathParts);
|
||||
return string.Join(PathConstants.SET_SEPARATOR, pathParts);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,5 +1,5 @@
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -10,29 +10,29 @@ public static class GeometryNodeMerger
|
||||
{
|
||||
/// <summary>
|
||||
/// Groups sibling geometry nodes based on material properties for merging.
|
||||
/// This only merges nodes that share the same parent and have identical material properties.
|
||||
/// Only merges nodes that share the same parent and have identical material properties.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The collection of ModelItems to process</param>
|
||||
/// <returns>Dictionary mapping parent paths (with material signature suffix) to their mergeable child nodes</returns>
|
||||
public static Dictionary<string, List<NAV.ModelItem>> GroupSiblingGeometryNodes(IReadOnlyList<NAV.ModelItem> nodes)
|
||||
{
|
||||
var selectionService = new ConnectorElementSelectionService();
|
||||
var selectionService = new ElementSelectionService();
|
||||
|
||||
// Group nameless geometry nodes by parent path and material signature
|
||||
var mergeableGroups = nodes
|
||||
.Where(node => node.HasGeometry && string.IsNullOrEmpty(node.DisplayName)) // Only anonymous geometry nodes
|
||||
.GroupBy(node =>
|
||||
{
|
||||
// Get the parent path
|
||||
// Get parent path
|
||||
var path = selectionService.GetModelItemPath(node);
|
||||
var lastSeparatorIndex = path.LastIndexOf(SEPARATOR);
|
||||
var lastSeparatorIndex = path.LastIndexOf(PathConstants.SEPARATOR);
|
||||
var parentPath = lastSeparatorIndex == -1 ? path : path[..lastSeparatorIndex];
|
||||
|
||||
// Generate material signature
|
||||
string signature = GenerateSignature(node);
|
||||
|
||||
// Combine parent path with signature
|
||||
return $"{parentPath}{MATERIAL_SEPARATOR}{signature}";
|
||||
return $"{parentPath}{PathConstants.MATERIAL_SEPARATOR}{signature}";
|
||||
})
|
||||
.Where(group => group.Count() > 1) // Only include groups with multiple children
|
||||
.ToDictionary(group => group.Key, group => group.ToList());
|
||||
@@ -95,7 +95,7 @@ public static class GeometryNodeMerger
|
||||
// Build a consistent string representation of all properties
|
||||
var hashInput = new System.Text.StringBuilder();
|
||||
|
||||
// Sort keys to ensure a consistent order
|
||||
// Sort keys to ensure consistent order
|
||||
var sortedKeys = properties.Keys.OrderBy(k => k).ToList();
|
||||
|
||||
foreach (var key in sortedKeys)
|
||||
@@ -139,7 +139,7 @@ public static class GeometryNodeMerger
|
||||
/// </summary>
|
||||
private static string GetMaterialName(NAV.ModelItem node)
|
||||
{
|
||||
// Check the Item category for material name
|
||||
// Check Item category for material name
|
||||
var itemCategory = node.PropertyCategories.FindCategoryByDisplayName("Item");
|
||||
if (itemCategory != null)
|
||||
{
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
using Speckle.Converter.Navisworks.Services;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Converter.Navisworks.Constants;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using static Speckle.Converter.Navisworks.Constants.PathConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -59,8 +59,8 @@ public class NavisworksHierarchyBuilder
|
||||
allPaths.Sort(
|
||||
(a, b) =>
|
||||
{
|
||||
var depthA = a.Count(c => c == SEPARATOR);
|
||||
var depthB = b.Count(c => c == SEPARATOR);
|
||||
var depthA = a.Count(c => c == PathConstants.SEPARATOR);
|
||||
var depthB = b.Count(c => c == PathConstants.SEPARATOR);
|
||||
return depthB.CompareTo(depthA); // <- Sort in ascending order of path length
|
||||
}
|
||||
);
|
||||
@@ -126,7 +126,7 @@ public class NavisworksHierarchyBuilder
|
||||
|
||||
private static string GetParentPath(string path)
|
||||
{
|
||||
var idx = path.LastIndexOf(SEPARATOR);
|
||||
var idx = path.LastIndexOf(PathConstants.SEPARATOR);
|
||||
return idx == -1 ? string.Empty : path[..idx];
|
||||
}
|
||||
|
||||
|
||||
+88
-164
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connector.Navisworks.HostApp;
|
||||
using Speckle.Connector.Navisworks.Services;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Conversion;
|
||||
@@ -13,10 +14,7 @@ using Speckle.Sdk;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using static Speckle.Connector.Navisworks.Operations.Send.GeometryNodeMerger;
|
||||
using static Speckle.Connectors.Common.Operations.ProxyKeys;
|
||||
using static Speckle.Converter.Navisworks.Constants.InstanceConstants;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send;
|
||||
|
||||
@@ -28,15 +26,13 @@ public class NavisworksRootObjectBuilder(
|
||||
ISdkActivityFactory activityFactory,
|
||||
NavisworksMaterialUnpacker materialUnpacker,
|
||||
NavisworksColorUnpacker colorUnpacker,
|
||||
Speckle.Converter.Navisworks.Constants.Registers.IInstanceFragmentRegistry instanceRegistry,
|
||||
IElementSelectionService elementSelectionService,
|
||||
IUiUnitsCache uiUnitsCache
|
||||
InstanceStoreManager instanceStoreManager
|
||||
) : IRootObjectBuilder<NAV.ModelItem>
|
||||
{
|
||||
#pragma warning disable CA1823
|
||||
#pragma warning restore CA1823
|
||||
private bool SkipNodeMerging { get; set; }
|
||||
private bool DisableGroupingForInstanceTesting { get; set; }
|
||||
|
||||
internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current;
|
||||
|
||||
public async Task<RootObjectBuilderResult> Build(
|
||||
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
|
||||
@@ -46,14 +42,19 @@ public class NavisworksRootObjectBuilder(
|
||||
)
|
||||
{
|
||||
#if DEBUG
|
||||
SkipNodeMerging = false;
|
||||
DisableGroupingForInstanceTesting = false;
|
||||
// This is a temporary workaround to disable node merging for debugging purposes - false is default, true is for debugging
|
||||
SkipNodeMerging = true;
|
||||
#endif
|
||||
using var activity = activityFactory.Start("Build");
|
||||
|
||||
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
|
||||
|
||||
// 2. Initialize root collection
|
||||
var rootCollection = InitializeRootCollection();
|
||||
|
||||
// InstanceStoreManager is scoped - starts fresh for each conversion session
|
||||
|
||||
// 3. Convert all model items and store results
|
||||
(Dictionary<string, Base?> convertedElements, List<SendConversionResult> conversionResults) =
|
||||
await ConvertModelItemsAsync(navisworksModelItems, projectId, onOperationProgressed, cancellationToken);
|
||||
|
||||
@@ -61,17 +62,32 @@ public class NavisworksRootObjectBuilder(
|
||||
|
||||
var groupedNodes = SkipNodeMerging ? [] : GroupSiblingGeometryNodes(navisworksModelItems);
|
||||
var finalElements = BuildFinalElements(convertedElements, groupedNodes);
|
||||
List<Base> geometryDefinitions = instanceStoreManager.GetGeometryDefinitions();
|
||||
|
||||
await AddProxiesToCollection(rootCollection, navisworksModelItems, groupedNodes);
|
||||
|
||||
AddInstanceDefinitionsToCollection(rootCollection, ref finalElements);
|
||||
int finalInstanceProxyCount = CountInstanceProxiesRecursive(finalElements);
|
||||
logger.LogInformation(
|
||||
"Final output contains {count} InstanceProxy objects in displayValues",
|
||||
finalInstanceProxyCount
|
||||
);
|
||||
// rootCollection.elements will contain two Collections: one for geometry definitions and one for the main elements
|
||||
|
||||
var geometryDefinitionsCollection = new Collection
|
||||
{
|
||||
name = "Geometry Definitions",
|
||||
["units"] = converterSettings.Current.Derived.SpeckleUnits,
|
||||
elements = geometryDefinitions
|
||||
};
|
||||
|
||||
var mainElementsCollection = new Collection
|
||||
{
|
||||
name = rootCollection.name,
|
||||
["units"] = converterSettings.Current.Derived.SpeckleUnits,
|
||||
elements = finalElements
|
||||
};
|
||||
|
||||
rootCollection.elements = [mainElementsCollection];
|
||||
if (geometryDefinitions.Count > 0)
|
||||
{
|
||||
rootCollection.elements.Add(geometryDefinitionsCollection);
|
||||
}
|
||||
|
||||
rootCollection.elements = finalElements;
|
||||
return new RootObjectBuilderResult(rootCollection, conversionResults);
|
||||
}
|
||||
|
||||
@@ -117,32 +133,16 @@ public class NavisworksRootObjectBuilder(
|
||||
var convertedBases = new Dictionary<string, Base?>();
|
||||
int processedCount = 0;
|
||||
int totalCount = navisworksModelItems.Count;
|
||||
int instanceProxyCount = 0;
|
||||
|
||||
foreach (var item in navisworksModelItems)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
|
||||
results.Add(converted);
|
||||
|
||||
if (
|
||||
converted.Status == Status.SUCCESS
|
||||
&& convertedBases.TryGetValue(elementSelectionService.GetModelItemPath(item), out var convertedBase)
|
||||
&& convertedBase?["displayValue"] is List<Base> displayValues
|
||||
)
|
||||
{
|
||||
instanceProxyCount += displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
"Converted {total} items, found {instanceProxies} InstanceProxy objects",
|
||||
totalCount,
|
||||
instanceProxyCount
|
||||
);
|
||||
return Task.FromResult((convertedBases, results));
|
||||
}
|
||||
|
||||
@@ -159,39 +159,27 @@ 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 (!DisableGroupingForInstanceTesting)
|
||||
{
|
||||
AddGroupedElements(finalElements, convertedBases, groupedNodes, processedPaths);
|
||||
logger.LogInformation(
|
||||
"After grouping: {grouped} paths processed, {elements} elements in collection",
|
||||
processedPaths.Count,
|
||||
finalElements.Count
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Grouping disabled for instance testing");
|
||||
}
|
||||
|
||||
// If hierarchy mode is enabled, reorganize into proper nested structure
|
||||
if (converterSettings.Current.User.PreserveModelHierarchy)
|
||||
{
|
||||
logger.LogInformation("Building hierarchy (PreserveModelHierarchy=true)");
|
||||
var hierarchyBuilder = new NavisworksHierarchyBuilder(
|
||||
convertedBases,
|
||||
rootToSpeckleConverter,
|
||||
elementSelectionService
|
||||
);
|
||||
|
||||
return hierarchyBuilder.BuildHierarchy();
|
||||
var hierarchy = hierarchyBuilder.BuildHierarchy();
|
||||
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
logger.LogInformation("Adding remaining elements (flat mode)");
|
||||
// Otherwise continue with flat mode
|
||||
AddRemainingElements(finalElements, convertedBases, processedPaths);
|
||||
|
||||
logger.LogInformation("Final elements count: {count}", finalElements.Count);
|
||||
return finalElements;
|
||||
}
|
||||
|
||||
@@ -204,7 +192,7 @@ public class NavisworksRootObjectBuilder(
|
||||
{
|
||||
foreach (var group in groupedNodes)
|
||||
{
|
||||
var siblingBases = new List<Base>(group.Value.Count);
|
||||
var siblingBases = new List<Base>();
|
||||
foreach (var itemPath in group.Value.Select(elementSelectionService.GetModelItemPath))
|
||||
{
|
||||
processedPaths.Add(itemPath);
|
||||
@@ -247,48 +235,41 @@ 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);
|
||||
|
||||
int estimatedCapacity = siblingBases.Sum(b => (b["displayValue"] as List<Base>)?.Count ?? 0);
|
||||
var displayValues = new List<Base>(estimatedCapacity);
|
||||
displayValues.AddRange(
|
||||
siblingBases
|
||||
.Where(sibling => sibling["displayValue"] is List<Base>)
|
||||
.SelectMany(sibling => (List<Base>)sibling["displayValue"]!)
|
||||
);
|
||||
|
||||
var instanceProxyCount = displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
if (instanceProxyCount > 0)
|
||||
{
|
||||
logger.LogDebug(
|
||||
"Group {groupKey} merging {siblings} siblings with {proxies} InstanceProxy objects",
|
||||
groupKey,
|
||||
siblingBases.Count,
|
||||
instanceProxyCount
|
||||
);
|
||||
}
|
||||
(string name, string path) = GetContext(cleanParentPath);
|
||||
|
||||
return new NavisworksObject
|
||||
{
|
||||
name = name,
|
||||
displayValue = displayValues,
|
||||
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)
|
||||
@@ -296,16 +277,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
|
||||
};
|
||||
@@ -322,100 +301,45 @@ public class NavisworksRootObjectBuilder(
|
||||
var renderMaterials = materialUnpacker.UnpackRenderMaterial(navisworksModelItems, groupedNodes);
|
||||
if (renderMaterials.Count > 0)
|
||||
{
|
||||
rootCollection[RENDER_MATERIAL] = renderMaterials;
|
||||
rootCollection[ProxyKeys.RENDER_MATERIAL] = renderMaterials;
|
||||
}
|
||||
|
||||
var colors = colorUnpacker.UnpackColor(navisworksModelItems, groupedNodes);
|
||||
if (colors.Count > 0)
|
||||
{
|
||||
rootCollection[COLOR] = colors;
|
||||
rootCollection[ProxyKeys.COLOR] = colors;
|
||||
}
|
||||
|
||||
// Add instance definition proxies from dual store
|
||||
var instanceDefinitionProxies = instanceStoreManager.GetInstanceDefinitionProxies();
|
||||
logger.LogDebug("Retrieved {Count} instance definition proxies from store", instanceDefinitionProxies.Count);
|
||||
|
||||
if (instanceDefinitionProxies.Count > 0)
|
||||
{
|
||||
rootCollection[ProxyKeys.INSTANCE_DEFINITION] = instanceDefinitionProxies.ToList();
|
||||
logger.LogDebug(
|
||||
"Added {Count} instance definition proxies to root collection under key '{Key}'",
|
||||
instanceDefinitionProxies.Count,
|
||||
ProxyKeys.INSTANCE_DEFINITION
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogDebug("No instance definition proxies to add to root collection");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AddInstanceDefinitionsToCollection(Collection rootCollection, ref List<Base> finalElements)
|
||||
{
|
||||
using var _ = activityFactory.Start("BuildInstanceDefinitions");
|
||||
|
||||
// Get all definition geometries from the registry
|
||||
var allDefinitions = instanceRegistry.GetAllDefinitionGeometries();
|
||||
|
||||
if (allDefinitions.Count == 0)
|
||||
{
|
||||
logger.LogInformation("No instance definitions found - instancing may be disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation("Building instance structure for {count} definition groups", allDefinitions.Count);
|
||||
|
||||
if (allDefinitions.Count > 100)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Large number of definition groups ({count}) detected - this may indicate instance grouping is not working effectively",
|
||||
allDefinitions.Count
|
||||
);
|
||||
}
|
||||
|
||||
var instanceDefinitionProxies = new List<InstanceDefinitionProxy>(allDefinitions.Count);
|
||||
|
||||
int estimatedGeometryCount = allDefinitions.Sum(kvp => kvp.Value.Count);
|
||||
var allDefinitionGeometries = new List<Base>(estimatedGeometryCount);
|
||||
|
||||
foreach (var kvp in allDefinitions)
|
||||
{
|
||||
var groupKey = kvp.Key;
|
||||
var geometries = kvp.Value;
|
||||
var groupKeyHash = groupKey.ToHashString();
|
||||
|
||||
var defProxy = new InstanceDefinitionProxy
|
||||
{
|
||||
name = $"Shared Geometry {groupKeyHash}",
|
||||
objects = geometries.Select(g => g.applicationId ?? "").Where(id => !string.IsNullOrEmpty(id)).ToList(),
|
||||
applicationId = $"{DEFINITION_ID_PREFIX}{groupKeyHash}",
|
||||
maxDepth = 0
|
||||
};
|
||||
|
||||
instanceDefinitionProxies.Add(defProxy);
|
||||
allDefinitionGeometries.AddRange(geometries);
|
||||
}
|
||||
|
||||
rootCollection[INSTANCE_DEFINITION] = instanceDefinitionProxies;
|
||||
var geometryDefinitionsCollection = new Collection
|
||||
{
|
||||
name = "Geometry Definitions",
|
||||
elements = allDefinitionGeometries
|
||||
};
|
||||
|
||||
var objectCollection = new Collection { name = "", elements = finalElements };
|
||||
|
||||
finalElements = [geometryDefinitionsCollection, objectCollection];
|
||||
|
||||
logger.LogInformation(
|
||||
"Added {proxyCount} instance definition proxies and {geomCount} definition geometries",
|
||||
instanceDefinitionProxies.Count,
|
||||
allDefinitionGeometries.Count
|
||||
);
|
||||
}
|
||||
|
||||
private int CountInstanceProxiesRecursive(List<Base> elements)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (element["displayValue"] is List<Base> displayValues)
|
||||
{
|
||||
count += displayValues.Count(dv => dv.GetType().Name == "InstanceProxy");
|
||||
}
|
||||
|
||||
if (element is Collection { elements: not null } collection)
|
||||
{
|
||||
count += CountInstanceProxiesRecursive(collection.elements);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <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,
|
||||
|
||||
+124
-71
@@ -1,4 +1,5 @@
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.DUI.Models.Card;
|
||||
using Speckle.Converter.Navisworks.Settings;
|
||||
using Speckle.InterfaceGenerator;
|
||||
@@ -7,106 +8,158 @@ using Speckle.Sdk.Common;
|
||||
namespace Speckle.Connector.Navisworks.Operations.Send.Settings;
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
|
||||
: IToSpeckleSettingsManagerNavisworks
|
||||
public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavisworks
|
||||
{
|
||||
// cache invalidation process run with ModelCardId since the settings are model-specific
|
||||
private readonly ISendConversionCache _sendConversionCache;
|
||||
|
||||
// cache invalidation process run with ModelCardId since the settings are model specific
|
||||
private readonly Dictionary<string, RepresentationMode> _visualRepresentationCache = [];
|
||||
private readonly Dictionary<string, OriginMode> _originModeCache = [];
|
||||
private readonly Dictionary<string, bool> _convertHiddenElementsCache = [];
|
||||
private readonly Dictionary<string, bool> _includeInternalPropertiesCache = [];
|
||||
private readonly Dictionary<string, bool> _preserveModelHierarchyCache = [];
|
||||
private readonly Dictionary<string, bool> _revitCategoryMappingCache = [];
|
||||
private readonly Dictionary<string, bool?> _convertHiddenElementsCache = [];
|
||||
private readonly Dictionary<string, bool?> _includeInternalPropertiesCache = [];
|
||||
private readonly Dictionary<string, bool?> _preserveModelHierarchyCache = [];
|
||||
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
|
||||
|
||||
/// <summary>
|
||||
/// Generic helper to get a setting value with caching and cache invalidation.
|
||||
/// </summary>
|
||||
private T GetCachedSetting<T>(
|
||||
SenderModelCard modelCard,
|
||||
string settingId,
|
||||
Dictionary<string, T> cache,
|
||||
Func<object?, T> valueExtractor,
|
||||
T defaultValue
|
||||
)
|
||||
public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
|
||||
{
|
||||
_sendConversionCache = sendConversionCache;
|
||||
}
|
||||
|
||||
public RepresentationMode GetVisualRepresentationMode(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var settingValue = modelCard.Settings?.FirstOrDefault(s => s.Id == settingId)?.Value;
|
||||
var returnValue = settingValue != null ? valueExtractor(settingValue) : defaultValue;
|
||||
var representationString = modelCard.Settings?.First(s => s.Id == "visualRepresentation").Value as string;
|
||||
|
||||
if (
|
||||
cache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue)
|
||||
&& !EqualityComparer<T>.Default.Equals(previousValue, returnValue)
|
||||
representationString is not null
|
||||
&& VisualRepresentationSetting.VisualRepresentationMap.TryGetValue(
|
||||
representationString,
|
||||
out RepresentationMode representation
|
||||
)
|
||||
)
|
||||
{
|
||||
if (_visualRepresentationCache.TryGetValue(modelCard.ModelCardId.NotNull(), out RepresentationMode previousType))
|
||||
{
|
||||
if (previousType != representation)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
_visualRepresentationCache[modelCard.ModelCardId.NotNull()] = representation;
|
||||
return representation;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid visual representation value: {representationString}");
|
||||
}
|
||||
|
||||
public OriginMode GetOriginMode(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var originString = modelCard.Settings?.FirstOrDefault(s => s.Id == "originMode")?.Value as string;
|
||||
if (!OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
|
||||
{
|
||||
return OriginMode.ModelOrigin; // Default to ModelOrigin if not specified or invalid
|
||||
}
|
||||
|
||||
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousType) && previousType != origin)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
|
||||
cache[modelCard.ModelCardId.NotNull()] = returnValue;
|
||||
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
|
||||
return origin;
|
||||
}
|
||||
|
||||
public bool GetMappingToRevitCategories(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public RepresentationMode GetVisualRepresentationMode(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"visualRepresentation",
|
||||
_visualRepresentationCache,
|
||||
value =>
|
||||
public bool GetConvertHiddenElements(SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelCard));
|
||||
}
|
||||
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "convertHiddenElements")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_convertHiddenElementsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
var representationString = value as string;
|
||||
return
|
||||
representationString is not null
|
||||
&& VisualRepresentationSetting.VisualRepresentationMap.TryGetValue(
|
||||
representationString,
|
||||
out RepresentationMode representation
|
||||
)
|
||||
? representation
|
||||
: throw new ArgumentException($"Invalid visual representation value: {representationString}");
|
||||
},
|
||||
RepresentationMode.Active // default value if setting not found
|
||||
);
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
public OriginMode GetOriginMode(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"originMode",
|
||||
_originModeCache,
|
||||
value =>
|
||||
_convertHiddenElementsCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public bool GetIncludeInternalProperties([NotNull] SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "includeInternalProperties")?.Value as bool?;
|
||||
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_includeInternalPropertiesCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
var originString = value as string;
|
||||
if (OriginModeSetting.OriginModeMap.TryGetValue(originString ?? string.Empty, out var origin))
|
||||
{
|
||||
return origin;
|
||||
}
|
||||
return OriginMode.ModelOrigin;
|
||||
},
|
||||
OriginMode.ModelOrigin
|
||||
);
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetMappingToRevitCategories(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "mappingToRevitCategories", _revitCategoryMappingCache, value => value is true, false);
|
||||
_includeInternalPropertiesCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public bool GetConvertHiddenElements(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "convertHiddenElements", _convertHiddenElementsCache, value => value is true, false);
|
||||
public bool GetPreserveModelHierarchy([NotNull] SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "preserveModelHierarchy")?.Value as bool?;
|
||||
|
||||
public bool GetIncludeInternalProperties(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(
|
||||
modelCard,
|
||||
"includeInternalProperties",
|
||||
_includeInternalPropertiesCache,
|
||||
value => value is true,
|
||||
false
|
||||
);
|
||||
var returnValue = value != null && value.NotNull();
|
||||
if (_preserveModelHierarchyCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetPreserveModelHierarchy(SenderModelCard modelCard) =>
|
||||
GetCachedSetting(modelCard, "preserveModelHierarchy", _preserveModelHierarchyCache, value => value is true, false);
|
||||
_preserveModelHierarchyCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private void EvictCacheForModelCard(SenderModelCard modelCard)
|
||||
{
|
||||
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
|
||||
sendConversionCache.EvictObjects(objectIds);
|
||||
_sendConversionCache.EvictObjects(objectIds);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public static class SpeckleV3Tool
|
||||
public const string RIBBON_STRINGS = "NavisworksRibbon.name";
|
||||
public const string PLUGIN_SUFFIX = ".Speckle";
|
||||
|
||||
public static Sdk.Application App =>
|
||||
public static Speckle.Sdk.Application App =>
|
||||
#if NAVIS
|
||||
HostApplications.Navisworks;
|
||||
#else
|
||||
|
||||
@@ -191,27 +191,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -297,20 +276,14 @@
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -333,23 +306,16 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2022": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Revit.API": "[2022.0.2.1, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -385,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -397,6 +363,29 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2022.0.2.1",
|
||||
"contentHash": "IrLN4WyI2ix+g3zCpo7sX8zNB3FrtrdQ3E2RpceGVPNG00v8OfD+Kei7o1bn1u/ML46iBYRAr/JcsLbwfUQsBw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,27 +191,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -297,20 +276,14 @@
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -333,23 +306,16 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2023": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Revit.API": "[2023.0.0, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -385,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -397,6 +363,29 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2023.0.0",
|
||||
"contentHash": "tq40eD7psgTbV+epNouYyqfo6+hEi7FmXZqcxEOsAV7zfYyWhL6Rt3vmojkWGNuerGbH6oRI6KIIxrnlCNb8Hw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,27 +191,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -297,20 +276,14 @@
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -333,23 +306,16 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2024": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Revit.API": "[2024.0.0, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -385,11 +351,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -397,6 +363,29 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2024.0.0",
|
||||
"contentHash": "a4dsvZ00ocvzTgCD6dUdydf0jIZDVcDhs6dUX9cv+y3aTDbU8rmzhYXWt8sThedIG+IPSVa0vHmAH9pKiJL3SQ=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,25 +171,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -240,20 +221,14 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -276,23 +251,16 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2025": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Revit.API": "[2025.0.0, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -328,11 +296,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -340,6 +308,27 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2025.0.0",
|
||||
"contentHash": "Hwf/3Ydc7KxvjgD9pSZKLSJRsFTsxYg95YyTm6f43hcsGjmk49GsLFQt921Z9OcvUVewOggQHcmBgti+P2EPHw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
|
||||
xmlns:dui="clr-namespace:Speckle.Connectors.DUI;assembly=Speckle.Connectors.DUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<!-- WebView2 is created lazily -->
|
||||
<Border Name="BrowserContainer" Background="White">
|
||||
<TextBlock Name="LoadingText"
|
||||
Text="Loading Speckle..."
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="Gray" />
|
||||
</Border>
|
||||
<UserControl.Resources>
|
||||
<wv2:CoreWebView2CreationProperties x:Key="EvergreenWebView2CreationProperties" UserDataFolder="C:\temp" />
|
||||
</UserControl.Resources>
|
||||
<DockPanel>
|
||||
<wv2:WebView2
|
||||
CreationProperties="{StaticResource EvergreenWebView2CreationProperties}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Name="Browser" Grid.Row="0" Source="{x:Static dui:Url.Netlify}" />
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -2,8 +2,6 @@ using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using Speckle.Connectors.DUI;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
@@ -14,10 +12,6 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IRevitTask _revitTask;
|
||||
#pragma warning disable CA2213
|
||||
private WebView2? _browser;
|
||||
#pragma warning restore CA2213
|
||||
private bool _isInitializing;
|
||||
|
||||
public RevitControlWebView(IServiceProvider serviceProvider, IRevitTask revitTask)
|
||||
{
|
||||
@@ -25,73 +19,36 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
_revitTask = revitTask;
|
||||
InitializeComponent();
|
||||
|
||||
// Delay WebView2 creation until the panel is actually visible
|
||||
// This avoids conflicts with other plugins (like pyRevit) during startup
|
||||
IsVisibleChanged += OnIsVisibleChanged;
|
||||
}
|
||||
|
||||
private void OnIsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is true && _browser == null && !_isInitializing)
|
||||
{
|
||||
_isInitializing = true;
|
||||
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, CreateWebView2);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateWebView2()
|
||||
{
|
||||
_browser = new WebView2
|
||||
{
|
||||
CreationProperties = new CoreWebView2CreationProperties { UserDataFolder = "C:\\temp" },
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
|
||||
Source = Url.Netlify
|
||||
};
|
||||
|
||||
_browser.CoreWebView2InitializationCompleted += (sender, args) =>
|
||||
Browser.CoreWebView2InitializationCompleted += (sender, args) =>
|
||||
_serviceProvider
|
||||
.GetRequiredService<ITopLevelExceptionHandler>()
|
||||
.CatchUnhandled(() => OnInitialized(sender, args));
|
||||
|
||||
BrowserContainer.Child = _browser;
|
||||
}
|
||||
|
||||
public bool IsBrowserInitialized => _browser?.IsInitialized ?? false;
|
||||
public bool IsBrowserInitialized => Browser.IsInitialized;
|
||||
|
||||
public object BrowserElement => _browser!;
|
||||
public object BrowserElement => Browser;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ExecuteScript(string script, CancellationToken cancellationToken)
|
||||
public void ExecuteScript(string script)
|
||||
{
|
||||
if (_browser == null || !_browser.IsInitialized)
|
||||
if (!Browser.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet");
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
|
||||
}
|
||||
|
||||
if (!_browser.CheckAccess())
|
||||
{
|
||||
ExecuteScriptDispatched(script, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
_browser.ExecuteScriptAsync(script);
|
||||
_revitTask.Run(() => Browser.ExecuteScriptAsync(script));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ExecuteScriptDispatched(string script, CancellationToken cancellationToken)
|
||||
public void SendProgress(string script)
|
||||
{
|
||||
if (_browser == null || !_browser.IsInitialized)
|
||||
if (!Browser.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet");
|
||||
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
|
||||
}
|
||||
|
||||
//Intentionally using the dispatcher even from the main thread
|
||||
//As it allows the UI to pump messages, and stay responsive
|
||||
_browser.Dispatcher.Invoke(
|
||||
() => _browser.ExecuteScriptAsync(script),
|
||||
DispatcherPriority.Background,
|
||||
cancellationToken
|
||||
//always invoke even on the main thread because it's better somehow
|
||||
Browser.Dispatcher.Invoke(
|
||||
//fire and forget
|
||||
() => Browser.ExecuteScriptAsync(script),
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,18 +74,11 @@ public sealed partial class RevitControlWebView : UserControl, IBrowserScriptExe
|
||||
private void SetupBinding(IBinding binding)
|
||||
{
|
||||
binding.Parent.AssociateWithBinding(binding);
|
||||
_browser!.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent);
|
||||
Browser.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent);
|
||||
}
|
||||
|
||||
public void ShowDevTools() => _browser?.CoreWebView2?.OpenDevToolsWindow();
|
||||
public void ShowDevTools() => Browser.CoreWebView2.OpenDevToolsWindow();
|
||||
|
||||
//https://github.com/MicrosoftEdge/WebView2Feedback/issues/2161
|
||||
public void Dispose()
|
||||
{
|
||||
if (_browser != null)
|
||||
{
|
||||
_browser.Dispatcher.Invoke(() => _browser.Dispose(), DispatcherPriority.Send);
|
||||
_browser = null;
|
||||
}
|
||||
}
|
||||
public void Dispose() => Browser.Dispatcher.Invoke(() => Browser.Dispose(), DispatcherPriority.Send);
|
||||
}
|
||||
|
||||
@@ -164,25 +164,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -233,20 +214,14 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,23 +244,16 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2026": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Revit.API": "[2026.0.0, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -312,11 +280,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -324,6 +292,27 @@
|
||||
"requested": "[2023.0.0, )",
|
||||
"resolved": "2026.0.0",
|
||||
"contentHash": "SiqqKbF1pXyZWXZhAl2JhjYhTt7RiYO5JaQrAjq+OlleAjT4zatwAp/DnTwQspFbP7UZr3b2Ed2kuWNN0ZFelw=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
using Autodesk.Revit.UI;
|
||||
@@ -13,50 +14,36 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ExecuteScript(string script, CancellationToken cancellationToken)
|
||||
public void ExecuteScript(string script)
|
||||
{
|
||||
if (!Browser.CheckAccess())
|
||||
try
|
||||
{
|
||||
ExecuteScriptDispatched(script, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
//avoid exceptions by checking if IBrowser is there
|
||||
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.ExecuteScriptAsync(script);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ExecuteScriptDispatched(string script, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Browser == null || !Browser.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to execute script, ChromiumWebBrowser is not initialized yet");
|
||||
}
|
||||
|
||||
//Intentionally using the dispatcher even from the main thread
|
||||
//As it allows the UI to pump messages, and stay responsive
|
||||
Browser.Dispatcher.Invoke(
|
||||
() =>
|
||||
{
|
||||
//avoid exceptions by checking if IBrowser is there
|
||||
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
|
||||
Browser.Dispatcher.Invoke(
|
||||
() =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
//avoid exceptions by checking if IBrowser is there
|
||||
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.ExecuteScriptAsync(script);
|
||||
},
|
||||
DispatcherPriority.Background,
|
||||
cancellationToken
|
||||
);
|
||||
Browser.ExecuteScriptAsync(script);
|
||||
},
|
||||
DispatcherPriority.Background
|
||||
);
|
||||
}
|
||||
catch (SEHException)
|
||||
{
|
||||
//do nothing as we can't control external components
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
//do nothing, happens when closing Revit while a script is being executed
|
||||
}
|
||||
}
|
||||
|
||||
public void SendProgress(string script) => ExecuteScript(script);
|
||||
|
||||
public bool IsBrowserInitialized => Browser.IsBrowserInitialized;
|
||||
public object BrowserElement => Browser;
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ public sealed class RevitReceiveBinding(
|
||||
toHostSettingsManager.GetReferencePointSetting(card),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.IO;
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -25,7 +24,7 @@ namespace Speckle.Connectors.Revit.Bindings;
|
||||
|
||||
internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
{
|
||||
private readonly RevitIdleManager _revitIdleManager;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ICancellationManager _cancellationManager;
|
||||
@@ -39,9 +38,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private readonly LinkedModelHandler _linkedModelHandler;
|
||||
private readonly IThreadContext _threadContext;
|
||||
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
|
||||
private bool _isDocChangedSubscribed;
|
||||
private EventHandler<Autodesk.Revit.DB.Events.DocumentChangedEventArgs>? _documentChangedHandler;
|
||||
private readonly ConnectorConfig _config;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
|
||||
@@ -52,7 +48,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private ConcurrentHashSet<ElementId> ChangedObjectIds { get; set; } = new();
|
||||
|
||||
public RevitSendBinding(
|
||||
RevitIdleManager revitIdleManager,
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
DocumentModelStore store,
|
||||
ICancellationManager cancellationManager,
|
||||
@@ -66,12 +62,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
LinkedModelHandler linkedModelHandler,
|
||||
IThreadContext threadContext,
|
||||
IRevitTask revitTask,
|
||||
ISendOperationManagerFactory sendOperationManagerFactory,
|
||||
IConfigStore configStore
|
||||
ISendOperationManagerFactory sendOperationManagerFactory
|
||||
)
|
||||
: base("sendBinding", bridge)
|
||||
{
|
||||
_revitIdleManager = revitIdleManager;
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_store = store;
|
||||
_cancellationManager = cancellationManager;
|
||||
@@ -84,7 +79,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_linkedModelHandler = linkedModelHandler;
|
||||
_threadContext = threadContext;
|
||||
_sendOperationManagerFactory = sendOperationManagerFactory;
|
||||
_config = configStore.GetConnectorConfig();
|
||||
|
||||
Commands = new SendBindingUICommands(bridge);
|
||||
// TODO expiry events
|
||||
@@ -92,61 +86,15 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
revitTask.Run(() =>
|
||||
{
|
||||
// revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
|
||||
// _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_documentChangedHandler = (_, e) => _topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_store.ModelCardsChanged += (_, e) => OnModelCardsChanged(e);
|
||||
revitContext.UIApplication.NotNull().Application.DocumentChanged += (_, e) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => DocChangeHandler(e));
|
||||
_store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged());
|
||||
});
|
||||
}
|
||||
|
||||
private void OnModelCardsChanged(ModelCardsChangedEventArgs e)
|
||||
{
|
||||
if (
|
||||
!_config.DocumentChangeListeningDisabled
|
||||
&& e.ModelCards.Count > 0
|
||||
&& e.ModelCards.Any(m => m.TypeDiscriminator == nameof(SenderModelCard))
|
||||
)
|
||||
{
|
||||
SubscribeDocChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
UnsubscribeDocChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeDocChanged()
|
||||
{
|
||||
if (_documentChangedHandler == null || _isDocChangedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_threadContext.RunOnMain(() =>
|
||||
{
|
||||
_revitContext.UIApplication.NotNull().Application.DocumentChanged += _documentChangedHandler;
|
||||
});
|
||||
_isDocChangedSubscribed = true;
|
||||
}
|
||||
|
||||
private void UnsubscribeDocChanged()
|
||||
{
|
||||
if (_documentChangedHandler == null || !_isDocChangedSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_threadContext.RunOnMain(() =>
|
||||
{
|
||||
_revitContext.UIApplication.NotNull().Application.DocumentChanged -= _documentChangedHandler;
|
||||
});
|
||||
_isDocChangedSubscribed = false;
|
||||
}
|
||||
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new RevitSelectionFilter { IsDefault = true },
|
||||
new RevitSelectionFilter() { IsDefault = true },
|
||||
new RevitViewsFilter(_revitContext),
|
||||
new RevitCategoriesFilter(_revitContext)
|
||||
];
|
||||
@@ -157,8 +105,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
new SendReferencePointSetting(),
|
||||
new SendParameterNullOrEmptyStringsSetting(),
|
||||
new LinkedModelsSetting(),
|
||||
new SendRebarsAsVolumetricSetting(),
|
||||
new SendAreasAsMeshSetting()
|
||||
new SendRebarsAsVolumetricSetting()
|
||||
];
|
||||
|
||||
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
@@ -173,7 +120,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
throw new SpeckleException("No document is active for sending.");
|
||||
}
|
||||
using var manager = _sendOperationManagerFactory.Create();
|
||||
var (fileName, fileBytes) = GetFileInfo(document);
|
||||
|
||||
await manager.Process<DocumentToConvert>(
|
||||
Commands,
|
||||
modelCardId,
|
||||
@@ -186,31 +133,14 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_toSpeckleSettingsManager.GetReferencePointSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetLinkedModelsSetting(document, card),
|
||||
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(document, card),
|
||||
_toSpeckleSettingsManager.GetSendAreasAsMesh(document, card)
|
||||
_toSpeckleSettingsManager.GetSendRebarsAsVolumetric(document, card)
|
||||
)
|
||||
);
|
||||
},
|
||||
async x => await RefreshElementsIdsOnSender(document, x.NotNull()),
|
||||
fileName: fileName,
|
||||
fileSizeBytes: fileBytes
|
||||
async x => await RefreshElementsIdsOnSender(document, x.NotNull())
|
||||
);
|
||||
}
|
||||
|
||||
private static (string? fileName, long? fileBytes) GetFileInfo(Document document)
|
||||
{
|
||||
string fullPath = document.PathName;
|
||||
if (File.Exists(document.PathName))
|
||||
{
|
||||
var fileInfo = new FileInfo(document.PathName);
|
||||
return (fileInfo.Name, fileInfo.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (fullPath.Split('/').LastOrDefault(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
|
||||
@@ -346,7 +276,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
if (addedElementIds.Count > 0)
|
||||
{
|
||||
_revitIdleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
|
||||
}
|
||||
|
||||
if (HaveUnitsChanged(doc))
|
||||
@@ -366,8 +296,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
_revitIdleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_revitIdleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
_idleManager.SubscribeToIdle(nameof(CheckFilterExpiration), CheckFilterExpiration);
|
||||
_idleManager.SubscribeToIdle(nameof(RunExpirationChecks), RunExpirationChecks);
|
||||
}
|
||||
|
||||
// Keeps track of doc and current units
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Common;
|
||||
@@ -18,32 +17,27 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, ID
|
||||
public SelectionBinding(
|
||||
RevitContext revitContext,
|
||||
IBrowserBridge parent,
|
||||
RevitIdleManager idleManager,
|
||||
#if REVIT2022
|
||||
IAppIdleManager idleManager,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
#endif
|
||||
IRevitTask revitTask,
|
||||
IConfigStore configStore
|
||||
IRevitTask revitTask
|
||||
)
|
||||
: base("selectionBinding", parent)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
|
||||
if (!configStore.GetConnectorConfig().SelectionChangeListeningDisabled)
|
||||
{
|
||||
#if REVIT2022
|
||||
// NOTE: getting the selection data should be a fast function all, even for '000s of elements - and having a timer hitting it every 1s is ok.
|
||||
_selectionTimer = new System.Timers.Timer(1000);
|
||||
_selectionTimer.Elapsed += (_, _) => topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged);
|
||||
_selectionTimer.Start();
|
||||
// NOTE: getting the selection data should be a fast function all, even for '000s of elements - and having a timer hitting it every 1s is ok.
|
||||
_selectionTimer = new System.Timers.Timer(1000);
|
||||
_selectionTimer.Elapsed += (_, _) => topLevelExceptionHandler.CatchUnhandled(OnSelectionChanged);
|
||||
_selectionTimer.Start();
|
||||
#else
|
||||
revitTask.Run(
|
||||
() =>
|
||||
_revitContext.UIApplication.NotNull().SelectionChanged += (_, _) =>
|
||||
idleManager.SubscribeToIdle(nameof(OnSelectionChanged), OnSelectionChanged)
|
||||
);
|
||||
|
||||
revitTask.Run(
|
||||
() =>
|
||||
_revitContext.UIApplication.NotNull().SelectionChanged += (_, _) =>
|
||||
idleManager.SubscribeToIdle(nameof(OnSelectionChanged), OnSelectionChanged)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
|
||||
+2
-4
@@ -48,18 +48,16 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddSingleton<IBinding, SelectionBinding>();
|
||||
serviceCollection.AddSingleton<IBinding, RevitSendBinding>();
|
||||
serviceCollection.AddSingleton<IBinding, RevitReceiveBinding>();
|
||||
serviceCollection.AddSingleton<RevitIdleManager>();
|
||||
|
||||
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
|
||||
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();
|
||||
|
||||
// serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
|
||||
// send operation and dependencies
|
||||
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
|
||||
serviceCollection.AddScoped<ElementUnpacker>();
|
||||
serviceCollection.AddScoped<LevelUnpacker>();
|
||||
serviceCollection.AddScoped<ViewUnpacker>();
|
||||
serviceCollection.AddScoped<SendCollectionManager>();
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
|
||||
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
@@ -72,11 +70,11 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<ITransactionManager, TransactionManager>();
|
||||
serviceCollection.AddScoped<RevitGroupBaker>();
|
||||
serviceCollection.AddScoped<RevitMaterialBaker>();
|
||||
serviceCollection.AddScoped<RevitViewBaker>();
|
||||
serviceCollection.AddScoped<RevitViewManager>();
|
||||
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();
|
||||
|
||||
@@ -17,16 +17,13 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
{
|
||||
private readonly ILogger<RevitDocumentStore> _logger;
|
||||
|
||||
//private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitIdleManager _idleManager;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
|
||||
public RevitDocumentStore(
|
||||
//IAppIdleManager idleManager,
|
||||
RevitIdleManager idleManager,
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
IJsonSerializer jsonSerializer,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
@@ -37,7 +34,6 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
: base(logger, jsonSerializer)
|
||||
{
|
||||
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
|
||||
//_idleManager = idleManager;
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
@@ -31,24 +32,6 @@ public class RevitMaterialBaker
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
private ElementId? FindExistingMaterialByName(string? materialName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(materialName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string sanitizedName = _revitUtils.RemoveInvalidChars(materialName!);
|
||||
|
||||
using var collector = new FilteredElementCollector(_converterSettings.Current.Document);
|
||||
var existingMaterial = collector
|
||||
.OfClass(typeof(Material))
|
||||
.Cast<Material>()
|
||||
.FirstOrDefault(m => string.Equals(m.Name, sanitizedName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return existingMaterial?.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the every atomic object has render material or not, if not it tries to find it from its layer tree and mutates
|
||||
/// its render material proxy objects list with the traversal current. It will also map displayable objects' display values to their
|
||||
@@ -123,9 +106,11 @@ public class RevitMaterialBaker
|
||||
/// Will bake render materials in the revit document.
|
||||
/// </summary>
|
||||
/// <param name="speckleRenderMaterialProxies"></param>
|
||||
/// <param name="baseLayerName"></param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, ElementId> BakeMaterials(
|
||||
IReadOnlyCollection<RenderMaterialProxy> speckleRenderMaterialProxies
|
||||
IReadOnlyCollection<RenderMaterialProxy> speckleRenderMaterialProxies,
|
||||
string baseLayerName
|
||||
)
|
||||
{
|
||||
Dictionary<string, ElementId> objectIdAndMaterialIndexMap = new();
|
||||
@@ -135,42 +120,27 @@ public class RevitMaterialBaker
|
||||
|
||||
try
|
||||
{
|
||||
// first try to match existing material by name
|
||||
ElementId? existingMaterialId = FindExistingMaterialByName(speckleRenderMaterial.name);
|
||||
// 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);
|
||||
|
||||
ElementId materialIdToUse;
|
||||
var diffuse = System.Drawing.Color.FromArgb(speckleRenderMaterial.diffuse);
|
||||
double transparency = 1 - opacity;
|
||||
double smoothness = 1 - roughness;
|
||||
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id.NotNull();
|
||||
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}");
|
||||
|
||||
if (existingMaterialId != null)
|
||||
{
|
||||
// Use existing material
|
||||
materialIdToUse = existingMaterialId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new material
|
||||
// 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;
|
||||
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}");
|
||||
|
||||
var newMaterialId = Material.Create(_converterSettings.Current.Document, matName);
|
||||
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.Smoothness = (int)(smoothness * 100);
|
||||
|
||||
materialIdToUse = revitMaterial.Id;
|
||||
}
|
||||
var newMaterialId = Material.Create(_converterSettings.Current.Document, matName);
|
||||
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.Smoothness = (int)(smoothness * 128);
|
||||
|
||||
foreach (var objectId in proxy.objects)
|
||||
{
|
||||
objectIdAndMaterialIndexMap[objectId] = materialIdToUse;
|
||||
objectIdAndMaterialIndexMap[objectId] = revitMaterial.Id;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class that creates View3D elements from Camera objects during receive.
|
||||
/// </summary>
|
||||
public class RevitViewBaker
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ILogger<RevitViewBaker> _logger;
|
||||
private readonly ITypedConverter<Speckle.Objects.Geometry.Point, XYZ> _pointConverter;
|
||||
private readonly ITypedConverter<Speckle.Objects.Geometry.Vector, XYZ> _vectorConverter;
|
||||
|
||||
public RevitViewBaker(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
ILogger<RevitViewBaker> logger,
|
||||
ITypedConverter<Speckle.Objects.Geometry.Point, XYZ> pointConverter,
|
||||
ITypedConverter<Speckle.Objects.Geometry.Vector, XYZ> vectorConverter
|
||||
)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_logger = logger;
|
||||
_pointConverter = pointConverter;
|
||||
_vectorConverter = vectorConverter;
|
||||
}
|
||||
|
||||
// Characters that are not allowed in Revit view names
|
||||
private readonly char[] _invalidViewNameChars = ['{', '}', '[', ']', '|', ';', '<', '>', '?', '`', '~', '\\', ':'];
|
||||
|
||||
/// <summary>
|
||||
/// Bakes Camera objects as View3D elements in Revit.
|
||||
/// </summary>
|
||||
public void BakeViews(IReadOnlyCollection<Camera> cameras)
|
||||
{
|
||||
if (cameras.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var camera in cameras)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(camera.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var restoredName = RestoreViewName(camera.name);
|
||||
if (string.IsNullOrWhiteSpace(restoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var existingView = FindViewByName(restoredName);
|
||||
|
||||
try
|
||||
{
|
||||
if (existingView != null)
|
||||
{
|
||||
UpdatePerspectiveView(existingView, camera);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatePerspectiveView(camera, restoredName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create view '{ViewName}'", restoredName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sanitizes the view name by removing invalid characters.
|
||||
/// </summary>
|
||||
private string RestoreViewName(string name)
|
||||
{
|
||||
var restored = name;
|
||||
|
||||
foreach (var c in _invalidViewNameChars)
|
||||
{
|
||||
restored = restored.Replace(c.ToString(), string.Empty);
|
||||
}
|
||||
|
||||
return restored.Trim();
|
||||
}
|
||||
|
||||
private View3D? FindViewByName(string name)
|
||||
{
|
||||
using var collector = new FilteredElementCollector(_converterSettings.Current.Document);
|
||||
return collector
|
||||
.OfClass(typeof(View3D))
|
||||
.Cast<View3D>()
|
||||
.FirstOrDefault(v => !v.IsTemplate && string.Equals(v.Name, name, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private void UpdatePerspectiveView(View3D view3D, Camera camera)
|
||||
{
|
||||
var eyePosition = _pointConverter.Convert(camera.position);
|
||||
var forwardDirection = _vectorConverter.Convert(camera.forward).Normalize();
|
||||
var upDirection = _vectorConverter.Convert(camera.up).Normalize();
|
||||
|
||||
var orientation = new ViewOrientation3D(eyePosition, upDirection, forwardDirection);
|
||||
view3D.SetOrientation(orientation);
|
||||
}
|
||||
|
||||
private void CreatePerspectiveView(Camera camera, string viewName)
|
||||
{
|
||||
var document = _converterSettings.Current.Document;
|
||||
|
||||
// Get ViewFamilyType for 3D views
|
||||
using var collector = new FilteredElementCollector(document);
|
||||
var viewFamilyType = collector
|
||||
.OfClass(typeof(ViewFamilyType))
|
||||
.Cast<ViewFamilyType>()
|
||||
.FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional);
|
||||
|
||||
if (viewFamilyType == null)
|
||||
{
|
||||
_logger.LogError("Could not find a 3D ViewFamilyType to create view '{ViewName}'", viewName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create perspective view (View3D is a document element, not disposable) - low happiness level
|
||||
#pragma warning disable CA2000
|
||||
var view3D = View3D.CreatePerspective(document, viewFamilyType.Id);
|
||||
#pragma warning restore CA2000
|
||||
|
||||
// Convert camera position, forward, and up vectors
|
||||
var eyePosition = _pointConverter.Convert(camera.position);
|
||||
var forwardDirection = _vectorConverter.Convert(camera.forward).Normalize();
|
||||
var upDirection = _vectorConverter.Convert(camera.up).Normalize();
|
||||
|
||||
var orientation = new ViewOrientation3D(eyePosition, upDirection, forwardDirection);
|
||||
view3D.SetOrientation(orientation);
|
||||
|
||||
view3D.Name = viewName;
|
||||
|
||||
// Set display style to Shaded (looks better than default wireframe)
|
||||
view3D.DisplayStyle = DisplayStyle.Shading;
|
||||
|
||||
// Disable far clipping so depth is infinite
|
||||
var farClipParam = view3D.get_Parameter(BuiltInParameter.VIEWER_BOUND_ACTIVE_FAR);
|
||||
if (farClipParam != null && !farClipParam.IsReadOnly)
|
||||
{
|
||||
farClipParam.Set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-112
@@ -21,7 +21,6 @@ using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
using Transform = Speckle.Objects.Other.Transform;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Receive;
|
||||
@@ -34,21 +33,17 @@ public sealed class RevitHostObjectBuilder(
|
||||
ILocalToGlobalUnpacker localToGlobalUnpacker,
|
||||
RevitGroupBaker groupManager,
|
||||
RevitMaterialBaker materialBaker,
|
||||
RevitViewBaker viewBaker,
|
||||
RootObjectUnpacker rootObjectUnpacker,
|
||||
ILogger<RevitHostObjectBuilder> logger,
|
||||
IThreadContext threadContext,
|
||||
RevitToHostCacheSingleton revitToHostCacheSingleton,
|
||||
ITypedConverter<
|
||||
(Base atomicObject, IReadOnlyCollection<Matrix4x4> matrix, DataObject? parentDataObject),
|
||||
(Base atomicObject, IReadOnlyCollection<Matrix4x4> matrix),
|
||||
DirectShape
|
||||
> localToGlobalDirectShapeConverter,
|
||||
IReceiveConversionHandler conversionHandler
|
||||
) : IHostObjectBuilder, IDisposable
|
||||
{
|
||||
// Maps atomic object applicationId -> parent DataObject
|
||||
private readonly Dictionary<string, DataObject> _atomicObjectToParentDataObject = new();
|
||||
|
||||
public Task<HostObjectBuilderResult> Build(
|
||||
Base rootObject,
|
||||
string projectName,
|
||||
@@ -107,9 +102,6 @@ public sealed class RevitHostObjectBuilder(
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
// Register DataObjects with InstanceProxy displayValues
|
||||
RegisterDataObjectsWithInstanceProxies(unpackedRoot);
|
||||
|
||||
// NOTE: below is 💩... https://github.com/specklesystems/speckle-sharp-connectors/pull/813 broke sketchup to revit workflow
|
||||
// ids were modified to fix receiving instances [CNX-1707](https://linear.app/speckle/issue/CNX-1707/revit-curves-and-meshes-in-blocks-come-as-duplicated)
|
||||
// but we then broke sketchup to revit because applicationIds in proxies didn't match modified application ids which cam from #813 hack
|
||||
@@ -184,15 +176,12 @@ public sealed class RevitHostObjectBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
// Update DataObject lookup IDs
|
||||
UpdateAtomicObjectLookupWithModifiedIds(originalToModifiedIds);
|
||||
|
||||
// 2 - Bake materials (now with the updated IDs)
|
||||
if (unpackedRoot.RenderMaterialProxies != null)
|
||||
{
|
||||
transactionManager.StartTransaction(true, "Baking materials");
|
||||
materialBaker.MapLayersRenderMaterials(unpackedRoot);
|
||||
var map = materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies);
|
||||
var map = materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName);
|
||||
foreach (var kvp in map)
|
||||
{
|
||||
revitToHostCacheSingleton.MaterialsByObjectId.Add(kvp.Key, kvp.Value);
|
||||
@@ -200,14 +189,6 @@ public sealed class RevitHostObjectBuilder(
|
||||
transactionManager.CommitTransaction();
|
||||
}
|
||||
|
||||
// 2.1 - Bake views
|
||||
if (unpackedRoot.Cameras is not null)
|
||||
{
|
||||
transactionManager.StartTransaction(true, "Baking views");
|
||||
viewBaker.BakeViews(unpackedRoot.Cameras);
|
||||
transactionManager.CommitTransaction();
|
||||
}
|
||||
|
||||
// 3 - Bake objects
|
||||
(
|
||||
HostObjectBuilderResult builderResult,
|
||||
@@ -253,87 +234,6 @@ public sealed class RevitHostObjectBuilder(
|
||||
return conversionResults.builderResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers DataObjects that have InstanceProxy displayValues and builds the lookup.
|
||||
/// </summary>
|
||||
private void RegisterDataObjectsWithInstanceProxies(RootObjectUnpackerResult unpackedRoot)
|
||||
{
|
||||
var definitionToDataObject = new Dictionary<string, DataObject>();
|
||||
|
||||
foreach (var tc in unpackedRoot.ObjectsToConvert)
|
||||
{
|
||||
if (tc.Current is DataObject dataObject)
|
||||
{
|
||||
var instanceProxies = dataObject.displayValue.OfType<InstanceProxy>().ToList();
|
||||
if (instanceProxies.Count > 0)
|
||||
{
|
||||
foreach (var ip in instanceProxies)
|
||||
{
|
||||
definitionToDataObject[ip.definitionId] = dataObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build lookup: definition object applicationId -> parent DataObject
|
||||
_atomicObjectToParentDataObject.Clear();
|
||||
if (unpackedRoot.DefinitionProxies is not null)
|
||||
{
|
||||
foreach (var defProxy in unpackedRoot.DefinitionProxies)
|
||||
{
|
||||
if (
|
||||
defProxy.applicationId is not null
|
||||
&& definitionToDataObject.TryGetValue(defProxy.applicationId, out var parentDataObject)
|
||||
)
|
||||
{
|
||||
foreach (var objectId in defProxy.objects)
|
||||
{
|
||||
_atomicObjectToParentDataObject[objectId] = parentDataObject;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogError(
|
||||
"Could not find parent DataObject for DefinitionProxy {ApplicationId}",
|
||||
defProxy.applicationId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the atomic object lookup with modified IDs
|
||||
/// </summary>
|
||||
private void UpdateAtomicObjectLookupWithModifiedIds(Dictionary<string, List<string>> originalToModifiedIds)
|
||||
{
|
||||
// Build updated entries first to avoid modifying collection during iteration
|
||||
var entriesToAdd = new List<KeyValuePair<string, DataObject>>();
|
||||
var keysToRemove = new List<string>();
|
||||
|
||||
foreach (var kvp in _atomicObjectToParentDataObject)
|
||||
{
|
||||
if (originalToModifiedIds.TryGetValue(kvp.Key, out var modifiedIds))
|
||||
{
|
||||
keysToRemove.Add(kvp.Key);
|
||||
foreach (var modifiedId in modifiedIds)
|
||||
{
|
||||
entriesToAdd.Add(new(modifiedId, kvp.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
_atomicObjectToParentDataObject.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var entry in entriesToAdd)
|
||||
{
|
||||
_atomicObjectToParentDataObject[entry.Key] = entry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private Autodesk.Revit.DB.Transform? CalculateNewTransform(
|
||||
Autodesk.Revit.DB.Transform? receiveTransform,
|
||||
Autodesk.Revit.DB.Transform? rootTransform
|
||||
@@ -378,17 +278,9 @@ public sealed class RevitHostObjectBuilder(
|
||||
onOperationProgressed.Report(new("Converting", (double)++count / localToGlobalMaps.Count));
|
||||
if (result is DirectShapeDefinitionWrapper)
|
||||
{
|
||||
// Look up parent DataObject for this atomic object (handles InstanceProxy displayValue)
|
||||
var atomicId = localToGlobalMap.AtomicObject.applicationId;
|
||||
DataObject? parentDataObject = null;
|
||||
if (atomicId is not null)
|
||||
{
|
||||
_atomicObjectToParentDataObject.TryGetValue(atomicId, out parentDataObject);
|
||||
}
|
||||
|
||||
// direct shape creation happens here
|
||||
DirectShape directShapes = localToGlobalDirectShapeConverter.Convert(
|
||||
(localToGlobalMap.AtomicObject, localToGlobalMap.Matrix, parentDataObject)
|
||||
(localToGlobalMap.AtomicObject, localToGlobalMap.Matrix)
|
||||
);
|
||||
|
||||
bakedObjectIds.Add(directShapes.UniqueId);
|
||||
@@ -459,7 +351,6 @@ public sealed class RevitHostObjectBuilder(
|
||||
DirectShapeLibrary.GetDirectShapeLibrary(converterSettings.Current.Document).Reset(); // Note: this needs to be cleared, as it is being used in the converter
|
||||
|
||||
revitToHostCacheSingleton.MaterialsByObjectId.Clear(); // Massive hack!
|
||||
_atomicObjectToParentDataObject.Clear();
|
||||
groupManager.PurgeGroups(baseGroupName);
|
||||
materialBaker.PurgeMaterials(baseGroupName);
|
||||
}
|
||||
|
||||
+1
-5
@@ -81,11 +81,7 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
|
||||
|
||||
foreach (Category category in _doc.Settings.Categories)
|
||||
{
|
||||
if (SupportedCategoriesUtils.IsSupportedCategory(category)
|
||||
#if REVIT2023_OR_GREATER
|
||||
&& category.BuiltInCategory != BuiltInCategory.INVALID
|
||||
#endif
|
||||
)
|
||||
if (SupportedCategoriesUtils.IsSupportedCategory(category))
|
||||
{
|
||||
categories.Add(new CategoryData(category.Name, category.Id.ToString()));
|
||||
}
|
||||
|
||||
+3
-22
@@ -1,4 +1,3 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
@@ -8,7 +7,6 @@ using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
using Speckle.Connectors.Revit.HostApp;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
@@ -26,13 +24,11 @@ public class RevitRootObjectBuilder(
|
||||
ISendConversionCache sendConversionCache,
|
||||
ElementUnpacker elementUnpacker,
|
||||
LevelUnpacker levelUnpacker,
|
||||
ViewUnpacker viewUnpacker,
|
||||
IThreadContext threadContext,
|
||||
SendCollectionManager sendCollectionManager,
|
||||
ILogger<RevitRootObjectBuilder> logger,
|
||||
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
|
||||
LinkedModelHandler linkedModelHandler,
|
||||
IConfigStore configStore
|
||||
LinkedModelHandler linkedModelHandler
|
||||
) : IRootObjectBuilder<DocumentToConvert>
|
||||
{
|
||||
public Task<RootObjectBuilderResult> Build(
|
||||
@@ -45,7 +41,6 @@ public class RevitRootObjectBuilder(
|
||||
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
|
||||
);
|
||||
|
||||
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling")]
|
||||
private RootObjectBuilderResult BuildSync(
|
||||
IReadOnlyList<DocumentToConvert> documentElementContexts,
|
||||
string projectId,
|
||||
@@ -138,8 +133,6 @@ public class RevitRootObjectBuilder(
|
||||
var cacheHitCount = 0;
|
||||
var skippedObjectCount = 0;
|
||||
|
||||
var config = configStore.GetConnectorConfig();
|
||||
|
||||
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
|
||||
{
|
||||
string? modelDisplayName = null;
|
||||
@@ -191,11 +184,7 @@ public class RevitRootObjectBuilder(
|
||||
// TODO: Potential here to transform cached objects and NOT reconvert,
|
||||
// TODO: we wont do !hasTransform here, and re-set application id before this
|
||||
|
||||
if (
|
||||
!hasTransform
|
||||
&& !config.DocumentChangeListeningDisabled //This is experimental
|
||||
&& sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value)
|
||||
)
|
||||
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
|
||||
{
|
||||
converted = value;
|
||||
cacheHitCount++;
|
||||
@@ -251,7 +240,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);
|
||||
|
||||
@@ -268,17 +256,10 @@ public class RevitRootObjectBuilder(
|
||||
new Collection()
|
||||
{
|
||||
elements = revitToSpeckleCacheSingleton.GetBaseObjectsForObjects(idsAndSubElementIds),
|
||||
name = "definitionGeometry"
|
||||
name = "revitInstancedObjects"
|
||||
}
|
||||
);
|
||||
|
||||
// STEP 6: Unpack all other objects to attach to root collection
|
||||
List<Objects.Other.Camera> views = viewUnpacker.Unpack(converterSettings.Current.Document);
|
||||
if (views.Count > 0)
|
||||
{
|
||||
rootObject[RootKeys.VIEW] = views;
|
||||
}
|
||||
|
||||
// NOTE: these are currently not used anywhere, we'll skip them until someone calls for it back
|
||||
// rootObject[ProxyKeys.PARAMETER_DEFINITIONS] = _parameterDefinitionHandler.Definitions;
|
||||
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class SendAreasAsMeshSetting(bool value = SendAreasAsMeshSetting.DEFAULT_VALUE) : ICardSetting
|
||||
{
|
||||
public const string SETTING_ID = "sendAreasAsMesh";
|
||||
public const bool DEFAULT_VALUE = false;
|
||||
|
||||
public string? Id { get; set; } = SETTING_ID;
|
||||
public string? Title { get; set; } = "Send Areas As Mesh";
|
||||
public string? Type { get; set; } = "boolean";
|
||||
public object? Value { get; set; } = value;
|
||||
public List<string>? Enum { get; set; }
|
||||
}
|
||||
-11
@@ -22,7 +22,6 @@ public class ToSpeckleSettingsManager(
|
||||
private readonly Dictionary<string, bool?> _sendNullParamsCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
|
||||
private readonly Dictionary<string, bool?> _sendAreasAsMeshCache = [];
|
||||
|
||||
public DetailLevelType GetDetailLevelSetting(Document document, SenderModelCard modelCard)
|
||||
{
|
||||
@@ -132,16 +131,6 @@ public class ToSpeckleSettingsManager(
|
||||
"Send rebars as volumetric"
|
||||
);
|
||||
|
||||
public bool GetSendAreasAsMesh(Document document, SenderModelCard modelCard) =>
|
||||
GetBooleanSettingWithCache(
|
||||
document,
|
||||
SendAreasAsMeshSetting.SETTING_ID,
|
||||
SendAreasAsMeshSetting.DEFAULT_VALUE,
|
||||
modelCard,
|
||||
_sendAreasAsMeshCache,
|
||||
"Send areas as mesh"
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to handle boolean settings with caching and logging
|
||||
/// </summary>
|
||||
|
||||
@@ -6,106 +6,38 @@ using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Plugin;
|
||||
|
||||
/// <remarks>
|
||||
/// Please do NOT try and refactor this class.
|
||||
/// Whether it's to try and generalize with the <see cref="IdleCallManager"/> class
|
||||
/// or to unnecessary try and make this class thread safe.
|
||||
/// This class is a simple singleton, targeted to a Revit's host app requirements
|
||||
/// where everything happens on the main thread, and we can avoid overly complex threading/thread-safety.
|
||||
///
|
||||
/// Previous refactors with good intention have lead to poor debugging experiences, over-engineered threading,
|
||||
/// and low confidence in the reliability.
|
||||
/// </remarks>
|
||||
/// should be registered as singleton
|
||||
public class RevitIdleManager(RevitContext revitContext, ITopLevelExceptionHandler topLevelExceptionHandler)
|
||||
public sealed class RevitIdleManager : AppIdleManager
|
||||
{
|
||||
private readonly UIApplication _uiApplication = revitContext.UIApplication.NotNull();
|
||||
private readonly UIApplication _uiApplication;
|
||||
private readonly IIdleCallManager _idleCallManager;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
|
||||
private readonly Dictionary<string, Func<Task>> _calls = new();
|
||||
private bool _hasSubscribed;
|
||||
private event EventHandler<IdlingEventArgs>? OnIdle;
|
||||
|
||||
private bool _isExecutingIdle;
|
||||
|
||||
/// <summary>
|
||||
/// Defers the invocation of an <paramref name="action"/> until next Revit idle tick (deduped by name).
|
||||
/// The <paramref name="action"/> will be called only once.
|
||||
/// </summary>
|
||||
/// <param name="name">A key that prevents enqueuing duplicate events</param>
|
||||
/// <param name="action">The action to be invoked</param>
|
||||
/// <example>
|
||||
/// Some events in host app are triggered many times, we might get 10x per object
|
||||
/// Making this more like a deferred action, so we don't update the UI many times
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// This function must be called on the main thread
|
||||
/// </remarks>
|
||||
public void SubscribeToIdle(string name, Action action)
|
||||
public RevitIdleManager(
|
||||
RevitContext revitContext,
|
||||
IIdleCallManager idleCallManager,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IRevitTask revitTask
|
||||
)
|
||||
: base(idleCallManager)
|
||||
{
|
||||
SubscribeToIdle(
|
||||
name,
|
||||
() =>
|
||||
{
|
||||
action.Invoke();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_uiApplication = revitContext.UIApplication.NotNull();
|
||||
_idleCallManager = idleCallManager;
|
||||
revitTask.Run(
|
||||
() => _uiApplication.Idling += (s, e) => OnIdle?.Invoke(s, e) // will be called on the main thread always and fixing the Revit exceptions on subscribing/unsubscribing Idle events
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="SubscribeToIdle(string, Action)"/>
|
||||
public void SubscribeToIdle(string name, Func<Task> action)
|
||||
protected override void AddEvent()
|
||||
{
|
||||
if (_isExecutingIdle)
|
||||
_topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
//Either, you're trying to SubscribeToIdle from not the main thread (don't do this)
|
||||
//OR, an OnIdle event handler is calling SubscribeToIdle (re-entry), check the call stack
|
||||
throw new InvalidOperationException("SubscribeToIdle called while already executing idle events");
|
||||
}
|
||||
|
||||
_calls[name] = action;
|
||||
|
||||
if (_hasSubscribed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_hasSubscribed = true;
|
||||
|
||||
_uiApplication.Idling += RevitAppOnIdle;
|
||||
}
|
||||
|
||||
private void RevitAppOnIdle(object? sender, IdlingEventArgs e)
|
||||
{
|
||||
topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
{
|
||||
if (_isExecutingIdle)
|
||||
{
|
||||
//Either, you're trying to SubscribeToIdle from not the main thread (don't do this)
|
||||
//OR, an OnIdle event handler is calling SubscribeToIdle (re-entry), check the call stack... avoid this
|
||||
throw new InvalidOperationException("SubscribeToIdle called while already executing idle events");
|
||||
}
|
||||
|
||||
_isExecutingIdle = true;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (KeyValuePair<string, Func<Task>> kvp in _calls)
|
||||
{
|
||||
topLevelExceptionHandler.FireAndForget(kvp.Value.Invoke);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_calls.Clear();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_uiApplication.Idling -= RevitAppOnIdle;
|
||||
|
||||
_isExecutingIdle = false;
|
||||
// setting last will delay entering re-subscription
|
||||
_hasSubscribed = false;
|
||||
}
|
||||
OnIdle += RevitAppOnIdle;
|
||||
});
|
||||
}
|
||||
|
||||
private void RevitAppOnIdle(object? sender, IdlingEventArgs e) =>
|
||||
_idleCallManager.AppOnIdle(() => OnIdle -= RevitAppOnIdle);
|
||||
}
|
||||
|
||||
+2
-5
@@ -24,8 +24,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitViewBaker.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" />
|
||||
@@ -49,18 +47,17 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitRootObjectBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\LinkedModelsSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendAreasAsMeshSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsVolumetricSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendReferencePointSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitTask.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitExternalApplication.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitIdleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitThreadContext.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -202,27 +202,6 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -357,7 +337,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino7": {
|
||||
@@ -402,12 +382,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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.13.1",
|
||||
"contentHash": "qCKCPT4HeSCJ7S+wnnjF+N+9Sd6lj5+Ra9DfxDHHrFli9rtXdnQRU5UOObyfcbiWQidVXhc2n0kbo3LPCEcvNw==",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "McLXS+Hd/bW+AdJifxGUIQi+ftofGY5r6i/X00HmlnbOvHJKZAR6fzJ12E8otMhMd78He8tcyjBD3R2jOR9ctA=="
|
||||
},
|
||||
"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.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )",
|
||||
"Speckle.Sdk": "[3.9.0, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -357,7 +337,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.13.1, )"
|
||||
"Speckle.Objects": "[3.9.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
@@ -401,12 +381,35 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.13.1, )",
|
||||
"resolved": "3.13.1",
|
||||
"contentHash": "VRG8SApTbAYA0YmgWTw0Eb+/AHeE0yOxDuKBTvFj3VipuSnwF29fV479BehnZdg3d8OBh4aP/YEx3vPAafybVw==",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "fAOUhScCfDFVVynvipczjyw9RZlOgPOo8aH5A7EDAwZiDuDdd4EsnrqBCSPlmuoPYzY7hsN+5mfRkfw2rB36Ig==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.13.1"
|
||||
"Speckle.Sdk": "3.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "GtbvnySinrE6Canm6fVjyUOxs4G1bw0aRLs9oPVMdodOKc9TxIQjp1lzVBtr6Jli+nzIxtC86xP5J6r9tufnrQ==",
|
||||
"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.9.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.9.0, )",
|
||||
"resolved": "3.9.0",
|
||||
"contentHash": "m/3i+DX/1McN8ig0CcjmHM1BcNmNxgmny/735sKntzzDw23wdo868eOOTrogzmDoYHTyc7J4IjK+GE7iAyWn/g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-11
@@ -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));
|
||||
}
|
||||
|
||||
-311
@@ -1,311 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Creates collections by matching name tree structure to elements tree structure.
|
||||
/// Each branch in the names tree corresponds to the same-path branch in the elements tree.
|
||||
/// </summary>
|
||||
[Guid("7E8F9A1B-2C3D-4E5F-6A7B-8C9D0E1F2A3B")]
|
||||
public class CollectionsByName : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_collections_create; // TODO: Update to specific icon if available
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
public CollectionsByName()
|
||||
: base(
|
||||
"Collections by Name",
|
||||
"CbN",
|
||||
"Creates collections by matching name tree structure to objects tree structure. Each branch in the names tree corresponds to the same-path branch in the objects tree.",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.COLLECTIONS
|
||||
) { }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter(
|
||||
"Names",
|
||||
"N",
|
||||
"Collection names (tree structure must match Objects tree structure)",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
"Objects",
|
||||
"O",
|
||||
"Objects to group into collections (tree structure must match Names tree structure)",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) =>
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(),
|
||||
"Collection",
|
||||
"C",
|
||||
"Root collection containing named sub-collections",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
// access the tree data directly from parameters
|
||||
var namesParam = Params.Input[0];
|
||||
var elementsParam = Params.Input[1];
|
||||
|
||||
var namesTree = namesParam.VolatileData;
|
||||
var elementsTree = elementsParam.VolatileData;
|
||||
|
||||
// validate that both inputs have data
|
||||
if (namesTree.IsEmpty)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Names tree is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (elementsTree.IsEmpty)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Objects tree is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// validate tree structures match exactly
|
||||
if (!TreeStructuresMatch(namesTree, elementsTree))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Tree structures and topologies must match exactly");
|
||||
return;
|
||||
}
|
||||
|
||||
// create root collection
|
||||
var rootCollection = CollectionHelpers.CreateRootCollection(InstanceGuid.ToString());
|
||||
|
||||
// process each path
|
||||
foreach (var path in namesTree.Paths)
|
||||
{
|
||||
var nameBranch = namesTree.get_Branch(path);
|
||||
var elementsBranch = elementsTree.get_Branch(path);
|
||||
|
||||
// validate name branch - throw if empty
|
||||
if (nameBranch.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Name branch at path {path} is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// validate name branch - just warn if multiple, use first
|
||||
if (nameBranch.Count > 1)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Name branch at path {path} has {nameBranch.Count} names - using first name only"
|
||||
);
|
||||
}
|
||||
|
||||
// get the collection name
|
||||
string collectionName = GetCollectionName(nameBranch[0]);
|
||||
if (string.IsNullOrWhiteSpace(collectionName))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Invalid collection name at path {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
// skip empty element branches with warning
|
||||
if (elementsBranch.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Skipping empty elements branch at path {path}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse nested collection path (e.g. parent::child)
|
||||
var collectionNames = collectionName.Split(
|
||||
new[] { Constants.LAYER_PATH_DELIMITER },
|
||||
StringSplitOptions.RemoveEmptyEntries
|
||||
);
|
||||
|
||||
// create or get nested collection structure
|
||||
var targetCollection = GetOrCreateNestedCollection(rootCollection, collectionNames, elementsBranch, path);
|
||||
|
||||
// add elements to deepest collection
|
||||
AddElementsToCollection(targetCollection, elementsBranch, path);
|
||||
}
|
||||
|
||||
// validate collection has content
|
||||
if (rootCollection.Elements.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
"Collection contains no valid geometry. All branches were empty or contained unsupported types."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate for duplicate application IDs (following CreateCollection pattern)
|
||||
if (CollectionHelpers.HasDuplicateApplicationIds(rootCollection))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The same object(s) cannot appear in multiple collections");
|
||||
return;
|
||||
}
|
||||
|
||||
da.SetData(0, new SpeckleCollectionWrapperGoo(rootCollection));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that two tree structures have exactly matching paths
|
||||
/// </summary>
|
||||
private bool TreeStructuresMatch(
|
||||
Grasshopper.Kernel.Data.IGH_Structure namesTree,
|
||||
Grasshopper.Kernel.Data.IGH_Structure elementsTree
|
||||
)
|
||||
{
|
||||
if (namesTree.PathCount != elementsTree.PathCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check that all paths match exactly
|
||||
var namePaths = namesTree.Paths.ToList();
|
||||
var elementPaths = elementsTree.Paths.ToList();
|
||||
|
||||
for (int i = 0; i < namePaths.Count; i++)
|
||||
{
|
||||
if (namePaths[i].CompareTo(elementPaths[i]) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts collection name, handling GH_String and other text types
|
||||
/// </summary>
|
||||
private string GetCollectionName(object nameObj) =>
|
||||
nameObj switch
|
||||
{
|
||||
GH_String ghString => ghString.Value,
|
||||
IGH_Goo goo => goo.ToString(),
|
||||
_ => nameObj.ToString()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a nested collection structure based on the collection names.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Handles paths like "parent::child::grandchild" by creating intermediate collections.
|
||||
/// </remarks>
|
||||
private SpeckleCollectionWrapper GetOrCreateNestedCollection(
|
||||
SpeckleCollectionWrapper rootCollection,
|
||||
string[] collectionNames,
|
||||
System.Collections.IList elementsBranch,
|
||||
Grasshopper.Kernel.Data.GH_Path path
|
||||
)
|
||||
{
|
||||
SpeckleCollectionWrapper currentCollection = rootCollection;
|
||||
var currentPath = new List<string>(rootCollection.Path);
|
||||
|
||||
foreach (var collectionName in collectionNames)
|
||||
{
|
||||
// build path for this level
|
||||
currentPath.Add(collectionName);
|
||||
|
||||
// check if child collection already exists
|
||||
var existingChild = currentCollection
|
||||
.Elements.OfType<SpeckleCollectionWrapper>()
|
||||
.FirstOrDefault(c => c.Name == collectionName);
|
||||
|
||||
if (existingChild != null)
|
||||
{
|
||||
// use existing collection
|
||||
currentCollection = existingChild;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new child collection
|
||||
var newChild = new SpeckleCollectionWrapper
|
||||
{
|
||||
Base = new Collection(),
|
||||
Name = collectionName,
|
||||
Path = currentPath.ToList(),
|
||||
Color = null,
|
||||
Material = null,
|
||||
Topology = null, // only set topology on leaf collections
|
||||
ApplicationId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
currentCollection.Elements.Add(newChild);
|
||||
currentCollection = newChild;
|
||||
}
|
||||
}
|
||||
|
||||
// set topology on the final (leaf) collection
|
||||
currentCollection.Topology = GetBranchTopology(path, elementsBranch.Count);
|
||||
|
||||
return currentCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds elements from a branch to the target collection
|
||||
/// </summary>
|
||||
private void AddElementsToCollection(
|
||||
SpeckleCollectionWrapper targetCollection,
|
||||
System.Collections.IList elementsBranch,
|
||||
Grasshopper.Kernel.Data.GH_Path path
|
||||
)
|
||||
{
|
||||
foreach (var item in elementsBranch)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
// preserve nulls for topology (CNX-2855 pattern)
|
||||
targetCollection.Elements.Add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert to SpeckleWrapper if possible - cast to IGH_Goo first
|
||||
if (item is not IGH_Goo goo)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Unsupported object type in branch {path}: {item.GetType().Name}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
var wrapper = goo.ToSpeckleObjectWrapper();
|
||||
if (wrapper == null)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Unsupported object type in branch {path}: {item.GetType().Name}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wrapper is ISpeckleCollectionObject collectionObject)
|
||||
{
|
||||
targetCollection.Elements.Add(collectionObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Object type {wrapper.GetType().Name} is not a valid collection element"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates topology string for a single branch (following GrasshopperHelpers.GetParamTopology pattern)
|
||||
/// </summary>
|
||||
private string GetBranchTopology(Grasshopper.Kernel.Data.GH_Path path, int count) =>
|
||||
$"{path.ToString(false)}-{count}";
|
||||
}
|
||||
+54
-50
@@ -47,8 +47,7 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
var rootCollection = CollectionHelpers.CreateRootCollection(InstanceGuid.ToString());
|
||||
bool hasAnyInput = false;
|
||||
var rootCollection = CreateRootCollection();
|
||||
|
||||
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,19 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
// Skip validation if no input provided
|
||||
if (!hasAnyInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// validate for duplicate application IDs across the entire collection hierarchy
|
||||
if (CollectionHelpers.HasDuplicateApplicationIds(rootCollection))
|
||||
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(CollectionHelpers.HasAnyValidContent))
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
"Collection contains no valid geometry. All input objects are unsupported types."
|
||||
);
|
||||
return;
|
||||
return; // error already added in validation method
|
||||
}
|
||||
|
||||
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 +85,7 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
Material = null,
|
||||
ApplicationId = InstanceGuid.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private SpeckleCollectionWrapper? ProcessInputParameter(IGH_Param inputParam, List<IGH_Goo> data, string rootName)
|
||||
{
|
||||
@@ -168,10 +140,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 +163,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,19 +184,56 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
}
|
||||
else
|
||||
{
|
||||
// add null placeholder to preserve topology (CNX-2855)
|
||||
parentCollection.Elements.Add(null);
|
||||
skippedCount++;
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"{obj?.GetType().Name} type cannot be added to collections.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add warning if objects were skipped (CNX-2855)
|
||||
if (skippedCount > 0)
|
||||
/// <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)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Skipped {skippedCount} unsupported object(s) (Leaders, TextDots, Dimensions, etc.)"
|
||||
);
|
||||
switch (element)
|
||||
{
|
||||
case SpeckleCollectionWrapper childCollection:
|
||||
// recurse into child collections
|
||||
ProcessAndCheckForDuplicateApplicationIds(childCollection, seenIds, duplicateIds);
|
||||
break;
|
||||
|
||||
case SpeckleWrapper wrapper:
|
||||
if (wrapper.ApplicationId != null && !seenIds.Add(wrapper.ApplicationId))
|
||||
{
|
||||
duplicateIds.Add(wrapper.ApplicationId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-12
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
|
||||
=> ParseSpeckleWrapper(
|
||||
collectionGoo.Value,
|
||||
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o!).CreateGoo()).ToList()
|
||||
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
|
||||
),
|
||||
|
||||
// get geometries from wrapper to override displayValue prop while parsing
|
||||
|
||||
+24
-25
@@ -1,6 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
@@ -8,7 +7,6 @@ using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Connectors.GrasshopperShared.Registration;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Dev;
|
||||
|
||||
@@ -17,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,
|
||||
@@ -63,23 +61,25 @@ public class TokenUrlComponent : GH_Component
|
||||
try
|
||||
{
|
||||
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
|
||||
var resource = Task.Run(() => SolveInstanceWithUrAndToken(urlInput, tokenInput)).GetAwaiter().GetResult();
|
||||
|
||||
var (resource, hasPermission) = SolveInstanceWithUrAndToken(urlInput, tokenInput, true);
|
||||
if (!hasPermission)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You do not have enough permission for this project.");
|
||||
}
|
||||
da.SetData(0, resource);
|
||||
}
|
||||
catch (SpeckleException ex)
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, ex.Message);
|
||||
da.AbortComponentSolution();
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, ex.ToFormattedString());
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
|
||||
da.AbortComponentSolution();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<SpeckleUrlModelResource> SolveInstanceWithUrAndToken(string input, string? token)
|
||||
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrAndToken(
|
||||
string input,
|
||||
string token,
|
||||
bool isSender
|
||||
)
|
||||
{
|
||||
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
|
||||
// Should perform validation, fill in all internal data of the component (project, model, version, account)
|
||||
@@ -89,7 +89,7 @@ public class TokenUrlComponent : GH_Component
|
||||
if (resources.Length != 1)
|
||||
{
|
||||
// POC: this shouldn't ever hit since exceptions are thrown in the FromUrlString method
|
||||
throw new SpeckleException("FromUrlString parser returned an invalid resource");
|
||||
throw new SpeckleException($"FromUrlString parser returned an invalid resource");
|
||||
}
|
||||
|
||||
var resource = resources.First();
|
||||
@@ -109,25 +109,24 @@ public class TokenUrlComponent : GH_Component
|
||||
throw new SpeckleException("Token requires profile:read and profile:email scopes");
|
||||
}
|
||||
|
||||
using var userScope = UserActivityScope.AddUserScope(account);
|
||||
IClient client = scope.Get<IClientFactory>().Create(account);
|
||||
|
||||
var project = await client.Project.Get(resource.ProjectId);
|
||||
if (project.workspaceId != null)
|
||||
var project = client.Project.Get(resource.ProjectId).Result;
|
||||
var projectPermissions = client.Project.GetPermissions(resource.ProjectId).Result;
|
||||
if (project != null && project.workspaceId != null)
|
||||
{
|
||||
_ = await client.Workspace.Get(project.workspaceId);
|
||||
var workspace = client.Workspace.Get(project.workspaceId).Result;
|
||||
}
|
||||
|
||||
switch (resource)
|
||||
{
|
||||
case SpeckleUrlLatestModelVersionResource r:
|
||||
_ = await client.Model.Get(r.ModelId, r.ProjectId);
|
||||
case SpeckleUrlLatestModelVersionResource latestVersionResource:
|
||||
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
|
||||
break;
|
||||
case SpeckleUrlModelVersionResource r:
|
||||
_ = await client.Model.Get(r.ModelId, r.ProjectId);
|
||||
|
||||
case SpeckleUrlModelVersionResource versionResource:
|
||||
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
|
||||
// TODO: this wont be the case when we have separation between send and receive components
|
||||
_ = await client.Version.Get(r.VersionId, r.ProjectId);
|
||||
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
|
||||
break;
|
||||
case SpeckleUrlModelObjectResource:
|
||||
throw new SpeckleException("Object URLs are not supported");
|
||||
@@ -135,6 +134,6 @@ public class TokenUrlComponent : GH_Component
|
||||
throw new SpeckleException("Unknown Speckle resource type");
|
||||
}
|
||||
|
||||
return resource;
|
||||
return (resource, isSender ? projectPermissions.canPublish.authorized : projectPermissions.canLoad.authorized);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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) { }
|
||||
|
||||
|
||||
+96
-210
@@ -1,6 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
@@ -13,7 +12,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
/// Given a list of objects, this component will filter the list for objects that match the queries.
|
||||
/// </summary>
|
||||
[Guid("26AEA046-4DD4-4F61-8251-E92A6D2AC880")]
|
||||
public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
public class FilterSpeckleObjects : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_objects_filter;
|
||||
@@ -50,6 +49,17 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[3].Optional = true;
|
||||
|
||||
pManager.AddTextParameter(
|
||||
"Application Id",
|
||||
"aID",
|
||||
"Find objects with a matching applicationId",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[4].Optional = true;
|
||||
|
||||
pManager.AddTextParameter("Speckle Id", "sID", "Find objects with a matching Speckle id", GH_ParamAccess.item);
|
||||
Params.Input[5].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
@@ -93,39 +103,101 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
dataAccess.GetData(2, ref property);
|
||||
string material = "";
|
||||
dataAccess.GetData(3, ref material);
|
||||
|
||||
// optional parameters - only read if they've been added via ⊕
|
||||
string appId = "";
|
||||
dataAccess.GetData(4, ref appId);
|
||||
string speckleId = "";
|
||||
int? appIdIndex = FindInputIndexByName("Application Id");
|
||||
int? speckleIdIndex = FindInputIndexByName("Speckle Id");
|
||||
|
||||
if (appIdIndex.HasValue)
|
||||
{
|
||||
dataAccess.GetData(appIdIndex.Value, ref appId);
|
||||
}
|
||||
|
||||
if (speckleIdIndex.HasValue)
|
||||
{
|
||||
dataAccess.GetData(speckleIdIndex.Value, ref speckleId);
|
||||
}
|
||||
|
||||
bool filterByAppId = appIdIndex.HasValue;
|
||||
bool filterBySpeckleId = speckleIdIndex.HasValue;
|
||||
dataAccess.GetData(5, ref speckleId);
|
||||
|
||||
List<SpeckleWrapper> matchedObjects = new();
|
||||
List<SpeckleWrapper> removedObjects = new();
|
||||
|
||||
foreach (SpeckleWrapper wrapper in objects.Cast<SpeckleWrapper>())
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
{
|
||||
if (MatchesAllFilters(wrapper, name, property, material, appId, filterByAppId, speckleId, filterBySpeckleId))
|
||||
SpeckleWrapper wrapper = objects[i]!;
|
||||
|
||||
// filter by name
|
||||
if (!MatchesSearchPattern(name, wrapper.Name))
|
||||
{
|
||||
matchedObjects.Add(wrapper);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by property
|
||||
bool foundProperty = false;
|
||||
if (string.IsNullOrEmpty(property))
|
||||
{
|
||||
foundProperty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
|
||||
? dataObjPropWrapper.Properties
|
||||
: wrapper is SpeckleGeometryWrapper geoPropWrapper
|
||||
? geoPropWrapper.Properties
|
||||
: null;
|
||||
|
||||
if (properties is not null)
|
||||
{
|
||||
// use flattened properties to search ALL nested property keys
|
||||
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
|
||||
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
|
||||
foreach (string key in flattenedProps.Keys)
|
||||
{
|
||||
if (MatchesSearchPattern(property, key))
|
||||
{
|
||||
foundProperty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundProperty)
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by material name
|
||||
bool materialMatches = true;
|
||||
if (!string.IsNullOrEmpty(material))
|
||||
{
|
||||
materialMatches = false;
|
||||
|
||||
if (wrapper is SpeckleGeometryWrapper geoWrapper)
|
||||
{
|
||||
materialMatches = MatchesSearchPattern(material, geoWrapper.Material?.Name ?? "");
|
||||
}
|
||||
else if (wrapper is SpeckleDataObjectWrapper dataObjWrapper)
|
||||
{
|
||||
// check if ANY geometry in the data object has a matching material (not sure about this...)
|
||||
// fix for [CNX-2512](https://linear.app/speckle/issue/CNX-2512/filter-objects-material-and-property-key-inputs-dont-work-as-expected)
|
||||
materialMatches = dataObjWrapper.Geometries.Any(geo =>
|
||||
MatchesSearchPattern(material, geo.Material?.Name ?? "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!materialMatches)
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by application id
|
||||
if (!MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by speckle id
|
||||
if (!MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
|
||||
{
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedObjects.Add(wrapper);
|
||||
}
|
||||
|
||||
// Set output objects
|
||||
@@ -142,190 +214,4 @@ public class FilterSpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
|
||||
return Operator.IsSymbolNameLike(target, searchPattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a wrapper matches all active filter criteria.
|
||||
/// </summary>
|
||||
private bool MatchesAllFilters(
|
||||
SpeckleWrapper wrapper,
|
||||
string name,
|
||||
string property,
|
||||
string material,
|
||||
string appId,
|
||||
bool filterByAppId,
|
||||
string speckleId,
|
||||
bool filterBySpeckleId
|
||||
)
|
||||
{
|
||||
// filter by name
|
||||
if (!MatchesSearchPattern(name, wrapper.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by property
|
||||
if (!MatchesPropertyFilter(wrapper, property))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by material name
|
||||
if (!MatchesMaterialFilter(wrapper, material))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by application id (only if parameter was added)
|
||||
if (filterByAppId && !MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter by speckle id (only if parameter was added)
|
||||
if (filterBySpeckleId && !MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool MatchesPropertyFilter(SpeckleWrapper wrapper, string property)
|
||||
{
|
||||
if (string.IsNullOrEmpty(property))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
|
||||
? dataObjPropWrapper.Properties
|
||||
: wrapper is SpeckleGeometryWrapper geoPropWrapper
|
||||
? geoPropWrapper.Properties
|
||||
: null;
|
||||
|
||||
if (properties is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// use flattened properties to search ALL nested property keys
|
||||
return properties.Flatten().Keys.Any(key => MatchesSearchPattern(property, key));
|
||||
}
|
||||
|
||||
private bool MatchesMaterialFilter(SpeckleWrapper wrapper, string material)
|
||||
{
|
||||
if (string.IsNullOrEmpty(material))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wrapper is SpeckleGeometryWrapper geoWrapper)
|
||||
{
|
||||
return MatchesSearchPattern(material, geoWrapper.Material?.Name ?? "");
|
||||
}
|
||||
|
||||
if (wrapper is SpeckleDataObjectWrapper dataObjWrapper)
|
||||
{
|
||||
// check if ANY geometry in the data object has a matching material
|
||||
return dataObjWrapper.Geometries.Any(geo => MatchesSearchPattern(material, geo.Material?.Name ?? ""));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of an input parameter by its Name.
|
||||
/// Returns null if the parameter doesn't exist.
|
||||
/// </summary>
|
||||
private int? FindInputIndexByName(string paramName)
|
||||
{
|
||||
for (int i = 0; i < Params.Input.Count; i++)
|
||||
{
|
||||
if (Params.Input[i].Name == paramName)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#region IGH_VariableParameterComponent
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
if (side != GH_ParameterSide.Input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow inserting after the fixed parameters (index 4+)
|
||||
if (index < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check how many optional params are already added (total inputs - 4 fixed)
|
||||
int addedOptionalCount = Params.Input.Count - 4;
|
||||
|
||||
// we have 2 optional parameters available
|
||||
return addedOptionalCount < 2;
|
||||
}
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index) =>
|
||||
// only allow removing optional input parameters (index 4+)
|
||||
side == GH_ParameterSide.Input
|
||||
&& index >= 4;
|
||||
|
||||
/// <remarks>
|
||||
/// The ternary operator for NickName is needed due to a Grasshopper quirk where
|
||||
/// dynamically created parameters don't respect the "Draw Full Names" setting automatically.
|
||||
/// We check CanvasFullNames at creation time to set the appropriate NickName.
|
||||
/// This does not handle the case where the user toggles "Draw Full Names" while the
|
||||
/// component is already on the canvas. Handling that would require subscribing to
|
||||
/// Grasshopper.CentralSettings.CanvasFullNamesChanged event, which is overkill for now.
|
||||
/// </remarks>
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
bool hasAppId = FindInputIndexByName("Application Id").HasValue;
|
||||
bool hasSpeckleId = FindInputIndexByName("Speckle Id").HasValue;
|
||||
|
||||
if (!hasAppId)
|
||||
{
|
||||
return new Param_String
|
||||
{
|
||||
Name = "Application Id",
|
||||
NickName = Grasshopper.CentralSettings.CanvasFullNames ? "Application Id" : "aID", // see remarks
|
||||
Description = "Find objects with a matching applicationId",
|
||||
Access = GH_ParamAccess.item,
|
||||
Optional = true
|
||||
};
|
||||
}
|
||||
|
||||
if (!hasSpeckleId)
|
||||
{
|
||||
return new Param_String
|
||||
{
|
||||
Name = "Speckle Id",
|
||||
NickName = Grasshopper.CentralSettings.CanvasFullNames ? "Speckle Id" : "sID", // see remarks
|
||||
Description = "Find objects with a matching Speckle id",
|
||||
Access = GH_ParamAccess.item,
|
||||
Optional = true
|
||||
};
|
||||
}
|
||||
|
||||
return new Param_String();
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Input && index >= 4;
|
||||
|
||||
public void VariableParameterMaintenance()
|
||||
{
|
||||
// ensure all optional parameters stay marked as optional
|
||||
for (int i = 4; i < Params.Input.Count; i++)
|
||||
{
|
||||
Params.Input[i].Optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
+3
-10
@@ -9,7 +9,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
|
||||
public class SpeckleBlockDefinitionPassthrough()
|
||||
: SpecklePassthroughComponentBase(
|
||||
: SpeckleSolveInstance(
|
||||
"Speckle Block Definition",
|
||||
"SBD",
|
||||
"Create or modify a Speckle Block Definition",
|
||||
@@ -21,9 +21,6 @@ public class SpeckleBlockDefinitionPassthrough()
|
||||
protected override Bitmap Icon => Resources.speckle_objects_block_def;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
protected override int FixedInputCount => 3;
|
||||
protected override int FixedOutputCount => 3;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
@@ -125,16 +122,12 @@ public class SpeckleBlockDefinitionPassthrough()
|
||||
result.Value.Name = inputName;
|
||||
}
|
||||
|
||||
// process application id (only if user provided one, otherwise preserve existing)
|
||||
if (TryGetApplicationIdInput(da, out string? inputAppId))
|
||||
{
|
||||
result.Value.ApplicationId = inputAppId;
|
||||
}
|
||||
// no need to process application Id.
|
||||
// New definitions should have a new appID generated in the new() constructor, and we want to preserve old appID otherwise for changetracking.
|
||||
|
||||
// set outputs
|
||||
da.SetData(0, result);
|
||||
da.SetDataList(1, result.Value.Objects.Select(o => o.CreateGoo()));
|
||||
da.SetData(2, result.Value.Name);
|
||||
SetApplicationIdOutput(da, result.Value.ApplicationId);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -9,7 +9,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
|
||||
public class SpeckleBlockInstancePassthrough()
|
||||
: SpecklePassthroughComponentBase(
|
||||
: SpeckleSolveInstance(
|
||||
"Speckle Block Instance",
|
||||
"SBI",
|
||||
"Create or modify a Speckle Block Instance",
|
||||
@@ -21,9 +21,6 @@ public class SpeckleBlockInstancePassthrough()
|
||||
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
protected override int FixedInputCount => 7;
|
||||
protected override int FixedOutputCount => 7;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
int instanceIndex = pManager.AddParameter(
|
||||
@@ -208,11 +205,8 @@ public class SpeckleBlockInstancePassthrough()
|
||||
result.Value.Material = inputMaterial.Value;
|
||||
}
|
||||
|
||||
// process application id (only if user provided one, otherwise preserve existing)
|
||||
if (TryGetApplicationIdInput(da, out string? inputAppId))
|
||||
{
|
||||
result.Value.ApplicationId = inputAppId;
|
||||
}
|
||||
// no need to process application id.
|
||||
// new appids are generated if this is a new object, otherwise the input object appID should be preserved for change tracking.
|
||||
|
||||
// Set outputs
|
||||
da.SetData(0, result);
|
||||
@@ -222,7 +216,6 @@ public class SpeckleBlockInstancePassthrough()
|
||||
da.SetData(4, result.Value.Properties);
|
||||
da.SetData(5, result.Value.Color);
|
||||
da.SetData(6, result.Value.Material);
|
||||
SetApplicationIdOutput(da, result.Value.ApplicationId);
|
||||
}
|
||||
|
||||
private Transform? ExtractTransform(IGH_Goo input) =>
|
||||
|
||||
+4
-16
@@ -8,7 +8,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("5CE8AA40-7706-4893-853D-4C77604548FA")]
|
||||
public class SpeckleDataObjectPassthrough()
|
||||
: SpecklePassthroughComponentBase(
|
||||
: SpeckleSolveInstance(
|
||||
"Speckle Data Object",
|
||||
"SDO",
|
||||
"Create or modify a Speckle Data Object",
|
||||
@@ -20,9 +20,6 @@ public class SpeckleDataObjectPassthrough()
|
||||
protected override Bitmap Icon => Resources.speckle_objects_dataobject;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override int FixedInputCount => 4;
|
||||
protected override int FixedOutputCount => 5;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
int objIndex = pManager.AddParameter(
|
||||
@@ -161,17 +158,9 @@ public class SpeckleDataObjectPassthrough()
|
||||
result.Properties = inputProperties;
|
||||
}
|
||||
|
||||
// process application id (only if user provided one)
|
||||
if (TryGetApplicationIdInput(da, out string? inputAppId))
|
||||
{
|
||||
result.ApplicationId = inputAppId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
// 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 =
|
||||
@@ -183,6 +172,5 @@ public class SpeckleDataObjectPassthrough()
|
||||
da.SetData(2, result.Name);
|
||||
da.SetData(3, result.Properties);
|
||||
da.SetData(4, path);
|
||||
SetApplicationIdOutput(da, result.ApplicationId);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -10,7 +10,7 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
|
||||
public class SpeckleGeometryPassthrough()
|
||||
: SpecklePassthroughComponentBase(
|
||||
: SpeckleSolveInstance(
|
||||
"Speckle Geometry",
|
||||
"SG",
|
||||
"Create or modify a Speckle Geometry",
|
||||
@@ -22,9 +22,6 @@ public class SpeckleGeometryPassthrough()
|
||||
protected override Bitmap Icon => Resources.speckle_objects_geometry;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override int FixedInputCount => 6;
|
||||
protected override int FixedOutputCount => 7;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
int objIndex = pManager.AddGenericParameter(
|
||||
@@ -223,11 +220,8 @@ public class SpeckleGeometryPassthrough()
|
||||
result.Material = inputMaterial.Value;
|
||||
}
|
||||
|
||||
// process application id (only if user provided one, otherwise preserve existing)
|
||||
if (TryGetApplicationIdInput(da, out string? inputAppId))
|
||||
{
|
||||
result.ApplicationId = inputAppId;
|
||||
}
|
||||
// no need to process application Id.
|
||||
// New definitions should have a new appID generated in the new() constructor, and we want to preserve old appID otherwise for changetracking.
|
||||
|
||||
// get the path
|
||||
string? path =
|
||||
@@ -241,7 +235,6 @@ public class SpeckleGeometryPassthrough()
|
||||
da.SetData(4, result.Color);
|
||||
da.SetData(5, result.Material);
|
||||
da.SetData(6, path);
|
||||
SetApplicationIdOutput(da, result.ApplicationId);
|
||||
}
|
||||
|
||||
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
|
||||
|
||||
+1
-13
@@ -1,19 +1,8 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
|
||||
|
||||
/// <param name="Account"></param>
|
||||
/// <param name="WorkspaceId"></param>
|
||||
/// <param name="ProjectId"></param>
|
||||
/// <param name="ProjectName"></param>
|
||||
/// <param name="ModelId"></param>
|
||||
/// <param name="ModelName"></param>
|
||||
/// <param name="SelectedVersionId"></param>
|
||||
/// <param name="SourceApplication">See <see cref="Version.sourceApplication"/></param>
|
||||
/// <param name="ReceivingApplicationSlug">Slug of the application doing the receiving (i.e. the current host app)</param>
|
||||
/// <param name="SelectedVersionUserId"></param>
|
||||
public record GrasshopperReceiveInfo(
|
||||
Account Account,
|
||||
string? WorkspaceId,
|
||||
@@ -23,6 +12,5 @@ public record GrasshopperReceiveInfo(
|
||||
string ModelName,
|
||||
string SelectedVersionId,
|
||||
string SourceApplication,
|
||||
string ReceivingApplicationSlug,
|
||||
string? SelectedVersionUserId
|
||||
) : ReceiveInfo(Account, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, ReceivingApplicationSlug);
|
||||
) : ReceiveInfo(Account, ProjectId, ProjectName, ModelId, ModelName, SelectedVersionId, SourceApplication);
|
||||
|
||||
+64
-80
@@ -130,7 +130,7 @@ public class ReceiveAsyncComponent : GH_AsyncComponent<ReceiveAsyncComponent>
|
||||
{
|
||||
var autoReceiveMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Load new versions automatically",
|
||||
"Load automatically",
|
||||
(s, e) =>
|
||||
{
|
||||
AutoReceive = !AutoReceive;
|
||||
@@ -447,89 +447,73 @@ 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();
|
||||
|
||||
SpecklePropertyGroupGoo? rootPropertiesGoo = null;
|
||||
if (Root is RootCollection rootCollection && rootCollection.properties.Count > 0)
|
||||
{
|
||||
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);
|
||||
rootPropertiesGoo = new SpecklePropertyGroupGoo(rootCollection.properties);
|
||||
}
|
||||
finally
|
||||
|
||||
// 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)
|
||||
{
|
||||
SpeckleConversionContext.EndCurrent();
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+84
-91
@@ -143,102 +143,95 @@ 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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, RootProperties = rootPropertiesGoo };
|
||||
}
|
||||
|
||||
private void SetupSubscription(SpeckleUrlModelResource resource)
|
||||
|
||||
+8
-3
@@ -1,7 +1,12 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
|
||||
|
||||
public record GrasshopperSendInfo(IClient Client, string? WorkspaceId, string ProjectId, string ModelId)
|
||||
: SendInfo(Client, ProjectId, ModelId);
|
||||
public record GrasshopperSendInfo(
|
||||
Account Account,
|
||||
string? WorkspaceId,
|
||||
string ProjectId,
|
||||
string ModelId,
|
||||
string SourceApplication
|
||||
) : SendInfo(Account, ProjectId, ModelId, SourceApplication);
|
||||
|
||||
+11
-11
@@ -87,7 +87,6 @@ public class SendAsyncComponent : GH_AsyncComponent<SendAsyncComponent>
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
pManager.AddTextParameter("Version ID", "V", "ID of the created version", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
@@ -322,7 +321,6 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
|
||||
private Stopwatch? _stopwatch;
|
||||
public SpeckleUrlModelResource? OutputParam { get; set; }
|
||||
public string? OutputVersionId { get; set; }
|
||||
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
|
||||
|
||||
public override WorkerInstance<SendAsyncComponent> Duplicate(string id, CancellationToken cancellationToken)
|
||||
@@ -334,7 +332,6 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
{
|
||||
_stopwatch = new Stopwatch();
|
||||
_stopwatch.Start();
|
||||
OutputVersionId = null;
|
||||
}
|
||||
|
||||
public override void SetData(IGH_DataAccess da)
|
||||
@@ -345,7 +342,6 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
{
|
||||
Parent.JustPastedIn = false;
|
||||
da.SetData(0, Parent.OutputParam);
|
||||
da.SetData(1, OutputVersionId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,7 +357,6 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
}
|
||||
|
||||
da.SetData(0, OutputParam);
|
||||
da.SetData(1, OutputVersionId);
|
||||
|
||||
Parent.CurrentComponentState = ComponentState.UpToDate;
|
||||
Parent.OutputParam = OutputParam; // ref the outputs in the parent too, so we can serialise them on write/read
|
||||
@@ -378,7 +373,7 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
*/
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
"Successfully published to Speckle. Right-click on the component to view online."
|
||||
$"Successfully published to Speckle. Right-click on the component to view online."
|
||||
);
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
@@ -437,16 +432,22 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
// Step 1 - SEND TO SERVER
|
||||
var sendInfo = await urlModelResource.GetSendInfo(Parent.ApiClient, CancellationToken).ConfigureAwait(false);
|
||||
|
||||
var (fileName, fileBytes) = SendComponent.GetGrasshopperFileInfo();
|
||||
var progress = new Progress<CardProgress>(p =>
|
||||
{
|
||||
reportProgress(Id, p.Progress ?? 0);
|
||||
//sendComponent.Message = $"{p.Status}";
|
||||
});
|
||||
|
||||
using var scope = PriorityLoader.CreateScopeForActiveDocument();
|
||||
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
(SendOperationResult result, string versionId) = await sendOperation
|
||||
.Send([rootCollectionWrapper], sendInfo, fileName, fileBytes, Parent.VersionMessage, progress, CancellationToken)
|
||||
SendOperationResult? result = await sendOperation
|
||||
.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo> { rootCollectionWrapper },
|
||||
sendInfo,
|
||||
Parent.VersionMessage,
|
||||
progress,
|
||||
CancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
@@ -467,10 +468,9 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
sendInfo.WorkspaceId,
|
||||
sendInfo.ProjectId,
|
||||
sendInfo.ModelId,
|
||||
versionId
|
||||
result.VersionId
|
||||
);
|
||||
OutputParam = createdVersion;
|
||||
OutputVersionId = versionId;
|
||||
Parent.Url = $"{createdVersion.Account.Server}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
}
|
||||
}
|
||||
|
||||
+11
-26
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using Grasshopper;
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Analytics;
|
||||
@@ -37,10 +36,9 @@ public class SendComponentInput
|
||||
}
|
||||
}
|
||||
|
||||
public class SendComponentOutput(SpeckleUrlModelResource? resource, string? versionId = null)
|
||||
public class SendComponentOutput(SpeckleUrlModelResource? resource)
|
||||
{
|
||||
public SpeckleUrlModelResource? Resource { get; } = resource;
|
||||
public string? VersionId { get; } = versionId;
|
||||
}
|
||||
|
||||
public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, SendComponentOutput>
|
||||
@@ -88,11 +86,8 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
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());
|
||||
pManager.AddTextParameter("Version ID", "V", "ID of the created version", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override SendComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
@@ -139,7 +134,6 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
else
|
||||
{
|
||||
da.SetData(0, result.Resource);
|
||||
da.SetData(1, result.VersionId);
|
||||
Message = "Done";
|
||||
}
|
||||
}
|
||||
@@ -214,8 +208,6 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
throw new SpeckleAccountManagerException("No default account was found");
|
||||
}
|
||||
|
||||
var (fileName, fileBytes) = GetGrasshopperFileInfo();
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
@@ -224,8 +216,14 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
var (result, versionId) = await sendOperation
|
||||
.Send([collectionToSend], sendInfo, fileName, fileBytes, VersionMessage, progress, cancellationToken)
|
||||
await sendOperation
|
||||
.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo> { collectionToSend },
|
||||
sendInfo,
|
||||
VersionMessage,
|
||||
progress,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
@@ -246,19 +244,6 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
sendInfo.ModelId
|
||||
);
|
||||
Url = $"{sendInfo.Account.serverInfo.url}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
return new SendComponentOutput(createdVersionResource, versionId);
|
||||
}
|
||||
|
||||
public static (string? fileName, long? fileSizeBytes) GetGrasshopperFileInfo()
|
||||
{
|
||||
var doc = Instances.ActiveCanvas?.Document;
|
||||
|
||||
if (doc is null || !File.Exists(doc.FilePath))
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
var fileInfo = new FileInfo(doc.FilePath);
|
||||
|
||||
return (fileInfo.Name, fileInfo.Length);
|
||||
return new SendComponentOutput(createdVersionResource);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-10
@@ -108,13 +108,11 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
try
|
||||
{
|
||||
// NOTE: once we split the logic in Sender and Receiver components, we need to set flag correctly
|
||||
var (resource, permissionCheck) = SpeckleOperationWizard.SolveInstanceWithUrlInput(urlInput, true, null);
|
||||
|
||||
if (!permissionCheck.authorized)
|
||||
var (resource, hasPermission) = SpeckleOperationWizard.SolveInstanceWithUrlInput(urlInput, true, null);
|
||||
if (!hasPermission)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, permissionCheck.message);
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "You do not have enough permission for this project.");
|
||||
}
|
||||
|
||||
_storedUserId = SpeckleOperationWizard.SelectedAccount?.id;
|
||||
_storedServer = resource.Account.Server;
|
||||
da.SetData(0, resource);
|
||||
@@ -164,14 +162,11 @@ public class SpeckleSelectModelComponent : GH_Component
|
||||
SpeckleOperationWizard.SetWorkspaceFromSavedIdSync(_storedWorkspaceId!);
|
||||
}
|
||||
|
||||
// NOTE FOR LATER: Need to be handled in SDK... and will come later by Jeddward Morgan...
|
||||
if (SpeckleOperationWizard.WorkspaceMenuHandler.Workspaces == null)
|
||||
{
|
||||
var workspaces = SpeckleOperationWizard.FetchWorkspacesSync("");
|
||||
if (workspaces == null)
|
||||
{
|
||||
_storedWorkspaceId = null;
|
||||
}
|
||||
else if (workspaces.items.Count == 0)
|
||||
if (workspaces.items.Count == 0)
|
||||
{
|
||||
// Create a workspace flow
|
||||
SpeckleOperationWizard.CreateNewWorkspaceUIState();
|
||||
|
||||
+65
-23
@@ -1,6 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Registration;
|
||||
@@ -67,7 +66,7 @@ public class SpeckleOperationWizard
|
||||
ModelMenuHandler.ModelSelected += OnModelSelected;
|
||||
}
|
||||
|
||||
public (SpeckleUrlModelResource, PermissionCheckResult) SolveInstanceWithUrlInput(
|
||||
public (SpeckleUrlModelResource resource, bool hasPermission) SolveInstanceWithUrlInput(
|
||||
string input,
|
||||
bool isSender,
|
||||
string? token
|
||||
@@ -115,7 +114,7 @@ public class SpeckleOperationWizard
|
||||
$"No appropriate account found for the given '{urlDerivedAccount?.serverInfo.url}' server"
|
||||
);
|
||||
}
|
||||
using var userScope = UserActivityScope.AddUserScope(SelectedAccount);
|
||||
|
||||
IClient client = _clientFactory.Create(SelectedAccount);
|
||||
|
||||
var project = client.Project.Get(resource.ProjectId).Result;
|
||||
@@ -128,20 +127,18 @@ public class SpeckleOperationWizard
|
||||
|
||||
ProjectMenuHandler.RedrawMenuButton(project);
|
||||
|
||||
ModelPermissionChecks modelPermissions;
|
||||
switch (resource)
|
||||
{
|
||||
case SpeckleUrlLatestModelVersionResource r:
|
||||
var model = client.Model.Get(r.ModelId, r.ProjectId).Result;
|
||||
modelPermissions = client.Model.GetPermissions(r.ProjectId, r.ModelId).Result;
|
||||
case SpeckleUrlLatestModelVersionResource latestVersionResource:
|
||||
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
|
||||
ModelMenuHandler.RedrawMenuButton(model);
|
||||
break;
|
||||
case SpeckleUrlModelVersionResource r:
|
||||
var m = client.Model.Get(r.ModelId, r.ProjectId).Result;
|
||||
case SpeckleUrlModelVersionResource versionResource:
|
||||
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
|
||||
ModelMenuHandler.RedrawMenuButton(m);
|
||||
modelPermissions = client.Model.GetPermissions(r.ProjectId, r.ModelId).Result;
|
||||
|
||||
// TODO: this wont be the case when we have separation between send and receive components
|
||||
var v = client.Version.Get(r.VersionId, r.ProjectId).Result;
|
||||
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
|
||||
VersionMenuHandler?.RedrawMenuButton(v, false);
|
||||
break;
|
||||
case SpeckleUrlModelObjectResource:
|
||||
@@ -150,7 +147,7 @@ public class SpeckleOperationWizard
|
||||
throw new SpeckleException("Unknown Speckle resource type");
|
||||
}
|
||||
|
||||
return (resource, isSender ? modelPermissions.canCreateVersion : projectPermissions.canLoad);
|
||||
return (resource, isSender ? projectPermissions.canPublish.authorized : projectPermissions.canLoad.authorized);
|
||||
}
|
||||
|
||||
public void SetAccount(Account? account, bool refreshComponent = true)
|
||||
@@ -270,7 +267,7 @@ public class SpeckleOperationWizard
|
||||
/// <summary>
|
||||
/// Callback function to retrieve workspaces with the search text
|
||||
/// </summary>
|
||||
public async Task<ResourceCollection<Workspace>?> FetchWorkspaces(string searchText)
|
||||
public async Task<ResourceCollection<Workspace>> FetchWorkspaces(string searchText)
|
||||
{
|
||||
if (SelectedAccount == null)
|
||||
{
|
||||
@@ -278,11 +275,6 @@ public class SpeckleOperationWizard
|
||||
}
|
||||
|
||||
using IClient client = _clientFactory.Create(SelectedAccount);
|
||||
if (!await client.Server.IsWorkspaceEnabled())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var workspaces = await client.ActiveUser.GetWorkspaces(10, null, new UserWorkspacesFilter(searchText));
|
||||
WorkspaceMenuHandler.Workspaces = workspaces;
|
||||
return workspaces;
|
||||
@@ -291,9 +283,17 @@ public class SpeckleOperationWizard
|
||||
/// <summary>
|
||||
/// Callback function to retrieve workspaces with the search text sync
|
||||
/// </summary>
|
||||
public ResourceCollection<Workspace>? FetchWorkspacesSync(string searchText)
|
||||
public ResourceCollection<Workspace> FetchWorkspacesSync(string searchText)
|
||||
{
|
||||
return Task.Run(async () => await FetchWorkspaces(searchText)).GetAwaiter().GetResult();
|
||||
if (SelectedAccount == null)
|
||||
{
|
||||
return new ResourceCollection<Workspace>();
|
||||
}
|
||||
|
||||
using IClient client = _clientFactory.Create(SelectedAccount);
|
||||
var workspaces = client.ActiveUser.GetWorkspaces(10, null, new UserWorkspacesFilter(searchText)).Result;
|
||||
WorkspaceMenuHandler.Workspaces = workspaces;
|
||||
return workspaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -327,7 +327,27 @@ public class SpeckleOperationWizard
|
||||
/// </summary>
|
||||
public ResourceCollection<ProjectWithPermissions> FetchProjectsSync(string searchText)
|
||||
{
|
||||
return Task.Run(async () => await FetchProjects(searchText)).GetAwaiter().GetResult();
|
||||
if (SelectedAccount == null)
|
||||
{
|
||||
return new ResourceCollection<ProjectWithPermissions>();
|
||||
}
|
||||
|
||||
using IClient client = _clientFactory.Create(SelectedAccount);
|
||||
var workspaceId = SelectedWorkspace?.id ?? null;
|
||||
var projects = client
|
||||
.ActiveUser.GetProjectsWithPermissions(
|
||||
10,
|
||||
null,
|
||||
new UserProjectsFilter(
|
||||
searchText,
|
||||
workspaceId: workspaceId,
|
||||
includeImplicitAccess: true,
|
||||
personalOnly: WorkspaceMenuHandler.IsPersonalProjects
|
||||
)
|
||||
)
|
||||
.Result;
|
||||
ProjectMenuHandler.Projects = projects;
|
||||
return projects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -353,7 +373,17 @@ public class SpeckleOperationWizard
|
||||
/// </summary>
|
||||
public ResourceCollection<Model> FetchModelsSync(string searchText)
|
||||
{
|
||||
return Task.Run(async () => await FetchModels(searchText)).GetAwaiter().GetResult();
|
||||
if (SelectedAccount == null || SelectedProject == null)
|
||||
{
|
||||
return new ResourceCollection<Model>();
|
||||
}
|
||||
|
||||
IClient client = _clientFactory.Create(SelectedAccount);
|
||||
var projectWithModels = client
|
||||
.Project.GetWithModels(SelectedProject.id, 10, modelsFilter: new ProjectModelsFilter(search: searchText))
|
||||
.Result;
|
||||
ModelMenuHandler.Models = projectWithModels.models;
|
||||
return projectWithModels.models;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -383,7 +413,19 @@ public class SpeckleOperationWizard
|
||||
/// </summary>
|
||||
public ResourceCollection<Version> FetchVersionsSync(int versionCount)
|
||||
{
|
||||
return Task.Run(async () => await FetchVersions(versionCount)).GetAwaiter().GetResult();
|
||||
if (SelectedAccount == null || SelectedProject == null || SelectedModel == null)
|
||||
{
|
||||
return new ResourceCollection<Version>();
|
||||
}
|
||||
|
||||
using IClient client = _clientFactory.Create(SelectedAccount);
|
||||
var newVersionsResult = client.Model.GetWithVersions(SelectedModel.id, SelectedProject.id, versionCount).Result;
|
||||
if (VersionMenuHandler != null)
|
||||
{
|
||||
VersionMenuHandler.Versions = newVersionsResult.versions;
|
||||
}
|
||||
|
||||
return newVersionsResult.versions;
|
||||
}
|
||||
|
||||
public void SetDefaultWorkspaceSync()
|
||||
|
||||
+14
-14
@@ -10,9 +10,9 @@ public class WorkspaceSelectedEventArgs(LimitedWorkspace? model) : EventArgs
|
||||
|
||||
public class WorkspaceMenuHandler
|
||||
{
|
||||
private readonly Func<string, Task<ResourceCollection<Workspace>?>> _fetchWorkspaces;
|
||||
private readonly Func<string, Task<ResourceCollection<Workspace>>> _fetchWorkspaces;
|
||||
private ToolStripDropDown? _menu;
|
||||
public bool IsPersonalProjects { get; private set; }
|
||||
public bool IsPersonalProjects { get; set; }
|
||||
private SearchToolStripMenuItem? _searchItem;
|
||||
private readonly Func<Task> _createWorkspace;
|
||||
private LimitedWorkspace? SelectedWorkspace { get; set; }
|
||||
@@ -24,7 +24,7 @@ public class WorkspaceMenuHandler
|
||||
public GhContextMenuButton WorkspaceContextMenuButton { get; }
|
||||
|
||||
public WorkspaceMenuHandler(
|
||||
Func<string, Task<ResourceCollection<Workspace>?>> fetchWorkspaces,
|
||||
Func<string, Task<ResourceCollection<Workspace>>> fetchWorkspaces,
|
||||
Func<Task> createWorkspace
|
||||
)
|
||||
{
|
||||
@@ -44,7 +44,6 @@ public class WorkspaceMenuHandler
|
||||
_menu?.Close();
|
||||
Workspaces = null;
|
||||
SelectedWorkspace = null;
|
||||
IsPersonalProjects = false;
|
||||
RedrawMenuButton(null);
|
||||
}
|
||||
|
||||
@@ -52,7 +51,7 @@ public class WorkspaceMenuHandler
|
||||
{
|
||||
Workspaces = await _fetchWorkspaces.Invoke(searchText);
|
||||
// NOTE: We shouldn't call PopulateMenu here bc it will reset the search item when search is happening, it borks the state.
|
||||
PopulateModelMenuItems(_menu!, _searchItem);
|
||||
PopulateModelMenuItems(_menu!, _searchItem!);
|
||||
}
|
||||
|
||||
private bool PopulateMenu(ToolStripDropDown menu)
|
||||
@@ -61,7 +60,7 @@ public class WorkspaceMenuHandler
|
||||
_menu = menu;
|
||||
_searchItem = new SearchToolStripMenuItem(menu, Refetch);
|
||||
|
||||
if (Workspaces?.items.Count == 0)
|
||||
if (Workspaces == null || Workspaces.items.Count == 0)
|
||||
{
|
||||
menu.Items.Clear();
|
||||
_searchItem.AddMenuItem("Create a new workspace", (_, _) => _createWorkspace.Invoke());
|
||||
@@ -73,7 +72,7 @@ public class WorkspaceMenuHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PopulateModelMenuItems(ToolStripDropDown menu, SearchToolStripMenuItem? searchItem)
|
||||
private void PopulateModelMenuItems(ToolStripDropDown menu, SearchToolStripMenuItem searchItem)
|
||||
{
|
||||
for (int i = menu.Items.Count - 1; i > 1; i--)
|
||||
{
|
||||
@@ -82,12 +81,6 @@ public class WorkspaceMenuHandler
|
||||
|
||||
if (Workspaces == null)
|
||||
{
|
||||
searchItem?.AddMenuItem(
|
||||
"Personal Projects",
|
||||
(_, _) => OnWorkspaceSelected(null),
|
||||
!IsPersonalProjects,
|
||||
IsPersonalProjects
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,6 +94,13 @@ public class WorkspaceMenuHandler
|
||||
Base64ToImage(workspace.logo)
|
||||
);
|
||||
}
|
||||
|
||||
searchItem?.AddMenuItem(
|
||||
"Personal Projects",
|
||||
(_, _) => OnWorkspaceSelected(null),
|
||||
!IsPersonalProjects,
|
||||
IsPersonalProjects
|
||||
);
|
||||
}
|
||||
|
||||
private void OnWorkspaceSelected(LimitedWorkspace? workspace)
|
||||
@@ -130,7 +130,7 @@ public class WorkspaceMenuHandler
|
||||
WorkspaceContextMenuButton.SetIconOverride(null);
|
||||
WorkspaceContextMenuButton.Name = "Personal Projects";
|
||||
WorkspaceContextMenuButton.NickName = "Personal Projects";
|
||||
WorkspaceContextMenuButton.Description = "Legacy"; //Servers that don't have workspaces enabled (e.g. public opensource server (v2.x))
|
||||
WorkspaceContextMenuButton.Description = "Legacy";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
-231
@@ -1,231 +0,0 @@
|
||||
using GH_IO.Serialization;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for passthrough components with "hidden" Application ID parameter.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Users can click ⊕ to add an optional Application ID input and output.
|
||||
/// </remarks>
|
||||
public abstract class SpecklePassthroughComponentBase : SpeckleSolveInstance, IGH_VariableParameterComponent
|
||||
{
|
||||
private const string APP_ID_NAME = "Application Id";
|
||||
private const string APP_ID_NICKNAME = "aID";
|
||||
private const string APP_ID_DESCRIPTION = "The application id of the Speckle objects";
|
||||
|
||||
protected abstract int FixedInputCount { get; }
|
||||
protected abstract int FixedOutputCount { get; }
|
||||
|
||||
private bool HasApplicationIdParam => Params.Input.Count > FixedInputCount;
|
||||
|
||||
protected SpecklePassthroughComponentBase(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
string category,
|
||||
string subCategory
|
||||
)
|
||||
: base(name, nickname, description, category, subCategory) { }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the optional Application Id input. Returns true if user provided a valid value.
|
||||
/// </summary>
|
||||
protected bool TryGetApplicationIdInput(IGH_DataAccess da, out string? applicationId)
|
||||
{
|
||||
applicationId = null;
|
||||
|
||||
if (!HasApplicationIdParam)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string appId = string.Empty;
|
||||
if (da.GetData(FixedInputCount, ref appId))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(appId))
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
"Empty Application Id ignored - existing or auto-generated id will be used"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
applicationId = appId;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Application Id output (if the parameter exists).
|
||||
/// </summary>
|
||||
protected void SetApplicationIdOutput(IGH_DataAccess da, string? applicationId)
|
||||
{
|
||||
if (!HasApplicationIdParam)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
da.SetData(FixedOutputCount, applicationId);
|
||||
}
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
// only allow inserting if not yet added
|
||||
if (HasApplicationIdParam)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow at the end position
|
||||
return side switch
|
||||
{
|
||||
GH_ParameterSide.Input => index == FixedInputCount,
|
||||
GH_ParameterSide.Output => index == FixedOutputCount,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
if (!HasApplicationIdParam)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return side switch
|
||||
{
|
||||
GH_ParameterSide.Input => index == FixedInputCount,
|
||||
GH_ParameterSide.Output => index == FixedOutputCount,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The ternary for NickName handles a Grasshopper quirk where dynamically created parameters
|
||||
/// don't respect the "Draw Full Names" setting automatically.
|
||||
/// </remarks>
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
// when adding on either side, add both input and output together
|
||||
if (side == GH_ParameterSide.Input && Params.Output.Count == FixedOutputCount)
|
||||
{
|
||||
OnPingDocument()?.ScheduleSolution(5, _ => AddApplicationIdOutput());
|
||||
}
|
||||
else if (side == GH_ParameterSide.Output && Params.Input.Count == FixedInputCount)
|
||||
{
|
||||
OnPingDocument()?.ScheduleSolution(5, _ => AddApplicationIdInput());
|
||||
}
|
||||
|
||||
return CreateApplicationIdParam();
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
// when removing from either side, remove both input and output together
|
||||
if (side == GH_ParameterSide.Input && index == FixedInputCount && Params.Output.Count > FixedOutputCount)
|
||||
{
|
||||
OnPingDocument()?.ScheduleSolution(5, _ => RemoveApplicationIdOutput());
|
||||
}
|
||||
else if (side == GH_ParameterSide.Output && index == FixedOutputCount && Params.Input.Count > FixedInputCount)
|
||||
{
|
||||
OnPingDocument()?.ScheduleSolution(5, _ => RemoveApplicationIdInput());
|
||||
}
|
||||
|
||||
return side switch
|
||||
{
|
||||
GH_ParameterSide.Input => index == FixedInputCount,
|
||||
GH_ParameterSide.Output => index == FixedOutputCount,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public void VariableParameterMaintenance()
|
||||
{
|
||||
// ensure the Application Id input stays optional
|
||||
if (HasApplicationIdParam && Params.Input.Count > FixedInputCount)
|
||||
{
|
||||
Params.Input[FixedInputCount].Optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static IGH_Param CreateApplicationIdParam() =>
|
||||
new Param_String
|
||||
{
|
||||
Name = APP_ID_NAME,
|
||||
NickName = Grasshopper.CentralSettings.CanvasFullNames ? APP_ID_NAME : APP_ID_NICKNAME,
|
||||
Description = APP_ID_DESCRIPTION,
|
||||
Access = GH_ParamAccess.item,
|
||||
Optional = true
|
||||
};
|
||||
|
||||
private void AddApplicationIdInput()
|
||||
{
|
||||
if (Params.Input.Count > FixedInputCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Params.RegisterInputParam(CreateApplicationIdParam());
|
||||
Params.OnParametersChanged();
|
||||
VariableParameterMaintenance();
|
||||
ExpireSolution(true);
|
||||
}
|
||||
|
||||
private void AddApplicationIdOutput()
|
||||
{
|
||||
if (Params.Output.Count > FixedOutputCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Params.RegisterOutputParam(CreateApplicationIdParam());
|
||||
Params.OnParametersChanged();
|
||||
ExpireSolution(true);
|
||||
}
|
||||
|
||||
private void RemoveApplicationIdInput()
|
||||
{
|
||||
if (Params.Input.Count <= FixedInputCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Params.UnregisterInputParameter(Params.Input[FixedInputCount]);
|
||||
Params.OnParametersChanged();
|
||||
ExpireSolution(true);
|
||||
}
|
||||
|
||||
private void RemoveApplicationIdOutput()
|
||||
{
|
||||
if (Params.Output.Count <= FixedOutputCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Params.UnregisterOutputParameter(Params.Output[FixedOutputCount]);
|
||||
Params.OnParametersChanged();
|
||||
ExpireSolution(true);
|
||||
}
|
||||
|
||||
public override bool Write(GH_IWriter writer)
|
||||
{
|
||||
var result = base.Write(writer);
|
||||
writer.SetBoolean("HasApplicationIdParam", HasApplicationIdParam);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Read(GH_IReader reader)
|
||||
{
|
||||
var result = base.Read(reader);
|
||||
// parameters are restored by GH serialization, this flag is for reference
|
||||
bool hasAppIdParam = false;
|
||||
reader.TryGetBoolean("HasApplicationIdParam", ref hasAppIdParam);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Shared helper methods for collection components to avoid code duplication
|
||||
/// </summary>
|
||||
public static class CollectionHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a root collection wrapper with default values
|
||||
/// </summary>
|
||||
public static SpeckleCollectionWrapper CreateRootCollection(string instanceGuid) =>
|
||||
new SpeckleCollectionWrapper
|
||||
{
|
||||
Base = new Collection(),
|
||||
Name = "Unnamed",
|
||||
Path = new List<string> { "Unnamed" },
|
||||
Color = null,
|
||||
Material = null,
|
||||
ApplicationId = instanceGuid
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Validates that all application IDs are unique across the entire collection hierarchy.
|
||||
/// </summary>
|
||||
/// <returns>True if duplicates exist, false if all IDs are unique</returns>
|
||||
public static bool HasDuplicateApplicationIds(SpeckleCollectionWrapper rootCollection)
|
||||
{
|
||||
var seenIds = new HashSet<string>();
|
||||
var duplicateIds = new HashSet<string>();
|
||||
|
||||
ProcessAndCheckForDuplicateApplicationIds(rootCollection, seenIds, duplicateIds);
|
||||
|
||||
return duplicateIds.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively collects application IDs from all wrappers in the collection hierarchy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only checks the wrapper's ApplicationId, not for example geometries within DataObjects.
|
||||
/// </remarks>
|
||||
private static 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively checks if collection or any descendants contain valid geometry/data objects
|
||||
/// </summary>
|
||||
public static bool HasAnyValidContent(ISpeckleCollectionObject? element) =>
|
||||
element switch
|
||||
{
|
||||
SpeckleGeometryWrapper => true,
|
||||
SpeckleDataObjectWrapper => true,
|
||||
SpeckleCollectionWrapper collection => collection.Elements.Any(HasAnyValidContent),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+3
-19
@@ -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,5 +1,4 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.Operations.Send;
|
||||
@@ -51,7 +50,6 @@ public record SpeckleUrlLatestModelVersionResource(
|
||||
model.name,
|
||||
version.id,
|
||||
version.sourceApplication.NotNull(),
|
||||
HostApplications.Grasshopper.Slug,
|
||||
version.authorUser?.id
|
||||
);
|
||||
|
||||
@@ -67,7 +65,13 @@ public record SpeckleUrlLatestModelVersionResource(
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new GrasshopperSendInfo(client, WorkspaceId, ProjectId, ModelId);
|
||||
return new GrasshopperSendInfo(
|
||||
client.Account,
|
||||
WorkspaceId,
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +101,6 @@ public record SpeckleUrlModelVersionResource(
|
||||
model.name,
|
||||
VersionId,
|
||||
version.sourceApplication.NotNull(),
|
||||
HostApplications.Grasshopper.Slug,
|
||||
version.authorUser?.id
|
||||
);
|
||||
|
||||
@@ -113,7 +116,13 @@ public record SpeckleUrlModelVersionResource(
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new GrasshopperSendInfo(client, WorkspaceId, ProjectId, ModelId);
|
||||
return new GrasshopperSendInfo(
|
||||
client.Account,
|
||||
WorkspaceId,
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ public class GrasshopperReceiveOperation
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await apiClient
|
||||
.Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.ReceivingApplicationSlug), cancellationToken)
|
||||
.Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return commitObject;
|
||||
}
|
||||
|
||||
+118
-354
@@ -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,412 +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);
|
||||
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;
|
||||
|
||||
var primitiveConverted = dataObject
|
||||
.displayValue.Where(item => item is not InstanceProxy)
|
||||
.SelectMany(item => SpeckleConversionContext.Current.ConvertToHost(item))
|
||||
.ToList();
|
||||
SpeckleMaterialWrapper? dataObjMat = _materialUnpacker.Cache.TryGetValue(
|
||||
dataObject.applicationId ?? "",
|
||||
out var cachedDataObjMaterial
|
||||
)
|
||||
? cachedDataObjMaterial
|
||||
: null;
|
||||
|
||||
resolvedGeometries.AddRange(ConvertToGeometryWrappers(primitiveConverted));
|
||||
// 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,
|
||||
};
|
||||
|
||||
var dataObjectWrapper = CreateDataObjectWrapper(dataObject, resolvedGeometries, path, objectCollection);
|
||||
geometries.Add(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(dataObjectWrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-14
@@ -78,18 +78,12 @@ public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollection
|
||||
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();
|
||||
@@ -138,13 +132,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+35
-47
@@ -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;
|
||||
|
||||
+1
-7
@@ -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,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user