Compare commits

..

41 Commits

Author SHA1 Message Date
Claire Kuang 94de06e842 Update SpecklePropertyGoo.cs 2025-06-30 15:08:43 +01:00
Björn Steinhagen 641f6fcc76 fix(grasshopper): prevent duplicate nested block instances in receive (#958)
* fix: `consumedObjectIds` tracking preventing duplicate nested block instances

* refactor: remove redundant `isDefinitionObject` logic
2025-06-30 13:48:10 +01:00
Björn Steinhagen 65313008a4 fix(grasshopper): receive pipeline polish (#957)
* fix: loading, publish and query

* docs: explanations

* fix: format

* docs: remove old comment

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-29 16:19:47 +02:00
Claire Kuang f5ad1cff39 fix(grasshopper): a bunch of instance vs object casting issues (#954)
* adds instance converter and filter objects casting helper

* adds missing casting an fixes casting to speckle object passthrough

* cleans up more casting logic

* more casting fixes

* Update InstanceReferenceGeometryToSpeckleConverter.cs

* fixes goos inputs and outputs for expand collection, query, filter, etc

* removes deep copying from casting

* more mutations on object passthrough

* fixes missing model object instance  casting properties

* fixes build

* model instance and def casting issues for nesting

* im literally crying

* Update InstanceReferenceGeometryToSpeckleConverter.cs

* fix: POC disabling cyclomatic complexity

* refactors speckle object code to reduce complexity

* further strips model object casting since rhino objects can be passed as model objects

* light at the end of the tunnel

* last commit i swear

fixes model insstance casting since this actually registers as IGH_GeometricGoo of type reference

* last LAST commit on god

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
2025-06-29 11:47:26 +02:00
Claire Kuang 5670b8e99e Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-27 20:06:26 +01:00
Björn 39c4f2c7d4 chore: removing todo comments 2025-06-26 23:09:49 +02:00
Björn Steinhagen c2bb8b319d fix(grasshopper): improve block instance preview for nested instances (#951)
* fix: display block instances

- problem on deeply nested instances
- good poc

* chore: missing method in block definition

* fix: re-instating changes after param classes refactor

* fix: add 3-level depth limit for block instance DrawPreview methods

* fixes collection preview and some isvalid props

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-26 17:46:34 +01:00
Björn 2e2c8e1732 fix: throw if id null
review comment
2025-06-26 17:18:04 +02:00
Björn 6e6e737807 fix: auto-sync block instance definition references 2025-06-26 16:55:01 +02:00
Björn 8c8320ea75 fix: url broken 2025-06-26 15:54:24 +02:00
Björn 4d81bce0ea fix: preserve id in SpeckleBlockInstanceWrapper round-trip casting 2025-06-26 14:00:02 +02:00
Björn d513187acd fix: SpeckleBlockInstanceWrapper inheritance in DeepCopy() 2025-06-26 12:59:41 +02:00
Björn af3810bc98 chore: xml comment on the parameterless constructor 2025-06-26 11:14:04 +02:00
Claire Kuang 02f3c1c3fa Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-26 01:58:20 +01:00
Claire Kuang 31c63cce22 moves all param classes to their own file (#950) 2025-06-26 01:37:43 +01:00
Claire Kuang f94acec6a5 adds create goo to wrapper class
also adds missing instance casting to create collection
2025-06-25 22:01:01 +01:00
Björn Steinhagen 093dbc826b feat(grasshopper): update load to handle blocks (#947)
* feat: block recognition in receive pipeline

* feat: `GrasshopperBlockUnpacker`

* feat: working nested blocks and removing `LocalToGlobalUnpacker`

* chore: cleanup

* refactor: remove unnecessary check

* refactor: better naming

* pr review fixes

* adds colors and materials to instance and defs on load

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-25 16:32:13 +01:00
Claire Kuang 5edaee1fa8 feat(grasshopper): adds color and materials to instances (#945)
* adds color and materials to instances

also includes refactoring for passthrough nodes

* some more appid and name changes

* removes unnecessary casting

also fixes some bugs with missing applicationIds on cast

* Update SpeckleBlockInstanceWrapper.ModelObjects.cs

* Update SpeckleBlockInstancePassthrough.cs

* fixes broken url
2025-06-25 14:26:09 +01:00
Claire Kuang 54b14b8bc9 fix dev merge changes 2025-06-25 11:20:26 +01:00
Claire Kuang a7ab4915c3 Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-25 11:09:21 +01:00
Björn 03d231b056 Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-23 15:31:27 +02:00
Björn Steinhagen 445e10dbb4 feat(grasshopper): nested block support (#934) 2025-06-23 14:55:47 +02:00
Björn c58a8d2757 Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-23 12:31:31 +02:00
Claire Kuang dfb75d98c1 adds new icons for blocks 2025-06-18 20:11:35 +01:00
Claire Kuang a93f9e9c42 Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-18 19:51:58 +01:00
Björn cd8dab48ae Merge remote-tracking branch 'origin/dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-17 16:22:12 +02:00
Björn Steinhagen a78c8962be refactor(grasshopper): make SpeckleBlockInstanceWrapper inherit from SpeckleObjectWrapper (#917)
* feat: first pass

* fix: deconstruct speckle params

* refactor: cleanup

* docs: `GeometryBase`note on `SpeckleBlockInstanceWrapper`

* refactor: CreateGoo() to remove type checking in ExpandCollection

* refactor: incorporating pr comments

* fixes deconstruct component

adds removed support for materials and collections

consolidates logic for name and output creation

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-17 16:19:08 +02:00
Claire Kuang a91ecd9e88 Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-16 20:37:05 +01:00
Björn 0ed086d336 fix: still need explicit cases 2025-06-12 19:26:17 +02:00
Björn 9132514d86 fix: compiler errors 2025-06-12 11:20:58 +02:00
Björn f5c0a9eb96 refactor: SpeckleBlockInstanceWrapper to use BakingHelper 2025-06-12 11:14:45 +02:00
Björn 5d4b08d1d4 Merge remote-tracking branch 'origin/bjorn/cnx-1625-blocks-in-gh8' into bjorn/cnx-1625-blocks-in-gh8 2025-06-12 11:14:08 +02:00
Björn e0148aba62 fix: removing unresolved references
`ISpeckleGoo` removed on `props` pr
2025-06-12 11:13:50 +02:00
Claire Kuang 221371d668 Update Helpers.cs 2025-06-12 09:50:30 +01:00
Claire Kuang 7206a4477c Update Helpers.cs 2025-06-12 09:42:04 +01:00
Claire Kuang e0f042615b Merge branch 'dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-12 09:41:43 +01:00
Björn Steinhagen 9482d1fe83 feat (grasshopper): update publish to handle block instances and definitions (#909)
* feat: add `SpeckleBlockInstanceWrapper` class

empty

* chore: adds `SpeckleBlockInstanceWrapperGoo`

* chore: adds `SpeckleBlockInstanceParameters`

* chore: `SpeckleBlockInstanceWrapper` wrapped `Base` is `InstanceProxy`

* docs: tiny typo

* feat: `SpeckleBlockInstanceWrapper` and parameters

* fix: missing default constructor and naming refactor

* feat: initial commit for `SpeckleBlockDefinitionWrapper`

* Revert "feat: initial commit for `SpeckleBlockDefinitionWrapper`"

This reverts commit a3c1fcf978.

* fix: `SpeckleBlockInstanceWrapper`

- `type` correction for `Properties`
- keeping transforms in sync (kinda, waiting for PR merge)
- `DeepCopy()`

* fix: `SpeckleBlockInstanceWrapperGoo`

- `CastFrom`and `CastTo`methods
- `Duplicate` method

* fix: `SpeckleBlockInstanceParam`

- adds missing implementations

* feat: placeholders for `Bake` and `DrawPreview`

* feat: `CreateSpeckleBlockInstance`

* feat: `ModelObjects`

* fix: Rhino7 preprocessor directives

* fix: casting for GH created / referenced instances

* fix: `Transform` casting

* chore: icon for `CreateSpeckleBlockInstance`

* feat: casting to and from `CreateSpeckleBlockInstance`

* chore: removing redundant code

* feat: validating `SpeckleWrapper`

* chore: updates to `.ToString()`

* fix: recompute when `doc` definition changes

* refactor: `!string.IsNullOrWhiteSpace()`

* feat: deconstruct

* fix: cast for `RhinoObject`

* refactor: consolidate duplicate baking code

* Cleans up casting logic in instance and definitions

* refactor: `RhinoObject` coming from referenced instances

* docs: comments

* refactor: code clean up for `ModelInstanceDefinition`

* refactor: consistency across wrappers

* fix: intercept block definitions from pure gh

* docs: gh defined model block definition

* docs: api limitation on `ModelInstanceDefinition` constructor

* fix: stop instances from overwriting shared block definitions on bake

* feat: disallowing block definitions in collections for now

* feat: baking instances within collection

* feat: update publish to handle block instances and definitions

* refactpr: consolidating switch statements

* feat: runtime message on unsupported inputs for creating collections

* docs: `GrasshopperBlockPacker`

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-11 19:51:07 +02:00
Björn Steinhagen 6797f4baa4 feat(grasshopper): add SpeckleBlockInstance support (#893)
* feat: add `SpeckleBlockInstanceWrapper` class

empty

* chore: adds `SpeckleBlockInstanceWrapperGoo`

* chore: adds `SpeckleBlockInstanceParameters`

* chore: `SpeckleBlockInstanceWrapper` wrapped `Base` is `InstanceProxy`

* docs: tiny typo

* feat: `SpeckleBlockInstanceWrapper` and parameters

* fix: missing default constructor and naming refactor

* feat: initial commit for `SpeckleBlockDefinitionWrapper`

* Revert "feat: initial commit for `SpeckleBlockDefinitionWrapper`"

This reverts commit a3c1fcf978.

* fix: `SpeckleBlockInstanceWrapper`

- `type` correction for `Properties`
- keeping transforms in sync (kinda, waiting for PR merge)
- `DeepCopy()`

* fix: `SpeckleBlockInstanceWrapperGoo`

- `CastFrom`and `CastTo`methods
- `Duplicate` method

* fix: `SpeckleBlockInstanceParam`

- adds missing implementations

* feat: placeholders for `Bake` and `DrawPreview`

* feat: `CreateSpeckleBlockInstance`

* feat: `ModelObjects`

* fix: Rhino7 preprocessor directives

* fix: casting for GH created / referenced instances

* fix: `Transform` casting

* chore: icon for `CreateSpeckleBlockInstance`

* feat: casting to and from `CreateSpeckleBlockInstance`

* chore: removing redundant code

* feat: validating `SpeckleWrapper`

* chore: updates to `.ToString()`

* fix: recompute when `doc` definition changes

* refactor: `!string.IsNullOrWhiteSpace()`

* feat: deconstruct

* fix: cast for `RhinoObject`

* refactor: consolidate duplicate baking code

* Cleans up casting logic in instance and definitions

* refactor: `RhinoObject` coming from referenced instances

* docs: comments

* refactor: code clean up for `ModelInstanceDefinition`

* refactor: consistency across wrappers

* fix: intercept block definitions from pure gh

* docs: gh defined model block definition

* docs: api limitation on `ModelInstanceDefinition` constructor

* fix: stop instances from overwriting shared block definitions on bake

* feat: disallowing block definitions in collections for now

* feat: baking instances within collection

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-06-11 16:46:52 +02:00
Björn 751170acf8 Merge remote-tracking branch 'origin/dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-04 16:28:29 +02:00
Björn Steinhagen 6241780615 feat(grasshopper): add SpeckleBlockDefinition support (#891)
* feat: initial commit `SpeckleBlockDefinitionWrapper`

* feat: `SpeckleBlockDefinitionWrapper``Bake`and `DrawPreview`

* feat: Casting for `SpeckleBlockDefinitionWrapperGoo`

* feat: `SpeckleBlockDefinitionParam` functionality to `BakeGeometry`

* feat: `CreateSpeckleBlockDefinition`

* fix: casting and other things

* fix: defining objects of a definition, not all instance objects (LOL)

* refactor: preview

- still not previewing nicely though

* refactor: transform in helpers class

* chore: `CreateSpeckleBlockDefinition` icon

* docs: pr comment re. api limitation
2025-06-04 16:26:18 +02:00
Björn 0136d3fa13 Merge remote-tracking branch 'origin/dev' into bjorn/cnx-1625-blocks-in-gh8 2025-06-03 22:00:06 +02:00
371 changed files with 3161 additions and 12019 deletions
+4 -4
View File
@@ -7,12 +7,12 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -28,12 +28,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
+4 -4
View File
@@ -16,12 +16,12 @@ jobs:
file_version: ${{ steps.set-version.outputs.file_version }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
@@ -83,12 +83,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.4xx # Align with global.json (including roll forward rules)
+1 -2
View File
@@ -69,8 +69,7 @@ public static class Consts
new("Connectors/CSi/Speckle.Connectors.ETABS21", "net48"),
new("Connectors/CSi/Speckle.Connectors.ETABS22", "net8.0-windows"),
]
),
new("rhino-importer", [new("Importers/Rhino/Speckle.Importers.JobProcessor", "net8.0-windows")]),
)
};
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -336,18 +336,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -357,14 +357,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -293,7 +293,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -288,18 +288,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -307,14 +307,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -60,7 +60,7 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
)]
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<AutocadRootObject> objects,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -95,19 +95,28 @@ public abstract class AutocadRootObjectBaseBuilder : IRootObjectBuilder<AutocadR
foreach (var (entity, applicationId) in atomicObjects)
{
cancellationToken.ThrowIfCancellationRequested();
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
using (var convertActivity = _activityFactory.Start("Converting object"))
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
// Create and add a collection for this entity if not done so already.
(Collection objectCollection, LayerTableRecord? autocadLayer) = CreateObjectCollection(entity, tr);
if (autocadLayer is not null)
{
usedAcadLayers.Add(autocadLayer);
root.elements.Add(objectCollection);
}
var result = ConvertAutocadEntity(
entity,
applicationId,
objectCollection,
instanceProxies,
sendInfo.ProjectId
);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
var result = ConvertAutocadEntity(entity, applicationId, objectCollection, instanceProxies, projectId);
results.Add(result);
onOperationProgressed.Report(new("Converting", (double)++count / atomicObjects.Count));
}
if (results.All(x => x.Status == Status.ERROR))
@@ -3,15 +3,6 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{41BC679F-887F-44CF-971D-A5502EE87DB0}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Local|AnyCPU' ">
<OutputPath>bin\Local\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props"/>
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -268,9 +268,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -302,7 +302,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -346,18 +346,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -367,14 +367,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -254,7 +254,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -298,18 +298,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -317,14 +317,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -96,7 +96,7 @@ public class CsiSharedSelectionBinding : ISelectionBinding, IDisposable
var typeKey = (ModelObjectType)objectType[i];
var typeName = typeKey.ToString();
encodedIds.Add(ObjectIdentifier.Encode(objectType[i], objectName[i]));
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1;
typeCounts[typeName] = (typeCounts.TryGetValue(typeName, out var count) ? count : 0) + 1; // NOTE: Cross-framework compatibility (net 48 and net8)
}
var summary =
encodedIds.Count == 0
@@ -1,8 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
using Speckle.Connectors.CSiShared.Settings;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -25,7 +23,6 @@ public sealed class CsiSharedSendBinding : ISendBinding
private readonly ICsiApplicationService _csiApplicationService;
private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory;
private readonly ISendOperationManagerFactory _sendOperationManagerFactory;
private readonly ToSpeckleSettingsManager _toSpeckleSettingsManager;
public CsiSharedSendBinding(
IBrowserBridge parent,
@@ -33,8 +30,7 @@ public sealed class CsiSharedSendBinding : ISendBinding
ICancellationManager cancellationManager,
ICsiConversionSettingsFactory csiConversionSettingsFactory,
ICsiApplicationService csiApplicationService,
ISendOperationManagerFactory sendOperationManagerFactory,
ToSpeckleSettingsManager toSpeckleSettingsManager
ISendOperationManagerFactory sendOperationManagerFactory
)
{
_sendFilters = sendFilters.ToList();
@@ -44,13 +40,11 @@ public sealed class CsiSharedSendBinding : ISendBinding
_csiConversionSettingsFactory = csiConversionSettingsFactory;
_csiApplicationService = csiApplicationService;
_sendOperationManagerFactory = sendOperationManagerFactory;
_toSpeckleSettingsManager = toSpeckleSettingsManager;
}
public List<ISendFilter> GetSendFilters() => _sendFilters;
public List<ICardSetting> GetSendSettings() =>
[new LoadCaseCombinationSetting([], _csiApplicationService.SapModel), new ResultTypeSetting([])];
public List<ICardSetting> GetSendSettings() => [];
public async Task Send(string modelCardId)
{
@@ -58,17 +52,9 @@ public sealed class CsiSharedSendBinding : ISendBinding
await manager.Process(
Commands,
modelCardId,
(sp, card) =>
{
(sp, _) =>
sp.GetRequiredService<IConverterSettingsStore<CsiConversionSettings>>()
.Initialize(
_csiConversionSettingsFactory.Create(
_csiApplicationService.SapModel,
_toSpeckleSettingsManager.GetLoadCasesAndCombinations(card),
_toSpeckleSettingsManager.GetResultTypes(card)
)
);
},
.Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel)),
card => card.SendFilter.NotNull().RefreshObjectIds().Select(DecodeObjectIdentifier).ToList()
);
}
@@ -89,11 +89,7 @@ public class CsiDocumentModelStore : DocumentModelStore, IDisposable
_speckleApplication.Slug
);
DocumentStateFile = Path.Combine(HostAppUserDataPath, $"{ModelPathHash}.json");
_logger.LogDebug(
"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}",
ModelPathHash,
DocumentStateFile
);
_logger.LogDebug($"Paths set - Hash: {ModelPathHash}, File: {DocumentStateFile}");
}
catch (Exception ex) when (!ex.IsFatal())
{
@@ -20,8 +20,23 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
_settingsStore = settingsStore;
}
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties) =>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
GetMaterialName(sectionName, properties);
GetSectionProperties(sectionName, properties);
GetPropertyModifiers(sectionName, properties);
}
private void GetMaterialName(string sectionName, Dictionary<string, object?> properties)
{
// get material name
string materialName = string.Empty;
_settingsStore.Current.SapModel.PropFrame.GetMaterial(sectionName, ref materialName);
// append to General Data of properties dictionary
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Material"] = materialName;
}
private void GetSectionProperties(string sectionName, Dictionary<string, object?> properties)
{
@@ -54,20 +69,47 @@ public class CsiFrameSectionPropertyExtractor : IFrameSectionPropertyExtractor
ref radiusOfGyrationAboutMinorAxis
);
string distanceUnit = _settingsStore.Current.SpeckleUnits;
string areaUnit = $"{distanceUnit}²"; // // TODO: Formalize this better
string modulusUnit = $"{distanceUnit}³"; // // TODO: Formalize this better
string inertiaUnit = $"{distanceUnit}\u2074"; // TODO: Formalize this better
Dictionary<string, object?> mechanicalProperties = properties.EnsureNested(
SectionPropertyCategory.SECTION_PROPERTIES
);
mechanicalProperties.Add("Area", crossSectionalArea);
mechanicalProperties.Add("As2", shearAreaInMajorAxisDirection);
mechanicalProperties.Add("As3", shearAreaInMinorAxisDirection);
mechanicalProperties.Add("J", torsionalConstant);
mechanicalProperties.Add("I22", momentOfInertiaAboutMajorAxis);
mechanicalProperties.Add("I33", momentOfInertiaAboutMinorAxis);
mechanicalProperties.Add("S22", sectionModulusAboutMajorAxis);
mechanicalProperties.Add("S33", sectionModulusAboutMinorAxis);
mechanicalProperties.Add("Z22", plasticModulusAboutMajorAxis);
mechanicalProperties.Add("Z33", plasticModulusAboutMinorAxis);
mechanicalProperties.Add("R22", radiusOfGyrationAboutMajorAxis);
mechanicalProperties.Add("R33", radiusOfGyrationAboutMinorAxis);
mechanicalProperties.AddWithUnits("Area", crossSectionalArea, areaUnit);
mechanicalProperties.AddWithUnits("As2", shearAreaInMajorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("As3", shearAreaInMinorAxisDirection, areaUnit);
mechanicalProperties.AddWithUnits("J", torsionalConstant, inertiaUnit);
mechanicalProperties.AddWithUnits("I22", momentOfInertiaAboutMajorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("I33", momentOfInertiaAboutMinorAxis, inertiaUnit);
mechanicalProperties.AddWithUnits("S22", sectionModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("S33", sectionModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z22", plasticModulusAboutMajorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("Z33", plasticModulusAboutMinorAxis, modulusUnit);
mechanicalProperties.AddWithUnits("R22", radiusOfGyrationAboutMajorAxis, distanceUnit);
mechanicalProperties.AddWithUnits("R33", radiusOfGyrationAboutMinorAxis, distanceUnit);
}
private void GetPropertyModifiers(string sectionName, Dictionary<string, object?> properties)
{
double[] stiffnessModifiersArray = [];
_settingsStore.Current.SapModel.PropFrame.GetModifiers(sectionName, ref stiffnessModifiersArray);
Dictionary<string, object?> modifiers =
new()
{
["Cross-section (Axial) Area"] = stiffnessModifiersArray[0],
["Shear Area in 2 Direction"] = stiffnessModifiersArray[1],
["Shear Area in 3 Direction"] = stiffnessModifiersArray[2],
["Torsional Constant"] = stiffnessModifiersArray[3],
["Moment of Inertia about 2 Axis"] = stiffnessModifiersArray[4],
["Moment of Inertia about 3 Axis"] = stiffnessModifiersArray[5],
["Mass"] = stiffnessModifiersArray[6],
["Weight"] = stiffnessModifiersArray[7],
};
Dictionary<string, object?> generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Modifiers"] = modifiers;
}
}
@@ -1,29 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.HostApp.Helpers;
public class CsiResultsExtractorFactory
{
private readonly IServiceProvider _serviceProvider;
public CsiResultsExtractorFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IApplicationResultsExtractor GetExtractor(string resultsKey) =>
resultsKey switch
{
ResultsKey.BASE_REACT => _serviceProvider.GetRequiredService<CsiBaseReactResultsExtractor>(),
ResultsKey.FRAME_FORCES => _serviceProvider.GetRequiredService<CsiFrameForceResultsExtractor>(),
ResultsKey.JOINT_REACT => _serviceProvider.GetRequiredService<CsiJointReactResultsExtractor>(),
ResultsKey.MODAL_PERIOD => _serviceProvider.GetRequiredService<CsiModalPeriodExtractor>(),
ResultsKey.PIER_FORCES => _serviceProvider.GetRequiredService<CsiPierForceResultsExtractor>(),
ResultsKey.SPANDREL_FORCES => _serviceProvider.GetRequiredService<CsiSpandrelForceResultsExtractor>(),
ResultsKey.STORY_DRIFTS => _serviceProvider.GetRequiredService<CsiStoryDriftsResultsExtractor>(),
ResultsKey.STORY_FORCES => _serviceProvider.GetRequiredService<CsiStoryForceResultsExtractor>(),
_ => throw new InvalidOperationException($"{resultsKey} not accounted for in CsiResultsExtractorFactory")
};
}
@@ -4,11 +4,9 @@ using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Extensions;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
@@ -39,7 +37,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
private readonly ILogger<CsiRootObjectBuilder> _logger;
private readonly ISdkActivityFactory _activityFactory;
private readonly ICsiApplicationService _csiApplicationService;
private readonly AnalysisResultsExtractor _analysisResultsExtractor;
public CsiRootObjectBuilder(
IRootToSpeckleConverter rootToSpeckleConverter,
@@ -49,8 +46,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
ISectionUnpacker sectionUnpacker,
ILogger<CsiRootObjectBuilder> logger,
ISdkActivityFactory activityFactory,
ICsiApplicationService csiApplicationService,
AnalysisResultsExtractor analysisResultsExtractor
ICsiApplicationService csiApplicationService
)
{
_converterSettings = converterSettings;
@@ -61,7 +57,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
_logger = logger;
_activityFactory = activityFactory;
_csiApplicationService = csiApplicationService;
_analysisResultsExtractor = analysisResultsExtractor;
}
/// <summary>
@@ -75,7 +70,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
/// </remarks>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<ICsiWrapper> csiObjects,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -94,6 +89,8 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
foreach (ICsiWrapper csiObject in csiObjects)
{
cancellationToken.ThrowIfCancellationRequested();
using var _2 = _activityFactory.Start("Convert");
var result = ConvertCsiObject(csiObject, rootObjectCollection);
results.Add(result);
@@ -105,7 +102,7 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
if (results.All(x => x.Status == Status.ERROR))
{
throw new SpeckleException("Failed to convert all objects");
throw new SpeckleException("Failed to convert all objects.");
}
using (var _ = _activityFactory.Start("Process Proxies"))
@@ -117,42 +114,6 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
rootObjectCollection[ProxyKeys.SECTION] = _sectionUnpacker.UnpackSections().ToList();
}
// Extract analysis results (if applicable)
// NOTE: objectSelectionSummary used to extract results for objects being published ONLY
// NOTE: etabs is complicated and we can't get specifics from original selection
var objectSelectionSummary = GetObjectSummary(csiObjects);
var selectedCasesAndCombinations = _converterSettings.Current.SelectedLoadCasesAndCombinations;
var requestedResultTypes = _converterSettings.Current.SelectedResultTypes;
if (selectedCasesAndCombinations?.Count > 0)
{
if (requestedResultTypes == null || requestedResultTypes.Count == 0)
{
throw new SpeckleException(
"Adjust publish settings - no result type input for the requested load cases and combinations"
);
}
if (!_csiApplicationService.SapModel.GetModelIsLocked())
{
throw new SpeckleException("Model unlocked, no access to analysis results");
}
try
{
var analysisResults = _analysisResultsExtractor.ExtractAnalysisResults(
selectedCasesAndCombinations,
requestedResultTypes,
objectSelectionSummary
);
rootObjectCollection["analysisResults"] = analysisResults;
}
catch (Exception ex)
{
throw new SpeckleException("Analysis result extraction failed", ex);
}
}
return new RootObjectBuilderResult(rootObjectCollection, results);
}
@@ -190,31 +151,13 @@ public class CsiRootObjectBuilder : IRootObjectBuilder<ICsiWrapper>
// NOTE: CsiTendonWrapper - not typically modelled in ETABS, rather SAFE
catch (NotImplementedException ex)
{
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
_logger.LogError(ex, sourceType);
return new(Status.WARNING, applicationId, sourceType, null, ex);
}
catch (Exception ex) when (!ex.IsFatal())
{
_logger.LogError(ex, "Failed to convert object {sourceType}", sourceType);
_logger.LogError(ex, sourceType);
return new(Status.ERROR, applicationId, sourceType, null, ex);
}
}
/// <summary>
/// Generates a summary of object types and their associated names from the collection of CSI wrappers.
/// </summary>
/// <remarks>
/// A summary of object names for each object type is needed for getting analysis results of the selected objects only.
/// During object conversion, however, we lose the selection (like a clear selection)(presumably because of other api calls).
/// This has to be recreated since GetSelection() return type is bound by the interface.
/// The LINQ-based implementation is computationally inexpensive as it operates on an already-loaded collection without additional API calls.
/// Also, we don't want to rely on user selection remaining active, what if someone re-publishes using model card cache?
/// </remarks>
private Dictionary<ModelObjectType, List<string>> GetObjectSummary(IReadOnlyList<ICsiWrapper> csiObjects) =>
csiObjects
.GroupBy(csiObject => csiObject.ObjectType)
.ToDictionary(
group => group.Key, // ModelObjectType (FRAME, JOINT, etc.)
group => group.Select(obj => obj.Name).ToList() // Extract Name from each ICsiWrapper and convert to List<string>
);
}
@@ -1,73 +0,0 @@
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.InterfaceGenerator;
using Speckle.Newtonsoft.Json.Linq;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.CSiShared.Operations.Send.Settings;
[GenerateAutoInterface]
public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
{
private readonly ISendConversionCache _sendConversionCache;
private readonly Dictionary<string, List<string>?> _loadCaseCombinationCache = new();
private readonly Dictionary<string, List<string>?> _resultTypeCache = new();
public ToSpeckleSettingsManager(ISendConversionCache sendConversionCache)
{
_sendConversionCache = sendConversionCache;
}
public List<string> GetLoadCasesAndCombinations(SenderModelCard modelCard)
{
var setting = modelCard.Settings?.FirstOrDefault(s => s.Id == "loadCasesAndCombinations");
var returnValue = (setting?.Value as JArray)?.Select(x => x.ToString()).ToList() ?? [];
if (_loadCaseCombinationCache.TryGetValue(modelCard.ModelCardId.NotNull(), out List<string>? previousValue))
{
if (!AreListsEqual(previousValue, returnValue))
{
EvictCacheForModelCard(modelCard);
}
}
_loadCaseCombinationCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public List<string> GetResultTypes(SenderModelCard modelCard)
{
var setting = modelCard.Settings?.FirstOrDefault(s => s.Id == "resultTypes");
var returnValue = (setting?.Value as JArray)?.Select(x => x.ToString()).ToList() ?? [];
if (_resultTypeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out List<string>? previousValue))
{
if (!AreListsEqual(previousValue, returnValue))
{
EvictCacheForModelCard(modelCard);
}
}
_resultTypeCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private static bool AreListsEqual(List<string>? list1, List<string>? list2)
{
if (list1 == null && list2 == null)
{
return true;
}
if (list1 == null || list2 == null)
{
return false;
}
return list1.Count == list2.Count && list1.OrderBy(x => x).SequenceEqual(list2.OrderBy(x => x));
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().RefreshObjectIds() : [];
_sendConversionCache.EvictObjects(objectIds);
}
}
@@ -8,8 +8,6 @@ using Speckle.Connectors.CSiShared.Builders;
using Speckle.Connectors.CSiShared.Filters;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.CSiShared.Operations.Send.Settings;
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
@@ -46,18 +44,13 @@ public static class ServiceRegistration
services.AddScoped<SendOperation<ICsiWrapper>>();
services.AddScoped<CsiMaterialPropertyExtractor>();
services.AddScoped<CsiResultsExtractorFactory>();
services.AddScoped<MaterialUnpacker>();
services.AddScoped<IFrameSectionPropertyExtractor, CsiFrameSectionPropertyExtractor>();
services.AddScoped<IShellSectionPropertyExtractor, CsiShellSectionPropertyExtractor>();
services.AddScoped<AnalysisResultsExtractor>();
// add converter caches
services.AddScoped<CsiToSpeckleCacheSingleton>();
// add settings manager
services.AddScoped<ToSpeckleSettingsManager>();
return services;
}
}
@@ -1,13 +0,0 @@
using Speckle.Connectors.CSiShared.Utils;
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.CSiShared.Settings;
public class LoadCaseCombinationSetting(List<string> values, cSapModel sapModel) : ICardSetting
{
public string? Id { get; set; } = "loadCasesAndCombinations";
public string? Title { get; set; } = "Load Cases & Combinations";
public string? Type { get; set; } = "array";
public object? Value { get; set; } = values;
public List<string>? Enum { get; set; } = LoadCaseHelper.GetLoadCasesAndCombinations(sapModel);
}
@@ -1,13 +0,0 @@
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.CSiShared.Utils;
namespace Speckle.Connectors.CSiShared.Settings;
public class ResultTypeSetting(List<string> values) : ICardSetting
{
public string? Id { get; set; } = "resultTypes";
public string? Title { get; set; } = "Result Type";
public string? Type { get; set; } = "array";
public object? Value { get; set; } = values;
public List<string>? Enum { get; set; } = ResultsKey.All.OrderBy(x => x).ToList();
}
@@ -18,7 +18,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\CsiSharedSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Filters\CsiSharedSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiResultsExtractorFactory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\MaterialUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\CsiFrameSectionPropertyExtractor.cs" />
@@ -28,17 +27,12 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\ISectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\CsiRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\CsiPluginBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleFormBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiApplicationService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\CsiDocumentModelStore.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Settings\LoadCaseCombinationSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Settings\ResultTypeSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\LoadCaseHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\AnalysisResultsExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Utils\ObjectIdentifiers.cs" />
</ItemGroup>
<ItemGroup>
@@ -1,184 +0,0 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.CSiShared.Utils;
public class AnalysisResultsExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _converterSettingsStore;
private readonly CsiResultsExtractorFactory _resultsExtractorFactory;
public AnalysisResultsExtractor(
IConverterSettingsStore<CsiConversionSettings> converterSettingsStore,
CsiResultsExtractorFactory resultsExtractorFactory
)
{
_converterSettingsStore = converterSettingsStore;
_resultsExtractorFactory = resultsExtractorFactory;
}
/// <summary>
/// Extracts complete analysis results including units retrieval, load case configuration, and results extraction.
/// Assumes inputs have been validated by caller.
/// </summary>
public Base ExtractAnalysisResults(
List<string> selectedCasesAndCombinations,
List<string> requestedResultTypes,
Dictionary<ModelObjectType, List<string>> objectSelectionSummary
)
{
// Step 1: get analysis units
var analysisResults = CreateAnalysisResultsWithUnits();
// Step 2: configure and validate load cases
ConfigureAndValidateSelectedLoadCases(selectedCasesAndCombinations);
// Step 3: extract results using clean factory pattern
ExtractResults(requestedResultTypes, objectSelectionSummary, analysisResults);
return analysisResults;
}
/// <summary>
/// Instantiates a Base object and pre-populates it with the models defined force units.
/// </summary>
/// <returns></returns>
/// <exception cref="SpeckleException"></exception>
private Base CreateAnalysisResultsWithUnits()
{
var forceUnit = eForce.NotApplicable;
var lengthUnit = eLength.NotApplicable;
var temperatureUnit = eTemperature.NotApplicable;
int success = _converterSettingsStore.Current.SapModel.GetDatabaseUnits_2(
ref forceUnit,
ref lengthUnit,
ref temperatureUnit
);
if (success != 0)
{
throw new SpeckleException("Failed to retrieve units for analysis results");
}
return new Base
{
["forceUnit"] = forceUnit.ToString(),
["lengthUnit"] = lengthUnit.ToString(),
["temperatureUnit"] = temperatureUnit.ToString()
};
}
private void ExtractResults(
List<string> requestedResultTypes,
Dictionary<ModelObjectType, List<string>> objectSelectionSummary,
Base analysisResults
)
{
foreach (var resultType in requestedResultTypes)
{
var extractor = _resultsExtractorFactory.GetExtractor(resultType);
objectSelectionSummary.TryGetValue(extractor.TargetObjectType, out var objectNames);
analysisResults[extractor.ResultsKey] = extractor.GetResults(objectNames);
}
}
/// <summary>
/// Responsible for two things. Firstly, we need to setup the results so that only the requested cases and combinations
/// are published. Secondly, we need to ensure that the requested cases and combinations are actually run.
/// </summary>
private void ConfigureAndValidateSelectedLoadCases(List<string> selectedLoadCases)
{
// step 1: configure load cases for output
ConfigureSelectedLoadCases(selectedLoadCases);
// step 2: validate they are complete (throws on failure)
ValidateSelectedCasesAreComplete(selectedLoadCases);
}
private void ConfigureSelectedLoadCases(List<string> selectedLoadCases)
{
// deselect all load cases and combos
_converterSettingsStore.Current.SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput();
// ui presents cases and combinations as a flat list. we need to distinguish if the string is a case or combo
// do this by seeing if the string is within the list of defined cases
// typically defined load cases << defined load combinations, so this approach should be more efficient
int numberOfLoadCases = 0;
string[] loadCaseNames = [];
_converterSettingsStore.Current.SapModel.LoadCases.GetNameList(ref numberOfLoadCases, ref loadCaseNames);
// set user selected combos to true (i.e. to export)
foreach (var selectedLoadCase in selectedLoadCases)
{
int success = loadCaseNames.Contains(selectedLoadCase)
? _converterSettingsStore.Current.SapModel.Results.Setup.SetCaseSelectedForOutput(selectedLoadCase)
: _converterSettingsStore.Current.SapModel.Results.Setup.SetComboSelectedForOutput(selectedLoadCase);
// ui should only present valid options
// `AnalysisResultsExtractor` only fetches load cases and load combinations (not patterns), so this should never throw
if (success != 0)
{
throw new InvalidOperationException($"Failed to set {selectedLoadCase} for output.");
}
}
}
private void ValidateSelectedCasesAreComplete(List<string> selectedCasesAndCombinations)
{
// get status for all load cases (combinations not included in this API call)
int numberItems = 0;
string[] caseNames = [];
int[] statuses = [];
int result = _converterSettingsStore.Current.SapModel.Analyze.GetCaseStatus(
ref numberItems,
ref caseNames,
ref statuses
);
if (result != 0)
{
throw new SpeckleException("Failed to retrieve load case status from model.");
}
// build lookup dictionary for load cases only
var caseStatusLookup = caseNames
.Zip(statuses, (name, status) => new { name, status })
.ToDictionary(x => x.name, x => x.status);
// separate selected items into cases and combinations
var selectedCases = selectedCasesAndCombinations.Where(item => caseStatusLookup.ContainsKey(item)).ToList();
var selectedCombinations = selectedCasesAndCombinations.Except(selectedCases).ToList();
// validate load cases status
var notFinishedCases = new List<string>();
foreach (var caseName in selectedCases)
{
int status = caseStatusLookup[caseName];
if (status != 4) // 1 = Not run, 2 = Could not start, 3 = Not finished, 4 = Finished
{
notFinishedCases.Add($"{caseName}");
}
}
// TODO: Validate load combinations status
// for now, assume combinations are valid if we can't validate them
if (selectedCombinations.Count != 0)
{
// combinations validation not implemented - assuming they're valid for now
// it'll get complicated, we can't get the status of a combination, so we need to check the constituent cases
}
if (notFinishedCases.Count != 0)
{
string errorMessage =
$"Analysis not complete for load cases: {string.Join(", ", notFinishedCases)}. Run analysis first.";
throw new SpeckleException(errorMessage);
}
}
}
@@ -1,56 +0,0 @@
namespace Speckle.Connectors.CSiShared.Utils;
public static class LoadCaseHelper
{
public static List<string> GetLoadCasesAndCombinations(cSapModel sapModel)
{
var loadCasesAndCombos = new List<string>();
try
{
// Check if model is loaded to prevent crashes
var modelFilename = sapModel.GetModelFilename();
if (string.IsNullOrEmpty(modelFilename))
{
return loadCasesAndCombos; // Return empty list if no model
}
// Get Load Cases
int numberItems = 0;
string[]? names = null;
int ret = sapModel.LoadCases.GetNameList(ref numberItems, ref names);
if (ret == 0 && names != null)
{
for (int i = 0; i < numberItems; i++)
{
loadCasesAndCombos.Add(names[i]);
}
}
// Get Load Combinations
numberItems = 0;
names = null;
ret = sapModel.RespCombo.GetNameList(ref numberItems, ref names);
if (ret == 0 && names != null)
{
for (int i = 0; i < numberItems; i++)
{
loadCasesAndCombos.Add(names[i]);
}
}
}
catch (System.Runtime.InteropServices.COMException)
{
// Return empty list on COM errors to prevent crashes
return new List<string>();
}
catch (System.InvalidOperationException)
{
// Return empty list on invalid operations to prevent crashes
return new List<string>();
}
return loadCasesAndCombos.Distinct().OrderBy(x => x).ToList();
}
}
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.etabs21": {
@@ -335,18 +335,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -356,14 +356,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -210,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -236,7 +236,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.etabs22": {
@@ -286,18 +286,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -47,17 +47,18 @@ public class EtabsSectionUnpacker : ISectionUnpacker
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// initialize properties
Dictionary<string, object?> properties = [];
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// Extract properties if valid section name
// "None" is weird but api returns that string if an opening, null element etc.
if (sectionName != "None" && !string.IsNullOrEmpty(sectionName))
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
// sectionName is initialized with string.Empty, but api ref returns string "None"
if (sectionName != "None")
{
properties = _propertyExtractor.ExtractFrameSectionProperties(sectionName);
}
// create section proxy
// create the section proxy
GroupProxy sectionProxy =
new()
{
@@ -65,8 +66,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
name = sectionName,
applicationId = sectionName,
objects = frameIds,
["type"] = "Frame Section",
["properties"] = properties
["type"] = "Frame Section", // since sectionProxies are a flat list, need some way to distinguish from shell
["properties"] = properties // openings will just have an empty dict here
};
yield return sectionProxy;
@@ -80,8 +81,8 @@ public class EtabsSectionUnpacker : ISectionUnpacker
string sectionName = entry.Key;
List<string> frameIds = entry.Value;
// initialize properties outside the if statement
Dictionary<string, object?> properties = [];
// Initialize properties outside the if statement
Dictionary<string, object?> properties = new Dictionary<string, object?>();
// get the properties of the section
// openings will have objects assigned to them, but won't have properties
@@ -91,7 +92,7 @@ public class EtabsSectionUnpacker : ISectionUnpacker
properties = _propertyExtractor.ExtractShellSectionProperties(sectionName);
}
// create section proxy
// create the section proxy
GroupProxy sectionProxy =
new()
{
@@ -1,5 +1,4 @@
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Services;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.Utils;
@@ -9,96 +8,69 @@ namespace Speckle.Connectors.ETABSShared.HostApp.Helpers;
/// <summary>
/// Extracts ETABS-specific frame section properties.
/// </summary>
/// <remarks>
/// The bulk loading strategy is necessary here because we can't know which database table contains which section
/// beforehand - there are multiple tables like "Frame Section Property Definitions - Steel",
/// "Frame Section Property Definitions - Concrete", etc.
/// </remarks>
public class EtabsFrameSectionPropertyExtractor : IApplicationFrameSectionPropertyExtractor
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
private readonly EtabsSectionPropertyDefinitionService _definitionService;
public EtabsFrameSectionPropertyExtractor(
IConverterSettingsStore<CsiConversionSettings> settingsStore,
EtabsSectionPropertyDefinitionService definitionService
)
public EtabsFrameSectionPropertyExtractor(IConverterSettingsStore<CsiConversionSettings> settingsStore)
{
_settingsStore = settingsStore;
_definitionService = definitionService;
}
/// <summary>
/// Gets frame section properties from preloaded database table data
/// Gets generalised frame section properties
/// </summary>
/// <remarks>
/// Property categorization is done heuristically - order matters in the parsing logic.
/// Sap2000 doesn't support this method, unfortunately
/// Alternative is to account for extraction according to section type - we're talking over 40 section types!
/// This way, we get basic information with minimal computational costs.
/// </remarks>
public void ExtractProperties(string sectionName, Dictionary<string, object?> properties)
{
// get frame definitions from the service (which uses database table extraction)
// this is a fast dictionary lookup since all data is preloaded
if (!_definitionService.FrameDefinitions.TryGetValue(sectionName, out var rawDatabaseTableProperties))
// Get all frame properties
int numberOfNames = 0;
string[] names = [];
eFramePropType[] propTypes = [];
double[] t3 = [],
t2 = [],
tf = [],
tw = [],
t2b = [],
tfb = [],
area = [];
_settingsStore.Current.SapModel.PropFrame.GetAllFrameProperties_2(
ref numberOfNames,
ref names,
ref propTypes,
ref t3,
ref t2,
ref tf,
ref tw,
ref t2b,
ref tfb,
ref area
);
// Find the index of the current section
int sectionIndex = Array.IndexOf(names, sectionName);
if (sectionIndex != -1)
{
return; // no definitions found for this section
}
// General Data
var generalData = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalData["Section Shape"] = propTypes[sectionIndex].ToString();
// define table keys that we don't want to include in the section proxy properties
var keysToExclude = new HashSet<string>
{
"GUID",
"Name",
"Color",
"Notes",
"FileName",
"FromFile",
"SectInFile",
"NotAutoFact"
};
// get the section type / shape using the dedicated api query (exception to the database approach)
// this specific property isn't available in the database table extraction
eFramePropType framePropType = 0;
_settingsStore.Current.SapModel.PropFrame.GetTypeOAPI(sectionName, ref framePropType);
Dictionary<string, object?> generalProperties = properties.EnsureNested(SectionPropertyCategory.GENERAL_DATA);
generalProperties.Add("Section Shape", framePropType.ToString());
// heuristic property categorization based on key patterns and parse-ability
// NOTE: this is gross and quite dangerous 🤨 but beats specific frame prop sect. property extractions imo
// order matters here! we check for known string props first, then modifiers, then assume doubles are dimensions
foreach (KeyValuePair<string, string> rawDatabaseTableProperty in rawDatabaseTableProperties)
{
string key = rawDatabaseTableProperty.Key;
string value = rawDatabaseTableProperty.Value;
// skip metadata fields we don't care about
if (!keysToExclude.Contains(key))
{
// material is always a string, grab it first
if (key == "Material")
{
generalProperties.Add(key, value);
}
// modifier properties end with "Mod" and should be numeric
else if (key.EndsWith("Mod") && double.TryParse(value, out double parsedModValue))
{
Dictionary<string, object?> modificationProperties = properties.EnsureNested(
SectionPropertyCategory.MODIFIERS
);
modificationProperties.Add(key, parsedModValue);
}
// anything else that parses as a double is assumed to be a section dimension
// this covers things like t3, t2, tf, tw, area, etc. without having to enumerate them all
else if (double.TryParse(value, out double parsedDimensionValue))
{
Dictionary<string, object?> sectionDimensions = properties.EnsureNested(
SectionPropertyCategory.SECTION_DIMENSIONS
);
sectionDimensions.Add(key, parsedDimensionValue);
}
// if it doesn't parse as double and isn't a known string property, we skip it
// this is acceptable - we'd rather miss some edge case properties than crash
}
// Section Dimensions
string unit = _settingsStore.Current.SpeckleUnits;
var sectionDimensions = properties.EnsureNested(SectionPropertyCategory.SECTION_DIMENSIONS);
sectionDimensions.AddWithUnits("t3", t3[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2", t2[sectionIndex], unit);
sectionDimensions.AddWithUnits("tf", tf[sectionIndex], unit);
sectionDimensions.AddWithUnits("tw", tw[sectionIndex], unit);
sectionDimensions.AddWithUnits("t2b", t2b[sectionIndex], unit);
sectionDimensions.AddWithUnits("tfb", tfb[sectionIndex], unit);
sectionDimensions.AddWithUnits("Area", area[sectionIndex], $"{unit}²");
}
}
}
@@ -1,105 +0,0 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Converters.CSiShared.ToSpeckle.Helpers;
namespace Speckle.Connectors.ETABSShared.HostApp.Services;
/// <summary>
/// Loads and caches section property definitions from database tables for both frame and shell sections.
/// </summary>
public class EtabsSectionPropertyDefinitionService
{
private readonly IConverterSettingsStore<CsiConversionSettings> _settingsStore;
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> FrameDefinitions { get; }
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> ShellDefinitions { get; }
public EtabsSectionPropertyDefinitionService(
DatabaseTableExtractor databaseTableExtractor,
IConverterSettingsStore<CsiConversionSettings> settingsStore
)
{
_settingsStore = settingsStore;
var availableTableKeys = GetAvailableTableKeys();
FrameDefinitions = LoadFrameDefinitions(databaseTableExtractor, availableTableKeys);
ShellDefinitions = LoadShellDefinitions(databaseTableExtractor, availableTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadFrameDefinitions(
DatabaseTableExtractor databaseTableExtractor,
string[] availableTableKeys
)
{
var frameTableKeys = GetFrameSectionPropertyDefinitionTableKeys(availableTableKeys);
return LoadDefinitionsFromTables(databaseTableExtractor, frameTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadShellDefinitions(
DatabaseTableExtractor databaseTableExtractor,
string[] availableTableKeys
)
{
var shellTableKeys = GetShellSectionPropertyDefinitionTableKeys(availableTableKeys);
return LoadDefinitionsFromTables(databaseTableExtractor, shellTableKeys);
}
private static IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> LoadDefinitionsFromTables(
DatabaseTableExtractor databaseTableExtractor,
IEnumerable<string> tableKeys
)
{
var definitions = new Dictionary<string, IReadOnlyDictionary<string, string>>();
foreach (string tableKey in tableKeys)
{
var tableData = databaseTableExtractor.GetTableData(tableKey, "Name");
foreach (var row in tableData.Rows)
{
definitions[row.Key] = row.Value;
}
}
return definitions;
}
private static IEnumerable<string> GetFrameSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
{
var keysToExclude = new HashSet<string>
{
"Frame Section Property Definitions - Summary",
"Frame Section Property Definitions - Concrete Beam Reinforcing",
"Frame Section Property Definitions - Concrete Column Reinforcing"
};
return availableTableKeys.Where(key =>
key.StartsWith("Frame Section Property Definitions") && !keysToExclude.Contains(key)
);
}
private static IEnumerable<string> GetShellSectionPropertyDefinitionTableKeys(string[] availableTableKeys)
{
var keysToExclude = new HashSet<string> { "Area Section Property Definitions - Summary" };
return availableTableKeys.Where(key =>
key.StartsWith("Area Section Property Definitions") && !keysToExclude.Contains(key)
);
}
private string[] GetAvailableTableKeys()
{
int numberTables = 0;
string[] tableKey = [],
tableName = [];
int[] importType = [];
_ = _settingsStore.Current.SapModel.DatabaseTables.GetAvailableTables(
ref numberTables,
ref tableKey,
ref tableName,
ref importType
);
return tableKey;
}
}
@@ -34,7 +34,7 @@ public class EtabsSectionPropertyExtractor
/// </summary>
public Dictionary<string, object?> ExtractFrameSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = [];
Dictionary<string, object?> properties = new();
_csiFrameExtractor.ExtractProperties(sectionName, properties);
_etabsFrameExtractor.ExtractProperties(sectionName, properties);
return properties;
@@ -45,7 +45,7 @@ public class EtabsSectionPropertyExtractor
/// </summary>
public Dictionary<string, object?> ExtractShellSectionProperties(string sectionName)
{
Dictionary<string, object?> properties = [];
Dictionary<string, object?> properties = new();
_csiShellExtractor.ExtractProperties(sectionName, properties);
_etabsShellExtractor.ExtractProperties(sectionName, properties);
return properties;
@@ -3,7 +3,6 @@ using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.CSiShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp;
using Speckle.Connectors.ETABSShared.HostApp.Helpers;
using Speckle.Connectors.ETABSShared.HostApp.Services;
using Speckle.Converters.ETABSShared;
namespace Speckle.Connectors.ETABSShared;
@@ -13,12 +12,11 @@ public static class ServiceRegistration
public static IServiceCollection AddEtabs(this IServiceCollection services)
{
services.AddEtabsConverters();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<EtabsSectionPropertyDefinitionService>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
services.AddScoped<IApplicationFrameSectionPropertyExtractor, EtabsFrameSectionPropertyExtractor>();
services.AddScoped<IApplicationShellSectionPropertyExtractor, EtabsShellSectionPropertyExtractor>();
services.AddScoped<EtabsSectionPropertyExtractor>();
services.AddScoped<EtabsShellSectionResolver>();
services.AddScoped<CsiSendCollectionManager, EtabsSendCollectionManager>();
services.AddScoped<ISectionUnpacker, EtabsSectionUnpacker>();
return services;
@@ -12,7 +12,6 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSectionUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\EtabsSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsFrameSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyDefinitionService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionPropertyExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers\EtabsShellSectionResolver.cs" />
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2020": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2021": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2022": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2023": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -259,9 +259,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -285,7 +285,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2024": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -265,9 +265,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -291,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2025": {
@@ -337,18 +337,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +358,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -266,9 +266,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -292,7 +292,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.navisworks2026": {
@@ -339,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +360,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -73,7 +73,6 @@ public class NavisworksSendBinding : ISendBinding
new IncludeInternalPropertiesSetting(false),
new ConvertHiddenElementsSetting(false),
new PreserveModelHierarchySetting(false),
new RevitCategoryMappingSetting(false)
];
public async Task Send(string modelCardId) =>
@@ -94,8 +93,7 @@ public class NavisworksSendBinding : ISendBinding
visualRepresentationMode: _toSpeckleSettingsManagerNavisworks.GetVisualRepresentationMode(modelCard),
convertHiddenElements: _toSpeckleSettingsManagerNavisworks.GetConvertHiddenElements(modelCard),
includeInternalProperties: _toSpeckleSettingsManagerNavisworks.GetIncludeInternalProperties(modelCard),
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard),
mappingToRevitCategories: _toSpeckleSettingsManagerNavisworks.GetMappingToRevitCategories(modelCard)
preserveModelHierarchy: _toSpeckleSettingsManagerNavisworks.GetPreserveModelHierarchy(modelCard)
)
);
@@ -151,22 +151,22 @@ public class NavisworksSavedViewsFilter : DiscriminatedObject, ISendFilterSelect
foreach (NAV.SavedItem item in ((NAV.GroupItem)parentItem).Children)
{
switch (item)
switch (item.IsGroup)
{
// case NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// 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:
// Skip animation cuts.
// THIS IS COMMENTED OUT AS IT IS LEGACY DEFENSIVE BEHAVIOUR - DISCUSSION REQUIRED
// case false when item is NAV.SavedViewpoint { ContainsVisibilityOverrides: false }:
// // If the saved view does not contain visibility overrides, this is effectively everything in the model.
// // This will need to be the documented behaviour.
// break;
case false:
collectedSets.Add((NAV.SavedViewpoint)item);
break;
case NAV.SavedViewpoint savedViewpoint:
collectedSets.Add(savedViewpoint);
default: // handles item.IsGroup == true
if (((NAV.GroupItem)item).Children.Count > 0) // Don't add empty groups
{
CollectSavedViews(item, collectedSets);
}
break;
case NAV.GroupItem groupItem when groupItem.Children.Count > 0:
CollectSavedViews(groupItem, collectedSets);
break;
// No action for empty groups or unknown types.
}
}
}
@@ -32,9 +32,18 @@ public class NavisworksRootObjectBuilder(
internal NavisworksConversionSettings GetCurrentSettings() => converterSettings.Current;
/// <summary>
/// Asynchronously builds a Speckle object hierarchy from Navisworks model items.
/// </summary>
/// <param name="navisworksModelItems">The list of Navisworks items to convert.</param>
/// <param name="sendInfo">Information about the send operation.</param>
/// <param name="onOperationProgressed">Progress reporting callback.</param>
/// <param name="cancellationToken">Token to cancel the operation.</param>
/// <returns>A result containing the root collection and conversion results.</returns>
/// <exception cref="SpeckleException">Thrown when no objects can be converted.</exception>
public async Task<RootObjectBuilderResult> Build(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -45,7 +54,7 @@ public class NavisworksRootObjectBuilder(
#endif
using var activity = activityFactory.Start("Build");
ValidateInputs(navisworksModelItems, projectId, onOperationProgressed);
ValidateInputs(navisworksModelItems, sendInfo, onOperationProgressed);
// 2. Initialize root collection
var rootCollection = InitializeRootCollection();
@@ -53,7 +62,7 @@ public class NavisworksRootObjectBuilder(
// 3. Convert all model items and store results
var (convertedElements, conversionResults) = await ConvertModelItemsAsync(
navisworksModelItems,
projectId,
sendInfo,
onOperationProgressed,
cancellationToken
);
@@ -71,7 +80,7 @@ public class NavisworksRootObjectBuilder(
private static void ValidateInputs(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed
)
{
@@ -85,11 +94,9 @@ public class NavisworksRootObjectBuilder(
throw new ArgumentNullException(nameof(navisworksModelItems));
}
if (onOperationProgressed == null || projectId == null)
if (onOperationProgressed == null || sendInfo == null)
{
throw new ArgumentNullException(
onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(projectId)
);
throw new ArgumentNullException(onOperationProgressed == null ? nameof(onOperationProgressed) : nameof(sendInfo));
}
}
@@ -102,7 +109,7 @@ public class NavisworksRootObjectBuilder(
private Task<(Dictionary<string, Base?> converted, List<SendConversionResult> results)> ConvertModelItemsAsync(
IReadOnlyList<NAV.ModelItem> navisworksModelItems,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -115,7 +122,7 @@ public class NavisworksRootObjectBuilder(
foreach (var item in navisworksModelItems)
{
cancellationToken.ThrowIfCancellationRequested();
var converted = ConvertNavisworksItem(item, convertedBases, projectId);
var converted = ConvertNavisworksItem(item, convertedBases, sendInfo);
results.Add(converted);
processedCount++;
onOperationProgressed.Report(new CardProgress("Converting", (double)processedCount / totalCount));
@@ -303,7 +310,7 @@ public class NavisworksRootObjectBuilder(
private SendConversionResult ConvertNavisworksItem(
NAV.ModelItem navisworksItem,
Dictionary<string, Base?> convertedBases,
string projectId
SendInfo sendInfo
)
{
string applicationId = elementSelectionService.GetModelItemPath(navisworksItem);
@@ -311,7 +318,7 @@ public class NavisworksRootObjectBuilder(
try
{
Base converted = sendConversionCache.TryGetValue(applicationId, projectId, out ObjectReference? cached)
Base converted = sendConversionCache.TryGetValue(applicationId, sendInfo.ProjectId, out ObjectReference? cached)
? cached
: rootToSpeckleConverter.Convert(navisworksItem);
@@ -1,12 +0,0 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connector.Navisworks.Operations.Send.Settings;
public class RevitCategoryMappingSetting(bool value) : ICardSetting
{
public string? Id { get; set; } = "mappingToRevitCategories";
public string? Title { get; set; } = "Map to Revit Categories";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
public object? Value { get; set; } = value;
}
@@ -18,7 +18,6 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
private readonly Dictionary<string, bool?> _convertHiddenElementsCache = [];
private readonly Dictionary<string, bool?> _includeInternalPropertiesCache = [];
private readonly Dictionary<string, bool?> _preserveModelHierarchyCache = [];
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
public ToSpeckleSettingsManagerNavisworks(ISendConversionCache sendConversionCache)
{
@@ -64,41 +63,23 @@ public class ToSpeckleSettingsManagerNavisworks : IToSpeckleSettingsManagerNavis
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))
var originString = modelCard.Settings?.First(s => s.Id == "originMode").Value as string;
if (originString is not null && OriginModeSetting.OriginModeMap.TryGetValue(originString, out OriginMode origin))
{
return OriginMode.ModelOrigin; // Default to ModelOrigin if not specified or invalid
}
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousType) && previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
}
public bool GetMappingToRevitCategories(SenderModelCard modelCard)
{
if (modelCard == null)
{
throw new ArgumentNullException(nameof(modelCard));
}
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
{
if (previousValue != returnValue)
if (_originModeCache.TryGetValue(modelCard.ModelCardId.NotNull(), out OriginMode previousType))
{
EvictCacheForModelCard(modelCard);
if (previousType != origin)
{
EvictCacheForModelCard(modelCard);
}
}
_originModeCache[modelCard.ModelCardId.NotNull()] = origin;
return origin;
}
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
return returnValue;
throw new ArgumentException($"Invalid origin mode value: {originString}");
}
public bool GetConvertHiddenElements(SenderModelCard modelCard)
@@ -25,11 +25,10 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GeometryNodeMerger.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksHierarchyBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\NavisworksRootObjectBuilder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenElementsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ConvertHiddenEleementsSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\IncludeInternalPropertiesSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\OriginModeSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\PreserveModelHierarchySetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\RevitCategoryMapping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManagerNavisworks.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\VisualRepresentationSetting.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\NavisworksSelectionFilter.cs"/>
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.revit2022": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.revit2023": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -281,9 +281,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -306,7 +306,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.revit2024": {
@@ -351,11 +351,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Revit.API": {
@@ -366,9 +366,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -378,14 +378,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -226,9 +226,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -251,7 +251,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.revit2025": {
@@ -296,11 +296,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Revit.API": {
@@ -311,9 +311,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -321,14 +321,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -219,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.dui": {
@@ -244,7 +244,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.revit2026": {
@@ -280,11 +280,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Revit.API": {
@@ -295,9 +295,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Data.Sqlite": "7.0.5",
@@ -305,14 +305,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -15,26 +15,18 @@ public partial class CefSharpPanel : Page, Autodesk.Revit.UI.IDockablePaneProvid
public void ExecuteScript(string script)
{
try
{
Browser.Dispatcher.Invoke(
() =>
Browser.Dispatcher.Invoke(
() =>
{
//avoid exceptions by checking if IBrowser is there
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
{
//avoid exceptions by checking if IBrowser is there
if (!Browser.IsBrowserInitialized || Browser.GetBrowser() is null)
{
return;
}
Browser.ExecuteScriptAsync(script);
},
DispatcherPriority.Background
);
}
catch (OperationCanceledException)
{
//do nothing, happens when closing Revit while a script is being executed
}
return;
}
Browser.ExecuteScriptAsync(script);
},
DispatcherPriority.Background
);
}
public void SendProgress(string script) => ExecuteScript(script);
@@ -72,7 +72,6 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
return new DocumentInfo("", "", "") { Message = "Family environment files not supported by Speckle." };
}
//should this use the Hashcode of the document instead of something like CreationGUID?
var info = new DocumentInfo(doc.PathName, doc.Title, doc.GetHashCode().ToString());
return info;
@@ -14,7 +14,7 @@ public sealed class RevitReceiveBinding(
ICancellationManager cancellationManager,
IBrowserBridge parent,
ILogger<RevitReceiveBinding> logger,
Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
Speckle.Connectors.Revit.Operations.Receive.Settings.ToHostSettingsManager toHostSettingsManager,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
IReceiveOperationManagerFactory receiveOperationManagerFactory
) : IReceiveBinding
@@ -24,7 +24,8 @@ public sealed class RevitReceiveBinding(
private IReceiveBindingUICommands Commands { get; } = new ReceiveBindingUICommands(parent);
#pragma warning disable CA1024
public List<ICardSetting> GetReceiveSettings() => [new Operations.Receive.Settings.ReceiveReferencePointSetting()];
public List<ICardSetting> GetReceiveSettings() =>
[new Speckle.Connectors.Revit.Operations.Receive.Settings.ReferencePointSetting(ReceiveReferencePointType.Source)];
#pragma warning restore CA1024
public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId);
@@ -97,11 +97,11 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
public List<ICardSetting> GetSendSettings() =>
[
new DetailLevelSetting(),
new SendReferencePointSetting(),
new SendParameterNullOrEmptyStringsSetting(),
new LinkedModelsSetting(),
new SendRebarsAsVolumetricSetting()
new DetailLevelSetting(DetailLevelType.Medium),
new ReferencePointSetting(ReferencePointType.InternalOrigin),
new SendParameterNullOrEmptyStringsSetting(false),
new LinkedModelsSetting(true),
new SendRebarsAsVolumetricSetting(false)
];
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
@@ -135,7 +135,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
{
var activeUIDoc =
_revitContext.UIApplication?.ActiveUIDocument
_revitContext.UIApplication.NotNull().ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
@@ -216,13 +216,12 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
newSelectedObjectIds.Add(element.UniqueId);
}
// NOTE: preserve & persist original user selection for selection filter implemented during
// [CNX-2400](https://linear.app/speckle/issue/CNX-2400/object-dont-update-on-publish)
// NOTE: update with current document for views and categories filter since these represent dynamic queries
// View & categories filters self-update their SelectedObjectIds in RefreshObjectIds(), maintaining consistency
var objectIds =
modelCard.SendFilter is RevitSelectionFilter ? modelCard.SendFilter.SelectedObjectIds : newSelectedObjectIds;
await Commands.SetFilterObjectIds(modelCard.ModelCardId.NotNull(), modelCard.SendFilter.IdMap, objectIds);
// We update the state on the UI SenderModelCard to prevent potential inconsistencies between hostApp IdMap in sendfilters.
await Commands.SetFilterObjectIds(
modelCard.ModelCardId.NotNull(),
modelCard.SendFilter.IdMap,
newSelectedObjectIds
);
}
return documentElementContexts;
@@ -236,16 +235,13 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private void DocChangeHandler(Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
ICollection<ElementId> modifiedElementIds = e.GetModifiedElementIds();
var doc = e.GetDocument();
if (doc == null)
{
return;
}
// NOTE: Whenever we save data into file this event also trigger changes on its DataStorage.
// On every add/remove/update model attempt triggers this handler and was causing unnecessary calls on `RunExpirationChecks`
// Re-check it once we implement Linked Documents
if (modifiedElementIds.Count == 1)
{
var doc = e.GetDocument();
if (modifiedElementIds.All(el => doc.GetElement(el) is DataStorage))
{
return;
@@ -275,7 +271,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_idleManager.SubscribeToIdle(nameof(PostSetObjectIds), PostSetObjectIds);
}
if (HaveUnitsChanged(doc))
if (HaveUnitsChanged(e.GetDocument()))
{
var objectIds = new List<string>();
foreach (var sender in _store.GetSenders().ToList())
@@ -288,7 +284,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
var selectedObjects = sender.SendFilter.NotNull().SelectedObjectIds;
objectIds.AddRange(selectedObjects);
}
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
_sendConversionCache.EvictObjects(unpackedObjectIds);
}
@@ -351,13 +347,10 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
// {
// await Commands.RefreshSendFilters();
// }
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
return;
}
if (ChangedObjectIds.Any(e => doc.GetElement(e) is View))
if (
ChangedObjectIds.Any(e => _revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View)
)
{
await Commands.RefreshSendFilters();
}
@@ -367,7 +360,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
var senders = _store.GetSenders().ToList();
// string[] objectIdsList = ChangedObjectIds.Keys.ToArray();
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
var doc = _revitContext.UIApplication.NotNull().ActiveUIDocument.Document;
if (doc == null)
{
@@ -409,7 +402,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
}
}
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds, doc);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objUniqueIds);
_sendConversionCache.EvictObjects(unpackedObjectIds);
// Note: we're doing object selection and card expiry management by old school ids
@@ -37,6 +37,7 @@ public static class ServiceRegistration
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
// Storage Schema
serviceCollection.AddScoped<DocumentModelStorageSchema>();
serviceCollection.AddScoped<IdStorageSchema>();
// POC: we need to review the scopes and create a document on what the policy is
@@ -0,0 +1,22 @@
using Autodesk.Revit.DB.ExtensibleStorage;
namespace Speckle.Connectors.Revit.HostApp;
public class DocumentModelStorageSchema : IStorageSchema
{
private readonly Guid _schemaGuid = new("D690F2B4-BDB0-4CB4-8657-17844ADF42AA");
public Schema GetSchema()
{
Schema schema = Schema.Lookup(_schemaGuid);
if (schema != null)
{
return schema;
}
using SchemaBuilder builder = new(_schemaGuid);
builder.SetSchemaName("DUI3State");
builder.AddSimpleField("contents", typeof(string));
return builder.Finish();
}
}
@@ -1,5 +1,8 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.HostApp;
@@ -8,6 +11,15 @@ namespace Speckle.Connectors.Revit.HostApp;
/// </summary>
public class ElementUnpacker
{
private readonly RevitContext _revitContext;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
public ElementUnpacker(RevitContext revitContext, IConverterSettingsStore<RevitConversionSettings> converterSettings)
{
_revitContext = revitContext;
_converterSettings = converterSettings;
}
/// <summary>
/// Unpacks a random set of revit objects into atomic objects. It currently unpacks groups recurisvely, nested families into atomic family instances.
/// This method will also "pack" curtain walls if necessary (ie, if mullions or panels are selected without their parent curtain wall, they are sent independently; if the parent curtain wall is selected, they will be removed out as the curtain wall will include all its children).
@@ -17,7 +29,7 @@ public class ElementUnpacker
/// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.<br/>
/// 2- Evicting the cache while introducing the settings</param>
/// <returns></returns>
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document doc)
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document? doc = null)
{
// Note: steps kept separate on purpose.
// Step 1: unpack groups
@@ -38,19 +50,24 @@ public class ElementUnpacker
/// <remarks>
/// This is used to invalidate object ids in the send conversion cache when the selected object id is only the parent element id
/// </remarks>
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds, Document doc)
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
var docElements = doc.GetElements(objectIds);
return UnpackSelectionForConversion(docElements, doc).Select(o => o.UniqueId).ToList();
return UnpackSelectionForConversion(docElements).Select(o => o.UniqueId).ToList();
}
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
// 2- Evicting the cache while introducing the settings
private List<Element> UnpackElements(IEnumerable<Element> elements, Document doc)
private List<Element> UnpackElements(IEnumerable<Element> elements, Document? doc = null)
{
var unpackedElements = new List<Element>(); // note: could be a hashset/map so we prevent duplicates (?)
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
foreach (var element in elements)
{
// UNPACK: Groups
@@ -60,7 +77,7 @@ public class ElementUnpacker
// We add null checks to handle cases where elements can't be properly resolved
// POC: this might screw up generating hosting rel generation here, because nested families in groups get flattened out by GetMemberIds().
var groupElements = g.GetMemberIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(groupElements, doc));
unpackedElements.AddRange(UnpackElements(groupElements));
}
else if (element is BaseArray baseArray)
{
@@ -68,8 +85,8 @@ public class ElementUnpacker
// This handles cases where some elements might not resolve in linked contexts
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement).Where(el => el != null);
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(arrayElements, doc));
unpackedElements.AddRange(UnpackElements(originalElements, doc));
unpackedElements.AddRange(UnpackElements(arrayElements));
unpackedElements.AddRange(UnpackElements(originalElements));
}
// UNPACK: Family instances (as they potentially have nested families inside)
else if (element is FamilyInstance familyInstance)
@@ -82,7 +99,7 @@ public class ElementUnpacker
if (familyElements.Length != 0)
{
unpackedElements.AddRange(UnpackElements(familyElements, doc));
unpackedElements.AddRange(UnpackElements(familyElements));
}
unpackedElements.Add(familyInstance);
@@ -90,7 +107,7 @@ public class ElementUnpacker
else if (element is MultistoryStairs multistoryStairs)
{
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(stairs, doc));
unpackedElements.AddRange(UnpackElements(stairs));
}
else
{
@@ -108,11 +125,13 @@ public class ElementUnpacker
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
// 2- Evicting the cache while introducing the settings
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document doc)
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document? doc = null)
{
//just used for contains so use ToHashSet
var ids = elements.Select(el => el.Id).ToHashSet();
var ids = elements.Select(el => el.Id).ToArray();
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
elements.RemoveAll(element =>
(element is Mullion { Host: not null } m && ids.Contains(m.Host.Id))
|| (
@@ -1,43 +1,50 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.SQLite;
namespace Speckle.Connectors.Revit.HostApp;
// POC: should be interfaced out
internal sealed class RevitDocumentStore : DocumentModelStore
{
private readonly ILogger<RevitDocumentStore> _logger;
// POC: move to somewhere central?
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
private readonly IAppIdleManager _idleManager;
private readonly RevitContext _revitContext;
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
private readonly IdStorageSchema _idStorageSchema;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
private readonly IThreadContext _threadContext;
public RevitDocumentStore(
ILogger<DocumentModelStore> logger,
IAppIdleManager idleManager,
RevitContext revitContext,
IJsonSerializer jsonSerializer,
DocumentModelStorageSchema documentModelStorageSchema,
IdStorageSchema idStorageSchema,
ITopLevelExceptionHandler topLevelExceptionHandler,
IRevitTask revitTask,
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
ILogger<RevitDocumentStore> logger
IThreadContext threadContext,
IRevitTask revitTask
)
: base(logger, jsonSerializer)
{
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
_idleManager = idleManager;
_revitContext = revitContext;
_documentModelStorageSchema = documentModelStorageSchema;
_idStorageSchema = idStorageSchema;
_topLevelExceptionHandler = topLevelExceptionHandler;
_logger = logger;
_threadContext = threadContext;
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
@@ -94,48 +101,103 @@ internal sealed class RevitDocumentStore : DocumentModelStore
return;
}
try
{
var key = GetKeyForDocument(document);
if (key != null)
_threadContext
.RunOnMain(() =>
{
_jsonCacheManager.UpdateObject(key, modelCardState);
}
}
catch (Exception ex) when (!ex.IsFatal())
{
var key = GetKeyForDocument(document);
_logger.LogError(ex, "Failed to save model card state for document {DocumentId}", key);
}
//if not the same active document then don't save the current cards to a bad document!
if (!EnsureActiveDocumentIsSame(document))
{
return;
}
using Transaction t = new(document, "Speckle Write State");
t.Start();
using DataStorage ds = GetSettingsDataStorage(document) ?? DataStorage.Create(document);
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
string serializedModels = Serialize();
stateEntity.Set("contents", serializedModels);
using Entity idEntity = new(_idStorageSchema.GetSchema());
idEntity.Set("Id", s_revitDocumentStoreId);
ds.SetEntity(idEntity);
ds.SetEntity(stateEntity);
t.Commit();
})
.FireAndForget();
}
private string? GetKeyForDocument(Document doc)
private bool EnsureActiveDocumentIsSame(Document document)
{
string? id = doc?.ProjectInformation?.UniqueId; //ProjectInformation Should only be null for family docs
#if REVIT_2024_OR_GREATER
id ??= doc.CreationGUID.ToString(); //fallback for family docs
#endif
return id;
var localDoc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (localDoc == null)
{
return false;
}
return localDoc.Equals(document);
}
protected override void LoadState()
{
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
// POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save)
if (document == null)
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
if (stateEntity == null || !stateEntity.IsValid())
{
ClearAndSave();
return;
}
var key = GetKeyForDocument(document);
if (key != null)
string modelsString = stateEntity.Get<string>("contents");
LoadFromString(modelsString);
}
private DataStorage? GetSettingsDataStorage(Document doc)
{
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
var state = _jsonCacheManager.GetObject(key);
if (state == null)
DataStorage dataStorage = (DataStorage)element;
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
if (!settingIdEntity.IsValid())
{
return;
continue;
}
LoadFromString(state);
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
}
return null;
}
private Entity? GetSpeckleEntity(Document? doc)
{
if (doc is null)
{
return null;
}
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingEntity = dataStorage.GetEntity(_documentModelStorageSchema.GetSchema());
if (!settingEntity.IsValid())
{
continue;
}
return settingEntity;
}
return null;
}
}
@@ -3,13 +3,9 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ReceiveReferencePointSetting(ReceiveReferencePointType value = ReceiveReferencePointSetting.DEFAULT_VALUE)
: ICardSetting
public class ReferencePointSetting(ReceiveReferencePointType value) : ICardSetting
{
public const string SETTING_ID = "referencePoint";
public const ReceiveReferencePointType DEFAULT_VALUE = ReceiveReferencePointType.Source;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "referencePoint";
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReceiveReferencePointType)).ToList();
@@ -1,6 +1,5 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
@@ -12,21 +11,18 @@ namespace Speckle.Connectors.Revit.Operations.Receive.Settings;
public class ToHostSettingsManager : IToHostSettingsManager
{
private readonly RevitContext _revitContext;
private readonly ILogger<ToHostSettingsManager> _logger;
public ToHostSettingsManager(RevitContext revitContext, ILogger<ToHostSettingsManager> logger)
public ToHostSettingsManager(RevitContext revitContext)
{
_revitContext = revitContext;
_logger = logger;
}
public Transform? GetReferencePointSetting(ModelCard modelCard)
{
var referencePointString =
modelCard.Settings?.FirstOrDefault(s => s.Id == ReceiveReferencePointSetting.SETTING_ID)?.Value as string;
var referencePointString = modelCard.Settings?.First(s => s.Id == "referencePoint").Value as string;
if (
referencePointString is not null
&& ReceiveReferencePointSetting.ReferencePointMap.TryGetValue(
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
referencePointString,
out ReceiveReferencePointType referencePoint
)
@@ -38,16 +34,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
return currentTransform;
}
// log the issue
_logger.LogWarning(
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
referencePointString,
modelCard.ModelCardId,
ReceiveReferencePointSetting.DEFAULT_VALUE
);
// return default (null for Source means no transform)
return null;
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
}
private Transform? GetTransform(ReceiveReferencePointType referencePointType)
@@ -104,7 +91,7 @@ public class ToHostSettingsManager : IToHostSettingsManager
}
throw new InvalidOperationException(
"Revit Context UI Application was null when retrieving reference point transform"
"Revit Context UI Application was null when retrieving reference point transform."
);
}
}
@@ -2,9 +2,7 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converters.RevitShared.Extensions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -77,8 +75,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
//this used to throw an exception, but we don't want to fail loudly if the view is not found
return [];
}
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var elementsInView = viewCollector.ToElements();
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -127,52 +125,4 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
// Below function collects list of ids to exclude from final list.
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
{
var elementsToExclude = new HashSet<ElementId>();
foreach (var element in elements)
{
// check if element is a part
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
{
try
{
// get source element ids from the part
var sourceIds = part.GetSourceElementIds();
if (sourceIds != null)
{
foreach (var sourceId in sourceIds)
{
elementsToExclude.Add(sourceId.HostElementId);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
// silently continue processing other Parts if one fails
// this follows the pattern used elsewhere in the codebase
}
}
}
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
{
var idsToExclude = GetSourceElementIdsToExclude(allElements);
return allElements.Where(e => !idsToExclude.Contains(e.Id));
}
return allElements;
}
}
@@ -33,17 +33,17 @@ public class RevitRootObjectBuilder(
{
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(documentElementContexts, projectId, onOperationProgressed, ct))
() => Task.FromResult(BuildSync(documentElementContexts, sendInfo, onOperationProgressed, ct))
);
private RootObjectBuilderResult BuildSync(
IReadOnlyList<DocumentToConvert> documentElementContexts,
string projectId,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
@@ -183,7 +183,10 @@ public class RevitRootObjectBuilder(
// non-transformed elements can safely rely on cache
// TODO: Potential here to transform cached objects and NOT reconvert,
// TODO: we wont do !hasTransform here, and re-set application id before this
if (!hasTransform && sendConversionCache.TryGetValue(projectId, applicationId, out ObjectReference? value))
if (
!hasTransform
&& sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value)
)
{
converted = value;
cacheHitCount++;
@@ -225,16 +228,7 @@ public class RevitRootObjectBuilder(
}
}
// if we ended up skipping everything, there is a reason for this, that users can diagnose themselves
// this can occur if a published view contains only unsupported objects or if user trying to ONLY send linked model
// docs but the setting is disabled
if (skippedObjectCount == atomicObjectCount)
{
throw new SpeckleException("No supported objects visible. Update publish filter or check publish settings.");
}
// this is, I suppose, fully on us?
if (results.All(x => x.Status == Status.ERROR))
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjectCount)
{
throw new SpeckleException("Failed to convert all objects.");
}
@@ -3,12 +3,9 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class DetailLevelSetting(DetailLevelType value = DetailLevelSetting.DEFAULT_VALUE) : ICardSetting
public class DetailLevelSetting(DetailLevelType value) : ICardSetting
{
public const string SETTING_ID = "detailLevel";
public const DetailLevelType DEFAULT_VALUE = DetailLevelType.Medium;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "detailLevel";
public string? Title { get; set; } = "Detail Level";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(DetailLevelType)).ToList();
@@ -2,12 +2,9 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class LinkedModelsSetting(bool value = LinkedModelsSetting.DEFAULT_VALUE) : ICardSetting
public class LinkedModelsSetting(bool value) : ICardSetting
{
public const string SETTING_ID = "includeLinkedModels";
public const bool DEFAULT_VALUE = true;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "includeLinkedModels";
public string? Title { get; set; } = "Include Linked Models";
public string? Type { get; set; } = "boolean";
public object? Value { get; set; } = value;
@@ -3,13 +3,9 @@ using Speckle.Converters.RevitShared.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class SendReferencePointSetting(ReferencePointType value = SendReferencePointSetting.DEFAULT_VALUE)
: ICardSetting
public class ReferencePointSetting(ReferencePointType value) : ICardSetting
{
public const string SETTING_ID = "referencePoint";
public const ReferencePointType DEFAULT_VALUE = ReferencePointType.InternalOrigin;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "referencePoint";
public string? Title { get; set; } = "Reference Point";
public string? Type { get; set; } = "string";
public List<string>? Enum { get; set; } = System.Enum.GetNames(typeof(ReferencePointType)).ToList();
@@ -2,13 +2,9 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class SendParameterNullOrEmptyStringsSetting(bool value = SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE)
: ICardSetting
public class SendParameterNullOrEmptyStringsSetting(bool value) : ICardSetting
{
public const string SETTING_ID = "nullemptyparams";
public const bool DEFAULT_VALUE = false;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "nullemptyparams";
public string? Title { get; set; } = "Send null/empty parameters";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
@@ -2,12 +2,9 @@ using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class SendRebarsAsVolumetricSetting(bool value = SendRebarsAsVolumetricSetting.DEFAULT_VALUE) : ICardSetting
public class SendRebarsAsVolumetricSetting(bool value) : ICardSetting
{
public const string SETTING_ID = "sendRebarsAsVolumetric";
public const bool DEFAULT_VALUE = false;
public string? Id { get; set; } = SETTING_ID;
public string? Id { get; set; } = "sendRebarsAsVolumetric";
public string? Title { get; set; } = "Send Rebars As Volumetric (disable for better performance)";
public string? Type { get; set; } = "boolean";
public object? Value { get; set; } = value;
@@ -1,13 +1,11 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.Revit.HostApp;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
@@ -18,32 +16,28 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
private readonly RevitContext _revitContext;
private readonly ISendConversionCache _sendConversionCache;
private readonly ElementUnpacker _elementUnpacker;
private readonly ILogger<ToSpeckleSettingsManager> _logger;
// cache invalidation process run with ModelCardId since the settings are model specific
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = [];
private readonly Dictionary<string, Transform?> _referencePointCache = [];
private readonly Dictionary<string, bool?> _sendNullParamsCache = [];
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = [];
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = [];
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = new();
private readonly Dictionary<string, Transform?> _referencePointCache = new();
private readonly Dictionary<string, bool?> _sendNullParamsCache = new();
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = new();
private readonly Dictionary<string, bool?> _sendRebarsAsVolumetricCache = new();
public ToSpeckleSettingsManager(
RevitContext revitContext,
ISendConversionCache sendConversionCache,
ElementUnpacker elementUnpacker,
ILogger<ToSpeckleSettingsManager> logger
ElementUnpacker elementUnpacker
)
{
_revitContext = revitContext;
_elementUnpacker = elementUnpacker;
_sendConversionCache = sendConversionCache;
_logger = logger;
}
public DetailLevelType GetDetailLevelSetting(SenderModelCard modelCard)
{
var fidelityString =
modelCard.Settings?.FirstOrDefault(s => s.Id == DetailLevelSetting.SETTING_ID)?.Value as string;
var fidelityString = modelCard.Settings?.First(s => s.Id == "detailLevel").Value as string;
if (
fidelityString is not null
&& DetailLevelSetting.GeometryFidelityMap.TryGetValue(fidelityString, out DetailLevelType fidelity)
@@ -60,27 +54,15 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return fidelity;
}
// log the issue
_logger.LogWarning(
"Invalid detail level setting received: '{FidelityString}' for model {ModelCardId}, using default: {DefaultValue}",
fidelityString,
modelCard.ModelCardId,
DetailLevelSetting.DEFAULT_VALUE
);
// return sensible default
DetailLevelType defaultValue = DetailLevelSetting.DEFAULT_VALUE;
_detailLevelCache[modelCard.ModelCardId.NotNull()] = defaultValue;
return defaultValue;
throw new ArgumentException($"Invalid geometry fidelity value: {fidelityString}");
}
public Transform? GetReferencePointSetting(ModelCard modelCard)
{
var referencePointString =
modelCard.Settings?.FirstOrDefault(s => s.Id == SendReferencePointSetting.SETTING_ID)?.Value as string;
var referencePointString = modelCard.Settings?.First(s => s.Id == "referencePoint").Value as string;
if (
referencePointString is not null
&& SendReferencePointSetting.ReferencePointMap.TryGetValue(
&& ReferencePointSetting.ReferencePointMap.TryGetValue(
referencePointString,
out ReferencePointType referencePoint
)
@@ -103,63 +85,14 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return currentTransform;
}
// log the issue
_logger.LogWarning(
"Invalid reference point setting received: '{ReferencePointString}' for model {ModelCardId}, using default: {DefaultValue}",
referencePointString,
modelCard.ModelCardId,
SendReferencePointSetting.DEFAULT_VALUE
);
// return default (null for InternalOrigin means no transform)
_referencePointCache[modelCard.ModelCardId.NotNull()] = null;
return null;
throw new ArgumentException($"Invalid reference point value: {referencePointString}");
}
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
SendParameterNullOrEmptyStringsSetting.SETTING_ID,
SendParameterNullOrEmptyStringsSetting.DEFAULT_VALUE,
modelCard,
_sendNullParamsCache,
"Send null/empty parameters"
);
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
public bool GetLinkedModelsSetting(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
LinkedModelsSetting.SETTING_ID,
LinkedModelsSetting.DEFAULT_VALUE,
modelCard,
_sendLinkedModelsCache,
"Linked models"
);
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard) =>
GetBooleanSettingWithCache(
SendRebarsAsVolumetricSetting.SETTING_ID,
SendRebarsAsVolumetricSetting.DEFAULT_VALUE,
modelCard,
_sendRebarsAsVolumetricCache,
"Send rebars as volumetric"
);
/// <summary>
/// Helper method to handle boolean settings with caching and logging
/// </summary>
private bool GetBooleanSettingWithCache(
string settingId,
bool defaultValue,
SenderModelCard modelCard,
Dictionary<string, bool?> cache,
string settingName
)
public bool GetSendParameterNullOrEmptyStringsSetting(SenderModelCard modelCard)
{
var settingValue = modelCard.Settings?.FirstOrDefault(s => s.Id == settingId)?.Value as bool?;
bool returnValue = settingValue ?? defaultValue;
if (cache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
var value = modelCard.Settings?.First(s => s.Id == "nullemptyparams").Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendNullParamsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
if (previousValue != returnValue)
{
@@ -167,32 +100,47 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
}
}
cache[modelCard.ModelCardId] = returnValue;
_sendNullParamsCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
// NOTE: we probably don't need to log here BUT considering users might complain that a setting might not have been
// respected (linked models disabled but still sent linked models), I think we should note this occurence so we know
if (settingValue == null)
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
public bool GetLinkedModelsSetting(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "includeLinkedModels").Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendLinkedModelsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
_logger.LogWarning(
"{SettingName} setting was null for model {ModelCardId}, using default: {DefaultValue}",
settingName,
modelCard.ModelCardId,
defaultValue
);
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_sendLinkedModelsCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
public bool GetSendRebarsAsVolumetric(SenderModelCard modelCard)
{
var value = modelCard.Settings?.First(s => s.Id == "sendRebarsAsVolumetric").Value as bool?;
var returnValue = value != null && value.NotNull();
if (_sendRebarsAsVolumetricCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_sendRebarsAsVolumetricCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var doc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (doc == null)
{
throw new SpeckleException("Unable to retrieve active UI document");
}
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds, doc);
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
_sendConversionCache.EvictObjects(unpackedObjectIds);
}
@@ -38,10 +38,6 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -65,10 +61,6 @@ public class RevitThreadContext : ThreadContext
return default;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -112,11 +104,6 @@ public class RevitThreadContext : ThreadContext
ex = e;
}
});
if (ex is OperationCanceledException operation)
{
throw operation;
}
if (ex is not null)
{
throw new SpeckleRevitTaskException(ex);
@@ -7,7 +7,7 @@ using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.Plugin;
#pragma warning disable CA1032
public class SpeckleRevitTaskException(Exception exception) : SpeckleException(exception.Message, exception)
public class SpeckleRevitTaskException(Exception exception) : SpeckleException("Revit operation failed", exception)
#pragma warning restore CA1032
{
public static async Task ProcessException<T>(
@@ -19,6 +19,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Bindings\SelectionBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitSendBinding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ElementIdHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LevelUnpacker.cs" />
@@ -36,9 +37,9 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ReceiveReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)operations\receive\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\RevitHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)operations\receive\ToHostSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\TransactionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\IRevitSendFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitCategoriesFilter.cs" />
@@ -49,7 +50,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendRebarsAsVolumetricSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\DetailLevelSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\IRevitPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCommand.cs" />
@@ -60,4 +61,4 @@
<Compile Include="$(MSBuildThisFileDirectory)Plugin\RevitCefPlugin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\SpeckleRevitTaskException.cs" />
</ItemGroup>
</Project>
</Project>
+1 -1
View File
@@ -5,7 +5,7 @@
</ItemGroup>
<Target AfterTargets="Build" Name="WarnIfPublicReleaseVersionInstalled" Condition="'$(RhinoVersion)' != '' And '$(ContinuousIntegrationBuild)' != 'true' And '$(OS)' == 'Windows_NT'">
<Warning
Text="Conflicting Rhino plugin detected - Do you have a public release installed? at '@(PublicReleasePath)'"
Text="Conflicting Rhino plugin detected - Do you have a public release installed?"
Condition="Exists(@(PublicReleasePath))" />
</Target>
@@ -13,9 +13,9 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"requested": "[2.0.1, )",
"resolved": "2.0.1",
"contentHash": "K/LyJbYscXIdV07tPhsZgyUdwzf7vsXJBraVik6ge35T9lLYrriG5Dy9XF8ox7Q2ko5y5aTzh768+vYCdDkL5g==",
"dependencies": {
"PolySharp": "1.14.1"
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.logging": {
@@ -337,7 +337,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.rhino7": {
@@ -382,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -403,14 +403,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -13,9 +13,9 @@
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
"requested": "[2.0.1, )",
"resolved": "2.0.1",
"contentHash": "K/LyJbYscXIdV07tPhsZgyUdwzf7vsXJBraVik6ge35T9lLYrriG5Dy9XF8ox7Q2ko5y5aTzh768+vYCdDkL5g==",
"dependencies": {
"PolySharp": "1.14.1"
}
@@ -325,9 +325,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.5.4, )",
"Speckle.Sdk": "[3.5.4, )",
"Speckle.Sdk.Dependencies": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )",
"Speckle.Sdk": "[3.4.3, )",
"Speckle.Sdk.Dependencies": "[3.4.3, )"
}
},
"speckle.connectors.logging": {
@@ -337,12 +337,13 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.5.4, )"
"Speckle.Objects": "[3.4.3, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -381,18 +382,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o7ex4+yHJYI8pJbsjNqw+D8r8WjkBoB5aK/GQlGJd/0zydrPxN4SMKS4arpRBR3CUD6JhtQMatScXZOrslGXQg==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "08BOhVaYUvFP8CgY5fGZJWvswIRsH2EbOBGguJnanx+RZqKYI9XJroGGew+CDfPU1c8ZWl37WX2bbdPhdSP2rQ==",
"dependencies": {
"Speckle.Sdk": "3.5.4"
"Speckle.Sdk": "3.4.3"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "o4bEJTz+OBI1koy9xqXSIq3UtUFCKtk6Btg82rdVM2aFMPT3ZoYVarG+ylPcUOHd684XpgGASxE6dIgXz2pvng==",
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "21Dg8kMOqUbV7jyhK7N+UwyO7S1++COMR5jU9mm/vx86pkj6pTAifNZMHmuG8/ZnwpBcUCmE4AzvW24mKlgEkg==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -402,14 +403,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.5.4"
"Speckle.Sdk.Dependencies": "3.4.3"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.5.4, )",
"resolved": "3.5.4",
"contentHash": "d0ZOHiK11Hq9r7YEkfTvVu33ygWtsrgysIWdCRAz6rdlcAgMCEkWVBoe3jDjxdmUy20TToaQlFKfMH4hTyzWXg=="
"requested": "[3.4.3, )",
"resolved": "3.4.3",
"contentHash": "PyJAt9WAeVLUfxp/l1ZvGw20HAsvfBY/JgZCJmY5doc8lFa/9dhUMBJN/cNsVM5XJK+xdIBoEemgl3SMFwXNcg=="
}
}
}
@@ -399,33 +399,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public string SearchPattern { get; set; } = string.Empty;
private bool _autoSelectAllItemsItems;
/// <summary>
/// When enabled, all available items will be selected automatically and persistently.
/// </summary>
public bool AutoSelectAllItems
{
get => _autoSelectAllItemsItems;
set
{
if (_autoSelectAllItemsItems == value)
{
return;
}
_autoSelectAllItemsItems = value;
if (value && _listItems.Count > 0)
{
SelectAllItems();
ResetPersistentData(_listItems.Select(x => x.Value), "Enable auto-select all items");
}
OnDisplayExpired(false);
}
}
protected internal int LayoutLevel { get; set; } = 1;
sealed class ListItem
@@ -464,14 +437,16 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public RectangleF BoxName;
}
private List<ListItem> _listItems = [];
private List<ListItem> _listItems = new List<ListItem>();
IEnumerable<ListItem> SelectedItems => _listItems.Where(x => x.Selected);
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
if (Kind == GH_ParamKind.floating || Kind == GH_ParamKind.input)
{
Menu_AppendInternaliseData(menu);
Menu_AppendDestroyPersistent(menu);
if (Exposure != GH_Exposure.hidden) { }
}
}
@@ -574,8 +549,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected override GH_GetterResult Prompt_Singular(ref T value) => GH_GetterResult.cancel;
// NOTE: removed from AppendAdditionalMenuItems as clearing selection simple enough. Keeping here just in case
// we want to bring it back
protected override void Menu_AppendDestroyPersistent(ToolStripDropDown menu) =>
Menu_AppendItem(menu, "Clear selection", Menu_DestroyPersistentData, PersistentDataCount > 0);
@@ -596,36 +569,11 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected override void Menu_AppendInternaliseData(ToolStripDropDown menu)
{
// Disabled Invert selection and one-off select all according to Discord chat. These are easy enough
// Select all also enabled through ctrl+a
//Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
//Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
var alwaysSelectAllItem = Menu_AppendItem(
menu,
"Auto-select all items",
Menu_AlwaysSelectAllClicked,
true,
_autoSelectAllItemsItems
);
alwaysSelectAllItem.ToolTipText = _autoSelectAllItemsItems
? "Currently auto-selecting all available items. Click to disable."
: "Enable automatic selection of all available items. Will persist when new data is input.";
Menu_AppendItem(menu, "Invert selection", Menu_InvertSelectionClicked, _listItems.Count != PersistentDataCount);
Menu_AppendItem(menu, "Select all", Menu_SelectAllClicked, _listItems.Count != PersistentDataCount);
Menu_AppendItem(menu, "Internalise selection", Menu_InternaliseDataClicked, SourceCount > 0);
}
/// <summary>
/// Helper method that reduces code duplication to select all items
/// </summary>
private void SelectAllItems()
{
foreach (var item in _listItems)
{
item.Selected = true;
}
}
private void Menu_InternaliseDataClicked(object sender, EventArgs e)
{
if (SourceCount == 0)
@@ -662,16 +610,14 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
protected void Menu_SelectAllClicked(object sender, EventArgs e)
{
SelectAllItems();
foreach (var item in _listItems)
{
item.Selected = true;
}
ResetPersistentData(_listItems.Select(x => x.Value), "Select all");
}
/// <summary>
/// Event handler for auto-select all items menu item
/// </summary>
private void Menu_AlwaysSelectAllClicked(object sender, EventArgs e) =>
AutoSelectAllItems = !_autoSelectAllItemsItems;
sealed class ResizableAttributes : GH_ResizableAttributes<ValueSet<T>>
{
public override bool HasInputGrip => true;
@@ -1390,6 +1336,11 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
return GH_ObjectResponse.Ignore;
}
public override GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
{
return base.RespondToKeyDown(sender, e);
}
private sealed class SearchInputBox : Grasshopper.GUI.Base.GH_TextBoxInputBase
{
private readonly ResizableAttributes _parentAttributes;
@@ -1447,12 +1398,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
reader.TryGetInt32("LayoutLevel", ref layoutLevel);
LayoutLevel = Rhino.RhinoMath.Clamp(layoutLevel, 1, 2);
bool alwaysSelectAll = false;
if (reader.TryGetBoolean("AutoSelectAllItems", ref alwaysSelectAll))
{
_autoSelectAllItemsItems = alwaysSelectAll;
}
return true;
}
@@ -1478,8 +1423,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
writer.SetInt32("LayoutLevel", LayoutLevel);
}
writer.SetBoolean("AutoSelectAllItems", _autoSelectAllItemsItems);
return true;
}
@@ -1741,8 +1684,11 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
public sealed override void PostProcessData()
{
LoadVolatileData();
PreProcessVolatileData();
ProcessVolatileData();
SortItems();
// Order by fuzzy token if suits.
@@ -1755,13 +1701,6 @@ public abstract class ValueSet<T> : GH_PersistentParam<T>, IGH_InitCodeAware, IG
}
PostProcessVolatileData();
// auto-select if enabled
if (_autoSelectAllItemsItems && _listItems.Count > 0 && _listItems.Any(item => !item.Selected))
{
SelectAllItems();
ResetPersistentData(_listItems.Select(x => x.Value), null);
}
}
public override void RegisterRemoteIDs(GH_GuidTable id_list)
@@ -1,4 +1,3 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.HostApp;
@@ -13,7 +12,7 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
: base(
"Collection Selector",
"cSelect",
"Allows you to select a set of collection paths for querying. Right-click for 'Auto-select all items' option.",
"Allows you to select a set of collection paths for querying",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.COLLECTIONS
) { }
@@ -23,18 +22,11 @@ public class CollectionPathsSelector : ValueSet<IGH_Goo>
protected override void LoadVolatileData()
{
List<SpeckleCollectionWrapper> collections = VolatileData
var collections = VolatileData
.AllData(true)
.OfType<SpeckleCollectionWrapperGoo>()
.Select(goo => goo.Value)
.ToList();
if (VolatileDataCount > collections.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Collections are accepted as inputs.");
return;
}
if (collections.Count == 0)
{
return;
@@ -158,19 +158,10 @@ public class CreateCollection : VariableParameterComponentBase
{
foreach (var obj in objects)
{
// handle data objects directly (deep copy to avoid mutations)
// NOTE: DataObject first, since a DataObject with one geo is castable to speckle geometry
if (obj is SpeckleDataObjectWrapperGoo dataObjectWrapperGoo)
// deep copy to avoid mutations
if (obj?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper objWrapper)
{
var dataObjectWrapper = dataObjectWrapperGoo.Value.DeepCopy();
dataObjectWrapper.Path = childPath;
dataObjectWrapper.Parent = parentCollection;
parentCollection.Elements.Add(dataObjectWrapper);
}
// handle geometry objects (deep copy to avoid mutations)
else if (obj?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper objWrapper)
{
SpeckleGeometryWrapper wrapper = objWrapper.DeepCopy();
SpeckleObjectWrapper wrapper = objWrapper.DeepCopy();
wrapper.Path = childPath;
wrapper.Parent = parentCollection;
parentCollection.Elements.Add(wrapper);
@@ -1,6 +1,7 @@
using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
@@ -54,18 +55,18 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
// 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.OfType<SpeckleCollectionWrapper>().ToList();
var objects = wrapper.Elements.OfType<SpeckleObjectWrapper>().ToList();
var collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
{
var param = new SpeckleOutputParam
var param = new Param_GenericObject()
{
Name = "_objects",
NickName = "_objs",
Description =
"Some collections may contain a mix of objects and other collections. These are the objects directly contained in this collection.",
"Some collections may contain a mix of objects and other collections. Here we output the atomic objects from within this collection.",
Access = GH_ParamAccess.list
};
@@ -93,7 +94,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
nickName += "..." + childWrapper.Name[^6..];
}
var param = new SpeckleOutputParam
var param = new Param_GenericObject()
{
Name = childWrapper.Name,
NickName = nickName,
@@ -112,7 +113,10 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
else
{
// Create appropriate Goo types for child objects
List<IGH_Goo> childObjectGoos = childWrapper.GetAtomicObjects().Select(obj => obj.CreateGoo()).ToList();
List<IGH_Goo> childObjectGoos = childWrapper
.Elements.OfType<SpeckleObjectWrapper>()
.Select(obj => obj.CreateGoo())
.ToList();
outputValue = childObjectGoos;
}
@@ -190,7 +194,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
foreach (var newParam in outputParams)
{
var param = new SpeckleOutputParam
var param = new Param_GenericObject
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
@@ -213,7 +217,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new SpeckleOutputParam
var myParam = new Param_GenericObject
{
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
MutableNickName = true,
@@ -226,4 +230,4 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
}
public record OutputParamWrapper(SpeckleOutputParam Param, object Values, string? Topology);
public record OutputParamWrapper(Param_GenericObject Param, object Values, string? Topology);
@@ -8,8 +8,8 @@ public static class ComponentCategories
// categories
public const string OPERATIONS = " Models";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
public const string OBJECTS = " Objects";
public const string COLLECTIONS = " Collections";
public const string PARAMETERS = " Params";
public const string DEVELOPER = "Dev";
}
@@ -28,202 +28,99 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_deconstruct;
protected override void RegisterInputParams(GH_InputParamManager pManager) =>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Speckle Param", "SP", "Speckle param to deconstruct", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
// on first iteration, discover all fields from all objects to create stable output structure
if (da.Iteration == 0)
{
var allFields = DiscoverAllFieldsFromInput();
if (allFields.Count > 0)
{
var requiredOutputs = CreateOutputParamsFromFieldNames(allFields);
if (OutputMismatch(requiredOutputs))
{
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(requiredOutputs));
return;
}
}
}
// process current object normally
object data = new();
if (!da.GetData(0, ref data))
da.GetData(0, ref data);
List<OutputParamWrapper> outputParams = new();
switch (data)
{
return;
case SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null:
// get children elements from the wrapper to override the elements prop while parsing
List<IGH_Goo> children = collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList();
outputParams = ParseSpeckleWrapper(collectionGoo.Value, children);
break;
case SpeckleObjectWrapperGoo objectGoo when objectGoo.Value != null:
outputParams = ParseSpeckleWrapper(objectGoo.Value);
break;
case SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null:
outputParams = ParseSpeckleWrapper(blockInstanceGoo.Value);
break;
case SpeckleBlockDefinitionWrapperGoo blockDef:
outputParams = ParseSpeckleWrapper(blockDef.Value);
break;
case SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null:
outputParams = ParseSpeckleWrapper(materialGoo.Value);
break;
case SpecklePropertyGroupGoo propGoo:
Name = $"properties ({propGoo.Value.Count})";
outputParams = new();
foreach (var key in propGoo.Value.Keys)
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value is SpecklePropertyGoo prop
? prop.Value
: value is SpecklePropertyGroupGoo propGroup
? propGroup
: value;
outputParams.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
}
break;
default:
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return;
}
var outputParams = DeconstructObject(data);
if (outputParams == null)
{
return;
}
// set component name based on the current object
NickName = Name;
// set output data - fill missing fields with nulls for objects that don't have all fields
SetOutputData(da, outputParams);
}
/// <summary>
/// Discovers all unique field names and their access types from all input objects by looking at volatile data directly.
/// </summary>
/// <returns>A dictionary mapping field names to their required parameter access types.</returns>
private IReadOnlyDictionary<string, GH_ParamAccess> DiscoverAllFieldsFromInput()
{
Dictionary<string, GH_ParamAccess> allFields = [];
foreach (var item in Params.Input[0].VolatileData.AllData(true))
if (da.Iteration == 0 && OutputMismatch(outputParams))
{
var objectOutputs = DeconstructObject(item);
if (objectOutputs != null)
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(outputParams);
}
);
}
else
{
for (int i = 0; i < outputParams.Count; i++)
{
foreach (var output in objectOutputs)
var outParam = Params.Output[i];
var outParamWrapper = outputParams[i];
switch (outParam.Access)
{
string fieldName = output.Param.Name;
allFields[fieldName] = output.Param.Access;
case GH_ParamAccess.item:
da.SetData(i, outParamWrapper.Value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outParamWrapper.Value as IList);
break;
}
}
}
return allFields;
}
/// <summary>
/// Creates output parameter wrappers from field names and their corresponding access types.
/// </summary>
/// <param name="fieldAccessTypes">Dictionary mapping field names to their required parameter access types.</param>
/// <returns>List of output parameter wrappers with correct access types.</returns>
private List<OutputParamWrapper> CreateOutputParamsFromFieldNames(
IReadOnlyDictionary<string, GH_ParamAccess> fieldAccessTypes
) => fieldAccessTypes.Select(kvp => CreateOutputParamByKeyValue(kvp.Key, null, kvp.Value)).ToList();
/// <summary>
/// Deconstructs a single object into its constituent fields/properties.
/// </summary>
private List<OutputParamWrapper>? DeconstructObject(object data) =>
data switch
{
// get children elements from wrapper to override elements prop while parsing
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
=> ParseSpeckleWrapper(
collectionGoo.Value,
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
),
// get geometries from wrapper to override displayValue prop while parsing
SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null
=> ParseSpeckleWrapper(
dataObjectGoo.Value,
null,
dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList()
),
SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null => ParseSpeckleWrapper(objectGoo.Value),
SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null
=> ParseSpeckleWrapper(blockInstanceGoo.Value),
SpeckleBlockDefinitionWrapperGoo blockDef when blockDef.Value != null => ParseSpeckleWrapper(blockDef.Value),
SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null => ParseSpeckleWrapper(materialGoo.Value),
SpecklePropertyGroupGoo propGoo when propGoo.Value != null => ParsePropertyGroup(propGoo),
_ => HandleUnsupportedType(data)
};
/// <summary>
/// Handles SpecklePropertyGroupGoo objects by extracting their key-value pairs.
/// </summary>
private List<OutputParamWrapper> ParsePropertyGroup(SpecklePropertyGroupGoo propGoo)
{
Name = $"properties ({propGoo.Value.Count})";
List<OutputParamWrapper> objectOutputs = new();
foreach (var key in propGoo.Value.Keys)
{
ISpecklePropertyGoo value = propGoo.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
// determine access type based on the value
GH_ParamAccess access = outputValue is IList ? GH_ParamAccess.list : GH_ParamAccess.item;
objectOutputs.Add(CreateOutputParamByKeyValue(key, outputValue, access));
}
return objectOutputs;
}
/// <summary>
/// Handles unsupported object types by logging an error and returning null.
/// </summary>
private List<OutputParamWrapper>? HandleUnsupportedType(object data)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
return null;
}
/// <summary>
/// Sets output data for the current iteration, filling missing fields with null values.
/// Uses a lookup dictionary for efficient field matching.
/// </summary>
private void SetOutputData(IGH_DataAccess da, List<OutputParamWrapper> currentOutputs)
{
if (Params.Output.Count == 0)
{
return;
}
// create a lookup for current outputs by field name
var outputLookup = currentOutputs.ToDictionary(o => o.Param.Name, o => o.Value);
// set data for each output parameter
for (int i = 0; i < Params.Output.Count; i++)
{
var outputParam = Params.Output[i];
// set the value if it exists, otherwise set null
object? value = outputLookup.TryGetValue(outputParam.Name, out var fieldValue) ? fieldValue : null;
switch (outputParam.Access)
{
case GH_ParamAccess.item:
da.SetData(i, value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, value as IList ?? new List<object?>());
break;
}
}
}
private List<OutputParamWrapper> ParseSpeckleWrapper(
SpeckleWrapper wrapper,
List<IGH_Goo>? elements = null,
List<IGH_Goo>? displayValue = null
)
private List<OutputParamWrapper> ParseSpeckleWrapper(SpeckleWrapper wrapper, List<IGH_Goo>? collElements = null)
{
Name = string.IsNullOrEmpty(wrapper.Name) ? wrapper.Base.speckle_type : wrapper.Name;
return CreateOutputParamsFromBase(wrapper.Base, elements, displayValue);
return CreateOutputParamsFromBase(wrapper.Base, collElements);
}
private List<OutputParamWrapper> CreateOutputParamsFromBase(
Base @base,
List<IGH_Goo>? elements = null,
List<IGH_Goo>? displayValue = null
)
private List<OutputParamWrapper> CreateOutputParamsFromBase(Base @base, List<IGH_Goo>? collElements = null)
{
List<OutputParamWrapper> result = new();
if (@base == null)
@@ -231,149 +128,121 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return result;
}
// process each property of the Base object
// cycle through base props
foreach (var prop in @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic))
{
// skip internal dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
// Convert and add to corresponding output structure
var value = prop.Value;
switch (value)
{
continue;
}
case null:
result.Add(CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item));
break;
var outputParam = CreateOutputParamForProperty(prop, @base, elements, displayValue);
if (outputParam != null)
{
result.Add(outputParam);
case IList list:
List<object> nativeObjects = new();
// override list value if base is a collection and this is the elements prop, since this is empty if coming from a collectionwrapper
if (@base is Collection && prop.Key == "elements" && collElements != null)
{
list = collElements;
}
foreach (var x in list)
{
switch (x)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base xBase:
nativeObjects.AddRange(ConvertOrCreateWrapper(xBase));
break;
default:
nativeObjects.Add(x);
break;
}
}
result.Add(CreateOutputParamByKeyValue(prop.Key, nativeObjects, GH_ParamAccess.list));
break;
case Dictionary<string, object?> dict: // this should be treated a properties dict
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
break;
case SpeckleWrapper wrapper:
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
break;
case Base baseValue:
result.Add(CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list));
break;
default:
// we don't want to output dynamic property keys
if (prop.Key == nameof(Base.DynamicPropertyKeys))
{
continue;
}
result.Add(CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item));
break;
}
}
return result;
}
/// <summary>
/// Creates an output parameter for a single property, handling different value types appropriately.
/// </summary>
private OutputParamWrapper CreateOutputParamForProperty(
KeyValuePair<string, object?> prop,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
) =>
prop.Value switch
{
null => CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item),
IList list => CreateListOutputParam(prop.Key, list, @base, elements, displayValue),
Dictionary<string, object?> dict => CreateDictionaryOutputParam(prop.Key, dict),
SpeckleWrapper wrapper => CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item),
Base baseValue => CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list),
_ => CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item)
};
/// <summary>
/// Creates an output parameter for list properties, with special handling for collection elements and display values.
/// </summary>
private OutputParamWrapper CreateListOutputParam(
string key,
IList list,
Base @base,
List<IGH_Goo>? elements,
List<IGH_Goo>? displayValue
)
{
// override list value for special cases
IList actualList = key switch
{
"elements" when @base is Collection && elements != null => elements,
"displayValue" when @base is Speckle.Objects.Data.DataObject && displayValue != null => displayValue,
_ => list
};
List<object> nativeObjects = new();
foreach (var item in actualList)
{
switch (item)
{
case SpeckleWrapper wrapper:
nativeObjects.Add(wrapper.CreateGoo());
break;
case Base baseItem:
nativeObjects.AddRange(ConvertOrCreateWrapper(baseItem));
break;
default:
nativeObjects.Add(item);
break;
}
}
return CreateOutputParamByKeyValue(key, nativeObjects, GH_ParamAccess.list);
}
/// <summary>
/// Creates an output parameter for dictionary properties, converting them to SpecklePropertyGroupGoo.
/// </summary>
private OutputParamWrapper CreateDictionaryOutputParam(string key, Dictionary<string, object?> dict)
{
SpecklePropertyGroupGoo propertyGoo = new();
propertyGoo.CastFrom(dict);
return CreateOutputParamByKeyValue(key, propertyGoo, GH_ParamAccess.item);
}
/// <summary>
/// Converts a Speckle Base object to host geometry or creates a wrapper if conversion fails.
/// Returns a list of SpeckleGeometryWrapperGoo objects.
/// </summary>
private List<SpeckleGeometryWrapperGoo> ConvertOrCreateWrapper(Base @base)
private List<SpeckleObjectWrapperGoo> ConvertOrCreateWrapper(Base @base)
{
try
{
// attempt conversion to host geometry
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
return convertedBase.Select(CreateGeometryWrapper).ToList();
// convert the base and create a wrapper for each result
List<(GeometryBase, Base)> convertedBase = SpeckleConversionContext.ConvertToHost(@base);
List<SpeckleObjectWrapperGoo> convertedWrappers = new();
foreach ((GeometryBase g, Base b) in convertedBase)
{
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = b,
GeometryBase = g,
Name = b["name"] as string ?? "",
Color = null,
Material = null
};
convertedWrappers.Add(new(convertedWrapper));
}
return convertedWrappers;
}
catch (ConversionException)
{
// fallback: create wrapper without conversion for objects that can't be converted
return new List<SpeckleGeometryWrapperGoo> { CreateFallbackWrapper(@base) };
// some classes, like RawEncoding, have no direct conversion or fallback value.
// when this is the case, wrap it to allow users to further expand the object.
SpeckleObjectWrapper convertedWrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new() { new SpeckleObjectWrapperGoo(convertedWrapper) };
}
}
/// <summary>
/// Creates a SpeckleGeometryWrapperGoo from a converted geometry and base object pair.
/// </summary>
private SpeckleGeometryWrapperGoo CreateGeometryWrapper((object geometry, Base @base) converted)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = converted.@base,
GeometryBase = converted.geometry as GeometryBase,
Name = converted.@base["name"] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
/// <summary>
/// Creates a fallback wrapper for Base objects that cannot be converted to host geometry.
/// </summary>
private SpeckleGeometryWrapperGoo CreateFallbackWrapper(Base @base)
{
SpeckleGeometryWrapper wrapper =
new()
{
Base = @base,
GeometryBase = null,
Name = @base[Constants.NAME_PROP] as string ?? "",
Color = null,
Material = null
};
return new SpeckleGeometryWrapperGoo(wrapper);
}
private OutputParamWrapper CreateOutputParamByKeyValue(string key, object? value, GH_ParamAccess access)
{
SpeckleOutputParam param =
Param_GenericObject param =
new()
{
Name = key,
@@ -403,20 +272,22 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
public bool DestroyParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Output;
}
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// remove all existing output parameters
// TODO: better, nicer handling of creation/removal
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
// add new output parameters
foreach (var newParam in outputParams)
{
var param = new SpeckleOutputParam
var param = new Param_GenericObject
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
@@ -426,15 +297,11 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
Params.RegisterOutputParam(param);
}
// notify Grasshopper of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
/// <summary>
/// Determines if the current output parameter structure differs from the required structure.
/// </summary>
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
{
if (Params.Output.Count != outputParams.Count)
@@ -442,10 +309,10 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
return true;
}
for (int i = 0; i < outputParams.Count; i++)
var count = 0;
foreach (var newParam in outputParams)
{
var newParam = outputParams[i];
var oldParam = Params.Output[i];
var oldParam = Params.Output[count];
if (
oldParam.NickName != newParam.Param.NickName
|| oldParam.Name != newParam.Param.Name
@@ -454,6 +321,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
{
return true;
}
count++;
}
return false;
@@ -28,12 +28,7 @@ public class TokenUrlComponent : GH_Component
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
pManager.AddTextParameter(
"Speckle Token",
"Token",
"Speckle Authorization Token. Requires profile:read, profile:email, stream:read, and workspace:read (unless on a non-workspace enable server), as well as any other write scopes needed",
GH_ParamAccess.item
);
pManager.AddTextParameter("Speckle Token", "Token", "Speckle Authorization Token", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
@@ -104,11 +99,6 @@ public class TokenUrlComponent : GH_Component
throw new SpeckleException("No account found for server URL");
}
if (account.userInfo.id is null || account.userInfo.email is null)
{
throw new SpeckleException("Token requires profile:read and profile:email scopes");
}
IClient client = scope.Get<IClientFactory>().Create(account);
var project = client.Project.Get(resource.ProjectId).Result;
@@ -17,7 +17,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_create;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public CreateSpeckleProperties()
: base(
@@ -73,10 +73,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
continue;
}
ISpecklePropertyGoo? propertyValue =
Params.Input[i].Access == GH_ParamAccess.item
? ExtractPropertyValue(da, i, paramName)
: ExtractPropertyListValue(da, i, paramName);
var propertyValue = ExtractPropertyValue(da, i, paramName);
if (propertyValue != null)
{
@@ -117,26 +114,6 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
return propertyGoo;
}
private ISpecklePropertyGoo? ExtractPropertyListValue(IGH_DataAccess da, int index, string paramName)
{
List<object?> value = new();
da.GetDataList(index, value);
var propertyGoo = new SpecklePropertyGoo();
if (!propertyGoo.CastFrom(value))
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter '{paramName}' contains invalid data type. Only strings, numbers, and booleans are supported."
);
return null;
}
return propertyGoo;
}
protected override void AppendComponentSpecificMenuItems(ToolStripDropDown menu)
{
Menu_AppendSeparator(menu);
@@ -1,170 +0,0 @@
using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
// NOTE: Why all this madness? The properties passthrough node is restrictive in output type being uniform
// Properties whose values were lists were not being displayed and couldn't be given back to the user as native
// lists. This was (it seemed) the only viable approach.
// [CNX-2364](https://linear.app/speckle/issue/CNX-2364/grasshopper-properties-passthrough-does-not-handle-list-values)
[Guid("474F4699-D641-444F-BC78-E22AAF40B240")]
public class ExpandSpeckleProperties : GH_Component, IGH_VariableParameterComponent
{
public ExpandSpeckleProperties()
: base(
"Expand Properties",
"eP",
"Expands Speckle Properties into their individual outputs with correct access types",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_expand;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Speckle Properties to expand",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties) || properties?.Value == null)
{
return;
}
Name = $"Properties ({properties.Value.Count})";
NickName = Name;
var outputParams = new List<OutputParamWrapper>();
foreach (var key in properties.Value.Keys)
{
ISpecklePropertyGoo value = properties.Value[key];
object? outputValue = value switch
{
SpecklePropertyGoo prop => prop.Value,
SpecklePropertyGroupGoo propGroup => propGroup,
_ => value
};
var param = new SpeckleOutputParam
{
Name = key,
NickName = key,
Access = outputValue is IList ? GH_ParamAccess.list : GH_ParamAccess.item
};
outputParams.Add(new OutputParamWrapper(param, outputValue));
}
// handle parameter creation/update (only on first iteration)
if (da.Iteration == 0 && OutputMismatch(outputParams))
{
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(outputParams));
return; // exit early
}
// only set data if we have the correct parameter structure
if (Params.Output.Count == outputParams.Count)
{
for (int i = 0; i < outputParams.Count; i++)
{
var outputParam = outputParams[i];
switch (outputParam.Param.Access)
{
case GH_ParamAccess.item:
da.SetData(i, outputParam.Value);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outputParam.Value as IList ?? new List<object?>());
break;
}
}
}
}
/// <summary>
/// Creates output parameters based on discovered properties.
/// </summary>
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// remove all existing output parameters
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
// add new output parameters
foreach (var newParam in outputParams)
{
var param = new SpeckleOutputParam
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
}
// notify gh of parameter changes
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
/// <summary>
/// Determines if the current output parameter structure differs from the required structure.
/// </summary>
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
{
if (Params.Output.Count != outputParams.Count)
{
return true;
}
for (int i = 0; i < outputParams.Count; i++)
{
var newParam = outputParams[i];
var oldParam = Params.Output[i];
if (
oldParam.NickName != newParam.Param.NickName
|| oldParam.Name != newParam.Param.Name
|| oldParam.Access != newParam.Param.Access
)
{
return true;
}
}
return false;
}
// IGH_VariableParameterComponent implementation
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
public void VariableParameterMaintenance() { }
public IGH_Param CreateParameter(GH_ParameterSide side, int index) => new SpeckleOutputParam();
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
}
public record OutputParamWrapper(SpeckleOutputParam Param, object? Value);
@@ -85,7 +85,7 @@ public class FilterSpeckleObjects : GH_Component
return;
}
List<SpeckleWrapper?> objects = inputObjects
List<SpeckleObjectWrapper?> objects = inputObjects
.Select(o => o.ToSpeckleObjectWrapper())
.Where(o => o is not null)
.ToList();
@@ -108,11 +108,11 @@ public class FilterSpeckleObjects : GH_Component
string speckleId = "";
dataAccess.GetData(5, ref speckleId);
List<SpeckleWrapper> matchedObjects = new();
List<SpeckleWrapper> removedObjects = new();
List<SpeckleObjectWrapper> matchedObjects = new();
List<SpeckleObjectWrapper> removedObjects = new();
for (int i = 0; i < objects.Count; i++)
{
SpeckleWrapper wrapper = objects[i]!;
SpeckleObjectWrapper wrapper = objects[i]!;
// filter by name
if (!MatchesSearchPattern(name, wrapper.Name))
@@ -129,24 +129,12 @@ public class FilterSpeckleObjects : GH_Component
}
else
{
SpecklePropertyGroupGoo? properties = wrapper is SpeckleDataObjectWrapper dataObjPropWrapper
? dataObjPropWrapper.Properties
: wrapper is SpeckleGeometryWrapper geoPropWrapper
? geoPropWrapper.Properties
: null;
if (properties is not null)
foreach (string key in wrapper.Properties.Value.Keys)
{
// 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))
{
if (MatchesSearchPattern(property, key))
{
foundProperty = true;
break;
}
foundProperty = true;
break;
}
}
}
@@ -158,26 +146,7 @@ public class FilterSpeckleObjects : GH_Component
}
// 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)
if (!MatchesSearchPattern(material, wrapper.Material?.Name ?? ""))
{
removedObjects.Add(wrapper);
continue;
@@ -0,0 +1,179 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class GetObjectProperties : GH_Component, IGH_VariableParameterComponent
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public GetObjectProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the properties inside Speckle Objects at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"Speckle Objects to retrieve properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { }
protected override void SolveInstance(IGH_DataAccess da)
{
List<string> paths = new();
da.GetDataList(1, paths);
if (paths.Count == 0)
{
return;
}
if (OutputMismatch(paths))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(paths);
}
);
}
else
{
SpeckleObjectWrapperGoo objectWrapperGoo = new();
da.GetData(0, ref objectWrapperGoo);
// flatten object properties, if any
SpecklePropertyGroupGoo properties = objectWrapperGoo.Value.Properties;
if (properties.Value.Count == 0)
{
return;
}
Dictionary<string, SpecklePropertyGoo> flattenedProps = properties.Flatten();
for (int i = 0; i < paths.Count; i++)
{
var name = paths[i];
if (FindProperty(flattenedProps, name) is SpecklePropertyGoo prop)
{
da.SetData(i, prop.Value);
}
else
{
da.SetData(i, null);
}
}
}
}
// attempts to find a property by concatenated key, or returns null if not
private SpecklePropertyGoo? FindProperty(Dictionary<string, SpecklePropertyGoo> props, string unifiedPath)
{
if (!props.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
{
return null;
}
return currentGoo;
}
private bool OutputMismatch(List<string> outputParams)
{
if (Params.Output.Count != outputParams.Count)
{
return true;
}
var count = 0;
foreach (var newParam in outputParams)
{
var oldParam = Params.Output[count];
if (oldParam.NickName != newParam || oldParam.Name != newParam)
{
return true;
}
count++;
}
return false;
}
private void CreateOutputs(List<string> outputParams)
{
// Ensure we have the required count of output parameters
while (Params.Output.Count != outputParams.Count)
{
if (Params.Output.Count > outputParams.Count) // if too many, unregister
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
if (Params.Output.Count < outputParams.Count) // if too little, add some
{
var param = new Param_GenericObject
{
Name = "newParam",
NickName = "newParam",
MutableNickName = false,
Access = GH_ParamAccess.item
};
Params.RegisterOutputParam(param);
}
}
// now unify names and nicknames
int index = 0;
foreach (var newParam in outputParams)
{
Params.Output[index].NickName = newParam;
Params.Output[index].Name = newParam;
index++;
}
// now we can update the output params
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new Param_GenericObject
{
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
MutableNickName = true,
Optional = true
};
myParam.NickName = myParam.Name;
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
public void VariableParameterMaintenance() { }
}
@@ -4,6 +4,9 @@ using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
#if RHINO8_OR_GREATER
using Grasshopper.Rhinoceros.Model;
#endif
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
@@ -13,34 +16,42 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
: base(
"Property Selector",
"pSelect",
"Allows you to select a set of property keys for querying. Right-click for 'Auto-select all items' option.",
"Allows you to select a set of property keys for querying",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
protected override Bitmap Icon => Resources.speckle_inputs_property;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
protected override void LoadVolatileData()
{
List<SpecklePropertyGroupGoo> propertyGroups = VolatileData
var objectPropertyGroups = VolatileData
.AllData(true)
.OfType<SpecklePropertyGroupGoo>()
.OfType<SpeckleObjectWrapperGoo>()
.Select(goo => goo.Value.Properties)
.ToList();
if (VolatileDataCount > propertyGroups.Count)
#if RHINO8_OR_GREATER
// support model objects direct piping also
if (objectPropertyGroups.Count != VolatileData.DataCount)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Only Speckle Properties are accepted as inputs.");
return;
var modelObjects = VolatileData
.AllData(true)
.OfType<ModelObject>()
.Select(mo => new SpeckleObjectWrapperGoo(mo).Value.Properties)
.ToList();
objectPropertyGroups.AddRange(modelObjects);
}
#endif
if (propertyGroups.Count == 0)
if (objectPropertyGroups.Count == 0)
{
return;
}
var paths = GetPropertyPaths(propertyGroups);
var paths = GetPropertyPaths(objectPropertyGroups);
m_data.Clear();
m_data.AppendRange(paths.Select(s => new GH_String(s)));
}
@@ -1,110 +0,0 @@
using System.Collections;
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("116F08A5-BAA7-45B3-B6C8-469E452C9AC7")]
public class QueryProperties : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_query;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public QueryProperties()
: base(
"Query Properties",
"qP",
"Retrieves the values of the Properties at the specified keys",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"Speckle Properties",
GH_ParamAccess.item
);
pManager.AddTextParameter("Keys", "K", "Property keys to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Values", "V", "The values of the specified keys", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess da)
{
SpecklePropertyGroupGoo? properties = null;
if (!da.GetData(0, ref properties))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a Speckle Properties item");
return;
}
List<string> keys = [];
if (!da.GetDataList(1, keys))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input a key");
return;
}
if (properties == null || properties.Value.Count == 0 || keys.Count == 0)
{
return;
}
List<object?> values = [];
foreach (string key in keys)
{
var value = GetValueByPath(properties, key);
var extractedValue = (value as SpecklePropertyGoo)?.Value ?? value;
// NOTE: if property is a list, flatten into individual items for native gh list access
if (extractedValue is IList itemList)
{
values.AddRange(itemList.Cast<object?>());
}
else
{
values.Add(extractedValue);
}
}
da.SetDataList(0, values);
}
public static ISpecklePropertyGoo? GetValueByPath(SpecklePropertyGroupGoo data, string path)
{
string[] keys = path.Split('.');
ISpecklePropertyGoo? current = data;
foreach (var key in keys)
{
if (current is SpecklePropertyGroupGoo dict)
{
if (dict.Value.TryGetValue(key, out ISpecklePropertyGoo? next))
{
current = next;
}
else
{
return null;
}
}
else
{
return null; // Current is not a dictionary, path is invalid
}
}
return current;
}
}
@@ -1,5 +1,7 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.DocObjects;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
@@ -38,8 +40,8 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
pManager.AddTextParameter(
"Path",
"P",
"Get the Speckle objects in the sub-collection indicated by this path",
"C",
"Get the Speckle objects in the subcollection indicated by this path",
GH_ParamAccess.item
);
@@ -89,7 +91,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
private List<int>? _outputFilterIndices;
// Caches the list of all objects by geometrybase type
private readonly Dictionary<ObjectType, List<SpeckleGeometryWrapper>> _filterDict = new();
private readonly Dictionary<ObjectType, List<SpeckleObjectWrapper>> _filterDict = new();
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
@@ -107,7 +109,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
// filter by collection path
// Note: the collection paths selector will omit the target collection from the path of nested collections.
// the discard ("_objects") will be used to indicate objects found directly in the target collection.
List<SpeckleWrapper> filteredObjects;
List<SpeckleObjectWrapper> filteredObjects;
SpeckleCollectionWrapper? targetCollectionWrapper = null;
if (!string.IsNullOrEmpty(path))
{
@@ -118,45 +120,25 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
return;
}
filteredObjects = targetCollectionWrapper.GetAtomicObjects(true).ToList();
filteredObjects = targetCollectionWrapper.Elements.OfType<SpeckleObjectWrapper>().ToList();
}
else
{
filteredObjects = collectionWrapperGoo.Value.GetAtomicObjects(true).ToList();
filteredObjects = GetAllObjectsFromCollection(collectionWrapperGoo.Value).ToList();
}
// sort geometry objects by filters
var geometryObjects = filteredObjects.OfType<SpeckleGeometryWrapper>().ToList();
// sort objects by filters
if (_filterDict.Count == 0)
{
SortObjectsByGeometryBaseType(geometryObjects);
SortObjectsByGeometryBaseType(filteredObjects);
}
// Set output objects
for (int i = 0; i < Params.Output.Count; i++)
{
// determine output values based on parameter type
List<SpeckleWrapper> outputValues;
if (i == 0)
{
outputValues = filteredObjects;
}
else if (
Enum.TryParse(Params.Output[i].Name, out ObjectType filterType)
&& _filterDict.TryGetValue(filterType, out var filteredList)
)
{
outputValues = filteredList.Cast<SpeckleWrapper>().ToList();
}
else
{
outputValues = [];
}
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))
List<SpeckleObjectWrapper> outputValues = i == 0 ? filteredObjects : _filterDict[Filters[i - 1]];
List<IGH_Goo> outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
{
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoos);
dataAccess.SetDataTree(i, tree);
@@ -169,7 +151,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
}
// Sort the input objects by the FilterType enums, based on the type of their geometryBase
private void SortObjectsByGeometryBaseType(List<SpeckleGeometryWrapper> objs)
private void SortObjectsByGeometryBaseType(List<SpeckleObjectWrapper> objs)
{
if (_filterDict.Count > 0)
{
@@ -186,7 +168,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
{
if (
wrapper.GeometryBase?.ObjectType is ObjectType objType
&& _filterDict.TryGetValue(objType, out List<SpeckleGeometryWrapper>? value)
&& _filterDict.TryGetValue(objType, out List<SpeckleObjectWrapper>? value)
)
{
value.Add(wrapper);
@@ -194,6 +176,25 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
}
}
private IEnumerable<SpeckleObjectWrapper> GetAllObjectsFromCollection(SpeckleCollectionWrapper collectionWrapper)
{
foreach (ISpeckleCollectionObject element in collectionWrapper.Elements)
{
switch (element)
{
case SpeckleCollectionWrapper childCollectionWrapper:
foreach (var item in GetAllObjectsFromCollection(childCollectionWrapper))
{
yield return item;
}
break;
case SpeckleObjectWrapper objectWrapper:
yield return objectWrapper;
break;
}
}
}
private SpeckleCollectionWrapper? FindCollectionAtPath(SpeckleCollectionWrapper root, string unifiedPath)
{
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
@@ -260,7 +261,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
_outputFilterIndices = null;
ObjectType filter = previousFilterIndex is null ? Filters.First() : Filters[(int)previousFilterIndex + 1];
return new SpeckleOutputParam
return new Param_GenericObject
{
Name = filter.ToString(),
NickName = GetFilterNickName(filter),
@@ -289,8 +290,10 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
base.RemovedFromDocument(document);
}
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args) =>
private void OnParameterSourceChanged(object sender, GH_ParamServerEventArgs args)
{
// an empty filter dict will trigger the SortObjectsByGeometryBaseType method.
// we only want to re-sort objects if an input has changed, not on every trigger of solve instance.
_filterDict.Clear();
}
}
@@ -8,18 +8,20 @@ using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
public class SpeckleBlockDefinitionPassthrough()
: SpeckleSolveInstance(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
public class SpeckleBlockDefinitionPassthrough : GH_Component
{
public SpeckleBlockDefinitionPassthrough()
: base(
"Speckle Block Definition",
"SBD",
"Create or modify a Speckle Block Definition",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_def;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -33,9 +35,9 @@ public class SpeckleBlockDefinitionPassthrough()
Params.Input[0].Optional = true;
pManager.AddGenericParameter(
"Geometry",
"G",
"Geometry to include in the Block Definition. Speckle geometry and instances or Model objects and instances are accepted.",
"Objects",
"O",
"Objects to include in the Block Definition. Speckle objects and instances or Model objects and instances are accepted.",
GH_ParamAccess.list
);
Params.Input[1].Optional = true;
@@ -54,7 +56,7 @@ public class SpeckleBlockDefinitionPassthrough()
GH_ParamAccess.item
);
pManager.AddGenericParameter("Geometry", "G", "Geometry contained in the Block Definition", GH_ParamAccess.list);
pManager.AddGenericParameter("Objects", "O", "Objects contained in the Block Definition", GH_ParamAccess.list);
pManager.AddTextParameter("Name", "N", "Name of the Block Definition", GH_ParamAccess.item);
}
@@ -69,7 +71,7 @@ public class SpeckleBlockDefinitionPassthrough()
if (inputDefinition == null && inputObjects.Count == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Geometry.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Objects.");
return;
}
@@ -82,21 +84,20 @@ public class SpeckleBlockDefinitionPassthrough()
return;
}
// keep track of mutation
bool mutated = false;
// process the definition
// deep copy so we don't mutate the object
SpeckleBlockDefinitionWrapperGoo result = new();
if (inputDefinition != null)
{
result = new SpeckleBlockDefinitionWrapperGoo(inputDefinition.Value.DeepCopy());
}
SpeckleBlockDefinitionWrapperGoo result = inputDefinition != null ? new(inputDefinition.Value.DeepCopy()) : new();
// process geometry
if (inputObjects.Count > 0)
{
List<SpeckleGeometryWrapper> processedObjects = new();
List<SpeckleObjectWrapper> processedObjects = new();
foreach (IGH_Goo goo in inputObjects)
{
if (goo.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
if (goo.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
{
processedObjects.Add(gooWrapper);
}
@@ -108,6 +109,7 @@ public class SpeckleBlockDefinitionPassthrough()
result.Value.Objects = processedObjects;
result.Value.InstanceDefinitionProxy.objects = processedObjects.Select(o => o.ApplicationId!).ToList(); // TODO: this could also be set at the same time as `Objects` on the definition wrapper.
mutated = true;
}
// process name
@@ -120,10 +122,13 @@ public class SpeckleBlockDefinitionPassthrough()
}
result.Value.Name = inputName;
mutated = true;
}
// 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.
// process application Id. Use a new appId if mutated, or if this is a new object
result.Value.ApplicationId = mutated
? Guid.NewGuid().ToString()
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
// set outputs
da.SetData(0, result);
@@ -8,18 +8,20 @@ using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
public class SpeckleBlockInstancePassthrough()
: SpeckleSolveInstance(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
public class SpeckleBlockInstancePassthrough : GH_Component
{
public SpeckleBlockInstancePassthrough()
: base(
"Speckle Block Instance",
"SBI",
"Create or modify a Speckle Block Instance",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
public override GH_Exposure Exposure => GH_Exposure.tertiary;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
@@ -149,18 +151,20 @@ public class SpeckleBlockInstancePassthrough()
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(6, ref inputMaterial);
// keep track of mutation
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
bool mutated = false;
// process the instance
// deep copy so we don't mutate the incoming object
SpeckleBlockInstanceWrapperGoo result = new();
if (inputInstance != null)
{
result = new SpeckleBlockInstanceWrapperGoo((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy());
}
// deep copy so we don't mutate the object
SpeckleBlockInstanceWrapperGoo result =
inputInstance != null ? new((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy()) : new();
// process definition
if (inputDefinition != null)
{
result.Value.Definition = inputDefinition.Value;
mutated = true;
}
// Process transform
@@ -170,6 +174,7 @@ public class SpeckleBlockInstancePassthrough()
if (extractedTransform.HasValue)
{
result.Value.Transform = extractedTransform.Value;
mutated = true;
}
else
{
@@ -185,12 +190,14 @@ public class SpeckleBlockInstancePassthrough()
if (inputName != null)
{
result.Value.Name = inputName;
mutated = true;
}
// Process properties
if (inputProperties != null)
{
result.Value.Properties = inputProperties;
mutated = true;
}
// process color (no mutation)
@@ -205,8 +212,10 @@ public class SpeckleBlockInstancePassthrough()
result.Value.Material = inputMaterial.Value;
}
// 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.
// Generate new ApplicationId if mutated
result.Value.ApplicationId = mutated
? Guid.NewGuid().ToString()
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
// Set outputs
da.SetData(0, result);
@@ -1,172 +0,0 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("5CE8AA40-7706-4893-853D-4C77604548FA")]
public class SpeckleDataObjectPassthrough()
: SpeckleSolveInstance(
"Speckle Data Object",
"SDO",
"Create or modify a Speckle Data Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_dataobject;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Input Speckle DataObject. Model Objects are also accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
int geoIndex = pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object. Speckle Geometry and Grasshopper geometry are accepted.",
GH_ParamAccess.list
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleDataObjectParam(),
"Speckle Data Object",
"SDO",
"Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddParameter(
new SpeckleGeometryWrapperParam(),
"Geometries",
"G",
"Geometries of the Speckle Data Object.",
GH_ParamAccess.list
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Data Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Data Object",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
// process the object
// deep copy so we don't mutate the object
SpeckleDataObjectWrapperGoo inputObject = new();
SpeckleDataObjectWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
result = inputObject.Value.DeepCopy();
}
List<SpeckleGeometryWrapperGoo> inputGeometry = new();
if (!da.GetDataList(1, inputGeometry) && result == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle DataObject or Geometries.");
return;
}
foreach (var inputGeo in inputGeometry)
{
if (inputGeo.Value is SpeckleBlockInstanceWrapper)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"DataObjects cannot contain Block Instances.");
return;
}
}
string? inputName = null;
da.GetData(2, ref inputName);
SpecklePropertyGroupGoo? inputProperties = null;
da.GetData(3, ref inputProperties);
// process geometry
if (result == null)
{
result = new SpeckleDataObjectWrapperGoo().Value;
}
if (inputGeometry.Count > 0)
{
result.Geometries.Clear();
foreach (var inputGeo in inputGeometry)
{
// deep copy so we don't mutate the input geo which may be speckle geometry
SpeckleGeometryWrapper mutatingGeo = inputGeo.Value.DeepCopy();
// assign fields before adding, otherwise they will be out of sync with wrapper
mutatingGeo.Base[Constants.NAME_PROP] = result.Name;
mutatingGeo.Properties = result.Properties;
mutatingGeo.Parent = result.Parent;
mutatingGeo.Path = result.Path;
result.Geometries.Add(mutatingGeo);
}
}
// process name
if (inputName != null)
{
result.Name = inputName;
}
// process properties
if (inputProperties != null)
{
result.Properties = inputProperties;
}
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.CreateGoo());
da.SetDataList(1, result.Geometries);
da.SetData(2, result.Name);
da.SetData(3, result.Properties);
da.SetData(4, path);
}
}
@@ -4,30 +4,31 @@ using Grasshopper.Kernel.Types;
using Speckle.Connectors.GrasshopperShared.HostApp;
using Speckle.Connectors.GrasshopperShared.Parameters;
using Speckle.Connectors.GrasshopperShared.Properties;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class SpeckleGeometryPassthrough()
: SpeckleSolveInstance(
"Speckle Geometry",
"SG",
"Create or modify a Speckle Geometry",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
public class SpeckleObjectPassthrough : GH_Component
{
public SpeckleObjectPassthrough()
: base(
"Speckle Object",
"SO",
"Create or modify a Speckle Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_objects_geometry;
public override GH_Exposure Exposure => GH_Exposure.secondary;
protected override Bitmap Icon => Resources.speckle_objects_object;
public override GH_Exposure Exposure => GH_Exposure.primary;
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
int objIndex = pManager.AddGenericParameter(
"Speckle Geometry",
"SG",
"Input Speckle Geometry. Model Objects are also accepted.",
"Object",
"O",
"Input Object. Speckle Objects and Model Objects are accepted.",
GH_ParamAccess.item
);
Params.Input[objIndex].Optional = true;
@@ -35,36 +36,31 @@ public class SpeckleGeometryPassthrough()
int geoIndex = pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Geometry.",
"Geometry of the Speckle Object.",
GH_ParamAccess.item
);
Params.Input[geoIndex].Optional = true;
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
Params.Input[nameIndex].Optional = true;
int propIndex = pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Geometry. Speckle Properties and User Content are accepted.",
"The properties of the Speckle Object. Speckle Properties and User Content are accepted.",
GH_ParamAccess.item
);
Params.Input[propIndex].Optional = true;
int colorIndex = pManager.AddColourParameter(
"Color",
"c",
"The color of the Speckle Geometry",
GH_ParamAccess.item
);
int colorIndex = pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
Params.Input[colorIndex].Optional = true;
int matIndex = pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"m",
"The material of the Speckle Geometry. Display Materials, Model Materials, and Speckle Materials are accepted.",
"The material of the Speckle Object. Display Materials, Model Materials, and Speckle Materials are accepted.",
GH_ParamAccess.item
);
Params.Input[matIndex].Optional = true;
@@ -82,34 +78,39 @@ public class SpeckleGeometryPassthrough()
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Speckle Geometry", "SG", "Speckle Geometry", GH_ParamAccess.item);
pManager.AddGenericParameter("Object", "O", "Speckle Object", GH_ParamAccess.item);
pManager.AddGeometryParameter("Geometry", "G", "Geometry of the Speckle Geometry.", GH_ParamAccess.item);
pManager.AddGeometryParameter(
"Geometry",
"G",
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
GH_ParamAccess.item
);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the Speckle Geometry",
"The properties of the Speckle Object",
GH_ParamAccess.item
);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Geometry", GH_ParamAccess.item);
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
pManager.AddParameter(
new SpeckleMaterialParam(),
"Material",
"M",
"The material of the Speckle Geometry.",
"The material of the Speckle Object.",
GH_ParamAccess.item
);
pManager.AddTextParameter(
"Path",
"p",
$"The Collection Path of the Speckle Geometry, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
$"The Collection Path of the Speckle Object, delimited with `{Constants.LAYER_PATH_DELIMITER}`",
GH_ParamAccess.item
);
}
@@ -119,10 +120,10 @@ public class SpeckleGeometryPassthrough()
// process the object
// deep copy so we don't mutate the object
IGH_Goo? inputObject = null;
SpeckleGeometryWrapper? result = null;
SpeckleObjectWrapper? result = null;
if (da.GetData(0, ref inputObject))
{
if (inputObject?.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper gooWrapper)
if (inputObject?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
{
result = gooWrapper.DeepCopy();
}
@@ -138,7 +139,7 @@ public class SpeckleGeometryPassthrough()
if (result == null && inputGeometry == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Speckle Geometry or Geometry.");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Object or Geometry.");
return;
}
@@ -154,13 +155,17 @@ public class SpeckleGeometryPassthrough()
SpeckleMaterialWrapperGoo? inputMaterial = null;
da.GetData(5, ref inputMaterial);
// keep track of mutation
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
bool mutated = false;
// process geometry
// deep copy so we don't mutate the input geo which may be speckle objects
if (inputGeometry != null)
{
if (inputGeometry.ToSpeckleGeometryWrapper() is SpeckleGeometryWrapper geoWrapper)
if (inputGeometry.ToSpeckleObjectWrapper() is SpeckleObjectWrapper geoWrapper)
{
SpeckleGeometryWrapper mutatingGeo = geoWrapper.DeepCopy();
SpeckleObjectWrapper mutatingGeo = geoWrapper.DeepCopy();
if (result is null)
{
result = mutatingGeo;
@@ -184,48 +189,53 @@ public class SpeckleGeometryPassthrough()
result.Base = mutatingGeo.Base;
result.GeometryBase = mutatingGeo.GeometryBase;
}
mutated = true;
}
else
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"{inputGeometry.TypeName} is not a valid type for Speckle Geometry."
$"{inputGeometry.TypeName} is not a valid type for Speckle Objects."
);
return;
}
}
result.NotNull();
// process name
if (inputName != null)
{
result.Name = inputName;
result!.Name = inputName;
mutated = true;
}
// process properties
if (inputProperties != null)
{
result.Properties = inputProperties;
result!.Properties = inputProperties;
mutated = true;
}
// process color (no mutation)
if (inputColor != null)
{
result.Color = inputColor;
result!.Color = inputColor;
}
// process material (no mutation)
if (inputMaterial != null)
{
result.Material = inputMaterial.Value;
result!.Material = inputMaterial.Value;
}
// 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.
// process application Id. Use a new appId if mutated, or if this is a new object
result!.ApplicationId = mutated ? Guid.NewGuid().ToString() : result!.ApplicationId ?? Guid.NewGuid().ToString();
// get the path
string? path =
result.Path.Count > 1 ? string.Join(Constants.LAYER_PATH_DELIMITER, result.Path) : result.Path.FirstOrDefault();
string path =
result!.Path.Count > 1
? string.Join(Constants.LAYER_PATH_DELIMITER, result!.Path)
: result!.Path.FirstOrDefault();
// set all the data
da.SetData(0, result.CreateGoo());
@@ -238,7 +248,7 @@ public class SpeckleGeometryPassthrough()
}
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
private void MatchNonGeometryProps(SpeckleGeometryWrapper wrapper, SpeckleGeometryWrapper wrapperToMatch)
private void MatchNonGeometryProps(SpeckleObjectWrapper wrapper, SpeckleObjectWrapper wrapperToMatch)
{
wrapper.Name = wrapperToMatch.Name;
wrapper.ApplicationId = wrapperToMatch.ApplicationId;
@@ -10,23 +10,11 @@ namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
/// CreateSpeckleProperties passthrough component by key value pairs
/// </summary>
[Guid("FED2298C-0D2B-4868-94B5-B8D17F9385A5")]
public class SpecklePropertiesPassthrough : SpeckleSolveInstance
public class SpecklePropertiesPassthrough : GH_Component
{
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => Resources.speckle_properties_properties;
public override GH_Exposure Exposure => GH_Exposure.quarternary;
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
public override GH_Exposure Exposure => GH_Exposure.tertiary;
private enum PropertyMode
{
@@ -50,6 +38,18 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
}
}
public SpecklePropertiesPassthrough()
: base(
"Speckle Properties",
"SP",
"Creates or modifies a set of properties for Speckle objects by keyvalue",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
)
{
Message = Mode.ToString();
}
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpecklePropertyGroupParam(), "Properties", "P", "Input properties", GH_ParamAccess.item);
@@ -95,16 +95,10 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
return;
}
// validate that keys and values are of valid length
if ((Mode == PropertyMode.Merge || Mode == PropertyMode.Replace) && inputKeys.Count != inputValues.Count)
// validate that keys and values are of same length
if (inputKeys.Count != inputValues.Count)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Keys and values are mismatched in length");
return;
}
if (Mode == PropertyMode.Remove && (inputKeys.Count == 0 || inputValues.Count > 0))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Only input keys to remove");
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Keys and values are mismatched in length");
return;
}
@@ -132,7 +126,7 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
for (int i = 0; i < inputKeys.Count; i++)
{
string key = inputKeys[i];
object? value = Mode == PropertyMode.Remove ? null : inputValues[i];
object? value = inputValues[i];
ISpecklePropertyGoo? convertedValue = null;
switch (value)
{
@@ -148,7 +142,7 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Values contain an invalid data type. Only strings, numbers, booleans, planes, vectors, intervals, and other Speckle properties are supported."
$"Values contain an invalid data type. Only strings, numbers, booleans, and other Speckle properties are supported."
);
return;
}
@@ -173,7 +167,13 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
result.Add(key, convertedValue);
break;
case PropertyMode.Remove:
result.Remove(key);
if (result.TryGetValue(key, out ISpecklePropertyGoo existingValue))
{
if (existingValue.Equals(convertedValue))
{
result.Remove(key);
}
}
break;
}
}
@@ -203,7 +203,8 @@ public class SpecklePropertiesPassthrough : SpeckleSolveInstance
modeItem.ToolTipText = "Existing properties will be cleared and replaced by input keyvalue pairs.";
break;
case PropertyMode.Remove:
modeItem.ToolTipText = "Existing keyvalue pairs that match the input keys will be removed from properties.";
modeItem.ToolTipText =
"Existing keyvalue pairs that match the input keyvalue pairs will be removed from properties.";
break;
}
}

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