Compare commits

..

80 Commits

Author SHA1 Message Date
Dimitrie Stefanescu 2101a9831b Merge remote-tracking branch 'origin/grasshopper' into dim/gh-component-buttons 2025-03-28 16:04:25 +00:00
Claire Kuang 64d1091b79 Merge branch 'dev' into grasshopper 2025-03-28 15:51:09 +00:00
Claire Kuang a411aaa3f0 feat(grasshopper): adds create object node (#724)
* Avoid multiple enumeration issues when saving if we copy the list first (#713)

* add create data object component

* fixes extrusion display

* adds name and user strings dynamically to output model objects

* undo geometry list to geometry in object goo

* Update SpeckleGrasshopperObject.cs

---------

Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2025-03-28 15:49:12 +00:00
Jedd Morgan 13f3bb8ae5 Merge pull request #723 from specklesystems/jrm/main-dev-merge
Main -> Dev
2025-03-28 14:49:47 +00:00
Jedd Morgan d31cb47a85 Merge branch 'dev' into jrm/main-dev-merge 2025-03-28 14:40:55 +00:00
Dimitrie Stefanescu 640cc92641 Merge pull request #722 from specklesystems/revert-719-claire/cnx-844-send-createobject-node
Revert "feat(grasshopper): add create data object node"
2025-03-28 14:37:58 +00:00
Dimitrie Stefanescu ef9c23f7de Revert "feat(grasshopper): add create data object node (#719)"
This reverts commit 04bd151da3.
2025-03-28 14:36:48 +00:00
Claire Kuang 04bd151da3 feat(grasshopper): add create data object node (#719)
* Avoid multiple enumeration issues when saving if we copy the list first (#713)

* add create data object component

* fixes extrusion display

* adds name and user strings dynamically to output model objects

---------

Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
2025-03-28 13:50:58 +00:00
Dimitrie Stefanescu edf3555289 wip 2025-03-28 09:15:12 +00:00
Björn Steinhagen 378438f1bc fix(revit): respect view visibility in linked models when sending (#716)
* fix(revit): sening via views with correct visibility to linked models

* handle not null

* pass path name to function instead full doc

* refactor: move common code outside

* docs: commenting on known limitations

* refactor: preprocessor directive specific stuff back in place

* refactor: oversight on common code

* fix: method name

* docs: cleanup

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
2025-03-27 14:44:11 +03:00
Jedd Morgan b485a4ff6f Don't build everything when zero changes (#715)
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
2025-03-26 15:21:12 +00:00
Jedd Morgan 5697afc292 fix(ifc): Fixed regression with IFC Site geometry not being converted (#712)
* IFC spatial elements now attach geometry as separate data object

* removed unnecessary attribute

* Updated tester for faster testing
2025-03-26 14:57:50 +00:00
Claire Kuang 03c1d4ed32 feat(grasshopper): polishes property filtering nodes (#714)
* refactors properties to cast to simple types

* updates property groups

* merge conflict fixes

* fixes property group bugs

* fixes model object property group cast

* fixes property group cast

* update param category

* fixes output tree generation

* supports model object casting in the path selector

note: model objects will also register in the path list, along with their props. need to fix this
2025-03-26 13:31:48 +00:00
Adam Hathcock 07a681eda7 Avoid multiple enumeration issues when saving if we copy the list first (#713) 2025-03-26 13:14:10 +00:00
Dimitrie Stefanescu b17f4b02aa chore: package.lock.json commit.
this might be not needed?
2025-03-25 15:02:10 +00:00
Dimitrie Stefanescu 03a780ffd5 Merge pull request #711 from specklesystems/grasshopper
Grasshopper
2025-03-25 14:43:49 +00:00
Dimitrie Stefanescu b1240cfbe8 Merge pull request #702 from specklesystems/claire/cnx-1450-add-bake-to-collection-and-object-nodes
feat(grasshopper): adds bake to collection and object params
2025-03-25 14:35:15 +00:00
Dimitrie Stefanescu fed185fbed fix/feat: various
re-applies topology; prevents mutation on send; renames wrapper classes to have wrapper in name etc.
2025-03-25 14:23:13 +00:00
Adam Hathcock 4ec45d3cd5 Merge pull request #709 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Bump sdk to 3.1.7 (#708)
2025-03-25 12:14:05 +00:00
Oğuzhan Koral 939c710bf2 Bump sdk to 3.1.7 (#708)
* Bump sdk to 3.1.7

* missing lock file changes

---------

Co-authored-by: Adam Hathcock <adam@hathcock.uk>
2025-03-25 12:05:00 +00:00
Jedd Morgan ac1345bbaf Merge pull request #703 from specklesystems/dev
Update dev into main
2025-03-25 11:54:03 +00:00
Jedd Morgan 95a7bdb81f Merge pull request #707 from specklesystems/main
Main to dev
2025-03-25 11:46:27 +00:00
Jedd Morgan b1a5824bcd Update pr.yml (#706) 2025-03-25 11:44:08 +00:00
Dimitrie Stefanescu 09f9b1ee51 Revit linked models (#699)
* Bjorn/cnx 1359 have a setting in UI whether include or not (#646)

* feat(revit): linked model settings

* docs: cache invalidation note

* Feat(revit): linked models POC (#656)

* cleanup on RefreshElementsIdsOnSender

* POC for multiple and copied linked models

* fix render materials for copied linked models

* comment

* comment

* split linked models with collections

* style: ci potential null on key

* style: not null

---------

Co-authored-by: Björn <steinhagen.bjoern@gmail.com>

* feat(revit): sending linked model poc round two (#657)

* Added IFC app name (#648)

* test: add tests for threadcontext (#651)

* add tests for threadcontext

* add test for extensions

* remove needed usage

* fix for looking for model store (#654)

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>

* test: Send Operation tests (#652)

* add tests for threadcontext

* add test for extensions

* remove needed usage

* move cancellation

* Only add send operation tests

---------

Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>

* chore (autocad/civil): add regen after receive (#650)

* regenerates doc

* moves regen to receive base binding

* Update AutocadReceiveBaseBinding.cs

* feat: linked model send responsive to ui

- current throw just a placeholder
- small variable name refactor

* fix: object reference not set to an instance of an object

* fix: display value extractor

* refactor: converterSettings as readonly field

* feat: warning in response to ui setting not enabled

* docs: todos etc.

* Revert "Merge branch 'dev' into bjorn/cnx-1360-get-linked-models-on-send-function-according-to-setting"

This reverts commit 7202058a98, reversing
changes made to 4bc9ec2352.

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>

* fix: transform needed to be inversed (#659)

* feat(revit): linked models send by category (#666)

* docs: notes on collection filtering

* feature: RevitCategoriesFilter with linked models too

poc

* docs: notes

* feat(revit): commit structure (#675)

* feat: collection structure switch

- poc
- if linked models, send nested
- if not linked, send flat

* docs: comments on updates

* docs: comment on exception swallowing

* fix: nullability

* refactor: linked model helper class and refactor (#682)

* fix: premature processing of linked models

- add early check for linked models setting
- skips expensive element collection for linked models when disabled
- add empty document contexts for linked models when disabled to maintain warning generation (HACK)
- improve code readability with clearer variable names and comments

* refactor: category ids outside of linked model processing

- inefficient to repeat for every document

* fix: improve separation of concerns for linked model handling

- extract linked model processing to a dedicated method
- add IsLinkedDocument flag to DocumentToConvert class
- move linked model warning generation to RevitRootObjectBuilder
- make RevitSendBinding responsible only for element collection

* refactor: dedicated LinkedModelHandler class for linked model processing

- extract linked model element collection logic to a separate class
- improve separation of concerns between flow control and element collection
- add clear documentation explaining the responsibility boundaries

* fix: duplicate if check still floating around

* docs: over-explaining myself

* Fix(revit): do not use converter settings in element unpacker (#687)

* fix resetting the selected object ids after collecting the elements

* Pass document to element unpacker for the sake of linked models

* fix: send by categories mode

---------

Co-authored-by: Björn <steinhagen.bjoern@gmail.com>

* fix(revit): handle multiple linked model instances with transform hashing (#688)

* feat: linked model transform caching in RevitRootObjectBuilder

Add transform-specific hashing to properly handle multiple instances of the same linked model. Uses transform properties to create a unique suffix for the applicationId, preventing cached objects from being incorrectly reused across different transform contexts.

* more comments

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>

* fix: handle null elements when unpacking linked model groups

- add null element check before GroupBy in ElementUnpacker to prevent NullReferenceException
- improve robustness when processing groups in linked models
- add documentation explaining linked model element handling throughout UnpackElements method
- edge case where group member elements might not resolve properly in linked contexts

* make linked model setting true by default

* Fix(revit): illegal attempt to modify document (#700)

* Run refresh object ids in revit task

* Proper not null

* await instead .result

* feat(revit): collection structure for multiple linked model instances  (#701)

* feat: suffix for instances of linked model

* Extract out GetIdFromDocumentToConvert

---------

Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>

* Correct receive default setting for linked models even if irrevelant

* Fix post conflict

* Cleanup the code

* Add comment

---------

Co-authored-by: Björn Steinhagen <steinhagen.bjoern@gmail.com>
Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com>
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-03-25 11:02:29 +00:00
Claire Kuang 11212c946a Update SpeckleCollectionWrapper.cs 2025-03-24 23:38:30 +00:00
Claire Kuang 2eee9561b4 uses observable collections to add proper baking to create collection nodes 2025-03-24 23:37:58 +00:00
Claire Kuang b221a69f76 fixes bake bug 2025-03-24 22:48:07 +00:00
Claire Kuang 6b0ed5c075 refactors everything to use new speckle collection class 2025-03-24 18:30:39 +00:00
KatKatKateryna 1a687fb188 ignore helper objects on selection (#689) 2025-03-25 00:02:14 +08:00
Dimitrie Stefanescu 30e050fff2 Merge pull request #698 from specklesystems/dimitrie/cnx-614-rhino-8-to-rhino-8-breps-not-working
fix: filter out elems with null geometry
2025-03-24 12:37:57 +00:00
Dimitrie Stefanescu 30ab3b108e fix: filter out elems with null geometry
this is a blind fix: works on bilal's computer, i could not reproduce
2025-03-24 12:29:33 +00:00
Claire Kuang da31864192 Merge branch 'dim/grassopper-v3-wip' into claire/cnx-1450-add-bake-to-collection-and-object-nodes 2025-03-24 11:56:29 +00:00
Claire Kuang f6239d279f feat(grasshopper): add property receive nodes (#690)
* adds property path selector and filter by path components

* Update PropertyGroupPathsSelector.cs

* Auto stash before merge of "claire/cnx-1428-property-paths-selector-node" and "origin/claire/cnx-1428-property-paths-selector-node"

* fix dev merge issues

* Update packages.lock.json

* Create packages.lock.json

---------

Co-authored-by: Dimitrie Stefanescu <didimitrie@gmail.com>
2025-03-24 11:40:48 +00:00
Adam Hathcock 1f3ac7a5ad Exclude library assets for all host applications for connectors/converters (#697)
* Exclude runtime assets from autocad 2022-2025

* add exclude to navisworks too

* exclude from remaining csprojs
2025-03-24 11:02:07 +00:00
Claire Kuang 7aff696bae updates object and collection baking 2025-03-23 22:06:07 +00:00
Claire Kuang 371722f28c adds bake to object param 2025-03-23 20:22:48 +00:00
Claire Kuang d25c40bcd6 Merge branch 'dim/grassopper-v3-wip' into claire/cnx-1450-add-bake-to-collection-and-object-nodes 2025-03-23 18:32:48 +00:00
Claire Kuang 913acc7707 fixes modelobject casting 2025-03-23 18:32:21 +00:00
Claire Kuang 1d83c98077 cleans up typed converters for brep, extrusion, subd 2025-03-23 17:43:20 +00:00
Claire Kuang 2c2a7929bf adds layer baker and bake to objects 2025-03-23 17:27:43 +00:00
Claire Kuang 5335329719 fix converter bugs after dev merge 2025-03-23 17:13:29 +00:00
Claire Kuang cc010c8cc8 Merge branch 'dev' into dim/grassopper-v3-wip 2025-03-23 15:27:20 +00:00
Claire Kuang 80b136b934 castfrom bug 2025-03-23 15:24:57 +00:00
Claire Kuang e8f61f8dbf adds bake to params 2025-03-23 15:11:14 +00:00
Claire Kuang bf2099f8a6 reorganize classes
- refactor create collection node to not duplicate logic
- adds model object support to object goo cast method
2025-03-23 15:01:08 +00:00
Claire Kuang f7f31263a6 feat(grasshopper): asyncify send and receive nodes (#694)
* adds async component base

* adds reference to async component

* adds async to send component

* updates receive async to be a separate component

also adds cancellation, auto receive, progress

* package lock updates

* updates send async with cancellation, progress, and more

* Update packages.lock.json

* Update Local.sln
2025-03-23 10:28:18 +00:00
KatKatKateryna 33e515efb6 feat(rhino): sending region display values as meshes (#685)
* send meshes

* comment

* refactor displayMeshExtractor

* comment

* error message

* comments

* Update HatchToSpeckleConverter.cs

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-03-22 09:21:15 +00:00
Claire Kuang b8f65d4ade fixes fallback conversion bug 2025-03-21 23:50:57 +00:00
jhdempsey86 4a5c91231d Caught error with SpiralDirection (#692)
Caught error with SpiralDirection

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-03-21 09:04:41 +00:00
KatKatKateryna 97a8df93d9 Arcgis send regions (#678)
* send regions

* send meshes

* comment

* typo

* adjust jsons

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-03-19 23:09:55 +08:00
Claire Kuang 96b2eb1832 Merge branch 'dev' into dim/grassopper-v3-wip 2025-03-19 11:57:05 +00:00
Claire Kuang b972a2f8bd adds url to send component 2025-03-19 11:55:18 +00:00
Claire Kuang c504848e0b Update GrasshopperSendOperation.cs 2025-03-19 10:31:34 +00:00
Claire Kuang 7d88e39272 fixes send component and adds additional bitmap icons 2025-03-19 10:27:29 +00:00
Claire Kuang 82adf0e20e adds send component 2025-03-18 20:22:49 +00:00
Claire Kuang 66de3f978a adds point, pointcloud, and hatch display to grasshopper object 2025-03-18 11:32:39 +00:00
Adam Hathcock 0106befa7d Caches visibility for the lifetime of the send (#672)
* Add selection progress and try/finally for exceptions

* format

* Caches visibility for the lifetime of the send

* clean up
2025-03-18 08:37:46 +00:00
KatKatKateryna ef87d5838b Rhino region conversions (#643)
* rhino hatch; autocad regions

* rhino: attempting to extract mesh

* stick to curves

* POC sending hatches

* receive region

* add tolerance

* change displayValue to curves

* revert autocad changes

* switch to curveConverter, add bbox

* lock files

* remove unused converter

* Revert "lock files"

This reverts commit 9ff42c00fe.

* comment

* remove checks, fix displayValue

* address comments

* remove displayValue

* bbox not required

* update nuget

* Revert "update nuget"

This reverts commit 28e6c30b2c.

* upgraded nuget

* convert Regions from DataObjects on Receive

* small fixes

* check result hatches

* json update

---------

Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
2025-03-17 17:16:10 +00:00
Claire Kuang 9031950868 Merge branch 'dev' into dim/grassopper-v3-wip 2025-03-13 12:14:26 +00:00
Claire Kuang 4cef9efd51 Adds Alan's select model component 2025-03-12 16:15:19 +00:00
Claire Kuang a40e9495e5 dev merge build fixes 2025-03-12 14:49:37 +00:00
Claire Kuang afd59df48a Merge branch 'dev' into dim/grassopper-v3-wip 2025-03-12 12:23:00 +00:00
Dimitrie Stefanescu 42c3ca1ec3 wip: more cleanup and comments 2024-12-19 16:16:48 +00:00
Dimitrie Stefanescu 44105d7d75 wip: various cleanup 2024-12-19 16:04:19 +00:00
Dimitrie Stefanescu 2aad40bd0a wip: filter objects by path 2024-12-18 19:13:18 +00:00
Dimitrie Stefanescu d0058d7ee5 wip: centralises converter access in grasshopper
makes life... easier
2024-12-18 13:57:18 +00:00
Dimitrie Stefanescu 38c9077831 wip: conversions (wip!) 2024-12-18 13:18:44 +00:00
Dimitrie Stefanescu 904e7ece88 wip 2024-12-17 17:11:01 +00:00
Dimitrie Stefanescu 267799d916 wip 2024-12-16 13:15:57 +00:00
Dimitrie Stefanescu 2eb872b5e5 wip wip 2024-12-11 17:24:27 +00:00
Dimitrie Stefanescu ce9a2c8807 mega wips 2024-12-11 16:19:50 +00:00
Dimitrie Stefanescu 729d1a4698 feat: adds value type component, all is very raw and wip 2024-12-09 16:04:16 +00:00
Dimitrie Stefanescu 4f74328ffc wip 2024-12-07 15:34:49 +00:00
Dimitrie Stefanescu 54370f3188 wip 2024-12-07 14:27:48 +00:00
Dimitrie Stefanescu 4a51eae628 feat: total wip expand collection node 2024-12-06 14:52:23 +00:00
Dimitrie Stefanescu 34241385f9 feat: unpacking logic wip 2024-12-06 11:56:33 +00:00
Alan Rynne 7ec01ed39f feat: Working initial nodes
Receive, Collection, URL parsing for models, root object unpacking
2024-12-05 11:00:37 +01:00
Alan Rynne 1ff861f9db v3 Receive outputs most objects, very basic 2024-11-28 12:42:44 +01:00
Alan Rynne 4c125afd7b Working POC receive with working Rhino conversions 2024-11-28 11:35:27 +01:00
Alan Rynne e561980e7f feat: Boilerplate project for Grasshopper v3 2024-11-25 16:04:52 +01:00
198 changed files with 10545 additions and 2006 deletions
+3
View File
@@ -314,6 +314,9 @@ dotnet_diagnostic.NUnit2037.severity = warning # Consider using Assert.That(coll
dotnet_diagnostic.NUnit2038.severity = warning # Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual)
dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actual, Is.Not.InstanceOf(expected)) instead of Assert.IsNotInstanceOf(expected, actual)
# note: added to allow the copy paste from rhino inside of the ValueSet component
dotnet_diagnostic.CA1033.severity = none
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 2
+1 -3
View File
@@ -1,9 +1,7 @@
name: .NET Build
on:
pull_request: # Run build on every pull request that is not to main
branches-ignore:
- main
pull_request
jobs:
build:
+1 -4
View File
@@ -66,10 +66,7 @@ public static class Affected
Console.WriteLine("Affected project group being built: " + group.HostAppSlug);
}
if (groups.Count > 0)
{
return groups.ToArray();
}
return groups.ToArray();
}
Console.WriteLine("Using all project groups: " + string.Join(',', Consts.ProjectGroups));
@@ -132,11 +132,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -221,14 +216,21 @@
"resolved": "4.5.1",
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
},
"speckle.common.meshtriangulation": {
"type": "Project",
"dependencies": {
"LibTessDotNet": "[1.1.15, )",
"Speckle.DoubleNumerics": "[4.1.0, )"
}
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -236,8 +238,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -254,6 +256,7 @@
"type": "Project",
"dependencies": {
"Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )",
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
@@ -261,9 +264,15 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"LibTessDotNet": {
"type": "CentralTransitive",
"requested": "[1.1.15, )",
"resolved": "1.1.15",
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -273,6 +282,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -299,18 +314,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -320,14 +335,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
"net6.0-windows7.0/win-x64": {
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -294,7 +289,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,6 +301,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -338,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -359,14 +360,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -294,7 +289,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -306,6 +301,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -338,18 +339,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -359,14 +360,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -295,7 +290,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -13,8 +13,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App"/>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -121,11 +121,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -215,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -225,8 +220,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -251,7 +246,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -263,6 +258,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -295,18 +296,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -315,14 +316,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -10,8 +10,8 @@
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" />
<PackageReference Include="Speckle.Civil3D.API" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -139,11 +139,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -268,9 +263,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +273,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +299,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,6 +311,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -348,18 +349,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -369,14 +370,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -11,8 +11,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -139,11 +139,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -268,9 +263,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +273,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +299,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,6 +311,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -348,18 +349,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -369,14 +370,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -11,8 +11,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -139,11 +139,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -268,9 +263,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +273,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -304,7 +299,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -316,6 +311,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -348,18 +349,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -369,14 +370,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -13,9 +13,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -224,9 +219,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -234,8 +229,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -261,7 +256,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
@@ -273,6 +268,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -305,18 +306,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -325,14 +326,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.etabs21": {
@@ -305,6 +300,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -337,18 +338,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -358,14 +359,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -121,11 +121,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -215,9 +210,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -225,8 +220,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -243,7 +238,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.etabs22": {
@@ -261,6 +256,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -293,18 +294,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -313,14 +314,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2020.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2020.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2020": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2021.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2021.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2021": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2022.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2022.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2022": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2023.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2023": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2024.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
</ItemGroup>
<ItemGroup>
@@ -130,11 +130,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -259,9 +254,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -269,8 +264,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -287,7 +282,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2024": {
@@ -307,6 +302,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -16,7 +16,7 @@
<ItemGroup>
<Reference Include="WindowsFormsIntegration"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2025.0.0"/>
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
<PackageReference Include="Microsoft.Web.WebView2" VersionOverride="1.0.2045.28" />
</ItemGroup>
@@ -136,11 +136,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -265,9 +260,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -275,8 +270,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -293,7 +288,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.navisworks2025": {
@@ -313,6 +308,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -339,18 +340,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -360,14 +361,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
".NETFramework,Version=v4.8/win-x64": {
@@ -38,7 +38,8 @@ public class NavisworksSelectionBinding : ISelectionBinding
{
// Ensure there is an active document and a valid selection
var activeDocument = NavisworksApp.ActiveDocument;
if (activeDocument == null || activeDocument.CurrentSelection.SelectedItems.IsEmpty)
var selection = activeDocument?.CurrentSelection?.SelectedItems ?? [];
if (selection.Count == 0)
{
// Return an empty list if no valid selection exists
return new SelectionInfo([], "No selection available");
@@ -46,8 +47,8 @@ public class NavisworksSelectionBinding : ISelectionBinding
// Ensure only visible elements are processed by filtering using IsElementVisible
var selectedObjectsIds = new HashSet<string>(
activeDocument
.CurrentSelection.SelectedItems.Where(_selectionService.IsVisible) // Exclude hidden elements
selection
.Where(_selectionService.IsVisible) // Exclude hidden elements
.Select(_selectionService.GetModelItemPath) // Resolve to index paths
);
@@ -6,11 +6,26 @@ namespace Speckle.Connector.Navisworks.Services;
[GenerateAutoInterface]
public class ElementSelectionService : IElementSelectionService
{
private readonly Dictionary<Guid, bool> _visibleCache = new();
public string GetModelItemPath(NAV.ModelItem modelItem) => ResolveModelItemToIndexPath(modelItem);
public NAV.ModelItem GetModelItemFromPath(string path) => ResolveIndexPathToModelItem(path);
public bool IsVisible(NAV.ModelItem modelItem) => IsElementVisible(modelItem);
public bool IsVisible(NAV.ModelItem modelItem)
{
var key = modelItem.InstanceGuid;
if (_visibleCache.TryGetValue(key, out var isVisible))
{
return isVisible;
}
//same as ElementSelectionHelper.IsElementVisible
foreach (var item in modelItem.AncestorsAndSelf)
{
_visibleCache[item.InstanceGuid] = !item.IsHidden;
}
return _visibleCache[key];
}
public IEnumerable<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem) => ResolveGeometryLeafNodes(modelItem);
}
@@ -158,11 +158,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -287,9 +282,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -297,8 +292,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.logging": {
@@ -308,7 +303,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.revit2022": {
@@ -327,6 +322,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -353,11 +354,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Revit.API": {
@@ -368,9 +369,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -380,14 +381,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -158,11 +158,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -287,9 +282,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -297,8 +292,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.logging": {
@@ -308,7 +303,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.revit2023": {
@@ -327,6 +322,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -353,11 +354,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Revit.API": {
@@ -368,9 +369,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -380,14 +381,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -158,11 +158,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -287,9 +282,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -297,8 +292,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.logging": {
@@ -308,7 +303,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.revit2024": {
@@ -327,6 +322,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -353,11 +354,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Revit.API": {
@@ -368,9 +369,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -380,14 +381,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -143,11 +143,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -237,9 +232,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -247,8 +242,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.logging": {
@@ -258,7 +253,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.revit2025": {
@@ -277,6 +272,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -303,11 +304,11 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Revit.API": {
@@ -318,9 +319,9 @@
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.CSharp": "4.7.0",
@@ -329,14 +330,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
},
"net8.0-windows7.0/win-x64": {
@@ -72,9 +72,10 @@ internal sealed class RevitReceiveBinding : IReceiveBinding
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
.Initialize(
_revitConversionSettingsFactory.Create(
DetailLevelType.Coarse, //TODO figure out
DetailLevelType.Coarse, // TODO figure out
null,
false
false,
true
)
);
// Receive host objects
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Exceptions;
@@ -40,6 +41,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
private readonly ISpeckleApplication _speckleApplication;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly LinkedModelHandler _linkedModelHandler;
private readonly IThreadContext _threadContext;
/// <summary>
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
@@ -63,7 +66,9 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
ElementUnpacker elementUnpacker,
IRevitConversionSettingsFactory revitConversionSettingsFactory,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler
ITopLevelExceptionHandler topLevelExceptionHandler,
LinkedModelHandler linkedModelHandler,
IThreadContext threadContext
)
: base("sendBinding", bridge)
{
@@ -80,6 +85,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_revitConversionSettingsFactory = revitConversionSettingsFactory;
_speckleApplication = speckleApplication;
_topLevelExceptionHandler = topLevelExceptionHandler;
_linkedModelHandler = linkedModelHandler;
_threadContext = threadContext;
Commands = new SendBindingUICommands(bridge);
// TODO expiry events
@@ -90,8 +97,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged());
}
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
public List<ISendFilter> GetSendFilters() =>
[
new RevitSelectionFilter() { IsDefault = true },
@@ -103,7 +108,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
[
new DetailLevelSetting(DetailLevelType.Medium),
new ReferencePointSetting(ReferencePointType.InternalOrigin),
new SendParameterNullOrEmptyStringsSetting(false)
new SendParameterNullOrEmptyStringsSetting(false),
new LinkedModelsSetting(true)
];
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
@@ -130,23 +136,23 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
_revitConversionSettingsFactory.Create(
_toSpeckleSettingsManager.GetDetailLevelSetting(modelCard),
_toSpeckleSettingsManager.GetReferencePointSetting(modelCard),
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard)
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard),
_toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard)
)
);
List<Element> elements = await RefreshElementsOnSender(modelCard.NotNull());
List<ElementId> elementIds = elements.Select(el => el.Id).ToList();
var elementsByTransform = await RefreshElementsIdsOnSender(modelCard.NotNull());
if (elementIds.Count == 0)
if (elementsByTransform.Count == 0)
{
// Handle as CARD ERROR in this function
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
}
var sendResult = await scope
.ServiceProvider.GetRequiredService<SendOperation<ElementId>>()
.ServiceProvider.GetRequiredService<SendOperation<DocumentToConvert>>()
.Execute(
elementIds,
elementsByTransform,
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
cancellationItem.Token
@@ -176,28 +182,76 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
}
}
private async Task<List<Element>> RefreshElementsOnSender(SenderModelCard modelCard)
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
{
var activeUIDoc =
_revitContext.UIApplication.NotNull().ActiveUIDocument
?? throw new SpeckleException("Unable to retrieve active UI document");
if (modelCard.SendFilter is IRevitSendFilter viewFilter)
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
{
viewFilter.SetContext(_revitContext);
}
var selectedObjects = modelCard.SendFilter.NotNull().RefreshObjectIds();
var selectedObjects = await _threadContext.RunOnMainAsync(
() => Task.FromResult(modelCard.SendFilter.NotNull().RefreshObjectIds())
);
List<Element> elements = selectedObjects
var allElements = selectedObjects
.Select(uid => activeUIDoc.Document.GetElement(uid))
.Where(el => el is not null)
.ToList();
// split elements between main model and linked models
var elementsOnMainModel = allElements.Where(el => el is not RevitLinkInstance).ToList();
var linkedModels = allElements.OfType<RevitLinkInstance>().ToList();
// create context for main document elements
List<DocumentToConvert> documentElementContexts = [new(null, activeUIDoc.Document, elementsOnMainModel)];
// get the linked models setting - this decision belongs at this level
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard);
// ⚠️ process linked models - RevitSendBinding controls the flow based on settings!
// If setting not enabled, we won't unpack (see if-else block)
if (linkedModels.Count > 0)
{
var linkedDocumentContexts = new List<DocumentToConvert>();
foreach (var linkedModel in linkedModels)
{
var linkedDoc = linkedModel.GetLinkDocument();
if (linkedDoc == null)
{
continue;
}
var transform = linkedModel.GetTotalTransform().Inverse;
// decision about whether to process elements is made here, not in the handler
// only collects elements from linked models when the setting is enabled
if (includeLinkedModels)
{
// handler is only responsible for element collection mechanics
var linkedElements = _linkedModelHandler.GetLinkedModelElements(modelCard.SendFilter, linkedDoc);
linkedDocumentContexts.Add(new(transform, linkedDoc, linkedElements));
}
// ⚠️ when disabled, still adds empty contexts to maintain warning generation in RevitRootObjectBuilder
// this approach (to signal that warnings are needed) relies on empty element lists which smells and is a bit of an implicit mechanism
// buuuuut, it works (for now 👀).
else
{
linkedDocumentContexts.Add(new(transform, linkedDoc, new List<Element>()));
}
}
documentElementContexts.AddRange(linkedDocumentContexts);
}
// update ID map
if (modelCard.SendFilter is not null && modelCard.SendFilter.IdMap is not null)
{
var newSelectedObjectIds = new List<string>();
foreach (Element element in elements)
foreach (Element element in allElements)
{
modelCard.SendFilter.IdMap[element.Id.ToString()] = element.UniqueId;
newSelectedObjectIds.Add(element.UniqueId);
@@ -211,7 +265,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
);
}
return elements;
return documentElementContexts;
}
/// <summary>
@@ -317,7 +371,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
{
foreach (var sender in _store.GetSenders().ToList())
{
await RefreshElementsOnSender(sender);
await RefreshElementsIdsOnSender(sender);
}
}
@@ -48,12 +48,13 @@ public static class ServiceRegistration
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
// send operation and dependencies
serviceCollection.AddScoped<SendOperation<ElementId>>();
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
serviceCollection.AddScoped<ElementUnpacker>();
serviceCollection.AddScoped<SendCollectionManager>();
serviceCollection.AddScoped<IRootObjectBuilder<ElementId>, RevitRootObjectBuilder>();
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
serviceCollection.AddSingleton<ToSpeckleSettingsManager>();
serviceCollection.AddSingleton<LinkedModelHandler>();
// receive operation and dependencies
serviceCollection.AddScoped<IHostObjectBuilder, RevitHostObjectBuilder>();
@@ -0,0 +1,5 @@
using Autodesk.Revit.DB;
namespace Speckle.Connectors.Revit.HostApp;
public record DocumentToConvert(Transform? Transform, Document Doc, List<Element> Elements);
@@ -1,6 +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;
@@ -10,10 +12,12 @@ namespace Speckle.Connectors.Revit.HostApp;
public class ElementUnpacker
{
private readonly RevitContext _revitContext;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
public ElementUnpacker(RevitContext revitContext)
public ElementUnpacker(RevitContext revitContext, IConverterSettingsStore<RevitConversionSettings> converterSettings)
{
_revitContext = revitContext;
_converterSettings = converterSettings;
}
/// <summary>
@@ -21,18 +25,21 @@ public class ElementUnpacker
/// 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).
/// </summary>
/// <param name="selectionElements"></param>
/// <param name="doc"> We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places <br/>
/// 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)
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document? doc = null)
{
// Note: steps kept separate on purpose.
// Step 1: unpack groups
var atomicObjects = UnpackElements(selectionElements);
var atomicObjects = UnpackElements(selectionElements, doc);
// Step 2: pack curtain wall elements, once we know the full extent of our flattened item list.
// The behaviour we're looking for:
// If parent wall is part of selection, does not select individual elements out. Otherwise, selects individual elements (Panels, Mullions) as atomic objects.
// NOTE: this also conditionally "packs" stacked wall elements if their parent is present. See detailed note inside the function.
return PackCurtainWallElementsAndStackedWalls(atomicObjects);
return PackCurtainWallElementsAndStackedWalls(atomicObjects, doc);
}
/// <summary>
@@ -50,31 +57,45 @@ public class ElementUnpacker
return UnpackSelectionForConversion(docElements).Select(o => o.UniqueId).ToList();
}
private List<Element> UnpackElements(IEnumerable<Element> elements)
// 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 = null)
{
var unpackedElements = new List<Element>(); // note: could be a hashset/map so we prevent duplicates (?)
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
foreach (var element in elements)
{
// UNPACK: Groups
if (element is Group g)
{
// When a group is from a linked model, GetMemberIds may behave differently
// 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);
var groupElements = g.GetMemberIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(groupElements));
}
else if (element is BaseArray baseArray)
{
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement);
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement);
// For arrays, collect both copied and original members with null checks
// 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));
unpackedElements.AddRange(UnpackElements(originalElements));
}
// UNPACK: Family instances (as they potentially have nested families inside)
else if (element is FamilyInstance familyInstance)
{
var familyElements = familyInstance.GetSubComponentIds().Select(doc.GetElement).ToArray();
var familyElements = familyInstance
.GetSubComponentIds()
.Select(doc.GetElement)
.Where(el => el != null)
.ToArray();
if (familyElements.Length != 0)
{
@@ -85,7 +106,7 @@ public class ElementUnpacker
}
else if (element is MultistoryStairs multistoryStairs)
{
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement);
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement).Where(el => el != null);
unpackedElements.AddRange(UnpackElements(stairs));
}
else
@@ -95,13 +116,22 @@ public class ElementUnpacker
}
// Why filtering for duplicates? Well, well, well... it's related to the comment above on groups: if a group
// contains a nested family, GetMemberIds() will return... duplicates of the exploded family components.
return unpackedElements.GroupBy(el => el.Id).Select(g => g.First()).ToList(); // no disinctBy in here sadly.
// Add null check before GroupBy to prevent NullReferenceException when processing linked models with groups
// This ensures we don't try to access .Id on any null elements that might have been added during the unpacking process
return unpackedElements.Where(el => el != null).GroupBy(el => el.Id).Select(g => g.First()).ToList(); // no disinctBy in here sadly.
}
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements)
// 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 = null)
{
var ids = elements.Select(el => el.Id).ToArray();
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
if (doc == null)
{
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
}
elements.RemoveAll(element =>
(element is Mullion { Host: not null } m && ids.Contains(m.Host.Id))
|| (element is Panel { Host: not null } p && ids.Contains(p.Host.Id))
@@ -0,0 +1,178 @@
using System.IO;
using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.RevitShared;
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Revit.HostApp;
/// <summary>
/// Handles unpacking elements inside linked models.
/// This class is responsible for the mechanics of retrieving elements from linked documents
/// based on different filter types, but not for making decisions about whether linked models
/// should be processed (which is the responsibility of the calling code)!
/// </summary>
public class LinkedModelHandler
{
private readonly RevitContext _revitContext;
public Dictionary<string, string> LinkedModelDisplayNames { get; } = new();
public LinkedModelHandler(RevitContext revitContext)
{
_revitContext = revitContext;
}
/// <summary>
/// Gets elements from a linked document based on the provided send filter.
/// This method handles the specifics of element collection but doesn't make decisions
/// about whether the linked model should be processed - that's the caller's responsibility.
/// </summary>
public List<Element> GetLinkedModelElements(ISendFilter sendFilter, Document linkedDocument)
{
// send mode → Categories
if (sendFilter is RevitCategoriesFilter categoryFilter && categoryFilter.SelectedCategories is not null)
{
var categoryIds = categoryFilter
.SelectedCategories.Select(c => ElementIdHelper.GetElementId(c))
.OfType<ElementId>()
.ToList();
if (categoryIds.Count > 0)
{
return GetElementsByCategory(linkedDocument, categoryIds);
}
return new List<Element>();
}
// send mode → Views (taken from the legacy code)
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView() != null)
{
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(
linkedDocument.PathName,
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document
);
#if REVIT2024_OR_GREATER
// revit 2024 and 2025 we can use the three-parameter constructor to get only visible elements
using var viewCollector = new FilteredElementCollector(
_revitContext.UIApplication.ActiveUIDocument.Document,
viewFilter.GetView().NotNull().Id,
linkInstance.Id
);
return viewCollector.WhereElementIsNotElementType().ToElements().ToList();
#else
// 🚨 LIMITATION: in Revit 2023 and below, we can only check if the entire linked model is visible,
// not individual elements within it. If the linked model is visible, all its elements will be included.
// constructor overload pertaining to searching and filtering visible elements from a revit link only added 2024.
if (linkInstance.IsHidden(viewFilter.GetView().NotNull()))
{
return new List<Element>(); // if the linked model is hidden, return no elements
}
// 💩 fallback to getting all elements if the linked model is visible
return GetAllElementsForLinkedModelSelection(linkedDocument);
#endif
}
// send mode → Selection
return GetAllElementsForLinkedModelSelection(linkedDocument);
}
/// <summary>
/// Prepares display names for linked model documents based on filename
/// </summary>
public void PrepareLinkedModelNames(IReadOnlyList<DocumentToConvert> documentElementContexts)
{
LinkedModelDisplayNames.Clear();
// Group linked models by filename
var linkedModels = documentElementContexts
.Where(ctx => ctx.Doc.IsLinked)
.GroupBy(ctx => Path.GetFileNameWithoutExtension(ctx.Doc.PathName))
.ToDictionary(g => g.Key, g => g.ToList());
// Create a unique key for each instance
foreach (var group in linkedModels)
{
string baseName = group.Key;
var instances = group.Value;
// Single instance - just use the base name
if (instances.Count == 1)
{
string id = GetIdFromDocumentToConvert(instances[0]);
LinkedModelDisplayNames[id] = baseName;
}
// Multiple instances - add numbering
else
{
for (int i = 0; i < instances.Count; i++)
{
string id = GetIdFromDocumentToConvert(instances[i]);
LinkedModelDisplayNames[id] = $"{baseName}_{i + 1}";
}
}
}
}
public string GetIdFromDocumentToConvert(DocumentToConvert documentToConvert) =>
documentToConvert.Doc.GetHashCode() + "-" + (documentToConvert.Transform?.GetHashCode() ?? 0);
/// <summary>
/// Gets elements from a document that belong to the specified categories.
/// </summary>
private List<Element> GetElementsByCategory(Document linkedDoc, List<ElementId> categoryIds)
{
using var multicategoryFilter = new ElementMulticategoryFilter(categoryIds);
using var collector = new FilteredElementCollector(linkedDoc);
return collector
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.WherePasses(multicategoryFilter)
.ToList();
}
// Helper method to generate a simple hash for a transform
// transformedElement.applicationId = ${applicationId}-t{transformHash}
public string GetTransformHash(Transform transform)
{
// create a simplified representation of the transform
string json =
$@"{{
""origin"": [{transform.Origin.X:F2}, {transform.Origin.Y:F2}, {transform.Origin.Z:F2}],
""basis"": [{transform.BasisX.X:F1}, {transform.BasisY.Y:F1}, {transform.BasisZ.Z:F1}]
}}";
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
#pragma warning disable CA1850
using (var sha256 = System.Security.Cryptography.SHA256.Create())
{
byte[] hashBytes = sha256.ComputeHash(jsonBytes);
// keep only the first 8 characters for a short but unique hash
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant()[..8];
}
#pragma warning restore CA1850
}
/// <summary>
/// Retrieves all elements from the linked document when using selection filters.
/// When a linked model is selected in the main document, we include all elements
/// from that linked model since the selection is of the entire linked instance.
/// </summary>
private List<Element> GetAllElementsForLinkedModelSelection(Document linkedDoc)
{
using var collector = new FilteredElementCollector(linkedDoc);
return collector.WhereElementIsNotElementType().WhereElementIsViewIndependent().ToList();
}
private RevitLinkInstance FindLinkInstanceForDocument(string linkedDocumentPath, Document mainDocument)
{
using var collector = new FilteredElementCollector(mainDocument);
return collector
.OfClass(typeof(RevitLinkInstance))
.Cast<RevitLinkInstance>()
.FirstOrDefault(link => link.GetLinkDocument()?.PathName == linkedDocumentPath)
.NotNull();
}
}
@@ -1,6 +1,8 @@
using System.IO;
using Autodesk.Revit.DB;
using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Revit.HostApp;
@@ -13,6 +15,8 @@ public class SendCollectionManager
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
private readonly Dictionary<string, Collection> _collectionCache = new();
private readonly Dictionary<ElementId, (string name, Dictionary<string, object?> props)> _levelCache = new(); // stores level id and its properties
private readonly Dictionary<string, Collection> _linkedModelCollections = new(); // cache for linked model collections
private Collection? _mainModelCollection; // collection for main model elements
public SendCollectionManager(IConverterSettingsStore<RevitConversionSettings> converterSettings)
{
@@ -20,18 +24,72 @@ public class SendCollectionManager
}
/// <summary>
/// Returns the element's host collection based on level, category and optional type. Additionally, places the host collection on the provided root object.
/// Returns the element's host collection based on level, category and optional type if the main model only is sent.
/// The host collection is placed on the provided root object.
/// If linked models are being sent, nested collections are sent under the provided root object.
/// Note, it's not nice we're mutating the root object in this function.
/// </summary>
/// <param name="element"></param>
/// <param name="rootObject"></param>
/// <returns></returns>
public Collection GetAndCreateObjectHostCollection(Element element, Collection rootObject)
public Collection GetAndCreateObjectHostCollection(
Element element,
Collection rootObject,
bool sendWithLinkedModels,
string? modelDisplayName = null
)
{
var doc = _converterSettings.Current.Document;
var path = new List<string>();
// Step 0: get the level and its properties
// Get model path and name
string modelPath = doc.PathName;
string modelName = Path.GetFileNameWithoutExtension(modelPath);
bool isLinkedModel = doc.IsLinked;
// Set up the correct hierarchy based on whether we have linked models or not
Collection startingCollection;
if (sendWithLinkedModels) // this arg comes from RevitRootObjectBuilder and check is setting enabled and linked models present
{
// if we're sending linked models, create a nested structure
// for the main model
if (!isLinkedModel)
{
// create main model collection if it doesn't exist yet
if (_mainModelCollection == null)
{
_mainModelCollection = new Collection(rootObject.name);
rootObject.elements.Add(_mainModelCollection);
}
startingCollection = _mainModelCollection;
}
// for linked models
else
{
// Use display name from settings if available, otherwise use original name
string displayName = modelDisplayName ?? modelName;
// Check if we already have a collection for this model display name
if (!_linkedModelCollections.TryGetValue(displayName, out Collection? linkedModelCollection))
{
// First time seeing this model with this display name
linkedModelCollection = new Collection(displayName);
rootObject.elements.Add(linkedModelCollection);
_linkedModelCollections[displayName] = linkedModelCollection;
}
startingCollection = linkedModelCollection;
}
}
else
{
// if we don't have linked models, use the root directly
startingCollection = rootObject;
}
// get the level and its properties
string levelName = "No Level";
Dictionary<string, object?> levelProperties = new();
if (element.LevelId != ElementId.InvalidElementId)
@@ -43,15 +101,20 @@ public class SendCollectionManager
}
else
{
var level = (Level)doc.GetElement(element.LevelId);
levelName = level.Name;
levelProperties.Add("elevation", level.Elevation);
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
_levelCache.Add(element.LevelId, (levelName, levelProperties));
try
{
var level = (Level)doc.GetElement(element.LevelId);
levelName = level.Name;
levelProperties.Add("elevation", level.Elevation);
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
_levelCache.Add(element.LevelId, (levelName, levelProperties));
}
// the exception is swallowed since if an exception occurs, we fall back to "No Level" for the element
catch (Exception e) when (!e.IsFatal()) { }
}
}
// Step 1: create path components. Currently, this is
// create path components. Currently, this is
// level > category > type
path.Add(levelName);
path.Add(element.Category?.Name ?? "No category");
@@ -69,19 +132,23 @@ public class SendCollectionManager
path.Add("No type");
}
string fullPathName = string.Concat(path);
// Use the collection's name for cache keys to ensure proper separation
string modelIdentifier = startingCollection.name;
// create a model-specific key for the collection cache
string fullPathName = $"{modelIdentifier}:{string.Join(":", path)}";
if (_collectionCache.TryGetValue(fullPathName, out Collection? value))
{
return value;
}
string flatPathName = "";
Collection previousCollection = rootObject;
string flatPathName = modelIdentifier;
Collection previousCollection = startingCollection;
for (int i = 0; i < path.Count; i++)
{
var pathItem = path[i];
flatPathName += pathItem;
flatPathName += ":" + pathItem;
Collection childCollection;
if (_collectionCache.TryGetValue(flatPathName, out Collection? collection))
{
@@ -90,8 +157,7 @@ public class SendCollectionManager
else
{
childCollection = new Collection(pathItem);
// add props if it's the 1st path item, representing level
// if the structure ever changes from level > category > type, this needs to be changed
// add properties to level collection
if (i == 0 && levelProperties.Count > 0)
{
childCollection["properties"] = levelProperties;
@@ -45,12 +45,26 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
return [];
}
// ⚠️ this is ugly, BUT we need to preserve RevitLinkInstances regardless of category.
// these get unpacked later in the RefreshElementsIdsOnSender, so if we don't do this, they'll get filtered out here
using var linkCollector = new FilteredElementCollector(_doc);
var linkInstanceIds = linkCollector.OfClass(typeof(RevitLinkInstance)).Select(link => link.UniqueId).ToList();
// get elements that match the selected categories (excluding RevitLinkInstance objects)
var elementIds = SelectedCategories.Select(c => ElementIdHelper.GetElementId(c)).Where(e => e is not null).ToList();
using var categoryFilter = new ElementMulticategoryFilter(elementIds);
using var collector = new FilteredElementCollector(_doc);
var elements = collector.WhereElementIsNotElementType().WhereElementIsViewIndependent().WherePasses(categoryFilter);
var elements = collector
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.WherePasses(categoryFilter)
.ToList();
// combine both sets
var objectIds = elements.Select(e => e.UniqueId).ToList();
objectIds.AddRange(linkInstanceIds);
SelectedObjectIds = objectIds;
return objectIds;
}
@@ -12,6 +12,7 @@ using Speckle.Converters.Common;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Converters.RevitShared.Settings;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
@@ -25,20 +26,22 @@ public class RevitRootObjectBuilder(
IThreadContext threadContext,
SendCollectionManager sendCollectionManager,
ILogger<RevitRootObjectBuilder> logger,
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton
) : IRootObjectBuilder<ElementId>
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
LinkedModelHandler linkedModelHandler
) : IRootObjectBuilder<DocumentToConvert>
{
// POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<ElementId> objects,
IReadOnlyList<DocumentToConvert> documentElementContexts,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
) => threadContext.RunOnMainAsync(() => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct)));
) =>
threadContext.RunOnMainAsync(
() => Task.FromResult(BuildSync(documentElementContexts, sendInfo, onOperationProgressed, ct))
);
private RootObjectBuilderResult BuildSync(
IReadOnlyList<ElementId> objects,
IReadOnlyList<DocumentToConvert> documentElementContexts,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
@@ -51,96 +54,187 @@ public class RevitRootObjectBuilder(
throw new SpeckleException("Family Environment documents are not supported.");
}
// 0 - Init the root
// init the root
Collection rootObject =
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
rootObject["units"] = converterSettings.Current.SpeckleUnits;
var revitElements = new List<Element>();
List<SendConversionResult> results = new(revitElements.Count);
// Convert ids to actual revit elements
foreach (var id in objects)
var filteredDocumentsToConvert = new List<DocumentToConvert>();
bool sendWithLinkedModels = converterSettings.Current.SendLinkedModels;
List<SendConversionResult> results = new();
// Prepare linked model display names if needed
if (sendWithLinkedModels)
{
var el = converterSettings.Current.Document.GetElement(id);
if (el == null)
{
continue;
}
if (el.Category == null)
{
continue;
}
revitElements.Add(el);
linkedModelHandler.PrepareLinkedModelNames(documentElementContexts);
}
if (revitElements.Count == 0)
foreach (var documentElementContext in documentElementContexts)
{
// add appropriate warnings for linked documents
if (documentElementContext.Doc.IsLinked && !sendWithLinkedModels)
{
results.Add(
new(
Status.WARNING,
documentElementContext.Doc.PathName,
typeof(RevitLinkInstance).ToString(),
null,
new SpeckleException("Enable linked model support from the settings to send this object")
)
);
continue;
}
// filter for valid elements
// if send linked models setting is disabled List<Elements> will be empty, and we won't enter foreach loop
var elementsInTransform = new List<Element>();
foreach (var el in documentElementContext.Elements)
{
if (el == null || el.Category == null)
{
continue;
}
elementsInTransform.Add(el);
}
// only add contexts with elements
if (elementsInTransform.Count > 0)
{
filteredDocumentsToConvert.Add(documentElementContext with { Elements = elementsInTransform });
}
}
// TODO: check the exception!!!!
if (filteredDocumentsToConvert.Count == 0)
{
throw new SpeckleSendFilterException("No objects were found. Please update your publish filter!");
}
// Unpack groups (& other complex data structures)
var atomicObjects = elementUnpacker.UnpackSelectionForConversion(revitElements).ToList();
var atomicObjectsByDocumentAndTransform = new List<DocumentToConvert>();
var atomicObjectCount = 0;
foreach (var filteredDocumentToConvert in filteredDocumentsToConvert)
{
using (
converterSettings.Push(currentSettings => currentSettings with { Document = filteredDocumentToConvert.Doc })
)
{
var atomicObjects = elementUnpacker
.UnpackSelectionForConversion(filteredDocumentToConvert.Elements, filteredDocumentToConvert.Doc)
.ToList();
atomicObjectsByDocumentAndTransform.Add(filteredDocumentToConvert with { Elements = atomicObjects });
atomicObjectCount += atomicObjects.Count;
}
}
var countProgress = 0;
var cacheHitCount = 0;
var skippedObjectCount = 0;
foreach (Element revitElement in atomicObjects)
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
{
cancellationToken.ThrowIfCancellationRequested();
string applicationId = revitElement.UniqueId;
string sourceType = revitElement.GetType().Name;
try
string? modelDisplayName = null;
if (atomicObjectByDocumentAndTransform.Doc.IsLinked)
{
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
string id = linkedModelHandler.GetIdFromDocumentToConvert(atomicObjectByDocumentAndTransform);
linkedModelHandler.LinkedModelDisplayNames.TryGetValue(id, out modelDisplayName);
}
// here we do magic for changing the transform and the related document according to model. first one is always the main model.
using (
converterSettings.Push(currentSettings =>
currentSettings with
{
ReferencePointTransform = atomicObjectByDocumentAndTransform.Transform,
Document = atomicObjectByDocumentAndTransform.Doc,
}
)
)
{
var atomicObjects = atomicObjectByDocumentAndTransform.Elements;
foreach (Element revitElement in atomicObjects)
{
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
results.Add(
new(
Status.WARNING,
revitElement.UniqueId,
cat,
null,
new SpeckleException($"Category {cat} is not supported.")
cancellationToken.ThrowIfCancellationRequested();
string applicationId = revitElement.UniqueId;
string sourceType = revitElement.GetType().Name;
try
{
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
{
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
results.Add(
new(
Status.WARNING,
revitElement.UniqueId,
cat,
null,
new SpeckleException($"Category {cat} is not supported.")
)
);
skippedObjectCount++;
continue;
}
Base converted;
bool hasTransform = atomicObjectByDocumentAndTransform.Transform != null;
// 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(sendInfo.ProjectId, applicationId, out ObjectReference? value)
)
);
skippedObjectCount++;
continue;
}
{
converted = value;
cacheHitCount++;
}
// not in cache means we convert
else
{
// if it has a transform we append transform hash to the applicationId to distinguish the elements from other instances
if (hasTransform)
{
string transformHash = linkedModelHandler.GetTransformHash(
atomicObjectByDocumentAndTransform.Transform.NotNull()
);
applicationId = $"{applicationId}_t{transformHash}";
}
// normal conversions
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
}
Base converted;
if (sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value))
{
converted = value;
cacheHitCount++;
}
else
{
converted = converter.Convert(revitElement);
converted.applicationId = applicationId;
}
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
revitElement,
rootObject,
sendWithLinkedModels,
modelDisplayName
);
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, rootObject);
collection.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogSendConversionError(ex, sourceType);
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
}
collection.elements.Add(converted);
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjectCount));
}
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogSendConversionError(ex, sourceType);
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
}
onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjects.Count));
}
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjects.Count)
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjectCount)
{
throw new SpeckleException("Failed to convert all objects.");
}
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(atomicObjects);
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(
atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList()
);
var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds);
rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
@@ -0,0 +1,12 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
public class LinkedModelsSetting(bool value) : ICardSetting
{
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;
public List<string>? Enum { get; set; }
}
@@ -21,6 +21,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
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();
public ToSpeckleSettingsManager(
RevitContext revitContext,
@@ -102,6 +103,24 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
return returnValue;
}
// 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))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_sendLinkedModelsCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
@@ -20,7 +20,9 @@
<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\LinkedModelHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
@@ -40,6 +42,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitSelectionFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitViewsFilter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\LinkedModelsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Grasshopper8.Registration;
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
public abstract class SpeckleScopedTaskCapableComponent<TInput, TOutput>(
string name,
string nickname,
string description,
string category,
string subCategory
) : SpeckleTaskCapableComponent<TInput, TOutput>(name, nickname, description, category, subCategory)
{
protected override Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default)
{
/*using*/var scope = PriorityLoader.Container.CreateScope(); // NOTE: this component does not work as intended in e.g the receive component; the scope gets disposed before the task completes.
return PerformScopedTask(input, scope, cancellationToken);
}
protected abstract Task<TOutput> PerformScopedTask(
TInput input,
IServiceScope scope,
CancellationToken cancellationToken = default
);
}
@@ -0,0 +1,60 @@
using Grasshopper.Kernel;
using Speckle.Sdk;
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
string name,
string nickname,
string description,
string category,
string subCategory
) : GH_TaskCapableComponent<TOutput>(name, nickname, description, category, subCategory)
{
protected override void SolveInstance(IGH_DataAccess da)
{
//TODO: We're missing activity and logging here. Will enable it for all inherited classes.
if (InPreSolve)
{
// Collect the data and create the task
try
{
var input = GetInput(da);
TaskList.Add(PerformTask(input, CancelToken));
}
catch (SpeckleException e)
{
Console.WriteLine(e);
}
return;
}
if (!GetSolveResults(da, out TOutput result))
{
// INFO: This will run synchronously. Useful for Rhino.Compute runs, but can also be enabled by user.
try
{
TInput input = GetInput(da);
var syncResult = PerformTask(input).Result;
result = syncResult;
}
catch (SpeckleException e)
{
Console.WriteLine(e);
return;
}
}
if (result is not null)
{
SetOutput(da, result);
}
}
protected abstract TInput GetInput(IGH_DataAccess da);
protected abstract void SetOutput(IGH_DataAccess da, TOutput result);
protected abstract Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,71 @@
using Grasshopper.Kernel.Types;
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
public class CollectionPathsSelector : ValueSet<IGH_Goo>
{
public CollectionPathsSelector()
: base(
"Collection Paths Selector",
"Paths",
"Allows you to select a set of collection paths for filtering",
"Speckle",
"Collections"
) { }
public override Guid ComponentGuid => new Guid("65FC4D58-2209-41B6-9B22-BE51C8B28604");
protected override void LoadVolatileData()
{
var collections = VolatileData
.AllData(true)
.OfType<SpeckleCollectionWrapperGoo>()
.Select(goo => goo.Value)
.ToList();
if (collections.Count == 0)
{
return;
}
// NOTE: supporting multiple collections? maybe? not really?
var paths = new List<string>();
foreach (SpeckleCollectionWrapper col in collections)
{
paths.AddRange(GetPaths(col.Collection));
}
m_data.AppendRange(paths.Select(s => new GH_String(s)));
}
private List<string> GetPaths(Collection c)
{
var currentPath = new List<string>();
var allPaths = new HashSet<string>();
void GetPathsInternal(Collection col)
{
currentPath.Add(col.name);
var subCols = col.elements.OfType<SpeckleCollectionWrapper>().ToList();
// NOTE: here we're basically outputting only paths that correspond to a collection
// that has values inside of it.
if (subCols.Count != col.elements.Count)
{
allPaths.Add(string.Join(Constants.LAYER_PATH_DELIMITER, currentPath));
}
foreach (var subCol in subCols)
{
GetPathsInternal(subCol.Collection);
}
currentPath.RemoveAt(currentPath.Count - 1);
}
GetPathsInternal(c);
return allPaths.ToList();
}
}
@@ -0,0 +1,178 @@
using System.Collections.ObjectModel;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.HostApp.Extras;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
#pragma warning disable CA1711
public class CreateCollection : GH_Component, IGH_VariableParameterComponent
#pragma warning restore CA1711
{
public override Guid ComponentGuid => new("BDCE743E-7BDB-479B-AA81-19854AB5A254");
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("cC");
private readonly DebounceDispatcher _debounceDispatcher = new();
public CreateCollection()
: base("Create collection", "Create collection", "Creates a new collection", "Speckle", "Collections") { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
var p = CreateParameter(GH_ParameterSide.Input, 0);
pManager.AddParameter(p);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Layer", "L", "Collection that was created", GH_ParamAccess.tree);
}
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
string rootName = "Unnamed";
Collection rootCollection = new() { name = rootName, applicationId = InstanceGuid.ToString() };
SpeckleCollectionWrapper rootSpeckleCollectionWrapper = new(rootCollection, new() { rootName }, null);
foreach (var inputParam in Params.Input)
{
var data = inputParam.VolatileData.AllData(true).ToList();
if (data.Count == 0)
{
continue;
}
var inputCollections = data.OfType<SpeckleCollectionWrapperGoo>().Empty().ToList();
var inputNonCollections = data.Where(t => t is not SpeckleCollectionWrapperGoo).Empty().ToList();
if (inputCollections.Count != 0 && inputNonCollections.Count != 0)
{
// TODO: error out! we want to disallow setting objects and collections in the same parent collection
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Parameter {inputParam.NickName} should not contain both objects and collections."
);
return;
}
List<string> childPath = new() { rootName };
childPath.Add(inputParam.NickName);
Collection childCollection = new(inputParam.NickName) { applicationId = inputParam.InstanceGuid.ToString() };
SpeckleCollectionWrapper childSpeckleCollectionWrapper =
new(childCollection, childPath, null) { Topology = GrasshopperHelpers.GetParamTopology(inputParam) };
// if on this port we're only receiving collections, we should become "pass-through" to not create
// needless nesting
if (inputCollections.Count == data.Count)
{
var nameTest = new HashSet<string>();
foreach (SpeckleCollectionWrapperGoo collection in inputCollections)
{
// update the speckle collection path
collection.Value.Path = new ObservableCollection<string>(childPath);
foreach (
string subCollectionName in collection
.Value.Collection.elements.OfType<SpeckleCollectionWrapper>()
.Select(v => v.Collection.name)
)
{
var hasNotSeenNameBefore = nameTest.Add(subCollectionName);
if (!hasNotSeenNameBefore)
{
AddRuntimeMessage(
GH_RuntimeMessageLevel.Error,
$"Duplicate collection name found: {subCollectionName} in input parameter {inputParam.NickName}. Please ensure collection names are unique per nesting level.\n See https://speckle.docs/grashopper/collections"
);
return;
}
}
childSpeckleCollectionWrapper.Collection.elements.AddRange(collection.Value.Collection.elements);
}
rootSpeckleCollectionWrapper.Collection.elements.Add(childSpeckleCollectionWrapper);
continue;
}
foreach (var obj in data)
{
SpeckleObjectWrapperGoo wrapperGoo = new();
if (wrapperGoo.CastFrom(obj))
{
wrapperGoo.Value.Path = childPath;
wrapperGoo.Value.Parent = childSpeckleCollectionWrapper;
childSpeckleCollectionWrapper.Collection.elements.Add(wrapperGoo.Value);
}
}
rootSpeckleCollectionWrapper.Collection.elements.Add(childSpeckleCollectionWrapper);
}
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootSpeckleCollectionWrapper));
}
public bool CanInsertParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Input;
}
public bool CanRemoveParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Input;
}
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
var myParam = new Param_GenericObject
{
Name = $"Layer {Params.Input.Count + 1}",
MutableNickName = true,
Optional = true,
Access = GH_ParamAccess.tree // always tree
};
myParam.NickName = myParam.Name;
myParam.Optional = true;
return myParam;
}
public bool DestroyParameter(GH_ParameterSide side, int index)
{
return side == GH_ParameterSide.Input;
}
public void VariableParameterMaintenance()
{
// TODO?
}
public override void AddedToDocument(GH_Document document)
{
base.AddedToDocument(document);
Params.ParameterChanged += (sender, args) =>
{
if (args.ParameterSide == GH_ParameterSide.Output)
{
return;
}
switch (args.OriginalArguments.Type)
{
case GH_ObjectEventType.NickName:
// This means the user is typing characters, debounce until it stops for 400ms before expiring the solution.
// Prevents UI from locking too soon while writing new names for inputs.
args.Parameter.Name = args.Parameter.NickName;
_debounceDispatcher.Debounce(500, e => ExpireSolution(true));
break;
case GH_ObjectEventType.NickNameAccepted:
args.Parameter.Name = args.Parameter.NickName;
ExpireSolution(true);
break;
}
};
}
}
@@ -0,0 +1,268 @@
using System.Collections;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Rhino.Geometry;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Common;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
#pragma warning disable CA1711
public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
#pragma warning restore CA1711
{
public override Guid ComponentGuid => new("69BC8CFB-A72F-4A83-9263-F3399DDA2E5E");
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("eC");
public ExpandCollection()
: base("Expand Collection", "expand", "Expands a new collection", "Speckle", "Collections") { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Data",
"D",
"The data you want to expand",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
private List<SpeckleObjectWrapper> _previewObjects = new();
protected override void SolveInstance(IGH_DataAccess da)
{
SpeckleCollectionWrapperGoo res = new();
da.GetData(0, ref res);
var c = res.Value;
if (c is null)
{
return;
}
Name = c.Collection.name;
NickName = c.Collection.name;
var objects = c
.Collection.elements.Where(el => el is not SpeckleCollectionWrapper)
.OfType<SpeckleObjectWrapper>()
.Select(o => new SpeckleObjectWrapperGoo(o))
.ToList();
var collections = c
.Collection.elements.Where(el => el is SpeckleCollectionWrapper)
.OfType<SpeckleCollectionWrapper>()
.ToList();
var outputParams = new List<OutputParamWrapper>();
if (objects.Count != 0)
{
var param = new Param_GenericObject()
{
Name = "Inner objects",
NickName = "Inner Objs",
Description =
"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
};
outputParams.Add(new OutputParamWrapper(param, objects, null));
}
foreach (SpeckleCollectionWrapper collection in collections)
{
// skip empty
if (collection.Collection.elements.Count == 0)
{
continue;
}
var hasInnerCollections = collection.Collection.elements.Any(el => el is SpeckleCollectionWrapper);
var topology = collection.Topology; // Note: this is a reminder for the future
var nickName = collection.Collection.name;
if (collection.Collection.name.Length > 16)
{
nickName = collection.Collection.name[..3];
nickName += "..." + collection.Collection.name[^3..];
}
var param = new Param_GenericObject()
{
Name = collection.Collection.name,
NickName = nickName,
Access = hasInnerCollections
? GH_ParamAccess.item
: topology is null
? GH_ParamAccess.list
: GH_ParamAccess.tree // we will directly set objects out; note access can be list or tree based on whether it will be a path based collection
};
if (!hasInnerCollections)
{
_previewObjects.AddRange(collection.Collection.elements.Cast<SpeckleObjectWrapper>());
}
outputParams.Add(
new OutputParamWrapper(
param,
hasInnerCollections
? new SpeckleCollectionWrapperGoo(collection)
: collection
.Collection.elements.OfType<SpeckleObjectWrapper>()
.Select(o => new SpeckleObjectWrapperGoo(o))
.ToList(),
topology
)
);
}
if (da.Iteration == 0 && OutputMismatch(outputParams))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(outputParams);
}
);
}
else
{
_previewObjects = new();
FlattenForPreview(c.Collection);
for (int i = 0; i < outputParams.Count; i++)
{
var outParam = Params.Output[i];
var outParamWrapper = outputParams[i];
switch (outParam.Access)
{
case GH_ParamAccess.item:
da.SetData(i, outParamWrapper.Values);
break;
case GH_ParamAccess.list:
da.SetDataList(i, outParamWrapper.Values as IList);
break;
case GH_ParamAccess.tree:
//TODO: means we need to convert the collection to a tree
var topo = outParamWrapper.Topology.NotNull();
var values = outParamWrapper.Values as IList;
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topo, values.NotNull());
da.SetDataTree(i, tree);
break;
}
}
}
}
private BoundingBox _clippingBox;
public override BoundingBox ClippingBox => _clippingBox;
private void FlattenForPreview(Collection c)
{
_clippingBox = new BoundingBox();
foreach (var element in c.elements)
{
if (element is Collection subCol)
{
FlattenForPreview(subCol);
}
if (element is SpeckleObjectWrapper sg)
{
_previewObjects.Add(sg);
var box = sg.GeometryBase.GetBoundingBox(false);
_clippingBox.Union(box);
}
}
}
// public override void DrawViewportWires(IGH_PreviewArgs args) => base.DrawViewportWires(args);
public override void DrawViewportMeshes(IGH_PreviewArgs args)
{
if (_previewObjects.Count == 0)
{
return;
}
var isSelected = args.Document.SelectedObjects().Contains(this);
foreach (var elem in _previewObjects)
{
elem.DrawPreview(args, isSelected);
}
}
private bool OutputMismatch(List<OutputParamWrapper> 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.Param.NickName
|| oldParam.Name != newParam.Param.Name
|| oldParam.Access != newParam.Param.Access
)
{
return true;
}
count++;
}
return false;
}
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// TODO: better, nicer handling of creation/removal
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
foreach (var newParam in outputParams)
{
var param = new Param_GenericObject
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
}
Params.OnParametersChanged();
VariableParameterMaintenance();
ExpireSolution(false);
}
public void VariableParameterMaintenance() { }
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 record OutputParamWrapper(Param_GenericObject Param, object Values, string? Topology);
@@ -0,0 +1,103 @@
using Grasshopper.Kernel;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk;
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
/// <summary>
/// Given a collection and a path, this component will output the objects in the corresponding collection.
/// Note: This component does not flatten the selected collection - if it contains sub collections those will not
/// be outputted.
///
/// To extract those objects out, you should select that specific sub path as well.
/// </summary>
public class FilterObjectsByCollectionPaths : GH_Component
{
public override Guid ComponentGuid => new("77CAEE94-F0B9-4611-897C-71F2A22BA311");
public FilterObjectsByCollectionPaths()
: base(
"FilterObjectsByCollectionPaths",
"ocF",
"Filters model objects by their collection path",
"Speckle",
"Collections"
) { }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(),
"Collection",
"C",
"Collection to filter objects from",
GH_ParamAccess.item
);
pManager.AddTextParameter("Path", "P", "Collection path to filter by", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"The contents of the selected collection",
GH_ParamAccess.tree
);
}
protected override void SolveInstance(IGH_DataAccess dataAccess)
{
string path = "";
dataAccess.GetData(1, ref path);
if (string.IsNullOrEmpty(path))
{
return;
}
SpeckleCollectionWrapperGoo collectionWrapperGoo = new();
dataAccess.GetData(0, ref collectionWrapperGoo);
if (collectionWrapperGoo.Value == null)
{
return;
}
SpeckleCollectionWrapper targetCollectionWrapper = FindCollection(collectionWrapperGoo.Value, path);
if (string.IsNullOrEmpty(targetCollectionWrapper.Topology))
{
dataAccess.SetDataList(0, targetCollectionWrapper.Collection.elements);
}
else
{
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(
targetCollectionWrapper.Topology,
targetCollectionWrapper.Collection.elements
);
dataAccess.SetDataTree(0, tree);
}
}
private SpeckleCollectionWrapper FindCollection(SpeckleCollectionWrapper root, string unifiedPath)
{
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
List<string> paths = unifiedPath.Split([Constants.LAYER_PATH_DELIMITER], StringSplitOptions.None).Skip(1).ToList();
SpeckleCollectionWrapper currentCollectionWrapper = root;
while (paths.Count != 0)
{
currentCollectionWrapper = currentCollectionWrapper
.Collection.elements.OfType<SpeckleCollectionWrapper>()
.First(col => col.Collection.name == paths.First());
paths.RemoveAt(0);
if (paths.Count == 0)
{
return currentCollectionWrapper;
}
}
throw new SpeckleException("Did not find collection");
}
}
@@ -0,0 +1,23 @@
namespace Speckle.Connectors.Grasshopper8.Components;
// NOTE: The number of spaces determines the order in which they display in the ribbon (nice hack)
public static class ComponentCategories
{
public const string OPERATIONS = " Operations";
public const string MODELS = " Model Management";
public const string PARAMETERS = "Parameters";
public const string COLLECTIONS = " Collections";
public const string PRIMARY_RIBBON = "Speckle";
public const string OBJECTS = " Objects";
}
public enum ComponentState
{
Cancelled,
Expired,
NeedsInput,
Receiving,
Ready,
Sending,
UpToDate
}
@@ -0,0 +1,19 @@
using Grasshopper.Kernel;
namespace Speckle.Connectors.Grasshopper8.Components;
public class GhContextMenuButton(
string name,
string nickname,
string description,
Func<ToolStripDropDown, bool>? populateMenuAction = null
) : GH_DocumentObject(name, nickname, description, "Speckle", "UI")
{
public bool Enabled { get; set; } = true;
public override void CreateAttributes() => Attributes = new GhContextMenuButtonAttributes(this);
public override Guid ComponentGuid => new("B01FFD91-F4EC-4332-A9AA-F917AEDAA51D");
public override bool AppendMenuItems(ToolStripDropDown menu) => populateMenuAction?.Invoke(menu) ?? false;
}
@@ -0,0 +1,39 @@
using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
namespace Speckle.Connectors.Grasshopper8.Components;
public class GhContextMenuButtonAttributes(GhContextMenuButton owner) : GH_Attributes<GhContextMenuButton>(owner)
{
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
if (channel != GH_CanvasChannel.Objects)
{
return; // No wires or other layers are being drawn in this component.
}
using var button1 = GH_Capsule.CreateTextCapsule(
Bounds,
Bounds,
Owner.Enabled ? GH_Palette.Black : GH_Palette.Grey,
Owner.Name,
2,
0
);
button1.Render(graphics, Parent.Selected, false, false);
}
public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
{
if (!Owner.Enabled && e.Button == MouseButtons.Right)
{
// Prevents canvas from triggering the right-click behaviour, and showing the context menu.
return GH_ObjectResponse.Handled;
}
// Allowing event to bubble up to canvas will handle the event and show the context menu.
return base.RespondToMouseUp(sender, e);
}
}
@@ -0,0 +1,71 @@
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
internal sealed class GrasshopperCollectionRebuilder
{
public SpeckleCollectionWrapper RootCollectionWrapper { get; }
// a cache of collection path (no delimiter) to the speckle collection
private readonly Dictionary<string, SpeckleCollectionWrapper> _cache = new();
public GrasshopperCollectionRebuilder(Collection baseCollection)
{
Collection newCollection = new() { name = baseCollection.name, applicationId = baseCollection.applicationId };
RootCollectionWrapper = new SpeckleCollectionWrapper(newCollection, new() { baseCollection.name }, null);
}
public void AppendSpeckleGrasshopperObject(
SpeckleObjectWrapper speckleGrasshopperObjectWrapper,
List<Collection> collectionPath
)
{
var collection = GetOrCreateSpeckleCollectionFromPath(collectionPath);
collection.Collection.elements.Add(speckleGrasshopperObjectWrapper);
}
public SpeckleCollectionWrapper GetOrCreateSpeckleCollectionFromPath(List<Collection> path)
{
// first check if cache already has this collection
string fullPath = string.Concat(path);
if (_cache.TryGetValue(fullPath, out SpeckleCollectionWrapper col))
{
return col;
}
// otherwise, iterate through the path and create speckle collections as needed
SpeckleCollectionWrapper previousCollectionWrapper = RootCollectionWrapper;
List<string> currentLayerPath = new();
foreach (var collection in path)
{
var collectionName = collection.name;
currentLayerPath.Add(collectionName);
string key = string.Concat(currentLayerPath);
// check cache
if (_cache.TryGetValue(key, out SpeckleCollectionWrapper currentCol))
{
previousCollectionWrapper = currentCol;
continue;
}
// create and cache if needed
Collection newCollection = new() { name = collectionName };
SpeckleCollectionWrapper newSpeckleCollectionWrapper = new(newCollection, currentLayerPath, null);
if (collection["topology"] is string topology)
{
newSpeckleCollectionWrapper.Topology = topology;
newCollection["topology"] = topology;
}
_cache[key] = newSpeckleCollectionWrapper;
previousCollectionWrapper.Collection.elements.Add(newSpeckleCollectionWrapper);
previousCollectionWrapper = newSpeckleCollectionWrapper;
}
return previousCollectionWrapper;
}
}
@@ -0,0 +1,725 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using GrasshopperAsyncComponent;
using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Rhino.Geometry;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Operations.Receive;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Connectors.Grasshopper8.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Extensions;
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
[Guid("1587DF34-83E5-4AFE-B42E-F7C5C37ECD68")]
public class ReceiveAsyncComponent : GH_AsyncComponent
{
public ReceiveAsyncComponent()
: base(
"Async Receive",
"aR",
"Receive objects async from speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
)
{
BaseWorker = new ReceiveComponentWorker(this);
Attributes = new ReceiveAsyncComponentAttributes(this);
}
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("aR");
public string InputType { get; set; }
public bool AutoReceive { get; set; }
public bool ReceiveOnOpen { get; set; }
public string ReceivedVersionId { get; set; }
public ComponentState CurrentComponentState { get; set; } = ComponentState.NeedsInput;
public bool JustPastedIn { get; set; }
public string LastVersionDate { get; set; }
public string LastInfoMessage { get; set; }
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
// DI props
public Client ApiClient { get; private set; }
public GrasshopperReceiveOperation ReceiveOperation { get; private set; }
public RootObjectUnpacker RootObjectUnpacker { get; private set; }
public static IServiceScope? Scope { get; private set; }
public AccountService AccountManager { get; private set; }
public IClientFactory ClientFactory { get; private set; }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Model",
"model",
"The model object for the received version",
GH_ParamAccess.item
);
}
protected override void SolveInstance(IGH_DataAccess da)
{
da.DisableGapLogic();
// Dependency Injection
Scope = PriorityLoader.Container.CreateScope();
ReceiveOperation = Scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
RootObjectUnpacker = Scope.ServiceProvider.GetService<RootObjectUnpacker>();
AccountManager = Scope.ServiceProvider.GetRequiredService<AccountService>();
ClientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
// We need to call this always in here to be able to react and set events :/
ParseInput(da);
if (
(
AutoReceive
|| CurrentComponentState == ComponentState.Ready
|| CurrentComponentState == ComponentState.Receiving
) && !JustPastedIn
)
{
CurrentComponentState = ComponentState.Receiving;
// Delegate control to parent async component.
base.SolveInstance(da);
return;
}
if (JustPastedIn)
{
// This ensures that we actually do a run. The worker will check and determine if it needs to pull an existing object or not.
OnDisplayExpired(true);
base.SolveInstance(da);
}
else
{
CurrentComponentState = ComponentState.Expired;
Message = "Expired";
OnDisplayExpired(true);
}
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
Menu_AppendSeparator(menu);
if (InputType == "Model")
{
var autoReceiveMi = Menu_AppendItem(
menu,
"Load automatically",
(s, e) =>
{
AutoReceive = !AutoReceive;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
OnDisplayExpired(true);
}
);
},
true,
AutoReceive
);
autoReceiveMi.ToolTipText =
"Toggle automatic loading. If set, any new version will be loaded instantly. This only is applicable when receiving from a model url.";
}
else
{
var autoReceiveMi = Menu_AppendItem(menu, "Automatic loading is disabled because you have specified a version.");
autoReceiveMi.ToolTipText = "To enable automatic loading, select a model without selecting a specific version.";
}
var receivOnOpenMi = Menu_AppendItem(
menu,
"Load when Document opened",
(sender, args) =>
{
ReceiveOnOpen = !ReceiveOnOpen;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
OnDisplayExpired(true);
}
);
},
!AutoReceive,
AutoReceive || ReceiveOnOpen
);
receivOnOpenMi.ToolTipText =
"The node will automatically perform a load operation as soon as the document is open, or the node is copy/pasted into a new document.";
Menu_AppendSeparator(menu);
if (CurrentComponentState == ComponentState.Receiving)
{
Menu_AppendItem(
menu,
"Cancel Load",
(s, e) =>
{
CurrentComponentState = ComponentState.Expired;
RequestCancellation();
}
);
}
}
private void HandleNewCommit()
{
Message = "Expired";
CurrentComponentState = ComponentState.Expired;
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"There is a newer version available for this {InputType}");
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
if (AutoReceive)
{
ExpireSolution(true);
}
else
{
OnDisplayExpired(true);
}
}
);
}
public override void RemovedFromDocument(GH_Document document)
{
RequestCancellation();
Scope?.Dispose();
base.RemovedFromDocument(document);
}
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context)
{
switch (context)
{
case GH_DocumentContext.Loaded:
{
// Will execute every time a document becomes active (from background or opening file.).
if (UrlModelResource != null)
{
Task.Run(async () =>
{
// Ensure fresh instance of client.
ResetApiClient(UrlModelResource);
// Get last commit from the branch
var b = UrlModelResource.GetReceiveInfo(ApiClient);
await b;
// Compare version ids. If they don't match, notify user or fetch data if in auto mode
if (b.Result.SelectedVersionId != ReceivedVersionId)
{
HandleNewCommit();
}
OnDisplayExpired(true);
});
}
break;
}
case GH_DocumentContext.Unloaded:
// Will execute every time a document becomes inactive (in background or closing file.)
// Correctly dispose of the client when changing documents to prevent subscription handlers being called in background.
CurrentComponentState = ComponentState.Expired;
RequestCancellation();
ApiClient?.Dispose();
break;
}
base.DocumentContextChanged(document, context);
}
private void ParseInput(IGH_DataAccess da)
{
HostApp.SpeckleUrlModelResource? dataInput = null;
da.GetData(0, ref dataInput);
if (dataInput is null)
{
UrlModelResource = null;
TriggerAutoSave();
return;
}
// set the type of url input
switch (dataInput)
{
case SpeckleUrlModelVersionResource:
InputType = "Version";
AutoReceive = false;
LastInfoMessage = "";
ResetApiClient(dataInput);
return;
case SpeckleUrlModelResource:
InputType = "Model";
// handled in do work
break;
default:
InputType = "Invalid";
break;
}
if (UrlModelResource != null && UrlModelResource.Equals(dataInput) && !JustPastedIn)
{
return;
}
UrlModelResource = dataInput;
ResetApiClient(UrlModelResource);
}
private void ApiClient_OnVersionCreated(object sender, ProjectVersionsUpdatedMessage e)
{
HandleNewCommit();
}
public void ResetApiClient(SpeckleUrlModelResource urlResource)
{
try
{
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
Account account = AccountManager.GetAccountWithServerUrlFallback("", new Uri(urlResource.Server));
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
}
ApiClient?.Dispose();
ApiClient = ClientFactory.Create(account);
ApiClient.Subscription.CreateProjectVersionsUpdatedSubscription(urlResource.ProjectId).Listeners +=
ApiClient_OnVersionCreated;
}
catch (Exception e) when (!e.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
}
}
}
public class ReceiveComponentWorker : WorkerInstance
{
public ReceiveComponentWorker(GH_Component p)
: base(p) { }
public Base Root { get; set; }
public SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo Result { get; set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance Duplicate()
{
return new ReceiveComponentWorker(Parent);
}
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
{
UrlModelResource = ((ReceiveAsyncComponent)Parent).UrlModelResource;
}
public override void SetData(IGH_DataAccess da)
{
if (CancellationToken.IsCancellationRequested)
{
return;
}
foreach (var (level, message) in RuntimeMessages)
{
Parent.AddRuntimeMessage(level, message);
}
var parent = (ReceiveAsyncComponent)Parent;
parent.CurrentComponentState = ComponentState.UpToDate;
parent.JustPastedIn = false;
if (Result == null)
{
return;
}
da.SetData(0, Result);
}
#pragma warning disable CA1506
public override void DoWork(Action<string, double> reportProgress, Action done)
{
var receiveComponent = (ReceiveAsyncComponent)Parent;
try
{
if (UrlModelResource is null)
{
throw new InvalidOperationException("Url Resource was null");
}
// Means it's a copy paste of an empty non-init component; set the record and exit fast unless ReceiveOnOpen is true.
if (receiveComponent.JustPastedIn && !receiveComponent.AutoReceive)
{
receiveComponent.JustPastedIn = false;
if (!receiveComponent.ReceiveOnOpen)
{
return;
}
receiveComponent.CurrentComponentState = ComponentState.Receiving;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
receiveComponent.OnDisplayExpired(true);
}
);
}
var t = Task.Run(async () =>
{
// Step 1 - RECEIVE FROM SERVER
var receiveInfo = await UrlModelResource
.GetReceiveInfo(receiveComponent.ApiClient, CancellationToken)
.ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//eceiveComponent.Message = $"{p.Status}";
});
if (CancellationToken.IsCancellationRequested)
{
return;
}
if (receiveInfo == null)
{
done();
return;
}
Root = await receiveComponent
.ReceiveOperation.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
.ConfigureAwait(false);
if (CancellationToken.IsCancellationRequested)
{
return;
}
// Step 2 - CONVERT
//receiveComponent.Message = $"Unpacking...";
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
var traversalContextUnpacker = new TraversalContextUnpacker();
var unpackedRoot = receiveComponent.RootObjectUnpacker.Unpack(Root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
);
// TODO: unpack colors and render materials
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(Root as Collection) ?? new Collection() { name = "unnamed" }
);
int count = 0;
int total = localToGlobalMaps.Count;
foreach (var map in localToGlobalMaps)
{
try
{
List<GeometryBase> converted = Convert(map.AtomicObject);
var path = traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
foreach (var matrix in map.Matrix)
{
var mat = GrasshopperHelpers.MatrixToTransform(matrix, "meters");
converted.ForEach(res => res.Transform(mat));
}
// get the collection
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
// get the name and properties
SpecklePropertyGroupGoo propertyGroup = new();
string name = "";
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
{
propertyGroup.CastFrom(da.properties);
name = da.name;
}
else
{
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
if (map.AtomicObject["name"] is string n)
{
name = n;
}
}
// create objects for every value in converted. This is where one to many is not handled very nicely.
foreach (var geometryBase in converted)
{
var gh = new SpeckleObjectWrapper()
{
Base = map.AtomicObject,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = name
};
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
}
}
catch (ConversionException)
{
// TODO
}
//reportProgress(Id, (double)count / total);
count++;
}
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
// DONE
done();
});
t.Wait();
}
catch (Exception ex) when (!ex.IsFatal())
{
RuntimeMessages.Add((GH_RuntimeMessageLevel.Error, ex.ToFormattedString()));
done();
}
}
#pragma warning restore CA1506
private List<GeometryBase> Convert(Base input)
{
var result = ToSpeckleConversionContext.ToHostConverter.Convert(input);
if (result is GeometryBase geometry)
{
return [geometry];
}
if (result is List<GeometryBase> geometryList)
{
return geometryList;
}
if (result is IEnumerable<(GeometryBase, Base)> fallbackConversionResult)
{
// note special handling for proxying render materials OR we don't care about revit
return fallbackConversionResult.Select(t => t.Item1).ToList();
}
throw new SpeckleException("Failed to convert input to rhino");
}
}
public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
{
private bool _selected;
private class SpeckleComponentButton
{
public int Height { get; set; } = 26;
public Rectangle ButtonBounds { get; set; }
public GH_Palette Palette { get; set; }
public string Text { get; set; }
}
private List<SpeckleComponentButton> Buttons { get; set; }
public ReceiveAsyncComponentAttributes(GH_Component owner)
: base(owner)
{
Buttons = new List<SpeckleComponentButton>()
{
new SpeckleComponentButton()
{
Text = "Select project",
Palette = GH_Palette.Blue,
Height = 18
},
new SpeckleComponentButton()
{
Text = "Select model",
Palette = GH_Palette.Blue,
Height = 18
},
new SpeckleComponentButton()
{
Text = "Select version",
Palette = GH_Palette.Blue,
Height = 18
},
new SpeckleComponentButton() { Text = "Test 2", Palette = GH_Palette.Black },
};
}
public override bool Selected
{
get => _selected;
set => _selected = value;
}
protected override void Layout()
{
base.Layout();
var baseRec = GH_Convert.ToRectangle(Bounds);
for (int i = 0; i < Buttons.Count; i++)
{
var button = Buttons[i];
var buttonBounds = i == 0 ? baseRec : Buttons[i - 1].ButtonBounds;
buttonBounds.Y = buttonBounds.Bottom;
buttonBounds.Height = button.Height;
buttonBounds.Width = baseRec.Width;
button.ButtonBounds = buttonBounds;
baseRec.Height += button.Height;
Bounds = baseRec;
}
}
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
var state = ((ReceiveAsyncComponent)Owner).CurrentComponentState;
if (channel == GH_CanvasChannel.Objects)
{
// if (((ReceiveAsyncComponent)Owner).AutoReceive)
// {
// var autoReceiveButton = GH_Capsule.CreateTextCapsule(
// ButtonBounds,
// ButtonBounds,
// GH_Palette.Blue,
// "Auto Receive",
// 2,
// 0
// );
//
// autoReceiveButton.Render(graphics, Selected, Owner.Locked, false);
// autoReceiveButton.Dispose();
// }
// else
// {
// var palette =
// state == ComponentState.Expired || state == ComponentState.UpToDate || state == ComponentState.Cancelled
// ? GH_Palette.Black
// : GH_Palette.Transparent;
// var text = state != ComponentState.Receiving ? "Receive" : "Receiving...";
//
// var button = GH_Capsule.CreateTextCapsule(
// ButtonBounds,
// ButtonBounds,
// palette,
// text,
// 2,
// state == ComponentState.Expired ? 10 : 0
// );
// button.Render(graphics, Selected, Owner.Locked, false);
// button.Dispose();
// }
foreach (var button in Buttons)
{
using var b = GH_Capsule.CreateTextCapsule(
button.ButtonBounds,
button.ButtonBounds,
button.Palette,
button.Text,
2,
0
);
b.Render(graphics, Selected, Owner.Locked, false);
}
}
}
public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
{
if (e.Button != MouseButtons.Left)
{
return base.RespondToMouseDown(sender, e);
}
foreach (var button in Buttons)
{
if (((RectangleF)button.ButtonBounds).Contains(e.CanvasLocation))
{
Debug.WriteLine($"Button was pressed: {button.Text}");
return GH_ObjectResponse.Handled;
}
}
return base.RespondToMouseDown(sender, e);
// if (!((RectangleF)ButtonBounds).Contains(e.CanvasLocation))
// {
// return base.RespondToMouseDown(sender, e);
// }
//
// if (((ReceiveAsyncComponent)Owner).CurrentComponentState == ComponentState.Receiving)
// {
// return GH_ObjectResponse.Handled;
// }
//
// if (((ReceiveAsyncComponent)Owner).AutoReceive)
// {
// ((ReceiveAsyncComponent)Owner).AutoReceive = false;
// Owner.OnDisplayExpired(true);
// return GH_ObjectResponse.Handled;
// }
//
// // TODO: check if owner has null account/client, and call the reset thing SYNC
// ((ReceiveAsyncComponent)Owner).CurrentComponentState = ComponentState.Ready;
// Owner.ExpireSolution(true);
// return GH_ObjectResponse.Handled;
}
}
@@ -0,0 +1,208 @@
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Rhino.Geometry;
using Speckle.Connectors.Common.Instances;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Operations.Receive;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Common.Exceptions;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
public class ReceiveComponentOutput
{
public SpeckleCollectionWrapperGoo RootObject { get; set; }
}
public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlModelResource, ReceiveComponentOutput>
{
public ReceiveComponent()
: base(
"Receive from Speckle",
"RFS",
"Receive objects from speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
) { }
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("R");
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Model",
"model",
"The model object for the received version",
GH_ParamAccess.item
);
}
protected override SpeckleUrlModelResource GetInput(IGH_DataAccess da)
{
SpeckleUrlModelResource? url = null;
da.GetData(0, ref url);
if (url is null)
{
throw new SpeckleException("Speckle url is null");
}
return url;
}
protected override void SetOutput(IGH_DataAccess da, ReceiveComponentOutput result)
{
da.SetData(0, result.RootObject);
Message = "Done";
}
protected override async Task<ReceiveComponentOutput> PerformScopedTask(
SpeckleUrlModelResource input,
IServiceScope scope,
CancellationToken cancellationToken = default
)
{
// TODO: Resolving dependencies here may be overkill in most cases. Must re-evaluate.
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
// Do the thing 👇🏼
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Server));
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
}
using var client = clientFactory.Create(account);
var receiveInfo = await input.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
var progress = new Progress<CardProgress>(_ =>
{
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
// Message = $"{progress.Status}: {progress.Progress}";
});
var root = await receiveOperation
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
.ConfigureAwait(false);
// We need to rethink these lovely unpackers, there's a bit too many of 'em
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
var traversalContextUnpacker = new TraversalContextUnpacker();
var unpackedRoot = rootObjectUnpacker.Unpack(root);
// "flatten" block instances
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
unpackedRoot.DefinitionProxies,
unpackedRoot.ObjectsToConvert.ToList()
);
var collectionRebuilder = new GrasshopperCollectionRebuilder(
(root as Collection) ?? new Collection() { name = "unnamed" }
);
foreach (var map in localToGlobalMaps)
{
try
{
List<GeometryBase> converted = Convert(map.AtomicObject);
List<Collection> path = traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
foreach (var matrix in map.Matrix)
{
var mat = GrasshopperHelpers.MatrixToTransform(matrix, "meters");
converted.ForEach(res => res.Transform(mat));
}
// get the collection
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
// get the name and properties
SpecklePropertyGroupGoo propertyGroup = new();
string name = "";
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
{
propertyGroup.CastFrom(da.properties);
name = da.name;
}
else
{
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
{
propertyGroup.CastFrom(props);
}
if (map.AtomicObject["name"] is string n)
{
name = n;
}
}
// create objects for every value in converted. This is where one to many is not handled very nicely.
foreach (var geometryBase in converted)
{
var gh = new SpeckleObjectWrapper()
{
Base = map.AtomicObject,
Path = path.Select(p => p.name).ToList(),
Parent = objectCollection,
GeometryBase = geometryBase,
Properties = propertyGroup,
Name = name
};
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
}
}
catch (ConversionException)
{
// TODO
}
}
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
return new ReceiveComponentOutput { RootObject = goo };
}
private List<GeometryBase> Convert(Base input)
{
var result = ToSpeckleConversionContext.ToHostConverter.Convert(input);
if (result is GeometryBase geometry)
{
return [geometry];
}
if (result is List<GeometryBase> geometryList)
{
return geometryList;
}
if (result is List<(GeometryBase, Base)> fallbackConversionResult)
{
// note special handling for proxying render materials OR we don't care about revit
return fallbackConversionResult.Select(t => t.Item1).ToList();
}
throw new SpeckleException("Failed to convert input to rhino");
}
}
@@ -0,0 +1,503 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Timers;
using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using GrasshopperAsyncComponent;
using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Connectors.Grasshopper8.Registration;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Models.Extensions;
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Send;
[Guid("52481972-7867-404F-8D9F-E1481183F355")]
public class SendAsyncComponent : GH_AsyncComponent
{
public SendAsyncComponent()
: base(
"Async Send",
"aS",
"Send objects async to speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
)
{
BaseWorker = new SendComponentWorker(this);
Attributes = new SendAsyncComponentAttributes(this);
}
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("aS");
public ComponentState CurrentComponentState { get; set; } = ComponentState.NeedsInput;
public bool AutoSend { get; set; }
public bool JustPastedIn { get; set; }
public double OverallProgress { get; set; }
public string? Url { get; set; }
public Client ApiClient { get; set; }
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
public SpeckleUrlModelResource? OutputParam { get; set; }
public SendOperation<SpeckleCollectionWrapperGoo> SendOperation { get; private set; }
public static IServiceScope? Scope { get; set; }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Model",
"model",
"The collection model object to send",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
static void Open(string url)
{
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
Process.Start(psi);
}
base.AppendAdditionalMenuItems(menu);
Menu_AppendSeparator(menu);
var autoSendMi = Menu_AppendItem(
menu,
"Send automatically",
(s, e) =>
{
AutoSend = !AutoSend;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
OnDisplayExpired(true);
}
);
},
true,
AutoSend
);
autoSendMi.ToolTipText =
"Toggle automatic data sending. If set, any change in any of the input parameters of this component will start sending.\n Please be aware that if a new send starts before an old one is finished, the previous operation is cancelled.";
if (Url != null)
{
Menu_AppendSeparator(menu);
Menu_AppendItem(menu, $"View created version online ↗", (s, e) => Open(Url));
}
Menu_AppendSeparator(menu);
if (CurrentComponentState == ComponentState.Sending)
{
Menu_AppendItem(
menu,
"Cancel Send",
(s, e) =>
{
CurrentComponentState = ComponentState.Expired;
RequestCancellation();
}
);
}
}
protected override void SolveInstance(IGH_DataAccess da)
{
// Dependency Injection
Scope = PriorityLoader.Container.CreateScope();
SendOperation = Scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
var rhinoConversionSettingsFactory = Scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
Scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
var accountManager = Scope.ServiceProvider.GetRequiredService<AccountService>();
var clientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
// We need to call this always in here to be able to react and set events :/
ParseInput(da, accountManager, clientFactory);
if (
(AutoSend || CurrentComponentState == ComponentState.Ready || CurrentComponentState == ComponentState.Sending)
&& !JustPastedIn
)
{
CurrentComponentState = ComponentState.Sending;
// Delegate control to parent async component.
base.SolveInstance(da);
return;
}
if (JustPastedIn)
{
// Set output data in a "first run" event. Note: we are not persisting the actual "sent" object as it can be very big.
base.SolveInstance(da);
return;
}
else
{
da.SetData(0, OutputParam);
CurrentComponentState = ComponentState.Expired;
Message = "Expired";
OnDisplayExpired(true);
}
}
public override void RemovedFromDocument(GH_Document document)
{
RequestCancellation();
Scope?.Dispose();
base.RemovedFromDocument(document);
}
public override void DisplayProgress(object sender, ElapsedEventArgs e)
{
if (Workers.Count == 0)
{
return;
}
Message = "";
var total = 0.0;
foreach (var kvp in ProgressReports)
{
Message += $"{kvp.Key}: {kvp.Value}\n";
total += kvp.Value;
}
OverallProgress = total / ProgressReports.Keys.Count;
RhinoApp.InvokeOnUiThread(
(Action)
delegate
{
OnDisplayExpired(true);
}
);
}
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context)
{
switch (context)
{
case GH_DocumentContext.Loaded:
OnDisplayExpired(true);
break;
case GH_DocumentContext.Unloaded:
// Will execute every time a document becomes inactive (in background or closing file.)
//Correctly dispose of the client when changing documents to prevent subscription handlers being called in background.
RequestCancellation();
break;
}
base.DocumentContextChanged(document, context);
}
private void ParseInput(IGH_DataAccess da, AccountService accountManager, IClientFactory clientFactory)
{
HostApp.SpeckleUrlModelResource? dataInput = null;
da.GetData(0, ref dataInput);
if (dataInput is null)
{
UrlModelResource = null;
TriggerAutoSave();
return;
}
UrlModelResource = dataInput;
try
{
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
Account account = accountManager.GetAccountWithServerUrlFallback("", new Uri(dataInput.Server));
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
}
ApiClient?.Dispose();
ApiClient = clientFactory.Create(account);
}
catch (Exception e) when (!e.IsFatal())
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
}
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
da.GetData(1, ref rootCollectionWrapper);
if (rootCollectionWrapper is null)
{
RootCollectionWrapper = null;
TriggerAutoSave();
return;
}
RootCollectionWrapper = rootCollectionWrapper;
}
}
public class SendComponentWorker : WorkerInstance
{
public SendComponentWorker(GH_Component p)
: base(p) { }
private Stopwatch _stopwatch;
public SpeckleUrlModelResource? OutputParam { get; set; }
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
public override WorkerInstance Duplicate()
{
return new SendComponentWorker(Parent);
}
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
{
_stopwatch = new Stopwatch();
_stopwatch.Start();
}
public override void SetData(IGH_DataAccess da)
{
_stopwatch.Stop();
if (((SendAsyncComponent)Parent).JustPastedIn)
{
((SendAsyncComponent)Parent).JustPastedIn = false;
da.SetData(0, ((SendAsyncComponent)Parent).OutputParam);
return;
}
if (CancellationToken.IsCancellationRequested)
{
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.Expired;
return;
}
foreach (var (level, message) in RuntimeMessages)
{
Parent.AddRuntimeMessage(level, message);
}
da.SetData(0, OutputParam);
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.UpToDate;
((SendAsyncComponent)Parent).OutputParam = OutputParam; // ref the outputs in the parent too, so we can serialise them on write/read
((SendAsyncComponent)Parent).OverallProgress = 0;
var hasWarnings = RuntimeMessages.Count > 0;
if (!hasWarnings)
{
Parent.AddRuntimeMessage(
GH_RuntimeMessageLevel.Remark,
$"Successfully sent {((SendAsyncComponent)Parent).RootCollectionWrapper?.Value.GetTotalChildrenCount()} objects to Speckle."
);
Parent.AddRuntimeMessage(
GH_RuntimeMessageLevel.Remark,
$"Send duration: {_stopwatch.ElapsedMilliseconds / 1000f}s"
);
}
}
public override void DoWork(Action<string, double> reportProgress, Action done)
{
var sendComponent = (SendAsyncComponent)Parent;
if (sendComponent.JustPastedIn)
{
done();
return;
}
if (CancellationToken.IsCancellationRequested)
{
sendComponent.CurrentComponentState = ComponentState.Expired;
return;
}
try
{
SpeckleUrlModelResource? urlModelResource = sendComponent.UrlModelResource;
if (urlModelResource is null)
{
throw new InvalidOperationException("Url Resource was null");
}
SpeckleCollectionWrapperGoo? rootCollectionWrapper = sendComponent.RootCollectionWrapper;
if (rootCollectionWrapper is null)
{
throw new InvalidOperationException("Root Collection was null");
}
var t = Task.Run(async () =>
{
if (CancellationToken.IsCancellationRequested)
{
sendComponent.CurrentComponentState = ComponentState.Expired;
return;
}
// Step 1 - SEND TO SERVER
var sendInfo = await urlModelResource
.GetSendInfo(sendComponent.ApiClient, CancellationToken)
.ConfigureAwait(false);
var progress = new Progress<CardProgress>(p =>
{
reportProgress(Id, p.Progress ?? 0);
//sendComponent.Message = $"{p.Status}";
});
var result = await sendComponent
.SendOperation.Execute(
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
sendInfo,
progress,
CancellationToken
)
.ConfigureAwait(false);
// TODO: need the created version id here from the send result, not the rootobj id
SpeckleUrlModelVersionResource? createdVersion =
new(sendInfo.ServerUrl.ToString(), sendInfo.ProjectId, sendInfo.ModelId, result.RootObjId);
OutputParam = createdVersion;
sendComponent.Url = $"{createdVersion.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}"; // TODO: missing "@VersionId"
// DONE
done();
});
t.Wait();
}
catch (Exception ex) when (!ex.IsFatal())
{
RuntimeMessages.Add((GH_RuntimeMessageLevel.Error, ex.ToFormattedString()));
done();
}
}
}
public class SendAsyncComponentAttributes : GH_ComponentAttributes
{
private bool _selected;
public SendAsyncComponentAttributes(GH_Component owner)
: base(owner) { }
private Rectangle ButtonBounds { get; set; }
public override bool Selected
{
get => _selected;
set => _selected = value;
}
protected override void Layout()
{
base.Layout();
var baseRec = GH_Convert.ToRectangle(Bounds);
baseRec.Height += 26;
var btnRec = baseRec;
btnRec.Y = btnRec.Bottom - 26;
btnRec.Height = 26;
btnRec.Inflate(-2, -2);
Bounds = baseRec;
ButtonBounds = btnRec;
}
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
var state = ((SendAsyncComponent)Owner).CurrentComponentState;
if (channel == GH_CanvasChannel.Objects)
{
if (((SendAsyncComponent)Owner).AutoSend)
{
var autoSendButton = GH_Capsule.CreateTextCapsule(
ButtonBounds,
ButtonBounds,
GH_Palette.Blue,
"Auto Send",
2,
0
);
autoSendButton.Render(graphics, Selected, Owner.Locked, false);
autoSendButton.Dispose();
}
else
{
var palette =
state == ComponentState.Expired || state == ComponentState.UpToDate
? GH_Palette.Black
: GH_Palette.Transparent;
var text = state == ComponentState.Sending ? "Sending..." : "Send";
var button = GH_Capsule.CreateTextCapsule(
ButtonBounds,
ButtonBounds,
palette,
text,
2,
state == ComponentState.Expired ? 10 : 0
);
button.Render(graphics, Selected, Owner.Locked, false);
button.Dispose();
}
}
}
public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
{
if (e.Button == MouseButtons.Left)
{
if (((RectangleF)ButtonBounds).Contains(e.CanvasLocation))
{
if (((SendAsyncComponent)Owner).AutoSend)
{
((SendAsyncComponent)Owner).AutoSend = false;
Owner.OnDisplayExpired(true);
return GH_ObjectResponse.Handled;
}
if (((SendAsyncComponent)Owner).CurrentComponentState == ComponentState.Sending)
{
return GH_ObjectResponse.Handled;
}
((SendAsyncComponent)Owner).CurrentComponentState = ComponentState.Ready;
Owner.ExpireSolution(true);
return GH_ObjectResponse.Handled;
}
}
return base.RespondToMouseDown(sender, e);
}
}
@@ -0,0 +1,153 @@
using System.Diagnostics;
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Send;
public class SendComponentInput
{
public SpeckleUrlModelResource Resource { get; }
public SpeckleCollectionWrapperGoo Input { get; }
public SendComponentInput(SpeckleUrlModelResource resource, SpeckleCollectionWrapperGoo input)
{
Resource = resource;
Input = input;
}
}
public class SendComponentOutput(SpeckleUrlModelResource resource)
{
public SpeckleUrlModelResource Resource { get; } = resource;
}
public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInput, SendComponentOutput>
{
public SendComponent()
: base(
"Send to Speckle",
"STS",
"Send objects to speckle",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OPERATIONS
) { }
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
public string? Url { get; private set; }
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("S");
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
pManager.AddParameter(
new SpeckleCollectionParam(GH_ParamAccess.item),
"Model",
"model",
"The collection model object to send",
GH_ParamAccess.item
);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
protected override SendComponentInput GetInput(IGH_DataAccess da)
{
if (da.Iteration != 0)
{
throw new SpeckleException("No more than 1 resource allowed");
}
SpeckleUrlModelResource? resource = null;
if (!da.GetData(0, ref resource))
{
throw new SpeckleException("Failed to get resource");
}
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
da.GetData(1, ref rootCollectionWrapper);
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper);
}
protected override void SetOutput(IGH_DataAccess da, SendComponentOutput result)
{
da.SetData(0, result.Resource);
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
Menu_AppendSeparator(menu);
if (Url != null)
{
Menu_AppendSeparator(menu);
Menu_AppendItem(menu, $"View created version online ↗", (s, e) => Open(Url));
}
static void Open(string url)
{
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
Process.Start(psi);
}
}
protected override async Task<SendComponentOutput> PerformScopedTask(
SendComponentInput input,
IServiceScope scope,
CancellationToken cancellationToken = default
)
{
var rhinoConversionSettingsFactory = scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server));
if (account is null)
{
throw new SpeckleAccountManagerException($"No default account was found");
}
var progress = new Progress<CardProgress>(_ =>
{
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
// Message = $"{progress.Status}: {progress.Progress}";
});
using var client = clientFactory.Create(account);
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
var result = await sendOperation
.Execute(new List<SpeckleCollectionWrapperGoo>() { input.Input }, sendInfo, progress, cancellationToken)
.ConfigureAwait(false);
SpeckleUrlLatestModelVersionResource createdVersionResource =
new(sendInfo.ServerUrl.ToString(), sendInfo.ProjectId, sendInfo.ModelId);
Url = $"{createdVersionResource.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}"; // TODO: missing "@VersionId"
return new SendComponentOutput(createdVersionResource);
}
}
@@ -0,0 +1,507 @@
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Connectors.Grasshopper8.Registration;
using Speckle.Sdk;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Credentials;
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Connectors.Grasshopper8.Components.Operations;
public class SpeckleSelectModelComponent : GH_Component
{
private Project? _project;
private Model? _model;
private Version? _version;
private Account? _account;
private bool _justPastedIn;
private string? _storedUserId;
private string? _storedServer;
private string? _storedProjectId;
private string? _storedModelId;
private string? _storedVersionId;
private readonly AccountService _accountService;
private readonly AccountManager _accountManager;
private readonly IClientFactory _clientFactory;
public ResourceCollection<Project>? LastFetchedProjects { get; set; }
public ResourceCollection<Model>? LastFetchedModels { get; set; }
public ResourceCollection<Version>? LastFetchedVersions { get; set; }
public GhContextMenuButton ProjectContextMenuButton { get; set; }
public GhContextMenuButton ModelContextMenuButton { get; set; }
public GhContextMenuButton VersionContextMenuButton { get; set; }
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("URL");
public SpeckleSelectModelComponent()
: base("Speckle Model URL", "URL", "User selectable model from speckle", "Speckle", "Models")
{
ProjectContextMenuButton = new GhContextMenuButton(
"Select Project",
"Select Project",
"Right-click to select project",
PopulateProjectMenu
);
ModelContextMenuButton = new GhContextMenuButton(
"Select Model",
"Select Project",
"Right-click to select a model",
PopulateModelMenu
);
VersionContextMenuButton = new GhContextMenuButton(
"Select Version",
"Select Version",
"Right-click to select a version",
PopulateVersionMenu
);
Attributes = new SpeckleSelectModelComponentAttributes(this);
_accountService = PriorityLoader.Container.GetRequiredService<AccountService>();
_accountManager = PriorityLoader.Container.GetRequiredService<AccountManager>();
_clientFactory = PriorityLoader.Container.GetRequiredService<IClientFactory>();
var account = _accountManager.GetDefaultAccount();
OnAccountSelected(account);
}
private bool PopulateVersionMenu(ToolStripDropDown menu)
{
if (LastFetchedVersions is null)
{
Menu_AppendItem(menu, "No versions were fetched");
return true;
}
if (LastFetchedVersions.items.Count == 0)
{
Menu_AppendItem(menu, "Model has no versions");
return true;
}
Menu_AppendItem(menu, "Search...", null, null, false, false);
Menu_AppendSeparator(menu);
Menu_AppendItem(
menu,
"Latest Version",
(_, _) => OnVersionSelected(null),
null,
_version != null,
_version == null
);
foreach (var version in LastFetchedVersions.items)
{
var desc = string.IsNullOrEmpty(version.message) ? "No description" : version.message;
Menu_AppendItem(
menu,
$"{version.id} - {desc}",
(_, _) => OnVersionSelected(version),
null,
_version?.id != version.id,
_version?.id == version.id
);
}
return true;
}
private bool PopulateModelMenu(ToolStripDropDown menu)
{
if (LastFetchedModels == null)
{
Menu_AppendItem(menu, "No models were fetched");
return true;
}
if (LastFetchedModels.items.Count == 0)
{
Menu_AppendItem(menu, "Project has no models");
return true;
}
Menu_AppendItem(menu, "Search...", null, null, false, false);
Menu_AppendSeparator(menu);
foreach (var model in LastFetchedModels.items)
{
var desc = string.IsNullOrEmpty(model.description) ? "No description" : model.description;
Menu_AppendItem(
menu,
$"{model.name} - {desc}",
(_, _) => OnModelSelected(model),
null,
_model?.id != model.id,
_model?.id == model.id
);
}
return true;
}
private bool PopulateProjectMenu(ToolStripDropDown menu)
{
if (LastFetchedProjects == null)
{
Menu_AppendItem(menu, "No projects were fetched");
return true;
}
Menu_AppendItem(menu, "Search...", null, null, false, false);
Menu_AppendSeparator(menu);
foreach (var project in LastFetchedProjects.items)
{
var desc = string.IsNullOrEmpty(project.description) ? "No description" : project.description;
Menu_AppendItem(
menu,
$"{project.name} - {desc}",
(_, _) => OnProjectSelected(project),
_project?.id != project.id,
_project?.id == project.id
);
}
return true;
}
private void OnAccountSelected(Account? account, bool expire = true, bool redraw = true)
{
_account = account;
Message = _account != null ? $"{_account.serverInfo.url}\n{_account.userInfo.email}" : null;
LastFetchedProjects = null;
OnProjectSelected(null, expire, redraw);
}
private void OnProjectSelected(Project? project, bool expire = true, bool redraw = true)
{
_project = project;
var suffix = ProjectContextMenuButton.Enabled
? "Right-click to select another project."
: "Selection is disabled due to component input.";
if (_project != null)
{
ProjectContextMenuButton.Name = _project.name;
ProjectContextMenuButton.NickName = _project.id;
ProjectContextMenuButton.Description = $"{_project.description ?? "No description"}\n\n{suffix}";
}
else
{
ProjectContextMenuButton.Name = "Select Project";
ProjectContextMenuButton.NickName = "Project";
ProjectContextMenuButton.Description = "Right-click to select project";
}
LastFetchedModels = null;
OnModelSelected(null, expire, redraw);
}
private void OnModelSelected(Model? model, bool expire = true, bool redraw = true)
{
_model = model;
var suffix = ModelContextMenuButton.Enabled
? "Right-click to select another model."
: "Selection is disabled due to component input.";
if (_model != null)
{
ModelContextMenuButton.Name = _model.name;
ModelContextMenuButton.NickName = _model.id;
ModelContextMenuButton.Description = $"{_model.description ?? "No description"}\n\n{suffix}";
}
else
{
ModelContextMenuButton.Name = "Select Model";
ModelContextMenuButton.NickName = "Model";
ModelContextMenuButton.Description = "Right-click to select model";
}
LastFetchedVersions = null;
OnVersionSelected(null, expire, redraw);
}
private void OnVersionSelected(Version? version, bool expire = true, bool redraw = true)
{
_version = version;
var suffix = VersionContextMenuButton.Enabled
? "Right-click to select another version."
: "Selection is disabled due to component input.";
if (_version != null)
{
VersionContextMenuButton.Name = _version.id;
VersionContextMenuButton.NickName = _version.id;
VersionContextMenuButton.Description = $"{_version.message ?? "No message"}\n\n{suffix}";
}
else if (_model != null)
{
VersionContextMenuButton.NickName = "Latest Version";
VersionContextMenuButton.Name = "Latest Version";
VersionContextMenuButton.Description = "Gets the latest version from the selected model";
}
else
{
VersionContextMenuButton.Name = "Select Version";
VersionContextMenuButton.NickName = "Version";
VersionContextMenuButton.Description = "Right-click to select version";
}
if (expire)
{
ExpirePreview(redraw);
ExpireSolution(true);
}
}
public override Guid ComponentGuid => new("9638B3B5-C469-4570-B69F-686D8DA5C48D");
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
var urlIndex = pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
pManager[urlIndex].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddParameter(new SpeckleUrlModelResourceParam());
}
protected override void SolveInstance(IGH_DataAccess da)
{
// Deal with inputs
string? urlInput = null;
// OPTION 1: Component has input wire connected
if (da.GetData(0, ref urlInput))
{
//Lock button interactions before anything else, to ensure any input (even invalid ones) lock the state.
SetComponentButtonsState(false);
if (urlInput == null || string.IsNullOrEmpty(urlInput))
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input url was empty or null");
return;
}
try
{
var resource = SolveInstanceWithUrlInput(urlInput);
da.SetData(0, resource);
}
catch (SpeckleException e)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
}
return; // Fast exit!
}
// OPTION 2: Component is running with no wires connected to input.
// Unlock button interactions when no input data is provided (no wires connected)
SetComponentButtonsState(true);
if (_justPastedIn && _storedUserId != null && !string.IsNullOrEmpty(_storedUserId))
{
try
{
var account = _accountManager.GetAccount(_storedUserId);
OnAccountSelected(account, false);
}
catch (SpeckleAccountManagerException e)
{
// Swallow and move onto checking server.
Console.WriteLine(e);
}
if (_storedServer != null && _account == null)
{
var account = _accountService.GetAccountWithServerUrlFallback(_storedUserId ?? "", new Uri(_storedServer));
OnAccountSelected(account, false);
}
}
// Validate backing data
if (_account == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Please select an account in the right click menu");
ProjectContextMenuButton.Enabled = false;
ModelContextMenuButton.Enabled = false;
VersionContextMenuButton.Enabled = false;
return;
}
Client client = _clientFactory.Create(_account);
LastFetchedProjects = client.ActiveUser.GetProjects(10, null, null).Result;
ProjectContextMenuButton.Enabled = true;
if (_justPastedIn && !string.IsNullOrEmpty(_storedProjectId))
{
var project = client.Project.Get(_storedProjectId!).Result;
OnProjectSelected(project, false);
}
if (_project == null)
{
ModelContextMenuButton.Enabled = false;
VersionContextMenuButton.Enabled = false;
return;
}
LastFetchedModels = client.Project.GetWithModels(_project.id, 10).Result.models;
ModelContextMenuButton.Enabled = true;
if (_justPastedIn && !string.IsNullOrEmpty(_storedModelId))
{
var model = client.Model.Get(_storedModelId!, _project.id).Result;
OnModelSelected(model, false);
}
if (_model == null)
{
VersionContextMenuButton.Enabled = false;
return;
}
LastFetchedVersions = client.Model.GetWithVersions(_model.id, _project.id, 10).Result.versions;
VersionContextMenuButton.Enabled = true;
if (_justPastedIn && !string.IsNullOrEmpty(_storedVersionId))
{
var version = client.Version.Get(_storedVersionId!, _project.id).Result;
OnVersionSelected(version);
}
if (_version == null)
{
// If no version selected, output `latest` resource
da.SetData(0, new SpeckleUrlLatestModelVersionResource(_account.serverInfo.url, _project.id, _model.id));
return;
}
// If all data points are selected, output specific version.
da.SetData(0, new SpeckleUrlModelVersionResource(_account.serverInfo.url, _project.id, _model.id, _version.id));
}
protected override void AfterSolveInstance()
{
// If the component runs once till the end, then it's no longer "just pasted in".
_justPastedIn = false;
base.AfterSolveInstance();
}
private void SetComponentButtonsState(bool enabled)
{
ProjectContextMenuButton.Enabled = enabled;
ModelContextMenuButton.Enabled = enabled;
VersionContextMenuButton.Enabled = enabled;
}
private SpeckleUrlModelResource SolveInstanceWithUrlInput(string urlInput)
{
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
// Should perform validation, fill in all internal data of the component (project, model, version, account)
// Should notify user if any of this goes wrong.
var resources = SpeckleResourceBuilder.FromUrlString(urlInput);
if (resources.Length == 0)
{
throw new SpeckleException($"Input url string was empty");
}
if (resources.Length > 1)
{
throw new SpeckleException($"Input multi-model url is not supported");
}
var resource = resources.First();
var account = _accountService.GetAccountWithServerUrlFallback(string.Empty, new Uri(resource.Server));
OnAccountSelected(account, false);
if (_account == null)
{
throw new SpeckleException("No account found for server URL");
}
Client client = _clientFactory.Create(_account);
var project = client.Project.Get(resource.ProjectId).Result;
OnProjectSelected(project, false);
switch (resource)
{
case SpeckleUrlLatestModelVersionResource latestVersionResource:
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
OnModelSelected(model, false);
break;
case SpeckleUrlModelVersionResource versionResource:
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
OnModelSelected(m, false);
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
OnVersionSelected(v, false);
break;
case SpeckleUrlModelObjectResource:
throw new SpeckleException("Object URLs are not supported");
default:
throw new SpeckleException("Unknown Speckle resource type");
}
return resource;
}
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
{
base.AppendAdditionalMenuItems(menu);
var accountsMenu = Menu_AppendItem(menu, "Account");
foreach (var account in _accountManager.GetAccounts())
{
Menu_AppendItem(
accountsMenu.DropDown,
account.ToString(),
(_, _) => OnAccountSelected(account),
null,
_account?.id != account.id,
_account?.id == account.id
);
}
}
public override bool Write(GH_IWriter writer)
{
var baseRes = base.Write(writer);
writer.SetString("Server", _account?.serverInfo.url);
writer.SetString("User", _account?.id);
writer.SetString("Project", _project?.id);
writer.SetString("Model", _model?.id);
writer.SetString("Version", _version?.id);
return baseRes;
}
public override bool Read(GH_IReader reader)
{
var readRes = base.Read(reader);
reader.TryGetString("Server", ref _storedServer);
reader.TryGetString("User", ref _storedUserId);
reader.TryGetString("Project", ref _storedProjectId);
reader.TryGetString("Model", ref _storedModelId);
reader.TryGetString("Version", ref _storedVersionId);
_justPastedIn = true;
return readRes;
}
public override void ExpirePreview(bool redraw)
{
ProjectContextMenuButton.ExpirePreview(redraw);
ModelContextMenuButton.ExpirePreview(redraw);
VersionContextMenuButton.ExpirePreview(redraw);
base.ExpirePreview(redraw);
}
}
@@ -0,0 +1,97 @@
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
namespace Speckle.Connectors.Grasshopper8.Components.Operations;
public class SpeckleSelectModelComponentAttributes : GH_ComponentAttributes
{
private readonly SpeckleSelectModelComponent _typedOwner;
public SpeckleSelectModelComponentAttributes(IGH_Component component)
: base(component)
{
_typedOwner = (SpeckleSelectModelComponent)component;
}
public override void AppendToAttributeTree(List<IGH_Attributes> attributes)
{
base.AppendToAttributeTree(attributes);
_typedOwner.ProjectContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
_typedOwner.ModelContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
_typedOwner.VersionContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
}
private void InitialiseAttributes()
{
if (_typedOwner.ProjectContextMenuButton.Attributes == null)
{
_typedOwner.ProjectContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
_typedOwner.ProjectContextMenuButton
)
{
Parent = this,
};
}
if (_typedOwner.ModelContextMenuButton.Attributes == null)
{
_typedOwner.ModelContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
_typedOwner.ModelContextMenuButton
)
{
Parent = this,
Pivot = Pivot
};
}
if (_typedOwner.VersionContextMenuButton.Attributes == null)
{
_typedOwner.VersionContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
_typedOwner.VersionContextMenuButton
)
{
Parent = this,
Pivot = Pivot
};
}
}
protected override void Layout()
{
base.Layout();
var baseRec = GH_Convert.ToRectangle(Bounds);
baseRec.Height += 26 * 3;
var btnRec = baseRec;
btnRec.Y = baseRec.Bottom - 26 * 3;
btnRec.Height = 26;
btnRec.Inflate(-2, -2);
var btnRec2 = btnRec;
btnRec2.Y = btnRec.Bottom + 2;
var btnRec3 = btnRec;
btnRec3.Y = btnRec2.Bottom + 2;
Bounds = baseRec;
InitialiseAttributes();
// Both pivot and bounds require updating to proper render buttons on location
_typedOwner.ProjectContextMenuButton.Attributes.Pivot = btnRec.Location;
_typedOwner.ProjectContextMenuButton.Attributes.Bounds = btnRec;
_typedOwner.ModelContextMenuButton.Attributes.Pivot = btnRec2.Location;
_typedOwner.ModelContextMenuButton.Attributes.Bounds = btnRec2;
_typedOwner.VersionContextMenuButton.Attributes.Pivot = btnRec3.Location;
_typedOwner.VersionContextMenuButton.Attributes.Bounds = btnRec3;
}
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
{
base.Render(canvas, graphics, channel);
// Draw custom buttons and dropdowns
_typedOwner.ProjectContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
_typedOwner.ModelContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
_typedOwner.VersionContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
}
}
@@ -0,0 +1,89 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
public class CreateSpeckleObject : GH_Component
{
public CreateSpeckleObject()
: base(
"Create Speckle Object",
"CSO",
"Creates a Speckle Object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.OBJECTS
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("cO");
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Geometry", "G", "The geometry of the new Speckle Object", GH_ParamAccess.item);
pManager.AddTextParameter("Name", "N", "Name of the new Speckle Object", GH_ParamAccess.item);
Params.Input[1].Optional = true;
pManager.AddParameter(
new SpecklePropertyGroupParam(),
"Properties",
"P",
"The properties of the new Speckle Object",
GH_ParamAccess.item
);
Params.Input[2].Optional = true;
// TODO: add render material and color
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Speckle Object", "SO", "The created Speckle Object", GH_ParamAccess.item);
}
protected override void SolveInstance(IGH_DataAccess da)
{
object gooGeometry = new();
da.GetData(0, ref gooGeometry);
GeometryBase geometry = ((IGH_GeometricGoo)gooGeometry).GeometricGooToGeometryBase();
string name = "";
da.GetData(1, ref name);
SpecklePropertyGroupGoo properties = new();
da.GetData(2, ref properties);
// convert the properties
Dictionary<string, object?> props = new();
properties.CastTo(ref props);
// convert the geometries
Base converted = ToSpeckleConversionContext.ToSpeckleConverter.Convert(geometry);
Objects.Data.DataObject grasshopperObject =
new()
{
name = name,
displayValue = new() { converted },
properties = props
};
SpeckleObjectWrapper so =
new()
{
Base = grasshopperObject,
GeometryBase = geometry,
Properties = properties,
Name = name
};
da.SetData(0, new SpeckleObjectWrapperGoo(so));
}
}
@@ -0,0 +1,208 @@
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Parameters;
using Speckle.Connectors.Grasshopper8.Parameters;
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
public class FilterPropertiesByPropertyGroupPaths : GH_Component, IGH_VariableParameterComponent
{
/// <summary>
/// Gets the unique ID for this component. Do not change this ID after release.
/// </summary>
public override Guid ComponentGuid => new Guid("BF517D60-B853-4C61-9574-AD8A718B995B");
public FilterPropertiesByPropertyGroupPaths()
: base(
"FilterPropertiesByPropertyGroupPaths",
"pgF",
"Filters object properties by their property group path",
"Speckle",
"Properties"
) { }
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddParameter(
new SpeckleObjectParam(),
"Objects",
"O",
"Speckle Objects to filter properties from",
GH_ParamAccess.list
);
pManager.AddTextParameter("Paths", "P", "Property Group paths to filter by", GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
// pManager.AddParameter( new SpecklePropertyParam(), "Properties", "P", "The properties of the selected Object", GH_ParamAccess.tree );
}
protected override void SolveInstance(IGH_DataAccess da)
{
List<string> paths = new();
da.GetDataList(1, paths);
if (paths.Count == 0)
{
return;
}
List<SpeckleObjectWrapperGoo> objectWrapperGoos = new();
da.GetDataList(0, objectWrapperGoos);
if (objectWrapperGoos.Count == 0)
{
return;
}
// we're creating an output param for every property path selected
// we're creating a branch in the output tree for every object for that property
List<OutputParamWrapper> outputParams = new();
foreach (string path in paths)
{
// create the output for this path
DataTree<object?> paramResult = new();
Param_GenericObject param =
new()
{
Name = path,
NickName = path,
Access = GH_ParamAccess.tree
};
// get the branch and property value for each input object
for (int i = 0; i < objectWrapperGoos.Count; i++)
{
// create the result branch for this object
SpeckleObjectWrapperGoo objectGoo = objectWrapperGoos[i];
GH_Path objectPath = new GH_Path(i);
SpecklePropertyGroupGoo properties = objectGoo.Value.Properties;
if (properties.Value.Count == 0)
{
paramResult.Add(null, objectPath);
continue;
}
SpecklePropertyGoo objectProperty = FindProperty(properties, path);
paramResult.Add(string.IsNullOrEmpty((string)objectProperty.Value) ? null : objectProperty.Value, objectPath);
}
outputParams.Add(new OutputParamWrapper(param, paramResult));
}
if (da.Iteration == 0 && OutputMismatch(outputParams))
{
OnPingDocument()
.ScheduleSolution(
5,
_ =>
{
CreateOutputs(outputParams);
}
);
}
else
{
for (int i = 0; i < outputParams.Count; i++)
{
var outParam = Params.Output[i];
var outParamWrapper = outputParams[i];
switch (outParam.Access)
{
case GH_ParamAccess.item:
da.SetData(i, outParamWrapper.Values);
break;
case GH_ParamAccess.tree:
da.SetDataTree(i, (DataTree<object?>)outParamWrapper.Values);
break;
}
}
}
}
private SpecklePropertyGoo FindProperty(SpecklePropertyGroupGoo root, string unifiedPath)
{
if (!root.Value.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
{
return new() { Path = unifiedPath, Value = "" };
}
return currentGoo;
}
private bool OutputMismatch(List<OutputParamWrapper> 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.Param.NickName
|| oldParam.Name != newParam.Param.Name
|| oldParam.Access != newParam.Param.Access
)
{
return true;
}
count++;
}
return false;
}
private void CreateOutputs(List<OutputParamWrapper> outputParams)
{
// TODO: better, nicer handling of creation/removal
while (Params.Output.Count > 0)
{
Params.UnregisterOutputParameter(Params.Output[^1]);
}
foreach (var newParam in outputParams)
{
var param = new Param_GenericObject
{
Name = newParam.Param.Name,
NickName = newParam.Param.NickName,
MutableNickName = false,
Access = newParam.Param.Access
};
Params.RegisterOutputParam(param);
}
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() { }
}
public record OutputParamWrapper(Param_GenericObject Param, object Values);
@@ -0,0 +1,61 @@
using Grasshopper.Kernel.Types;
using Grasshopper.Rhinoceros.Model;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
using Speckle.Connectors.Grasshopper8.Parameters;
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
{
public PropertyGroupPathsSelector()
: base(
"Property Group Paths Selector",
"Paths",
"Allows you to select a set of property group paths for filtering",
"Speckle",
"Properties"
) { }
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
protected override void LoadVolatileData()
{
var objectPropertyGroups = VolatileData
.AllData(true)
.OfType<SpeckleObjectWrapperGoo>()
.Select(goo => goo.Value.Properties.Value)
.ToList();
// support model objects direct piping also
if (objectPropertyGroups.Count != VolatileData.DataCount)
{
var modelObjects = VolatileData
.AllData(true)
.OfType<ModelObject>()
.Select(mo => new SpeckleObjectWrapperGoo(mo).Value.Properties.Value)
.ToList();
objectPropertyGroups.AddRange(modelObjects);
}
if (objectPropertyGroups.Count == 0)
{
return;
}
var paths = GetPropertyPaths(objectPropertyGroups);
m_data.AppendRange(paths.Select(s => new GH_String(s)));
}
private static List<string> GetPropertyPaths(List<Dictionary<string, SpecklePropertyGoo>> objectPropertyGroups)
{
var result = new HashSet<string>();
foreach (var dict in objectPropertyGroups)
{
result.AddRange(
dict.Keys.Where(k => !(k.EndsWith(".name") || k.EndsWith(".units") || k.EndsWith(".internalDefinitionName")))
);
}
return result.ToList();
}
}
@@ -0,0 +1,113 @@
using System.Drawing.Drawing2D;
namespace Speckle.Connectors.Grasshopper8.HostApp;
public static class BitmapBuilder
{
public static Bitmap CreateSquareIconBitmap(string text, int width = 24, int height = 24)
{
Bitmap bitmap = new(width, height);
using Graphics graphics = Graphics.FromImage(bitmap);
// Enable high-quality rendering
graphics.SmoothingMode = SmoothingMode.AntiAlias;
// Set background to transparent
graphics.Clear(Color.Transparent);
// Rectangle with a 1px offset
Rectangle squareRect = new(1, 1, width - 2, height - 2);
using (Brush blueBrush = new SolidBrush(Color.Blue))
{
graphics.FillRectangle(blueBrush, squareRect);
}
// Draw white letters in the center
using (Font font = new("Arial", 8, FontStyle.Bold, GraphicsUnit.Pixel))
using (Brush whiteBrush = new SolidBrush(Color.White))
{
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.DrawString(text, font, whiteBrush, new RectangleF(1, 1, width - 2, height - 2), format);
}
return bitmap;
}
public static Bitmap CreateCircleIconBitmap(string text, int width = 24, int height = 24)
{
Bitmap bitmap = new(width, height);
using Graphics graphics = Graphics.FromImage(bitmap);
// Enable high-quality rendering
graphics.SmoothingMode = SmoothingMode.AntiAlias;
// Set background to transparent
graphics.Clear(Color.Transparent);
// Rectangle with a 1px offset
Rectangle squareRect = new(1, 1, width - 2, height - 2);
using (Brush blueBrush = new SolidBrush(Color.Blue))
{
graphics.FillEllipse(blueBrush, squareRect);
}
// Draw white letters in the center
using (Font font = new("Arial", 8, FontStyle.Bold, GraphicsUnit.Pixel))
using (Brush whiteBrush = new SolidBrush(Color.White))
{
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.DrawString(text, font, whiteBrush, new RectangleF(1, 1, width - 2, height - 2), format);
}
return bitmap;
}
public static Bitmap CreateHexagonalBitmap(string text, int width = 24, int height = 24)
{
Bitmap bitmap = new(width, height);
using Graphics graphics = Graphics.FromImage(bitmap);
// Enable high-quality rendering
graphics.SmoothingMode = SmoothingMode.AntiAlias;
// Set background to transparent
graphics.Clear(Color.Transparent);
// Calculate hexagon points centered within the bitmap
float side = (width - 2) / 2.236f; // 2.236f approximates 4 / √3 for regular hex dimensions
float h = side * (float)Math.Sqrt(3) / 2;
float centerX = width / 2f;
float centerY = height / 2f;
Point[] hexagonPoints =
[
new((int)(centerX - side / 2), (int)(centerY - h)),
new((int)(centerX + side / 2), (int)(centerY - h)),
new((int)(centerX + side), (int)centerY),
new((int)(centerX + side / 2), (int)(centerY + h)),
new((int)(centerX - side / 2), (int)(centerY + h)),
new((int)(centerX - side), (int)centerY)
];
using (Brush blueBrush = new SolidBrush(Color.Blue))
{
graphics.FillPolygon(blueBrush, hexagonPoints);
}
// Draw white letters in the center
using Font font = new("Monospace", 10, FontStyle.Bold, GraphicsUnit.Pixel);
using Brush whiteBrush = new SolidBrush(Color.White);
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.DrawString(text, font, whiteBrush, new RectangleF(0, 1, width, height), format);
return bitmap;
}
}
@@ -0,0 +1,136 @@
using System.Windows.Threading;
namespace Speckle.Connectors.Grasshopper8.HostApp.Extras;
/// <summary>
/// Provides Debounce() and Throttle() methods.
/// Use these methods to ensure that events aren't handled too frequently.
///
/// Throttle() ensures that events are throttled by the interval specified.
/// Only the last event in the interval sequence of events fires.
///
/// Debounce() fires an event only after the specified interval has passed
/// in which no other pending event has fired. Only the last event in the
/// sequence is fired.
/// </summary>
public class DebounceDispatcher
{
private DispatcherTimer? _timer;
private DateTime TimerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
/// <summary>
/// Debounce an event by resetting the event timeout every time the event is
/// fired. The behavior is that the Action passed is fired only after events
/// stop firing for the given timeout period.
///
/// Use Debounce when you want events to fire only after events stop firing
/// after the given interval timeout period.
///
/// Wrap the logic you would normally use in your event code into
/// the Action you pass to this method to debounce the event.
/// Example: https://gist.github.com/RickStrahl/0519b678f3294e27891f4d4f0608519a
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
public void Debounce(
int interval,
Action<object?> action,
object? param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher? disp = null
)
{
// kill pending timer and pending ticks
_timer?.Stop();
_timer = null;
if (disp == null)
{
disp = Dispatcher.CurrentDispatcher;
}
// timer is recreated for each event and effectively
// resets the timeout. Action only fires after timeout has fully
// elapsed without other events firing in between
_timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(interval),
priority,
(s, e) =>
{
if (_timer == null)
{
return;
}
_timer?.Stop();
_timer = null;
action.Invoke(param);
},
disp
);
_timer.Start();
}
/// <summary>
/// This method throttles events by allowing only 1 event to fire for the given
/// timeout period. Only the last event fired is handled - all others are ignored.
/// Throttle will fire events every timeout ms even if additional events are pending.
///
/// Use Throttle where you need to ensure that events fire at given intervals.
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
public void Throttle(
int interval,
Action<object?> action,
object? param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher? disp = null
)
{
// kill pending timer and pending ticks
_timer?.Stop();
_timer = null;
if (disp == null)
{
disp = Dispatcher.CurrentDispatcher;
}
var curTime = DateTime.UtcNow;
// if timeout is not up yet - adjust timeout to fire
// with potentially new Action parameters
if (curTime.Subtract(TimerStarted).TotalMilliseconds < interval)
{
interval -= (int)curTime.Subtract(TimerStarted).TotalMilliseconds;
}
_timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(interval),
priority,
(s, e) =>
{
if (_timer == null)
{
return;
}
_timer?.Stop();
_timer = null;
action.Invoke(param);
},
disp
);
_timer.Start();
TimerStarted = curTime;
}
}
@@ -0,0 +1,7 @@
namespace Speckle.Connectors.Grasshopper8.HostApp;
public static class Constants
{
public const string LAYER_PATH_DELIMITER = "::";
public const string PROPERTY_PATH_DELIMITER = ".";
}
@@ -0,0 +1,157 @@
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;
using Rhino;
using Rhino.Geometry;
using Speckle.DoubleNumerics;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Common.Exceptions;
namespace Speckle.Connectors.Grasshopper8.HostApp;
public static class GrasshopperHelpers
{
public static string ToSpeckleString(this UnitSystem unitSystem)
{
switch (unitSystem)
{
case UnitSystem.None:
return Units.Meters;
case UnitSystem.Millimeters:
return Units.Millimeters;
case UnitSystem.Centimeters:
return Units.Centimeters;
case UnitSystem.Meters:
return Units.Meters;
case UnitSystem.Kilometers:
return Units.Kilometers;
case UnitSystem.Inches:
return Units.Inches;
case UnitSystem.Feet:
return Units.Feet;
case UnitSystem.Yards:
return Units.Yards;
case UnitSystem.Miles:
return Units.Miles;
case UnitSystem.Unset:
return Units.Meters;
default:
throw new UnitNotSupportedException($"The Unit System \"{unitSystem}\" is unsupported.");
}
}
public static Transform MatrixToTransform(Matrix4x4 matrix, string units)
{
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
var conversionFactor = Units.GetConversionFactor(units, currentDoc.ModelUnitSystem.ToSpeckleString());
var t = Transform.Identity;
t.M00 = matrix.M11;
t.M01 = matrix.M12;
t.M02 = matrix.M13;
t.M03 = matrix.M14 * conversionFactor;
t.M10 = matrix.M21;
t.M11 = matrix.M22;
t.M12 = matrix.M23;
t.M13 = matrix.M24 * conversionFactor;
t.M20 = matrix.M31;
t.M21 = matrix.M32;
t.M22 = matrix.M33;
t.M23 = matrix.M34 * conversionFactor;
t.M30 = matrix.M41;
t.M31 = matrix.M42;
t.M32 = matrix.M43;
t.M33 = matrix.M44;
return t;
}
/// <summary>
/// Attempts to cast the goo to a geometry base object.
/// </summary>
/// <param name="geoGeo"></param>
/// <returns></returns>
/// <exception cref="SpeckleException">If it fails to cast</exception>
public static GeometryBase GeometricGooToGeometryBase(this IGH_GeometricGoo geoGeo)
{
var value = geoGeo.GetType().GetProperty("Value")?.GetValue(geoGeo);
switch (value)
{
case GeometryBase gb:
return gb;
case Point3d pt:
return new Rhino.Geometry.Point(pt);
case Line ln:
return new LineCurve(ln);
case Rectangle3d rec:
return rec.ToNurbsCurve();
case Circle c:
return new ArcCurve(c);
case Arc ac:
return new ArcCurve(ac);
case Ellipse el:
return el.ToNurbsCurve();
case Sphere sp:
return sp.ToBrep();
}
throw new SpeckleException("Failed to cast IGH_GeometricGoo to geometry base");
}
/// <summary>
/// Creates a tree based of a string that encodes the grasshopper topology.
/// </summary>
/// <param name="topology"></param>
/// <param name="subset"></param>
/// <returns></returns>
public static DataTree<object> CreateDataTreeFromTopologyAndItems(string topology, System.Collections.IList subset)
{
var tree = new DataTree<object>();
var treeTopo = topology.Split(' ');
int subsetCount = 0;
foreach (var branch in treeTopo)
{
if (!string.IsNullOrEmpty(branch))
{
var branchTopo = branch.Split('-')[0].Split(';');
var branchIndexes = new List<int>();
foreach (var t in branchTopo)
{
branchIndexes.Add(Convert.ToInt32(t));
}
var elCount = Convert.ToInt32(branch.Split('-')[1]);
var myPath = new GH_Path(branchIndexes.ToArray());
for (int i = 0; i < elCount; i++)
{
tree.EnsurePath(myPath).Add(new Grasshopper.Kernel.Types.GH_ObjectWrapper(subset[subsetCount + i]));
}
subsetCount += elCount;
}
}
return tree;
}
/// <summary>
/// Encodes a tree topology into an exhaustive string which can be used to recreate it using
/// <see cref="CreateDataTreeFromTopologyAndItems"/>.
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public static string GetParamTopology(IGH_Param param)
{
string topology = "";
foreach (GH_Path myPath in param.VolatileData.Paths)
{
topology += myPath.ToString(false) + "-" + param.VolatileData.get_Branch(myPath).Count + " ";
}
return topology;
}
}
@@ -0,0 +1,104 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Common;
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Connectors.Grasshopper8.HostApp;
public abstract record SpeckleUrlModelResource(string Server, string ProjectId)
{
public abstract Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default);
public abstract Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default);
}
public record SpeckleUrlLatestModelVersionResource(string Server, string ProjectId, string ModelId)
: SpeckleUrlModelResource(Server, ProjectId)
{
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
{
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
ModelWithVersions model = await client
.Model.GetWithVersions(ModelId, ProjectId, 1, null, null, cancellationToken)
.ConfigureAwait(false);
Version version = model.versions.items[0];
var info = new ReceiveInfo(
client.Account.id,
new Uri(Server),
ProjectId,
project.name,
ModelId,
model.name,
version.id,
version.sourceApplication.NotNull()
);
return info;
}
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
{
// We don't care about the return info, we just want to be sure we have access and everything exists.
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
return new SendInfo(
client.Account.id,
new Uri(Server),
ProjectId,
ModelId,
"Grasshopper8" // TODO: Grab from the right place!
);
}
}
public record SpeckleUrlModelVersionResource(string Server, string ProjectId, string ModelId, string VersionId)
: SpeckleUrlModelResource(Server, ProjectId)
{
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
{
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
Model model = await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
Version version = await client.Version.Get(VersionId, ProjectId, cancellationToken).ConfigureAwait(false);
var info = new ReceiveInfo(
client.Account.id,
new Uri(Server),
ProjectId,
project.name,
ModelId,
model.name,
VersionId,
version.sourceApplication.NotNull()
);
return info;
}
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
{
// We don't care about the return info, we just want to be sure we have access and everything exists.
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
return new SendInfo(
client.Account.id,
new Uri(Server),
ProjectId,
ModelId,
"Grasshopper8" // TODO: Grab from the right place!
);
}
}
public record SpeckleUrlModelObjectResource(string Server, string ProjectId, string ObjectId)
: SpeckleUrlModelResource(Server, ProjectId)
{
public override Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default) =>
throw new NotImplementedException("Object Resources are not supported yet");
public override Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default) =>
throw new NotImplementedException("Object Resources are not supported yet");
}
@@ -0,0 +1,82 @@
using System.Text.RegularExpressions;
using Speckle.Sdk;
namespace Speckle.Connectors.Grasshopper8.HostApp;
public record SpeckleResourceBuilder
{
/// <summary>
/// The ReGex pattern to determine if a URL's AbsolutePath is a Frontend2 URL or not.
/// </summary>
private static readonly Regex s_fe2UrlRegex =
new(
@"/projects/(?<projectId>[\w\d]+)(?:/models/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?"
);
public static SpeckleUrlModelResource[] FromUrlString(string speckleModel)
{
var uri = new Uri(speckleModel);
var serverUrl = uri.GetLeftPart(UriPartial.Authority);
var match = s_fe2UrlRegex.Match(speckleModel);
var result = ParseFe2RegexMatch(serverUrl, match);
return result;
}
private static SpeckleUrlModelResource[] ParseFe2RegexMatch(string serverUrl, Match match)
{
var projectId = match.Groups["projectId"];
var model = match.Groups["model"];
var additionalModels = match.Groups["additionalModels"];
if (!projectId.Success)
{
throw new SpeckleException("The provided url is not a valid Speckle url");
}
if (!model.Success)
{
throw new SpeckleException("The provided url is not pointing to any model in the project.");
}
if (model.Value == "all")
{
throw new NotSupportedException("Fetching all models is not supported.");
}
if (model.Value.StartsWith("$"))
{
throw new NotSupportedException("Federation model urls are not supported");
}
var modelRes = GetUrlModelResource(serverUrl, projectId.Value, model.Value);
var result = new List<SpeckleUrlModelResource> { modelRes };
if (additionalModels.Success)
{
foreach (Capture additionalModelsCapture in additionalModels.Captures)
{
var extraModel = GetUrlModelResource(serverUrl, projectId.Value, additionalModelsCapture.Value);
result.Add(extraModel);
}
}
return result.ToArray();
}
private static SpeckleUrlModelResource GetUrlModelResource(string serverUrl, string projectId, string modelValue)
{
if (modelValue.Length == 32)
{
return new SpeckleUrlModelObjectResource(serverUrl, projectId, modelValue); // Model value is an ObjectID
}
if (!modelValue.Contains('@'))
{
return new SpeckleUrlLatestModelVersionResource(serverUrl, projectId, modelValue); // Model has no version attached
}
var res = modelValue.Split('@');
return new SpeckleUrlModelVersionResource(serverUrl, projectId, res[0], res[1]);
}
}
@@ -0,0 +1,39 @@
using Microsoft.Extensions.DependencyInjection;
using Rhino;
using Speckle.Connectors.Grasshopper8.Registration;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
namespace Speckle.Connectors.Grasshopper8.HostApp;
/// <summary>
/// Handles grasshopper wide converters. We don't need new converters, unless the document changes - this class should handle this (untested).
/// </summary>
public static class ToSpeckleConversionContext
{
private static IServiceScope? Scope { get; set; }
public static IRootToHostConverter ToHostConverter { get; private set; }
public static IRootToSpeckleConverter ToSpeckleConverter { get; private set; }
static ToSpeckleConversionContext()
{
RhinoDoc.ActiveDocumentChanged += RhinoDocOnActiveDocumentChanged;
InitializeConverters();
}
private static void RhinoDocOnActiveDocumentChanged(object sender, DocumentEventArgs e) => InitializeConverters(); // note: untested, and wrong on mac
private static void InitializeConverters()
{
Scope?.Dispose();
Scope = PriorityLoader.Container.CreateScope();
var rhinoConversionSettingsFactory = Scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
Scope
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
ToHostConverter = Scope.ServiceProvider.GetService<IRootToHostConverter>();
ToSpeckleConverter = Scope.ServiceProvider.GetService<IRootToSpeckleConverter>();
}
}
@@ -0,0 +1,189 @@
using Rhino.Geometry;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Conversion;
using Speckle.Connectors.Common.Extensions;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Converters.Common;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Speckle.Sdk.Models.Instances;
namespace Speckle.Connectors.Grasshopper8.Operations.Receive;
public sealed class GrasshopperReceiveConversionResult : ReceiveConversionResult
{
public object? Result { get; set; }
public Base Source { get; set; }
public GrasshopperReceiveConversionResult(
Status status,
Base source,
object? result,
string? resultId = null,
string? resultType = null,
Exception? exception = null
)
: base(status, source, resultId, resultType, exception)
{
Result = result;
Source = source;
}
}
public class GrasshopperHostObjectBuilder : IHostObjectBuilder
{
private readonly IRootToHostConverter _converter;
private readonly IConverterSettingsStore<RhinoConversionSettings> _converterSettings;
private readonly TraversalContextUnpacker _contextUnpacker;
private readonly RootObjectUnpacker _rootObjectUnpacker;
private readonly ISdkActivityFactory _activityFactory;
public GrasshopperHostObjectBuilder(
IRootToHostConverter converter,
IConverterSettingsStore<RhinoConversionSettings> converterSettings,
RootObjectUnpacker rootObjectUnpacker,
ISdkActivityFactory activityFactory,
TraversalContextUnpacker contextUnpacker
)
{
_converter = converter;
_converterSettings = converterSettings;
_rootObjectUnpacker = rootObjectUnpacker;
_activityFactory = activityFactory;
_contextUnpacker = contextUnpacker;
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async Task<HostObjectBuilderResult> Build(
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
Base rootObject,
string projectName,
string modelName,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var activity = _activityFactory.Start("Build");
// POC: This is where the top level base-layer name is set. Could be abstracted or injected in the context?
var baseLayerName = $"Project {projectName}: Model {modelName}";
// 1 - Unpack objects and proxies from root commit object
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
// 2 - Split atomic objects and instance components with their path
var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances(
unpackedRoot.ObjectsToConvert
);
var atomicObjectsWithPath = _contextUnpacker.GetAtomicObjectsWithPath(atomicObjects);
var instanceComponentsWithPath = _contextUnpacker.GetInstanceComponentsWithPath(instanceComponents);
// 2.1 - these are not captured by traversal, so we need to re-add them here
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
{
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
(Array.Empty<Collection>(), proxy as IInstanceComponent)
);
instanceComponentsWithPath.AddRange(transformed);
}
// 3 - Bake materials and colors, as they are used later down the line by layers and objects
onOperationProgressed.Report(new("Converting materials and colors", null));
if (unpackedRoot.RenderMaterialProxies != null)
{
using var _ = _activityFactory.Start("Render Materials");
//_materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseLayerName);
}
if (unpackedRoot.ColorProxies != null)
{
//_colorBaker.ParseColors(unpackedRoot.ColorProxies);
}
// 5 - Convert atomic objects
List<ReceiveConversionResult> conversionResults = new();
Dictionary<string, List<string>> applicationIdMap = new(); // This map is used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking
int count = 0;
using (var _ = _activityFactory.Start("Converting objects"))
{
foreach (var (path, obj) in atomicObjectsWithPath)
{
using (var convertActivity = _activityFactory.Start("Converting object"))
{
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
try
{
// 0: get pre-created layer from cache in layer baker
// int layerIndex = _layerBaker.GetLayerIndex(path, baseLayerName);
// 1: create object attributes for baking
string name = obj["name"] as string ?? "";
// 2: convert
var result = _converter.Convert(obj);
// 3: bake
if (result is GeometryBase geometryBase)
{
//var guid = BakeObject(geometryBase, obj, atts);
}
else if (result is List<GeometryBase> geometryBases) // one to many raw encoding case
{
foreach (var gb in geometryBases)
{
//var guid = BakeObject(gb, obj, atts);
}
}
else if (result is IEnumerable<(object, Base)> fallbackConversionResult) // one to many fallback conversion
{
//var guids = BakeObjectsAsFallbackGroup(fallbackConversionResult, obj, atts, baseLayerName);
}
// 4: log
conversionResults.Add(
new GrasshopperReceiveConversionResult(Status.SUCCESS, obj, result, null, result.GetType().ToString())
);
// applicationIdMap[obj.applicationId ?? obj.id] = conversionIds;
convertActivity?.SetStatus(SdkActivityStatusCode.Ok);
}
catch (Exception ex) when (!ex.IsFatal())
{
// TODO: No conversion report yet
conversionResults.Add(new GrasshopperReceiveConversionResult(Status.ERROR, obj, null, null, null, ex));
convertActivity?.SetStatus(SdkActivityStatusCode.Error);
convertActivity?.RecordException(ex);
}
}
}
}
// 6 - Convert instances
using (var _ = _activityFactory.Start("Converting instances"))
{
// TODO: No instances yet
// var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker
// .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerName, onOperationProgressed)
// .ConfigureAwait(false);
// TODO: No conversion report yet
// conversionResults.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); // remove all conversion results for atomic objects that have been consumed (POC: not that cool, but prevents problems on object highlighting)
// conversionResults.AddRange(instanceConversionResults); // add instance conversion results to our list
}
// 7 - Create groups
if (unpackedRoot.GroupProxies is not null)
{
// TODO: No groups yet
// _groupBaker.BakeGroups(unpackedRoot.GroupProxies, applicationIdMap, baseLayerName);
}
return new HostObjectBuilderResult([], conversionResults);
}
}
@@ -0,0 +1,100 @@
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Logging;
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Transports;
namespace Speckle.Connectors.Grasshopper8.Operations.Receive;
public class GrasshopperReceiveOperation
{
private readonly AccountService _accountService;
private readonly IServerTransportFactory _serverTransportFactory;
private readonly IProgressDisplayManager _progressDisplayManager;
private readonly ISdkActivityFactory _activityFactory;
private readonly IOperations _operations;
private readonly IClientFactory _clientFactory;
public GrasshopperReceiveOperation(
AccountService accountService,
IServerTransportFactory serverTransportFactory,
IProgressDisplayManager progressDisplayManager,
ISdkActivityFactory activityFactory,
IOperations operations,
IClientFactory clientFactory
)
{
_accountService = accountService;
_serverTransportFactory = serverTransportFactory;
_progressDisplayManager = progressDisplayManager;
_activityFactory = activityFactory;
_operations = operations;
_clientFactory = clientFactory;
}
public async Task<Base> ReceiveCommitObject(
ReceiveInfo receiveInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken cancellationToken
)
{
using var execute = _activityFactory.Start("Receive Operation");
execute?.SetTag("receiveInfo", receiveInfo);
// 2 - Check account exist
Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl);
using Client apiClient = _clientFactory.Create(account);
using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail());
var version = await apiClient
.Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken)
.ConfigureAwait(false);
using var transport = _serverTransportFactory.Create(account, receiveInfo.ProjectId);
double? previousPercentage = null;
_progressDisplayManager.Begin();
Base commitObject = await _operations
.Receive2(
new Uri(account.serverInfo.url),
receiveInfo.ProjectId,
version.referencedObject,
account.token,
onProgressAction: new PassthroughProgress(args =>
{
if (args.ProgressEvent == ProgressEvent.CacheCheck || args.ProgressEvent == ProgressEvent.DownloadBytes)
{
switch (args.ProgressEvent)
{
case ProgressEvent.CacheCheck:
previousPercentage = _progressDisplayManager.CalculatePercentage(args);
break;
}
}
if (!_progressDisplayManager.ShouldUpdate())
{
return;
}
switch (args.ProgressEvent)
{
case ProgressEvent.CacheCheck:
case ProgressEvent.DownloadBytes:
onOperationProgressed.Report(new("Checking and Downloading... ", previousPercentage));
break;
case ProgressEvent.DeserializeObject:
onOperationProgressed.Report(new("Deserializing ...", _progressDisplayManager.CalculatePercentage(args)));
break;
}
}),
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
await apiClient
.Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken)
.ConfigureAwait(false);
return commitObject;
}
}
@@ -0,0 +1,69 @@
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Sdk.Models.Collections;
namespace Speckle.Connectors.Grasshopper8.Operations.Send;
public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollectionWrapperGoo>
{
public Task<RootObjectBuilderResult> Build(
IReadOnlyList<SpeckleCollectionWrapperGoo> input,
SendInfo sendInfo,
IProgress<CardProgress> onOperationProgressed,
CancellationToken ct = default
)
{
// TODO: Send info is used in other connectors to get the project ID to populate the SendConversionCache
Console.WriteLine($"Send Info {sendInfo}");
// set the input collection name to "Grasshopper Model"
var rootCollection = new Collection { name = "Grasshopper model", elements = input[0].Value.Collection.elements };
// reconstruct the input collection by substituting all of the objectgoos with base
var collection = ReplaceAndRebuild(rootCollection);
// TODO: Not getting any conversion results yet
var result = new RootObjectBuilderResult(collection, []);
return Task.FromResult(result);
}
/// <summary>
/// Unwraps collection wrappers and object wrapppers.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
private Collection ReplaceAndRebuild(Collection c)
{
// Iterate over the current collection's elements
var myCollection = new Collection() { name = c.name };
if (c["topology"] is string topology)
{
myCollection["topology"] = topology;
}
for (int i = 0; i < c.elements.Count; i++)
{
var element = c.elements[i];
if (element is SpeckleCollectionWrapper collectionWrapper)
{
var newCollection = new Collection
{
name = collectionWrapper.Collection.name,
["topology"] = collectionWrapper.Topology,
elements = collectionWrapper.Collection.elements
};
var unwrapped = ReplaceAndRebuild(newCollection);
myCollection.elements.Add(unwrapped);
}
else if (element is SpeckleObjectWrapper so)
{
// If it's not a Collection, replace the non-Collection element
myCollection.elements.Add(so.Base);
}
}
return myCollection;
}
}
@@ -0,0 +1,3 @@
namespace Speckle.Connectors.Grasshopper8.Parameters;
internal interface ISpeckleGoo { }
@@ -0,0 +1,245 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Grasshopper.Rhinoceros;
using Grasshopper.Rhinoceros.Model;
using Rhino;
using Rhino.DocObjects;
using Speckle.Connectors.Grasshopper8.Components;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;
using Layer = Rhino.DocObjects.Layer;
namespace Speckle.Connectors.Grasshopper8.Parameters;
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
public class SpeckleCollectionWrapper : Base
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
{
// the original collection
public Collection Collection { get; set; }
// the list of layer names that build up the path to this collection, including this collection name
public ObservableCollection<string> Path { get; set; }
public string Topology { get; set; }
public int? Color { get; set; }
public override string ToString() => $"{Collection.name} [{Collection.elements.Count}]";
public SpeckleCollectionWrapper(Collection value, List<string> path, int? color)
{
Collection = value;
Path = new ObservableCollection<string>(path);
Color = color;
// add listener on path changing.
// this can be triggered by a create collection node, that changes the path of this collection.
// when this happens, we want to update the paths of all elements downstream
Path.CollectionChanged += OnPathChanged;
}
private void OnPathChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var newPath = e.NewItems.Cast<string>().ToList();
foreach (var element in Collection.elements)
{
if (element is SpeckleObjectWrapper o)
{
o.Path = newPath;
}
else if (element is SpeckleCollectionWrapper c)
{
c.Path = new ObservableCollection<string>(newPath);
}
}
}
public void Bake(
RhinoDoc doc,
List<Guid> obj_ids,
List<string> path,
bool bakeObjects,
List<Sdk.Models.Base>? elements = null
)
{
if (!LayerExists(doc, path, out int currentLayerIndex))
{
currentLayerIndex = CreateLayerByPath(doc, path);
}
// then bake elements in this collection
List<Sdk.Models.Base> e = elements ?? Collection.elements;
foreach (var obj in e)
{
if (obj is SpeckleObjectWrapper so)
{
if (bakeObjects)
{
so.Bake(doc, obj_ids, currentLayerIndex, true);
}
}
else if (obj is SpeckleCollectionWrapper c)
{
path.Add(c.Collection.name);
Bake(doc, obj_ids, path, bakeObjects, c.Collection.elements);
}
}
}
private bool LayerExists(RhinoDoc doc, List<string> path, out int layerIndex)
{
var fullPath = string.Join(Constants.LAYER_PATH_DELIMITER, path);
layerIndex = doc.Layers.FindByFullPath(fullPath, -1);
return layerIndex != -1;
}
private int CreateLayer(RhinoDoc doc, string name, Guid parentId)
{
Layer layer = new() { Name = name, ParentLayerId = parentId };
return doc.Layers.Add(layer);
}
public int CreateLayerByPath(RhinoDoc doc, List<string> path)
{
if (path.Count == 0 || doc == null)
{
return -1;
}
int parentLayerIndex = -1;
List<string> currentfullpath = new();
Guid currentLayerId = Guid.Empty;
foreach (string layerName in path)
{
currentfullpath.Add(layerName);
// Find or create the layer at this level
if (LayerExists(doc, currentfullpath, out int currentLayerIndex))
{
currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
}
else
{
currentLayerIndex = CreateLayer(doc, layerName, currentLayerId);
currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
}
parentLayerIndex = currentLayerIndex;
}
return parentLayerIndex;
}
}
public class SpeckleCollectionWrapperGoo : GH_Goo<SpeckleCollectionWrapper>, ISpeckleGoo //, IGH_PreviewData // can be made previewable later
{
public override IGH_Goo Duplicate() => throw new NotImplementedException();
public override string ToString() =>
$@"Speckle Collection Goo [{m_value.Collection?.name} ({Value.Collection.elements.Count})]";
public override bool IsValid => true;
public override string TypeName => "Speckle collection wrapper";
public override string TypeDescription => "Speckle collection wrapper";
public override bool CastFrom(object source)
{
switch (source)
{
case SpeckleCollectionWrapper speckleGrasshopperCollection:
Value = speckleGrasshopperCollection;
return true;
case GH_Goo<SpeckleCollectionWrapper> speckleGrasshopperCollectionGoo:
Value = speckleGrasshopperCollectionGoo.Value;
return true;
case ModelLayer modelLayer:
Collection modelCollection = new() { name = modelLayer.Name, elements = new() };
Value = new SpeckleCollectionWrapper(
modelCollection,
GetModelLayerPath(modelLayer),
modelLayer.DisplayColor?.ToArgb()
);
return true;
}
return false;
}
private List<string> GetModelLayerPath(ModelLayer modellayer)
{
ModelContentName currentParent = modellayer.Parent;
ModelContentName stem = modellayer.Parent.Stem;
List<string> path = new() { modellayer.Name };
while (currentParent != stem)
{
path.Add(currentParent);
currentParent = currentParent.Parent;
}
path.Add(stem);
path.Reverse();
return path;
}
public SpeckleCollectionWrapperGoo() { }
public SpeckleCollectionWrapperGoo(SpeckleCollectionWrapper value)
{
Value = value;
}
}
public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH_BakeAwareObject
{
public SpeckleCollectionParam()
: this(GH_ParamAccess.item) { }
public SpeckleCollectionParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpeckleCollectionParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpeckleCollectionParam(GH_ParamAccess access)
: base(
"Speckle Collection",
"SCO",
"XXXXX",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
public override Guid ComponentGuid => new("6E871D5B-B221-4992-882A-EFE6796F3010");
protected override Bitmap Icon => BitmapBuilder.CreateHexagonalBitmap("C");
bool IGH_BakeAwareObject.IsBakeCapable => // False if no data
!VolatileData.IsEmpty;
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, List<Guid> obj_ids)
{
// Iterate over all data stored in the parameter
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleCollectionWrapperGoo goo)
{
goo.Value.Bake(doc, obj_ids, goo.Value.Path.ToList(), true, goo.Value.Collection.elements);
}
}
}
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> obj_ids)
{
// Iterate over all data stored in the parameter
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleCollectionWrapperGoo goo)
{
goo.Value.Bake(doc, obj_ids, goo.Value.Path.ToList(), true, goo.Value.Collection.elements);
}
}
}
}
@@ -0,0 +1,317 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Grasshopper.Rhinoceros.Model;
using Rhino;
using Rhino.Display;
using Rhino.DocObjects;
using Rhino.Geometry;
using Speckle.Connectors.Grasshopper8.Components;
using Speckle.Connectors.Grasshopper8.HostApp;
using Speckle.Sdk.Models;
namespace Speckle.Connectors.Grasshopper8.Parameters;
/// <summary>
/// Wrapper around a geometry base object and its converted speckle equivalent.
/// </summary>
public class SpeckleObjectWrapper : Base
{
public required Base Base { get; set; }
// note: how will we send intervals and other gh native objects? do we? maybe not for now
// note: this does not handle on to many relationship well.
// For receiving data objects, we are wrapping every value in the data object display value, and storing a reference to the same data object in each wrapped object.
public required GeometryBase GeometryBase { get; set; }
// The list of layer/collection names that forms the full path to this object
public List<string> Path { get; set; } = new();
public SpeckleCollectionWrapper? Parent { get; set; }
// A dictionary of property path to property
public SpecklePropertyGroupGoo Properties { get; set; } = new();
public string Name { get; set; } = "";
public int? Color { get; set; }
public string? RenderMaterialName { get; set; }
// RenderMaterial, ColorProxies, Properties (?)
public override string ToString() => $"Speckle Wrapper [{GeometryBase.GetType().Name}]";
public void DrawPreview(IGH_PreviewArgs args, bool isSelected = false)
{
switch (GeometryBase)
{
case Mesh m:
args.Display.DrawMeshShaded(m, isSelected ? args.ShadeMaterial_Selected : args.ShadeMaterial);
break;
case Brep b:
args.Display.DrawBrepShaded(b, isSelected ? args.ShadeMaterial_Selected : args.ShadeMaterial);
args.Display.DrawBrepWires(
b,
isSelected ? args.WireColour_Selected : args.WireColour,
args.DefaultCurveThickness
);
break;
case Extrusion e:
args.Display.DrawMeshShaded(
e.GetMesh(MeshType.Any),
isSelected ? args.ShadeMaterial_Selected : args.ShadeMaterial
);
break;
case SubD d:
args.Display.DrawSubDShaded(d, isSelected ? args.ShadeMaterial_Selected : args.ShadeMaterial);
args.Display.DrawSubDWires(
d,
isSelected ? args.WireColour_Selected : args.WireColour,
args.DefaultCurveThickness
);
break;
case Curve c:
args.Display.DrawCurve(c, isSelected ? args.WireColour_Selected : args.WireColour, args.DefaultCurveThickness);
break;
case Rhino.Geometry.Point p:
args.Display.DrawPoint(p.Location, isSelected ? args.WireColour_Selected : args.WireColour);
break;
case PointCloud pc:
args.Display.DrawPointCloud(pc, 1, isSelected ? args.WireColour_Selected : args.WireColour);
break;
case Hatch h:
args.Display.DrawHatch(
h,
isSelected ? args.WireColour_Selected : args.WireColour,
isSelected ? args.WireColour_Selected : args.WireColour
);
break;
}
}
public void DrawPreviewRaw(DisplayPipeline display, DisplayMaterial material)
{
switch (GeometryBase)
{
case Mesh m:
display.DrawMeshShaded(m, material);
break;
case Brep b:
display.DrawBrepShaded(b, material);
display.DrawBrepWires(b, material.Diffuse);
break;
case Extrusion e:
var eBrep = e.ToBrep();
display.DrawBrepShaded(eBrep, material);
display.DrawBrepWires(eBrep, material.Diffuse);
break;
case SubD d:
display.DrawSubDShaded(d, material);
display.DrawSubDWires(d, material.Diffuse, display.DefaultCurveThickness);
break;
case Curve c:
display.DrawCurve(c, material.Diffuse);
break;
case Rhino.Geometry.Point p:
display.DrawPoint(p.Location, material.Diffuse);
break;
case PointCloud pc:
display.DrawPointCloud(pc, 1, material.Diffuse);
break;
case Hatch h:
display.DrawHatch(h, material.Diffuse, material.Diffuse);
break;
}
}
public void Bake(RhinoDoc doc, List<Guid> obj_ids, int bakeLayerIndex = -1, bool layersAlreadyCreated = false)
{
// get or make layers
if (!layersAlreadyCreated && bakeLayerIndex < 0)
{
if (Path.Count > 0 && Parent != null)
{
bakeLayerIndex = Parent.CreateLayerByPath(doc, Path);
}
}
// create attributes
using ObjectAttributes att = new() { Name = Name };
if (Color is int argb)
{
att.ObjectColor = System.Drawing.Color.FromArgb(argb);
att.ColorSource = ObjectColorSource.ColorFromObject;
att.LayerIndex = bakeLayerIndex;
}
foreach (var kvp in Properties.Value)
{
att.SetUserString(kvp.Key, kvp.Value.Value.ToString());
}
// add to doc
Guid guid = doc.Objects.Add(GeometryBase, att);
obj_ids.Add(guid);
}
}
public class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData, ISpeckleGoo
{
public override IGH_Goo Duplicate() => throw new NotImplementedException();
public override string ToString() => $@"Speckle Object Goo [{m_value.Base?.speckle_type}]";
public override bool IsValid => true;
public override string TypeName => "Speckle object wrapper";
public override string TypeDescription => "A wrapper around speckle grasshopper objects.";
BoundingBox IGH_PreviewData.ClippingBox => Value.GeometryBase.GetBoundingBox(false);
public SpeckleObjectWrapperGoo(ModelObject mo)
{
CastFrom(mo);
}
public override bool CastFrom(object source)
{
switch (source)
{
case SpeckleObjectWrapper speckleGrasshopperObject:
Value = speckleGrasshopperObject;
return true;
case GH_Goo<SpeckleObjectWrapper> speckleGrasshopperObjectGoo:
Value = speckleGrasshopperObjectGoo.Value;
return true;
case IGH_GeometricGoo geometricGoo:
var gooGB = geometricGoo.GeometricGooToGeometryBase();
var gooConverted = ToSpeckleConversionContext.ToSpeckleConverter.Convert(gooGB);
Value = new SpeckleObjectWrapper() { GeometryBase = gooGB, Base = gooConverted };
return true;
case ModelObject modelObject:
if (GetGeometryFromModelObject(modelObject) is GeometryBase modelGB)
{
var modelConverted = ToSpeckleConversionContext.ToSpeckleConverter.Convert(modelGB);
SpecklePropertyGroupGoo propertyGroup = new();
propertyGroup.CastFrom(modelObject.UserText);
// update the converted Base with props as well
modelConverted["name"] = modelObject.Name.ToString();
Dictionary<string, object?> propertyDict = new();
foreach (var entry in propertyGroup.Value)
{
propertyDict.Add(entry.Key, entry.Value.Value);
}
modelConverted["properties"] = propertyDict;
SpeckleObjectWrapper so =
new()
{
GeometryBase = modelGB,
Base = modelConverted,
Name = modelObject.Name.ToString(),
Color = modelObject.Display.Color?.Color.ToArgb(),
RenderMaterialName = modelObject.Render.Material?.Material?.Name,
Properties = propertyGroup
};
Value = so;
return true;
}
return false;
}
return false;
}
private GeometryBase? GetGeometryFromModelObject(ModelObject modelObject) =>
RhinoDoc.ActiveDoc.Objects.FindId(modelObject.Id ?? Guid.Empty).Geometry;
public override bool CastTo<T>(ref T target)
{
var type = typeof(T);
if (type == typeof(IGH_GeometricGoo))
{
target = (T)(object)GH_Convert.ToGeometricGoo(Value.GeometryBase);
return true;
}
// TODO: cast to material, etc.
return false;
}
public void DrawViewportWires(GH_PreviewWireArgs args)
{
// TODO ?
}
public void DrawViewportMeshes(GH_PreviewMeshArgs args)
{
Value.DrawPreviewRaw(args.Pipeline, args.Material);
}
public SpeckleObjectWrapperGoo(SpeckleObjectWrapper value)
{
Value = value;
}
public SpeckleObjectWrapperGoo() { }
}
public class SpeckleObjectParam : GH_Param<SpeckleObjectWrapperGoo>, IGH_BakeAwareObject
{
public SpeckleObjectParam()
: this(GH_ParamAccess.item) { }
public SpeckleObjectParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpeckleObjectParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpeckleObjectParam(GH_ParamAccess access)
: base(
"Speckle Object",
"SO",
"Represents a Speckle object",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
public override Guid ComponentGuid => new("22FD5510-D5D3-4101-8727-153FFD329E4F");
protected override Bitmap Icon => BitmapBuilder.CreateHexagonalBitmap("SO");
public bool IsBakeCapable =>
// False if no data
!VolatileData.IsEmpty;
public void BakeGeometry(RhinoDoc doc, List<Guid> obj_ids)
{
// Iterate over all data stored in the parameter
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleObjectWrapperGoo goo)
{
goo.Value.Bake(doc, obj_ids);
}
}
}
public void BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> obj_ids)
{
// Iterate over all data stored in the parameter
foreach (var item in VolatileData.AllData(true))
{
if (item is SpeckleObjectWrapperGoo goo)
{
goo.Value.Bake(doc, obj_ids);
}
}
}
}
@@ -0,0 +1,140 @@
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Grasshopper.Rhinoceros;
using Speckle.Connectors.Grasshopper8.Components;
using Speckle.Connectors.Grasshopper8.HostApp;
namespace Speckle.Connectors.Grasshopper8.Parameters;
/// <summary>
/// The Speckle Property Group Goo is a flat dictionary of (speckle property path, speckle property).
/// The speckle property path is the concatenated string of all original flattened keys with the property delimiter
/// </summary>
public class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, SpecklePropertyGoo>>, ISpeckleGoo
{
public override IGH_Goo Duplicate() => throw new NotImplementedException();
public override string ToString() => $"PropertyGroup ({Value.Count})";
public override bool IsValid => true;
public override string TypeName => "Speckle property group wrapper";
public override string TypeDescription => "Speckle property group wrapper";
public SpecklePropertyGroupGoo()
{
Value = new();
}
public SpecklePropertyGroupGoo(Dictionary<string, SpecklePropertyGoo> value)
{
Value = value;
}
public SpecklePropertyGroupGoo(Dictionary<string, object?> value)
{
CastFrom(value);
}
public override bool CastFrom(object source)
{
switch (source)
{
case SpecklePropertyGroupGoo specklePropertyGroup:
Value = specklePropertyGroup.Value;
return true;
case ModelUserText userText:
Dictionary<string, SpecklePropertyGoo> dictionary = new();
foreach (KeyValuePair<string, string> entry in userText)
{
string key = entry.Key;
SpecklePropertyGoo value = new() { Path = key, Value = entry.Value };
dictionary.Add(key, value);
}
Value = dictionary;
return true;
case Dictionary<string, object?> properties:
Dictionary<string, object> flattenedProperties = new();
FlattenDictionary(properties, flattenedProperties, "");
Dictionary<string, SpecklePropertyGoo> speckleProperties = new();
foreach (var kvp in flattenedProperties)
{
speckleProperties.Add(kvp.Key, new() { Value = kvp.Value });
}
Value = speckleProperties;
return true;
}
return false;
}
public override bool CastTo<T>(ref T target)
{
var type = typeof(T);
if (type == typeof(Dictionary<string, object?>))
{
Dictionary<string, object?> dictionary = new();
foreach (var entry in Value)
{
dictionary.Add(entry.Key, entry.Value);
}
target = (T)(object)dictionary;
return true;
}
// TODO: cast to material, model object, etc.
return false;
}
// Flattens a dictionary that may contain more dictionaries of the same type
private void FlattenDictionary(
Dictionary<string, object?> dict,
Dictionary<string, object> flattenedDict,
string keyPrefix = ""
)
{
foreach (var kvp in dict)
{
string newKey = string.IsNullOrEmpty(keyPrefix)
? kvp.Key
: $"{keyPrefix}{Constants.PROPERTY_PATH_DELIMITER}{kvp.Key}";
if (kvp.Value is Dictionary<string, object?> childDict)
{
FlattenDictionary(childDict, flattenedDict, newKey);
}
else
{
flattenedDict.Add(newKey, kvp.Value ?? "");
}
}
}
}
public class SpecklePropertyGroupParam : GH_Param<SpecklePropertyGroupGoo>
{
public override Guid ComponentGuid => new("AF4757C3-BA33-4ACD-A92B-C80356043129");
protected override Bitmap Icon => BitmapBuilder.CreateHexagonalBitmap("PG");
public SpecklePropertyGroupParam()
: this(GH_ParamAccess.item) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpecklePropertyGroupParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpecklePropertyGroupParam(GH_ParamAccess access)
: base(
"Speckle Property Group",
"SPGO",
"Represents a Dictionary property group",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
}
@@ -0,0 +1,140 @@
using System.Runtime.InteropServices;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Speckle.Connectors.Grasshopper8.Components;
using Speckle.Connectors.Grasshopper8.HostApp;
namespace Speckle.Connectors.Grasshopper8.Parameters;
public class SpecklePropertyGoo : GH_Goo<object>, ISpeckleGoo
{
public override IGH_Goo Duplicate() => throw new NotImplementedException();
public override string ToString() => $"{Path} - {Value}";
public string Path { get; set; }
public override bool IsValid => true;
public override string TypeName => "Speckle property wrapper";
public override string TypeDescription => "Speckle property wrapper";
public SpecklePropertyGoo() { }
public SpecklePropertyGoo(object value)
{
SpecklePropertyGoo goo = new();
if (goo.CastFrom(value))
{
Value = goo;
}
else
{
//TODO: throw
}
}
public override bool CastFrom(object source)
{
switch (source)
{
case SpecklePropertyGoo speckleProperty:
Value = speckleProperty.Value;
Path = speckleProperty.Path;
return true;
case double d:
Value = new SpecklePropertyGoo() { Value = d, Path = string.Empty };
return true;
case int i:
Value = new SpecklePropertyGoo() { Value = i, Path = string.Empty };
return true;
case string s:
Value = new SpecklePropertyGoo() { Value = s, Path = string.Empty };
return true;
case bool b:
Value = new SpecklePropertyGoo() { Value = b, Path = string.Empty };
return true;
case KeyValuePair<string, object?> kvp:
Value = new SpecklePropertyGoo() { Value = kvp.Value ?? "", Path = kvp.Key };
return true;
case KeyValuePair<string, string> kvp:
Value = new SpecklePropertyGoo() { Value = kvp.Value, Path = kvp.Key };
return true;
}
return false;
}
public override bool CastTo<T>(ref T target)
{
var type = typeof(T);
if (
type.IsAssignableFrom(typeof(int))
|| type.IsAssignableFrom(typeof(double))
|| type.IsAssignableFrom(typeof(bool))
|| type.IsAssignableFrom(typeof(string))
)
{
object? ptr = Value;
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Integer)))
{
object ptr = new GH_Integer((int)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Number)))
{
object ptr = new GH_Number((double)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_Boolean)))
{
object ptr = new GH_Boolean((bool)Value);
target = (T)ptr;
return true;
}
if (type.IsAssignableFrom(typeof(GH_String)))
{
object ptr = new GH_String((string)Value);
target = (T)ptr;
return true;
}
return false;
}
}
[Guid("B3101D12-DA73-45DF-B617-16E1C65BB37C")]
public class SpecklePropertyParam : GH_Param<SpecklePropertyGoo>
{
public SpecklePropertyParam(GH_ParamAccess access)
: base(
"Speckle Property",
"SPO",
"Represents a Speckle Property",
ComponentCategories.PRIMARY_RIBBON,
ComponentCategories.PARAMETERS,
access
) { }
public override Guid ComponentGuid => GetType().GUID;
protected override Bitmap Icon => BitmapBuilder.CreateHexagonalBitmap("P");
public SpecklePropertyParam()
: this(GH_ParamAccess.item) { }
public SpecklePropertyParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpecklePropertyParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
}
@@ -0,0 +1,40 @@
using Grasshopper.Kernel.Types;
using Speckle.Connectors.Grasshopper8.HostApp;
namespace Speckle.Connectors.Grasshopper8.Parameters;
public class SpeckleUrlModelResourceGoo : GH_Goo<SpeckleUrlModelResource>
{
public override IGH_Goo Duplicate() => new SpeckleUrlModelResourceGoo() { Value = Value };
public override string ToString() => Value.ToString();
public override bool IsValid => true;
public override string TypeName => "SpeckleUrlModelResource";
public override string TypeDescription => "Points to a model/version/object in a Speckle server";
public override bool CastFrom(object source)
{
switch (source)
{
case SpeckleUrlModelResource resource:
Value = resource;
return true;
default:
return false;
}
}
public override bool CastTo<TOut>(ref TOut target)
{
var type = typeof(TOut);
var success = false;
if (type == typeof(SpeckleUrlModelResource))
{
target = (TOut)(object)Value;
success = true;
}
return success;
}
}
@@ -0,0 +1,20 @@
using Grasshopper.Kernel;
namespace Speckle.Connectors.Grasshopper8.Parameters;
public class SpeckleUrlModelResourceParam : GH_Param<SpeckleUrlModelResourceGoo>
{
public SpeckleUrlModelResourceParam()
: this(GH_ParamAccess.item) { }
public SpeckleUrlModelResourceParam(IGH_InstanceDescription tag)
: base(tag) { }
public SpeckleUrlModelResourceParam(IGH_InstanceDescription tag, GH_ParamAccess access)
: base(tag, access) { }
public SpeckleUrlModelResourceParam(GH_ParamAccess access)
: base("Speckle URL", "URL", "A Speckle resource", "Speckle", "Resources", access) { }
public override Guid ComponentGuid => new Guid("E5421FC2-F10D-447F-BF23-5C934ABDB2D3");
}
@@ -0,0 +1,20 @@
{
"profiles": {
"Rhino8Core": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
"commandLineArgs": "/netcore /runscript=\"_Grasshopper\"",
"environmentVariables": {
"RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
}
},
"Rhino8NetFx": {
"commandName": "Executable",
"executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
"commandLineArgs": "/netfx /runscript=\"_Grasshopper\"",
"environmentVariables": {
"RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
}
}
}
}
@@ -0,0 +1,66 @@
using Grasshopper.Kernel;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.Common.Operations;
using Speckle.Connectors.Common.Operations.Receive;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.Grasshopper8.Operations.Receive;
using Speckle.Connectors.Grasshopper8.Operations.Send;
using Speckle.Connectors.Grasshopper8.Parameters;
using Speckle.Converters.Rhino;
using Speckle.Sdk;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models.GraphTraversal;
namespace Speckle.Connectors.Grasshopper8.Registration;
public class PriorityLoader : GH_AssemblyPriority
{
private IDisposable? _disposableLogger;
public static ServiceProvider? Container { get; set; }
public override GH_LoadingInstruction PriorityLoad()
{
try
{
var services = new ServiceCollection();
_disposableLogger = services.Initialize(HostApplications.Grasshopper, GetVersion());
services.AddRhinoConverters().AddConnectorUtils();
// receive
services.AddTransient<IHostObjectBuilder, GrasshopperHostObjectBuilder>();
services.AddTransient<GrasshopperReceiveOperation>();
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
services.AddScoped<RootObjectUnpacker>();
services.AddTransient<TraversalContextUnpacker>();
services.AddTransient<AccountManager>();
// send
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
services.AddTransient<SendOperation<SpeckleCollectionWrapperGoo>>();
services.AddSingleton<IThreadContext>(new DefaultThreadContext());
Container = services.BuildServiceProvider();
return GH_LoadingInstruction.Proceed;
}
catch (Exception e) when (!e.IsFatal())
{
// TODO: Top level exception handling here
return GH_LoadingInstruction.Abort;
}
}
private HostAppVersion GetVersion()
{
#if RHINO7
return HostAppVersion.v7;
#elif RHINO8
return HostAppVersion.v8;
#else
throw new NotImplementedException();
#endif
}
}
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Select the framework(s) you wish to target.
Rhino 6: net45
Rhino 7: net48
Rhino 8 Windows: net48, net7.0, net7.0-windows, net7.0-windows10.0.22000.0, etc
Rhino 8 Mac: net7.0, net7.0-macos, net7.0-macos12.0, etc
-->
<TargetFramework>net48</TargetFramework>
<Configurations>Debug;Release;Local</Configurations>
<TargetExt>.gha</TargetExt>
<NoWarn>$(NoWarn);NU1701;NETSDK1086</NoWarn>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UseWindowsForms>true</UseWindowsForms>
<DefineConstants>$(DefineConstants);GRASSHOPPER;RHINO8;RHINO7_OR_GREATER;RHINO8_OR_GREATER</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Title>Speckle.Connectors.Grasshopper8</Title>
<Description>Description of Speckle.Connectors.Grasshopper8</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GrasshopperAsyncComponent" />
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,25 @@
using Grasshopper.Kernel;
namespace Speckle.Connectors.Grasshopper8;
public class Speckle_Connectors_Grasshopper8Info : GH_AssemblyInfo
{
public override string Name => "Speckle.Connector.Grasshopper8";
// Return a 24x24 pixel bitmap to represent this GHA library.
// public override Bitmap Icon => null;
// Return a short string describing the purpose of this GHA library.
public override string Description => "x";
public override Guid Id => new Guid("d711dd2a-9c17-483c-a92d-45c1fc736c46");
// Return a string identifying you or your company.
public override string AuthorName => "Speckle";
// Return a string representing your preferred contact details.
public override string AuthorContact => "info@speckle.systems";
// Return a string representing the version. This returns the same version as the assembly.
public override string AssemblyVersion => GetType().Assembly.GetName().Version.ToString();
}
@@ -0,0 +1,367 @@
{
"version": 2,
"dependencies": {
".NETFramework,Version=v4.8": {
"Grasshopper": {
"type": "Direct",
"requested": "[8.9.24194.18121, )",
"resolved": "8.9.24194.18121",
"contentHash": "ZQ7vT1urn9jG2KLMdT/aVhCsijN349lj2Lrg7+Cd5A84KOW+RIErG6IqH+133hc9HT9D10+7oi/XnIlgYZRzqQ==",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121]"
}
},
"GrasshopperAsyncComponent": {
"type": "Direct",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
"requested": "[1.0.3, )",
"resolved": "1.0.3",
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
"dependencies": {
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
}
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"PolySharp": {
"type": "Direct",
"requested": "[1.14.1, )",
"resolved": "1.14.1",
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
},
"RhinoCommon": {
"type": "Direct",
"requested": "[8.9.24194.18121, )",
"resolved": "8.9.24194.18121",
"contentHash": "XRMnm38sBFeMT5AAtRTJdSaql/YNtT02AGi8TEVP1VZ4fkm8VJ1q2nNioWN3tW/+H8Tdi4nV+DuhB/5uE41MCg=="
},
"Speckle.InterfaceGenerator": {
"type": "Direct",
"requested": "[0.9.6, )",
"resolved": "0.9.6",
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
},
"GraphQL.Client": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0",
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
"System.Net.WebSockets.Client.Managed": "1.0.22",
"System.Reactive": "5.0.0"
}
},
"GraphQL.Client.Abstractions": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
"dependencies": {
"GraphQL.Primitives": "6.0.0"
}
},
"GraphQL.Client.Abstractions.Websocket": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
"dependencies": {
"GraphQL.Client.Abstractions": "6.0.0"
}
},
"GraphQL.Primitives": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.CSharp": {
"type": "Transitive",
"resolved": "4.7.0",
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
},
"Microsoft.Data.Sqlite": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "7.0.5",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
}
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "7.0.5",
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
"dependencies": {
"Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Primitives": "2.2.0",
"System.ComponentModel.Annotations": "4.5.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
"dependencies": {
"System.Memory": "4.5.1",
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
}
},
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
"type": "Transitive",
"resolved": "1.0.3",
"contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Speckle.Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.2",
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
"SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4"
}
},
"SQLitePCLRaw.core": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
},
"SQLitePCLRaw.provider.dynamic_cdecl": {
"type": "Transitive",
"resolved": "2.1.4",
"contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.4"
}
},
"System.Buffers": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw=="
},
"System.ComponentModel.Annotations": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==",
"dependencies": {
"System.Buffers": "4.4.0",
"System.Numerics.Vectors": "4.4.0",
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
}
},
"System.Net.WebSockets.Client.Managed": {
"type": "Transitive",
"resolved": "1.0.22",
"contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==",
"dependencies": {
"System.Buffers": "4.4.0",
"System.Numerics.Vectors": "4.4.0"
}
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
},
"System.Reactive": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw=="
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
},
"speckle.connectors.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.logging": {
"type": "Project"
},
"speckle.converters.common": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.rhino8": {
"type": "Project",
"dependencies": {
"RhinoCommon": "[8.9.24194.18121, )",
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
"Microsoft.Extensions.Options": "2.2.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
},
"Speckle.DoubleNumerics": {
"type": "CentralTransitive",
"requested": "[4.1.0, )",
"resolved": "4.1.0",
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
"Microsoft.CSharp": "4.7.0",
"Microsoft.Data.Sqlite": "7.0.5",
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
}
@@ -139,11 +139,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -268,9 +263,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +273,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -296,7 +291,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.rhino7": {
@@ -315,6 +310,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -347,18 +348,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -368,14 +369,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -21,6 +21,7 @@
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
<ProjectReference Include="..\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj" />
</ItemGroup>
<ItemGroup>
@@ -139,11 +139,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -268,9 +263,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -278,8 +273,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -289,6 +284,14 @@
"Speckle.Connectors.DUI": "[1.0.0, )"
}
},
"speckle.connectors.grasshopper8": {
"type": "Project",
"dependencies": {
"GrasshopperAsyncComponent": "[1.2.3, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Converters.Rhino8": "[1.0.0, )"
}
},
"speckle.connectors.logging": {
"type": "Project"
},
@@ -296,7 +299,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"speckle.converters.rhino8": {
@@ -306,6 +309,12 @@
"Speckle.Converters.Common": "[1.0.0, )"
}
},
"GrasshopperAsyncComponent": {
"type": "CentralTransitive",
"requested": "[1.2.3, )",
"resolved": "1.2.3",
"contentHash": "KdCmyZ7Su8T3wb5t5BEbSg2inz+aJfGFHpDysColTdeyYX9S6MRJK69UV4kYYHE+ro1FKPADOwoSE6eLKq/yDA=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -315,6 +324,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -347,18 +362,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -368,14 +383,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -169,11 +169,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -325,9 +320,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -335,8 +330,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -362,7 +357,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"LibTessDotNet": {
@@ -380,6 +375,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -412,18 +413,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -433,14 +434,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -183,11 +183,6 @@
"Microsoft.Extensions.Configuration": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "2.2.0",
@@ -406,9 +401,9 @@
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
"Speckle.Connectors.Logging": "[1.0.0, )",
"Speckle.Objects": "[3.1.4, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )",
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui": {
@@ -416,8 +411,8 @@
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Connectors.Common": "[1.0.0, )",
"Speckle.Sdk": "[3.1.4, )",
"Speckle.Sdk.Dependencies": "[3.1.4, )"
"Speckle.Sdk": "[3.1.7, )",
"Speckle.Sdk.Dependencies": "[3.1.7, )"
}
},
"speckle.connectors.dui.webview": {
@@ -443,7 +438,7 @@
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
"Speckle.Objects": "[3.1.4, )"
"Speckle.Objects": "[3.1.7, )"
}
},
"LibTessDotNet": {
@@ -461,6 +456,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "2.2.0",
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
},
"Microsoft.Extensions.Logging": {
"type": "CentralTransitive",
"requested": "[2.2.0, )",
@@ -493,18 +494,18 @@
},
"Speckle.Objects": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "3l7BaePUVvZ5LbXQoudZpSo7ntNkrXYVe5VhokKp0HsrnwPcA0A2WgDcuZC3mss204UNezu5lZUqv5lM2GXXgg==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
"dependencies": {
"Speckle.Sdk": "3.1.4"
"Speckle.Sdk": "3.1.7"
}
},
"Speckle.Sdk": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "aYiItasQxpGbSIQEg3sM8jk+5SV4jt2Gk+0wKr1EBUndA/l8eB5tAi6eMInL7pGnErEHjj+En9DJU4WRzo3wdw==",
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
"dependencies": {
"GraphQL.Client": "6.0.0",
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
@@ -514,14 +515,14 @@
"Microsoft.Extensions.Logging": "2.2.0",
"Speckle.DoubleNumerics": "4.1.0",
"Speckle.Newtonsoft.Json": "13.0.2",
"Speckle.Sdk.Dependencies": "3.1.4"
"Speckle.Sdk.Dependencies": "3.1.7"
}
},
"Speckle.Sdk.Dependencies": {
"type": "CentralTransitive",
"requested": "[3.1.4, )",
"resolved": "3.1.4",
"contentHash": "9w6uBekthmmKZhV4bau36Fu8HRPSq4UsS4UB1I9IUsh9xF9IPHt0hVvDBpwQB1P0Gy9fVrcZQeAuP4TMRQqv0A=="
"requested": "[3.1.7, )",
"resolved": "3.1.7",
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
}
}
}
@@ -1,5 +1,6 @@
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Tekla.Structures;
using Tekla.Structures.Model;
namespace Speckle.Connectors.TeklaShared.Bindings;
@@ -10,6 +11,7 @@ public class TeklaSelectionBinding : ISelectionBinding
private readonly object _selectionEventHandlerLock = new object();
private readonly IAppIdleManager _idleManager;
private readonly Events _events;
private readonly Model _model;
private readonly Tekla.Structures.Model.UI.ModelObjectSelector _selector;
public string Name => "selectionBinding";
@@ -26,6 +28,7 @@ public class TeklaSelectionBinding : ISelectionBinding
Parent = parent;
_selector = selector;
_events = events;
_model = new Model();
_events.SelectionChange += OnSelectionChangeEvent;
_events.Register();
@@ -70,10 +73,15 @@ public class TeklaSelectionBinding : ISelectionBinding
objectTypes.Add(modelObject.GetType().Name);
}
// Filter out the objects that Tekla API ignores (e.g. Construction objects with "000000.." GUID)
List<string> filteredObjectIds = objectIds
.Where(id => _model.SelectModelObject(new Identifier(new Guid(id))) != null)
.ToList();
string typesString = string.Join(", ", objectTypes.Distinct());
return new SelectionInfo(
objectIds,
objectIds.Count == 0 ? "No objects selected." : $"{objectIds.Count} objects ({typesString})"
filteredObjectIds,
filteredObjectIds.Count == 0 ? "No objects selected." : $"{filteredObjectIds.Count} objects ({typesString})"
);
}
}

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