Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94de06e842 | |||
| 641f6fcc76 | |||
| 65313008a4 | |||
| f5ad1cff39 | |||
| 5670b8e99e | |||
| 39c4f2c7d4 | |||
| c2bb8b319d | |||
| 2e2c8e1732 | |||
| 6e6e737807 | |||
| 8c8320ea75 | |||
| 4d81bce0ea | |||
| d513187acd | |||
| af3810bc98 | |||
| 02f3c1c3fa | |||
| 31c63cce22 | |||
| f94acec6a5 | |||
| 093dbc826b | |||
| 5edaee1fa8 | |||
| 54b14b8bc9 | |||
| a7ab4915c3 | |||
| 03d231b056 | |||
| 445e10dbb4 | |||
| c58a8d2757 | |||
| dfb75d98c1 | |||
| a93f9e9c42 | |||
| cd8dab48ae | |||
| a78c8962be | |||
| a91ecd9e88 | |||
| 0ed086d336 | |||
| 9132514d86 | |||
| f5c0a9eb96 | |||
| 5d4b08d1d4 | |||
| e0148aba62 | |||
| 221371d668 | |||
| 7206a4477c | |||
| e0f042615b | |||
| 9482d1fe83 | |||
| 6797f4baa4 | |||
| 751170acf8 | |||
| 6241780615 | |||
| 0136d3fa13 |
+10
-5
@@ -158,12 +158,17 @@ public class CreateCollection : VariableParameterComponentBase
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
var wrapperGoo = new SpeckleObjectWrapperGoo();
|
||||
if (wrapperGoo.CastFrom(obj))
|
||||
// deep copy to avoid mutations
|
||||
if (obj?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper objWrapper)
|
||||
{
|
||||
wrapperGoo.Value.Path = childPath;
|
||||
wrapperGoo.Value.Parent = parentCollection;
|
||||
parentCollection.Elements.Add(wrapperGoo.Value);
|
||||
SpeckleObjectWrapper wrapper = objWrapper.DeepCopy();
|
||||
wrapper.Path = childPath;
|
||||
wrapper.Parent = parentCollection;
|
||||
parentCollection.Elements.Add(wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"{obj?.GetType().Name} type cannot be added to collections.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-25
@@ -2,6 +2,7 @@ using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
@@ -30,9 +31,9 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Data",
|
||||
"D",
|
||||
"The data you want to expand",
|
||||
"Collection",
|
||||
"C",
|
||||
"The Collection you want to expand",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
@@ -51,15 +52,11 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
Name = wrapper.Name;
|
||||
NickName = wrapper.Name;
|
||||
|
||||
var objects = wrapper
|
||||
.Elements.Where(el => el is not SpeckleCollectionWrapper)
|
||||
.OfType<SpeckleObjectWrapper>()
|
||||
.Select(o => new SpeckleObjectWrapperGoo(o))
|
||||
.ToList();
|
||||
var collections = wrapper
|
||||
.Elements.Where(el => el is SpeckleCollectionWrapper)
|
||||
.OfType<SpeckleCollectionWrapper>()
|
||||
.ToList();
|
||||
// Separate objects and collections
|
||||
// Note: SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper,
|
||||
// so it will be included in objects
|
||||
var objects = wrapper.Elements.OfType<SpeckleObjectWrapper>().ToList();
|
||||
var collections = wrapper.Elements.OfType<SpeckleCollectionWrapper>().ToList();
|
||||
|
||||
var outputParams = new List<OutputParamWrapper>();
|
||||
if (objects.Count != 0)
|
||||
@@ -73,7 +70,10 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
Access = GH_ParamAccess.list
|
||||
};
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, objects, null));
|
||||
// Create appropriate Goo types for each object (downside of the inheritance refactor)
|
||||
List<IGH_Goo> atomicObjectGoos = objects.Select(obj => obj.CreateGoo()).ToList();
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, atomicObjectGoos, null));
|
||||
}
|
||||
|
||||
foreach (SpeckleCollectionWrapper childWrapper in collections)
|
||||
@@ -86,7 +86,7 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
*/
|
||||
|
||||
var hasInnerCollections = childWrapper.Elements.Any(el => el is SpeckleCollectionWrapper);
|
||||
var topology = childWrapper.Topology; // Note: this is a reminder for the future
|
||||
var topology = childWrapper.Topology;
|
||||
var nickName = childWrapper.Name;
|
||||
if (nickName.Length > 16)
|
||||
{
|
||||
@@ -102,18 +102,25 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
? 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
|
||||
: GH_ParamAccess.tree
|
||||
};
|
||||
|
||||
outputParams.Add(
|
||||
new OutputParamWrapper(
|
||||
param,
|
||||
hasInnerCollections
|
||||
? new SpeckleCollectionWrapperGoo(childWrapper)
|
||||
: childWrapper.Elements.OfType<SpeckleObjectWrapper>().Select(o => new SpeckleObjectWrapperGoo(o)).ToList(),
|
||||
topology
|
||||
)
|
||||
);
|
||||
object outputValue;
|
||||
if (hasInnerCollections)
|
||||
{
|
||||
outputValue = new SpeckleCollectionWrapperGoo(childWrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create appropriate Goo types for child objects
|
||||
List<IGH_Goo> childObjectGoos = childWrapper
|
||||
.Elements.OfType<SpeckleObjectWrapper>()
|
||||
.Select(obj => obj.CreateGoo())
|
||||
.ToList();
|
||||
outputValue = childObjectGoos;
|
||||
}
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, outputValue, topology));
|
||||
}
|
||||
|
||||
if (da.Iteration == 0 && OutputMismatch(outputParams))
|
||||
@@ -142,7 +149,6 @@ public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
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());
|
||||
|
||||
+34
-57
@@ -2,6 +2,7 @@ using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
@@ -29,12 +30,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter(
|
||||
"Speckle Param",
|
||||
"SP",
|
||||
"Speckle param to deconstruct. Expects Collections, Objects, Materials, or Properties",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
pManager.AddGenericParameter("Speckle Param", "SP", "Speckle param to deconstruct", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
|
||||
@@ -48,37 +44,24 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
|
||||
switch (data)
|
||||
{
|
||||
case SpeckleObjectWrapperGoo obj:
|
||||
Name = string.IsNullOrEmpty(obj.Value.Name) ? obj.Value.Base.speckle_type : obj.Value.Name;
|
||||
outputParams = CreateOutputParamsFromBase(obj.Value.Base);
|
||||
case SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null:
|
||||
// get children elements from the wrapper to override the elements prop while parsing
|
||||
List<IGH_Goo> children = collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList();
|
||||
outputParams = ParseSpeckleWrapper(collectionGoo.Value, children);
|
||||
break;
|
||||
case SpeckleCollectionWrapperGoo coll:
|
||||
Name = string.IsNullOrEmpty(coll.Value.Collection.name)
|
||||
? coll.Value.Collection.speckle_type
|
||||
: coll.Value.Collection.name;
|
||||
|
||||
// the elements prop of collections is empty. Need to also process the elements in the wrapper and add it to the outputParams
|
||||
List<object> children = new();
|
||||
foreach (var element in coll.Value.Elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case SpeckleCollectionWrapper childColl:
|
||||
children.Add(new SpeckleCollectionWrapperGoo(childColl));
|
||||
break;
|
||||
case SpeckleObjectWrapper childObj:
|
||||
children.Add(new SpeckleObjectWrapperGoo(childObj));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outputParams = CreateOutputParamsFromBase(coll.Value.Collection, children);
|
||||
|
||||
case SpeckleObjectWrapperGoo objectGoo when objectGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(objectGoo.Value);
|
||||
break;
|
||||
case SpeckleMaterialWrapperGoo matGoo:
|
||||
Name = string.IsNullOrEmpty(matGoo.Value.Name) ? matGoo.Value.Material.speckle_type : matGoo.Value.Name;
|
||||
outputParams = CreateOutputParamsFromBase(matGoo.Value.Base);
|
||||
case SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(blockInstanceGoo.Value);
|
||||
break;
|
||||
case SpeckleBlockDefinitionWrapperGoo blockDef:
|
||||
outputParams = ParseSpeckleWrapper(blockDef.Value);
|
||||
break;
|
||||
case SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(materialGoo.Value);
|
||||
break;
|
||||
|
||||
case SpecklePropertyGroupGoo propGoo:
|
||||
Name = $"properties ({propGoo.Value.Count})";
|
||||
outputParams = new();
|
||||
@@ -93,7 +76,9 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
outputParams.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,7 +114,13 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
}
|
||||
}
|
||||
|
||||
private List<OutputParamWrapper> CreateOutputParamsFromBase(Base @base, List<object>? children = null)
|
||||
private List<OutputParamWrapper> ParseSpeckleWrapper(SpeckleWrapper wrapper, List<IGH_Goo>? collElements = null)
|
||||
{
|
||||
Name = string.IsNullOrEmpty(wrapper.Name) ? wrapper.Base.speckle_type : wrapper.Name;
|
||||
return CreateOutputParamsFromBase(wrapper.Base, collElements);
|
||||
}
|
||||
|
||||
private List<OutputParamWrapper> CreateOutputParamsFromBase(Base @base, List<IGH_Goo>? collElements = null)
|
||||
{
|
||||
List<OutputParamWrapper> result = new();
|
||||
if (@base == null)
|
||||
@@ -152,21 +143,17 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
List<object> nativeObjects = new();
|
||||
|
||||
// override list value if base is a collection and this is the elements prop, since this is empty if coming from a collectionwrapper
|
||||
if (@base is Collection && prop.Key == "elements" && children != null)
|
||||
if (@base is Collection && prop.Key == "elements" && collElements != null)
|
||||
{
|
||||
list = children;
|
||||
list = collElements;
|
||||
}
|
||||
|
||||
foreach (var x in list)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case SpeckleCollectionWrapper collWrapper:
|
||||
nativeObjects.Add(new SpeckleCollectionWrapperGoo(collWrapper));
|
||||
break;
|
||||
|
||||
case SpeckleObjectWrapper objWrapper:
|
||||
nativeObjects.Add(new SpeckleObjectWrapperGoo(objWrapper));
|
||||
case SpeckleWrapper wrapper:
|
||||
nativeObjects.Add(wrapper.CreateGoo());
|
||||
break;
|
||||
|
||||
case Base xBase:
|
||||
@@ -188,16 +175,8 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
|
||||
break;
|
||||
|
||||
case SpeckleCollectionWrapper collWrapper:
|
||||
result.Add(
|
||||
CreateOutputParamByKeyValue(prop.Key, new SpeckleCollectionWrapperGoo(collWrapper), GH_ParamAccess.item)
|
||||
);
|
||||
break;
|
||||
|
||||
case SpeckleObjectWrapper objWrapper:
|
||||
result.Add(
|
||||
CreateOutputParamByKeyValue(prop.Key, new SpeckleObjectWrapperGoo(objWrapper), GH_ParamAccess.item)
|
||||
);
|
||||
case SpeckleWrapper wrapper:
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
|
||||
break;
|
||||
|
||||
case Base baseValue:
|
||||
@@ -235,8 +214,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
GeometryBase = g,
|
||||
Name = b["name"] as string ?? "",
|
||||
Color = null,
|
||||
Material = null,
|
||||
WrapperGuid = null
|
||||
Material = null
|
||||
};
|
||||
|
||||
convertedWrappers.Add(new(convertedWrapper));
|
||||
@@ -255,8 +233,7 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
GeometryBase = null,
|
||||
Name = @base[Constants.NAME_PROP] as string ?? "",
|
||||
Color = null,
|
||||
Material = null,
|
||||
WrapperGuid = null,
|
||||
Material = null
|
||||
};
|
||||
|
||||
return new() { new SpeckleObjectWrapperGoo(convertedWrapper) };
|
||||
|
||||
+15
-2
@@ -17,7 +17,7 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_properties_create;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
public CreateSpeckleProperties()
|
||||
: base(
|
||||
@@ -36,7 +36,13 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Properties", "P", "Properties for Speckle Objects", GH_ParamAccess.item);
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"Properties for Speckle Objects",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
@@ -60,6 +66,13 @@ public class CreateSpeckleProperties : VariableParameterComponentBase
|
||||
for (int i = 0; i < Params.Input.Count; i++)
|
||||
{
|
||||
var paramName = Params.Input[i].NickName;
|
||||
|
||||
var data = Params.Input[i].VolatileData.AllData(true).ToList();
|
||||
if (data.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyValue = ExtractPropertyValue(da, i, paramName);
|
||||
|
||||
if (propertyValue != null)
|
||||
|
||||
+34
-31
@@ -1,6 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
@@ -27,7 +29,7 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleObjectParam(), "Objects", "O", "Speckle Objects to filter", GH_ParamAccess.list);
|
||||
pManager.AddGenericParameter("Objects", "O", "Speckle Objects to filter", GH_ParamAccess.list);
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Find objects with a matching name", GH_ParamAccess.item);
|
||||
Params.Input[1].Optional = true;
|
||||
@@ -62,16 +64,9 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(),
|
||||
"Objects",
|
||||
"O",
|
||||
"The objects that match the queries",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
pManager.AddGenericParameter("Objects", "O", "The objects that match the queries", GH_ParamAccess.tree);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(),
|
||||
pManager.AddGenericParameter(
|
||||
"Culled Objects",
|
||||
"co",
|
||||
"The objects that did not match the queries",
|
||||
@@ -81,11 +76,24 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
List<SpeckleObjectWrapperGoo?> inputObjects = new();
|
||||
List<IGH_Goo> inputObjects = new();
|
||||
dataAccess.GetDataList(0, inputObjects);
|
||||
|
||||
if (inputObjects.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Add objects to filter");
|
||||
return;
|
||||
}
|
||||
|
||||
List<SpeckleObjectWrapper?> objects = inputObjects
|
||||
.Select(o => o.ToSpeckleObjectWrapper())
|
||||
.Where(o => o is not null)
|
||||
.ToList();
|
||||
|
||||
int unsupported = inputObjects.Count - objects.Count;
|
||||
if (unsupported > 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Input contained {unsupported} unsupported objects.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,19 +110,14 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
List<SpeckleObjectWrapper> matchedObjects = new();
|
||||
List<SpeckleObjectWrapper> removedObjects = new();
|
||||
for (int i = 0; i < inputObjects.Count; i++)
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
{
|
||||
SpeckleObjectWrapperGoo? inputObject = inputObjects[i];
|
||||
if (inputObject is null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"A null input object was detected.");
|
||||
return;
|
||||
}
|
||||
SpeckleObjectWrapper wrapper = objects[i]!;
|
||||
|
||||
// filter by name
|
||||
if (!MatchesSearchPattern(name, inputObject.Value.Name))
|
||||
if (!MatchesSearchPattern(name, wrapper.Name))
|
||||
{
|
||||
removedObjects.Add(inputObject.Value);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -126,7 +129,7 @@ public class FilterSpeckleObjects : GH_Component
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string key in inputObject.Value.Properties.Value.Keys)
|
||||
foreach (string key in wrapper.Properties.Value.Keys)
|
||||
{
|
||||
if (MatchesSearchPattern(property, key))
|
||||
{
|
||||
@@ -138,37 +141,37 @@ public class FilterSpeckleObjects : GH_Component
|
||||
|
||||
if (!foundProperty)
|
||||
{
|
||||
removedObjects.Add(inputObject.Value);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by material name
|
||||
if (!MatchesSearchPattern(material, inputObject.Value.Material?.Name ?? ""))
|
||||
if (!MatchesSearchPattern(material, wrapper.Material?.Name ?? ""))
|
||||
{
|
||||
removedObjects.Add(inputObject.Value);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by application id
|
||||
if (!MatchesSearchPattern(appId, inputObject.Value.Base.applicationId ?? ""))
|
||||
if (!MatchesSearchPattern(appId, wrapper.Base.applicationId ?? ""))
|
||||
{
|
||||
removedObjects.Add(inputObject.Value);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter by speckle id
|
||||
if (!MatchesSearchPattern(speckleId, inputObject.Value.Base.id ?? ""))
|
||||
if (!MatchesSearchPattern(speckleId, wrapper.Base.id ?? ""))
|
||||
{
|
||||
removedObjects.Add(inputObject.Value);
|
||||
removedObjects.Add(wrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedObjects.Add(inputObject.Value);
|
||||
matchedObjects.Add(wrapper);
|
||||
}
|
||||
|
||||
// Set output objects
|
||||
dataAccess.SetDataList(0, matchedObjects);
|
||||
dataAccess.SetDataList(1, removedObjects);
|
||||
dataAccess.SetDataList(0, matchedObjects.Select(o => o.CreateGoo()));
|
||||
dataAccess.SetDataList(1, removedObjects.Select(o => o.CreateGoo()));
|
||||
}
|
||||
|
||||
private bool MatchesSearchPattern(string searchPattern, string target)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public class GetObjectProperties : GH_Component, IGH_VariableParameterComponent
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
|
||||
protected override Bitmap Icon => Resources.speckle_properties_query;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
public GetObjectProperties()
|
||||
: base(
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
|
||||
|
||||
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
|
||||
protected override Bitmap Icon => Resources.speckle_inputs_property;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
protected override void LoadVolatileData()
|
||||
{
|
||||
|
||||
+6
-5
@@ -1,6 +1,7 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
@@ -49,8 +50,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(),
|
||||
pManager.AddGenericParameter(
|
||||
"Objects",
|
||||
"O",
|
||||
"The objects in the input collection that match the queries",
|
||||
@@ -137,14 +137,15 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
for (int i = 0; i < Params.Output.Count; i++)
|
||||
{
|
||||
List<SpeckleObjectWrapper> outputValues = i == 0 ? filteredObjects : _filterDict[Filters[i - 1]];
|
||||
List<IGH_Goo> outputGoos = outputValues.Select(o => o.CreateGoo()).ToList();
|
||||
if (targetCollectionWrapper?.Topology is string topology && !string.IsNullOrEmpty(topology))
|
||||
{
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputValues);
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topology, outputGoos);
|
||||
dataAccess.SetDataTree(i, tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataAccess.SetDataList(i, outputValues);
|
||||
dataAccess.SetDataList(i, outputGoos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +178,7 @@ public class QuerySpeckleObjects : GH_Component, IGH_VariableParameterComponent
|
||||
|
||||
private IEnumerable<SpeckleObjectWrapper> GetAllObjectsFromCollection(SpeckleCollectionWrapper collectionWrapper)
|
||||
{
|
||||
foreach (SpeckleWrapper element in collectionWrapper.Elements)
|
||||
foreach (ISpeckleCollectionObject element in collectionWrapper.Elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("8D2E3F4A-1B5C-4E7F-9A8B-3C6D9E2F1A4B")]
|
||||
public class SpeckleBlockDefinitionPassthrough : GH_Component
|
||||
{
|
||||
public SpeckleBlockDefinitionPassthrough()
|
||||
: base(
|
||||
"Speckle Block Definition",
|
||||
"SBD",
|
||||
"Create or modify a Speckle Block Definition",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OBJECTS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_objects_block_def;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleBlockDefinitionWrapperParam(),
|
||||
"Block Definition",
|
||||
"BD",
|
||||
"Input Block Definition. Speckle definitions and Model definitions are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[0].Optional = true;
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
"Objects",
|
||||
"O",
|
||||
"Objects to include in the Block Definition. Speckle objects and instances or Model objects and instances are accepted.",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
Params.Input[1].Optional = true;
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the Speckle Definition", GH_ParamAccess.item);
|
||||
Params.Input[2].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleBlockDefinitionWrapperParam(),
|
||||
"Block Definition",
|
||||
"BD",
|
||||
"Speckle Block Definition",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddGenericParameter("Objects", "O", "Objects contained in the Block Definition", GH_ParamAccess.list);
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the Block Definition", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
|
||||
da.GetData(0, ref inputDefinition);
|
||||
|
||||
List<IGH_Goo> inputObjects = new();
|
||||
da.GetDataList(1, inputObjects);
|
||||
|
||||
if (inputDefinition == null && inputObjects.Count == 0)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Definition or Objects.");
|
||||
return;
|
||||
}
|
||||
|
||||
string? inputName = null;
|
||||
da.GetData(2, ref inputName);
|
||||
|
||||
if (inputDefinition == null && string.IsNullOrWhiteSpace(inputName))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in a Name for the definition.");
|
||||
return;
|
||||
}
|
||||
|
||||
// keep track of mutation
|
||||
bool mutated = false;
|
||||
|
||||
// process the definition
|
||||
// deep copy so we don't mutate the object
|
||||
SpeckleBlockDefinitionWrapperGoo result = inputDefinition != null ? new(inputDefinition.Value.DeepCopy()) : new();
|
||||
|
||||
// process geometry
|
||||
if (inputObjects.Count > 0)
|
||||
{
|
||||
List<SpeckleObjectWrapper> processedObjects = new();
|
||||
foreach (IGH_Goo goo in inputObjects)
|
||||
{
|
||||
if (goo.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
|
||||
{
|
||||
processedObjects.Add(gooWrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"Unsupported type {goo.TypeName} not added to definition");
|
||||
}
|
||||
}
|
||||
|
||||
result.Value.Objects = processedObjects;
|
||||
result.Value.InstanceDefinitionProxy.objects = processedObjects.Select(o => o.ApplicationId!).ToList(); // TODO: this could also be set at the same time as `Objects` on the definition wrapper.
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// process name
|
||||
if (inputName != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(inputName))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Pass in a non-empty name for the definition.");
|
||||
return;
|
||||
}
|
||||
|
||||
result.Value.Name = inputName;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// process application Id. Use a new appId if mutated, or if this is a new object
|
||||
result.Value.ApplicationId = mutated
|
||||
? Guid.NewGuid().ToString()
|
||||
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
|
||||
|
||||
// set outputs
|
||||
da.SetData(0, result);
|
||||
da.SetDataList(1, result.Value.Objects.Select(o => o.CreateGoo()));
|
||||
da.SetData(2, result.Value.Name);
|
||||
}
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
[Guid("2F8A9B1C-3D4E-5F6A-7B8C-9D0E1F2A3B4C")]
|
||||
public class SpeckleBlockInstancePassthrough : GH_Component
|
||||
{
|
||||
public SpeckleBlockInstancePassthrough()
|
||||
: base(
|
||||
"Speckle Block Instance",
|
||||
"SBI",
|
||||
"Create or modify a Speckle Block Instance",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OBJECTS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_objects_block_inst;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
int instanceIndex = pManager.AddParameter(
|
||||
new SpeckleBlockInstanceParam(),
|
||||
"Block Instance",
|
||||
"BI",
|
||||
"Input Block Instance. Speckle instances and Grasshopper instances are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[instanceIndex].Optional = true;
|
||||
|
||||
int definitionIndex = pManager.AddParameter(
|
||||
new SpeckleBlockDefinitionWrapperParam(),
|
||||
"Definition",
|
||||
"D",
|
||||
"Block Instance Definition. Speckle definitions and Grasshopper definitions are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[definitionIndex].Optional = true;
|
||||
|
||||
int transformIndex = pManager.AddGenericParameter(
|
||||
"Transform",
|
||||
"T",
|
||||
"Transform of the Speckle instance. Transforms and Planes are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[transformIndex].Optional = true;
|
||||
|
||||
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Instance", GH_ParamAccess.item);
|
||||
Params.Input[nameIndex].Optional = true;
|
||||
|
||||
int propIndex = pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"The properties of the Speckle Instance. Speckle Properties and User Content are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[propIndex].Optional = true;
|
||||
|
||||
int colorIndex = pManager.AddColourParameter(
|
||||
"Color",
|
||||
"c",
|
||||
"The color of the Speckle Instance",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[colorIndex].Optional = true;
|
||||
|
||||
int matIndex = pManager.AddParameter(
|
||||
new SpeckleMaterialParam(),
|
||||
"Material",
|
||||
"m",
|
||||
"The material of the Speckle Instance. Display Materials, Model Materials, and Speckle Materials are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[matIndex].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleBlockInstanceParam(),
|
||||
"Block Instance",
|
||||
"BI",
|
||||
"Speckle Block Instance",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpeckleBlockDefinitionWrapperParam(),
|
||||
"Definition",
|
||||
"D",
|
||||
"Block Definition of the instance",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddGenericParameter("Transform", "T", "Transform of the Block Instance", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the Block Instance", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"Properties of the Block Instance",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
|
||||
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpeckleMaterialParam(),
|
||||
"Material",
|
||||
"M",
|
||||
"The material of the Block Instance.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
SpeckleBlockInstanceWrapperGoo? inputInstance = null;
|
||||
da.GetData(0, ref inputInstance);
|
||||
|
||||
SpeckleBlockDefinitionWrapperGoo? inputDefinition = null;
|
||||
da.GetData(1, ref inputDefinition);
|
||||
|
||||
if (inputInstance == null && inputDefinition == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Instance or Definition.");
|
||||
return;
|
||||
}
|
||||
|
||||
IGH_Goo? inputTransform = null;
|
||||
da.GetData(2, ref inputTransform);
|
||||
|
||||
string? inputName = null;
|
||||
da.GetData(3, ref inputName);
|
||||
|
||||
SpecklePropertyGroupGoo? inputProperties = null;
|
||||
da.GetData(4, ref inputProperties);
|
||||
|
||||
Color? inputColor = null;
|
||||
da.GetData(5, ref inputColor);
|
||||
|
||||
SpeckleMaterialWrapperGoo? inputMaterial = null;
|
||||
da.GetData(6, ref inputMaterial);
|
||||
|
||||
// keep track of mutation
|
||||
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
|
||||
bool mutated = false;
|
||||
|
||||
// process the instance
|
||||
// deep copy so we don't mutate the object
|
||||
SpeckleBlockInstanceWrapperGoo result =
|
||||
inputInstance != null ? new((SpeckleBlockInstanceWrapper)inputInstance.Value.DeepCopy()) : new();
|
||||
|
||||
// process definition
|
||||
if (inputDefinition != null)
|
||||
{
|
||||
result.Value.Definition = inputDefinition.Value;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// Process transform
|
||||
if (inputTransform != null)
|
||||
{
|
||||
Transform? extractedTransform = ExtractTransform(inputTransform);
|
||||
if (extractedTransform.HasValue)
|
||||
{
|
||||
result.Value.Transform = extractedTransform.Value;
|
||||
mutated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
"Transform input is not valid. Only Transforms and Planes are accepted."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Process name
|
||||
if (inputName != null)
|
||||
{
|
||||
result.Value.Name = inputName;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// Process properties
|
||||
if (inputProperties != null)
|
||||
{
|
||||
result.Value.Properties = inputProperties;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// process color (no mutation)
|
||||
if (inputColor != null)
|
||||
{
|
||||
result.Value.Color = inputColor;
|
||||
}
|
||||
|
||||
// process material (no mutation)
|
||||
if (inputMaterial != null)
|
||||
{
|
||||
result.Value.Material = inputMaterial.Value;
|
||||
}
|
||||
|
||||
// Generate new ApplicationId if mutated
|
||||
result.Value.ApplicationId = mutated
|
||||
? Guid.NewGuid().ToString()
|
||||
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
|
||||
|
||||
// Set outputs
|
||||
da.SetData(0, result);
|
||||
da.SetData(1, result.Value.Definition);
|
||||
da.SetData(2, new GH_Transform(result.Value.Transform));
|
||||
da.SetData(3, result.Value.Name);
|
||||
da.SetData(4, result.Value.Properties);
|
||||
da.SetData(5, result.Value.Color);
|
||||
da.SetData(6, result.Value.Material);
|
||||
}
|
||||
|
||||
private Transform? ExtractTransform(IGH_Goo input) =>
|
||||
input switch
|
||||
{
|
||||
GH_Transform ghTransform => ghTransform.Value,
|
||||
GH_Plane ghPlane => Transform.PlaneToPlane(Plane.WorldXY, ghPlane.Value),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
+114
-95
@@ -4,7 +4,6 @@ using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Components.Objects;
|
||||
|
||||
@@ -26,43 +25,45 @@ public class SpeckleObjectPassthrough : GH_Component
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter(
|
||||
int objIndex = pManager.AddGenericParameter(
|
||||
"Object",
|
||||
"O",
|
||||
"Input Object. Speckle Objects, Model Objects, and geometry are accepted.",
|
||||
"Input Object. Speckle Objects and Model Objects are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[0].Optional = true;
|
||||
Params.Input[objIndex].Optional = true;
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
int geoIndex = pManager.AddGeometryParameter(
|
||||
"Geometry",
|
||||
"G",
|
||||
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
|
||||
"Geometry of the Speckle Object.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[1].Optional = true;
|
||||
Params.Input[geoIndex].Optional = true;
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[2].Optional = true;
|
||||
int nameIndex = pManager.AddTextParameter("Name", "N", "Name of the Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[nameIndex].Optional = true;
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
int propIndex = pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"The properties of the Speckle Object. Speckle Properties and User Content are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[3].Optional = true;
|
||||
Params.Input[propIndex].Optional = true;
|
||||
|
||||
pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[4].Optional = true;
|
||||
int colorIndex = pManager.AddColourParameter("Color", "c", "The color of the Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[colorIndex].Optional = true;
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
int matIndex = pManager.AddParameter(
|
||||
new SpeckleMaterialParam(),
|
||||
"Material",
|
||||
"m",
|
||||
"The material of the Speckle Object. Display Materials, Model Materials, and Speckle Materials are accepted.",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[5].Optional = true;
|
||||
Params.Input[matIndex].Optional = true;
|
||||
|
||||
/* POC: disable for now as we are doing anything with this
|
||||
pManager.AddTextParameter(
|
||||
@@ -77,9 +78,9 @@ public class SpeckleObjectPassthrough : GH_Component
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleObjectParam(), "Object", "O", "Speckle Object", GH_ParamAccess.item);
|
||||
pManager.AddGenericParameter("Object", "O", "Speckle Object", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddGenericParameter(
|
||||
pManager.AddGeometryParameter(
|
||||
"Geometry",
|
||||
"G",
|
||||
"Geometry of the Speckle Object. GeometryBase in Grasshopper includes text entities.",
|
||||
@@ -116,127 +117,145 @@ public class SpeckleObjectPassthrough : GH_Component
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
IGH_Goo? inputObject = null;
|
||||
da.GetData(0, ref inputObject);
|
||||
|
||||
IGH_GeometricGoo? inputGeometry = null;
|
||||
da.GetData(1, ref inputGeometry);
|
||||
|
||||
string? inputName = null;
|
||||
da.GetData(2, ref inputName);
|
||||
|
||||
IGH_Goo? inputProperties = null;
|
||||
da.GetData(3, ref inputProperties);
|
||||
|
||||
Color? inputColor = null;
|
||||
da.GetData(4, ref inputColor);
|
||||
|
||||
IGH_Goo? inputMaterial = null;
|
||||
da.GetData(5, ref inputMaterial);
|
||||
|
||||
//string? inputPath = null;
|
||||
//da.GetData(6, ref inputPath);
|
||||
|
||||
// keep track of mutation
|
||||
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
|
||||
bool mutated = false;
|
||||
|
||||
// process the object
|
||||
SpeckleObjectWrapperGoo result = new();
|
||||
if (inputObject != null)
|
||||
// deep copy so we don't mutate the object
|
||||
IGH_Goo? inputObject = null;
|
||||
SpeckleObjectWrapper? result = null;
|
||||
if (da.GetData(0, ref inputObject))
|
||||
{
|
||||
if (!result.CastFrom(inputObject))
|
||||
if (inputObject?.ToSpeckleObjectWrapper() is SpeckleObjectWrapper gooWrapper)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Object input is not valid. Only Speckle Objects, Baked Model Objects, and Geometry are accepted."
|
||||
);
|
||||
result = gooWrapper.DeepCopy();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Unsupported object type: {inputObject?.TypeName}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// process geometry
|
||||
// at this point, we can ensure that the Base in the wrapper is a DataObject.
|
||||
if (inputObject == null && inputGeometry == null)
|
||||
IGH_GeometricGoo? inputGeometry = null;
|
||||
da.GetData(1, ref inputGeometry);
|
||||
|
||||
if (result == null && inputGeometry == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Pass in an Object or Geometry.");
|
||||
return;
|
||||
}
|
||||
|
||||
string? inputName = null;
|
||||
da.GetData(2, ref inputName);
|
||||
|
||||
SpecklePropertyGroupGoo? inputProperties = null;
|
||||
da.GetData(3, ref inputProperties);
|
||||
|
||||
Color? inputColor = null;
|
||||
da.GetData(4, ref inputColor);
|
||||
|
||||
SpeckleMaterialWrapperGoo? inputMaterial = null;
|
||||
da.GetData(5, ref inputMaterial);
|
||||
|
||||
// keep track of mutation
|
||||
// poc: we should not mark mutations on color or material, as this shouldn't affect the appId of the object, and will allow original display values to stay intact on send.
|
||||
bool mutated = false;
|
||||
|
||||
// process geometry
|
||||
// deep copy so we don't mutate the input geo which may be speckle objects
|
||||
if (inputGeometry != null)
|
||||
{
|
||||
result.Value.GeometryBase = inputGeometry.GeometricGooToGeometryBase();
|
||||
Base converted = SpeckleConversionContext.ConvertToSpeckle(result.Value.GeometryBase);
|
||||
converted[Constants.NAME_PROP] = result.Value.Name;
|
||||
converted.applicationId = result.Value.ApplicationId;
|
||||
result.Value.Base = converted;
|
||||
mutated = true;
|
||||
if (inputGeometry.ToSpeckleObjectWrapper() is SpeckleObjectWrapper geoWrapper)
|
||||
{
|
||||
SpeckleObjectWrapper mutatingGeo = geoWrapper.DeepCopy();
|
||||
if (result is null)
|
||||
{
|
||||
result = mutatingGeo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to switch to the actual object wrapper type of the incoming geo if this is a mutation on the object
|
||||
if (mutatingGeo is SpeckleBlockInstanceWrapper mutatingInstance && result is not SpeckleBlockInstanceWrapper)
|
||||
{
|
||||
MatchNonGeometryProps(mutatingInstance, result);
|
||||
result = mutatingInstance;
|
||||
}
|
||||
else if (mutatingGeo is not SpeckleBlockInstanceWrapper && result is SpeckleBlockInstanceWrapper)
|
||||
{
|
||||
MatchNonGeometryProps(mutatingGeo, result);
|
||||
result = mutatingGeo;
|
||||
}
|
||||
|
||||
mutatingGeo.Base[Constants.NAME_PROP] = result.Name; // assign these before assigning base since otherwise wrapper name and app will reset
|
||||
mutatingGeo.Base.applicationId = result.ApplicationId; // assign these before assigning base since otherwise wrapper name and app will reset
|
||||
result.Base = mutatingGeo.Base;
|
||||
result.GeometryBase = mutatingGeo.GeometryBase;
|
||||
}
|
||||
|
||||
mutated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
$"{inputGeometry.TypeName} is not a valid type for Speckle Objects."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// process name
|
||||
if (inputName != null)
|
||||
{
|
||||
result.Value.Name = inputName;
|
||||
result!.Name = inputName;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// process properties
|
||||
if (inputProperties != null)
|
||||
{
|
||||
SpecklePropertyGroupGoo propGoo = new();
|
||||
if (!propGoo.CastFrom(inputProperties))
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Properties input is not valid. Only Speckle Properties and User Content are accepted."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
result.Value.Properties = propGoo;
|
||||
result!.Properties = inputProperties;
|
||||
mutated = true;
|
||||
}
|
||||
|
||||
// process color (no mutation)
|
||||
if (inputColor != null)
|
||||
{
|
||||
result.Value.Color = inputColor;
|
||||
result!.Color = inputColor;
|
||||
}
|
||||
|
||||
// process material (no mutation)
|
||||
// process material (no mutation)
|
||||
if (inputMaterial != null)
|
||||
{
|
||||
SpeckleMaterialWrapperGoo matWrapperGoo = new();
|
||||
if (!matWrapperGoo.CastFrom(inputMaterial))
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Warning,
|
||||
$"Material input is not valid. Only Display Materials, Baked Model Materials, and Speckle Materials are accepted."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
result.Value.Material = matWrapperGoo.Value;
|
||||
result!.Material = inputMaterial.Value;
|
||||
}
|
||||
|
||||
// process application Id. Use a new appId if mutated, or if this is a new object
|
||||
result.Value.ApplicationId = mutated
|
||||
? Guid.NewGuid().ToString()
|
||||
: result.Value.ApplicationId ?? Guid.NewGuid().ToString();
|
||||
result!.ApplicationId = mutated ? Guid.NewGuid().ToString() : result!.ApplicationId ?? Guid.NewGuid().ToString();
|
||||
|
||||
// get the path
|
||||
string path =
|
||||
result.Value.Path.Count > 1
|
||||
? string.Join(Constants.LAYER_PATH_DELIMITER, result.Value.Path)
|
||||
: result.Value.Path.FirstOrDefault();
|
||||
result!.Path.Count > 1
|
||||
? string.Join(Constants.LAYER_PATH_DELIMITER, result!.Path)
|
||||
: result!.Path.FirstOrDefault();
|
||||
|
||||
// set all the data
|
||||
da.SetData(0, result);
|
||||
da.SetData(1, result.Value.GeometryBase);
|
||||
da.SetData(2, result.Value.Name);
|
||||
da.SetData(3, result.Value.Properties);
|
||||
da.SetData(4, result.Value.Color);
|
||||
da.SetData(5, result.Value.Material);
|
||||
da.SetData(0, result.CreateGoo());
|
||||
da.SetData(1, result.GeometryBase);
|
||||
da.SetData(2, result.Name);
|
||||
da.SetData(3, result.Properties);
|
||||
da.SetData(4, result.Color);
|
||||
da.SetData(5, result.Material);
|
||||
da.SetData(6, path);
|
||||
}
|
||||
|
||||
// keeps the geometry and wrapped base the same while assigning all other props from the inut wrapper
|
||||
private void MatchNonGeometryProps(SpeckleObjectWrapper wrapper, SpeckleObjectWrapper wrapperToMatch)
|
||||
{
|
||||
wrapper.Name = wrapperToMatch.Name;
|
||||
wrapper.ApplicationId = wrapperToMatch.ApplicationId;
|
||||
wrapper.Properties = wrapperToMatch.Properties;
|
||||
wrapper.Parent = wrapperToMatch.Parent;
|
||||
wrapper.Path = wrapperToMatch.Path;
|
||||
wrapper.Color = wrapperToMatch.Color;
|
||||
wrapper.Material = wrapperToMatch.Material;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ public class SpecklePropertiesPassthrough : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
protected override Bitmap Icon => Resources.speckle_properties_properties;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
public override GH_Exposure Exposure => GH_Exposure.tertiary;
|
||||
|
||||
private enum PropertyMode
|
||||
{
|
||||
|
||||
+23
-21
@@ -7,7 +7,6 @@ using GrasshopperAsyncComponent;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Analytics;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
@@ -420,35 +419,38 @@ public sealed class ReceiveComponentWorker : WorkerInstance<ReceiveAsyncComponen
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
LocalToGlobalUnpacker localToGlobalUnpacker = new();
|
||||
TraversalContextUnpacker traversalContextUnpacker = new();
|
||||
var unpackedRoot = scope.Get<RootObjectUnpacker>().Unpack(Root);
|
||||
|
||||
// "flatten" block instances
|
||||
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
|
||||
unpackedRoot.DefinitionProxies,
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
// separate atomic objects from block instances
|
||||
var (atomicObjects, blockInstances) = scope
|
||||
.Get<RootObjectUnpacker>()
|
||||
.SplitAtomicObjectsAndInstances(unpackedRoot.ObjectsToConvert);
|
||||
|
||||
// initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
// TODO: unpack colors and render materials
|
||||
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
|
||||
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
GrasshopperCollectionRebuilder collectionRebuilder =
|
||||
new((Root as Collection) ?? new Collection() { name = "unnamed" });
|
||||
|
||||
LocalToGlobalMapHandler mapHandler =
|
||||
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
|
||||
|
||||
int count = 0;
|
||||
int total = localToGlobalMaps.Count;
|
||||
|
||||
foreach (var map in localToGlobalMaps)
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
{
|
||||
mapHandler.CreateGrasshopperObjectFromMap(map);
|
||||
count++;
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
|
||||
// TODO: If we have NodeRun events later, better to have `ComponentTracker` to use across components
|
||||
|
||||
+22
-16
@@ -2,7 +2,6 @@ using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Analytics;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.Components.BaseComponents;
|
||||
@@ -159,32 +158,39 @@ public class ReceiveComponent : SpeckleTaskCapableComponent<ReceiveComponentInpu
|
||||
|
||||
// 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()
|
||||
// split atomic objects from block components before conversion
|
||||
var (atomicObjects, blockInstances) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
// unpack colors and render materials
|
||||
GrasshopperColorUnpacker colorUnpacker = new(unpackedRoot);
|
||||
GrasshopperMaterialUnpacker materialUnpacker = new(unpackedRoot);
|
||||
// Initialize unpackers and collection builder
|
||||
var colorUnpacker = new GrasshopperColorUnpacker(unpackedRoot);
|
||||
var materialUnpacker = new GrasshopperMaterialUnpacker(unpackedRoot);
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection { name = "unnamed" }
|
||||
);
|
||||
|
||||
GrasshopperCollectionRebuilder collectionRebuilder =
|
||||
new((root as Collection) ?? new Collection() { name = "unnamed" });
|
||||
// convert atomic objects directly
|
||||
var mapHandler = new LocalToGlobalMapHandler(
|
||||
traversalContextUnpacker,
|
||||
collectionRebuilder,
|
||||
colorUnpacker,
|
||||
materialUnpacker
|
||||
);
|
||||
|
||||
LocalToGlobalMapHandler mapHandler =
|
||||
new(traversalContextUnpacker, collectionRebuilder, colorUnpacker, materialUnpacker);
|
||||
|
||||
foreach (var map in localToGlobalMaps)
|
||||
foreach (var atomicContext in atomicObjects)
|
||||
{
|
||||
mapHandler.CreateGrasshopperObjectFromMap(map);
|
||||
mapHandler.ConvertAtomicObject(atomicContext);
|
||||
}
|
||||
|
||||
// process block instances using converted atomic objects
|
||||
// block processing needs converted objects, but object filtering needs block definitions.
|
||||
mapHandler.ConvertBlockInstances(blockInstances, unpackedRoot.DefinitionProxies);
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo };
|
||||
|
||||
+1
-1
@@ -425,7 +425,7 @@ public class SendComponentWorker : WorkerInstance<SendAsyncComponent>
|
||||
result.VersionId
|
||||
);
|
||||
OutputParam = createdVersion;
|
||||
Parent.Url = $"{createdVersion.Account.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
Parent.Url = $"{createdVersion.Account.Server}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ public class SendComponent : SpeckleTaskCapableComponent<SendComponentInput, Sen
|
||||
sendInfo.ProjectId,
|
||||
sendInfo.ModelId
|
||||
);
|
||||
Url = $"{sendInfo.Account.serverInfo.url}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
Url = $"{sendInfo.Account.serverInfo.url}/projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}";
|
||||
return new SendComponentOutput(createdVersionResource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +96,67 @@ public static class GrasshopperHelpers
|
||||
return t;
|
||||
}
|
||||
|
||||
public static Matrix4x4 TransformToMatrix(Transform rhinoTransform, string? units)
|
||||
{
|
||||
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
|
||||
var conversionFactor = Units.GetConversionFactor(currentDoc.ModelUnitSystem.ToSpeckleString(), units);
|
||||
|
||||
var m = new Matrix4x4
|
||||
{
|
||||
M11 = rhinoTransform.M00,
|
||||
M12 = rhinoTransform.M01,
|
||||
M13 = rhinoTransform.M02,
|
||||
M14 = rhinoTransform.M03 * conversionFactor,
|
||||
|
||||
M21 = rhinoTransform.M10,
|
||||
M22 = rhinoTransform.M11,
|
||||
M23 = rhinoTransform.M12,
|
||||
M24 = rhinoTransform.M13 * conversionFactor,
|
||||
|
||||
M31 = rhinoTransform.M20,
|
||||
M32 = rhinoTransform.M21,
|
||||
M33 = rhinoTransform.M22,
|
||||
M34 = rhinoTransform.M23 * conversionFactor,
|
||||
|
||||
M41 = rhinoTransform.M30,
|
||||
M42 = rhinoTransform.M31,
|
||||
M43 = rhinoTransform.M32,
|
||||
M44 = rhinoTransform.M33
|
||||
};
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to cast an IGH_Goo to a Speckle Object Wrapper
|
||||
/// </summary>
|
||||
/// <param name="goo"></param>
|
||||
/// <returns>A reference to the Speckle Object Wrapper from the goo, if any</returns>
|
||||
/// <remarks>This method **does not** deep copy the return value</remarks>
|
||||
public static SpeckleObjectWrapper? ToSpeckleObjectWrapper(this IGH_Goo goo)
|
||||
{
|
||||
SpeckleBlockInstanceWrapperGoo instanceGoo = new();
|
||||
if (instanceGoo.CastFrom(goo))
|
||||
{
|
||||
return instanceGoo.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpeckleObjectWrapperGoo objGoo = new();
|
||||
return objGoo.CastFrom(goo) ? objGoo.Value : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to cast the goo to a geometry base object.
|
||||
/// </summary>
|
||||
/// <param name="geoGeo"></param>
|
||||
/// <param name="geoGoo"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SpeckleException">If it fails to cast</exception>
|
||||
public static GeometryBase GeometricGooToGeometryBase(this IGH_GeometricGoo geoGeo)
|
||||
public static GeometryBase ToGeometryBase(this IGH_GeometricGoo geoGoo)
|
||||
{
|
||||
// note: some objects (like text entities) can have multiple properties of name "Value"
|
||||
var value = geoGeo.GetType().GetProperties().FirstOrDefault(x => x.Name == "Value")?.GetValue(geoGeo);
|
||||
var value = geoGoo.GetType().GetProperties().FirstOrDefault(x => x.Name == "Value")?.GetValue(geoGoo);
|
||||
switch (value)
|
||||
{
|
||||
case GeometryBase gb:
|
||||
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// Reconstructs block instances and definitions from received proxies back into Grasshopper wrapper objects.
|
||||
/// Tracks and returns object IDs consumed by block definitions to prevent duplication in collection hierarchy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Geometry objects that define blocks must already be converted and present in convertedObjectsMap at this stage.
|
||||
/// Follows Rhino's pattern where objects consumed by block definitions should not appear as standalone objects.
|
||||
/// </remarks>
|
||||
internal sealed class GrasshopperBlockUnpacker
|
||||
{
|
||||
private readonly TraversalContextUnpacker _traversalContextUnpacker;
|
||||
private readonly GrasshopperColorUnpacker _colorUnpacker;
|
||||
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
|
||||
|
||||
public GrasshopperBlockUnpacker(
|
||||
TraversalContextUnpacker traversalContextUnpacker,
|
||||
GrasshopperColorUnpacker colorUnpacker,
|
||||
GrasshopperMaterialUnpacker materialUnpacker
|
||||
)
|
||||
{
|
||||
_traversalContextUnpacker = traversalContextUnpacker;
|
||||
_colorUnpacker = colorUnpacker;
|
||||
_materialUnpacker = materialUnpacker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates block definitions and instances from receive pipeline, returning consumed object IDs.
|
||||
/// </summary>
|
||||
/// <returns>Set of object IDs that have been consumed by block definitions and should not appear standalone</returns>
|
||||
public HashSet<string> UnpackBlocks(
|
||||
IReadOnlyCollection<TraversalContext> blockComponents,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies,
|
||||
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder
|
||||
)
|
||||
{
|
||||
var consumedObjectIds = new HashSet<string>();
|
||||
var sortedComponents = ExtractAndSortBlocks(blockComponents, definitionProxies);
|
||||
|
||||
CreateBlocksInDependencyOrder(sortedComponents, convertedObjectsMap, collectionRebuilder, consumedObjectIds);
|
||||
|
||||
return consumedObjectIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts blocks from TraversalContext and adds metadata definitions, then sorts by depth.
|
||||
/// Deepest definitions first, then instances, to handle nested hierarchies correctly.
|
||||
/// </summary>
|
||||
private List<(Collection[] path, IInstanceComponent component)> ExtractAndSortBlocks(
|
||||
IReadOnlyCollection<TraversalContext> blockComponents,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
)
|
||||
{
|
||||
var allComponents = new List<(Collection[] path, IInstanceComponent component)>();
|
||||
|
||||
// Extract instances from traversal contexts
|
||||
foreach (var traversalContext in blockComponents)
|
||||
{
|
||||
if (traversalContext.Current is IInstanceComponent instanceComponent)
|
||||
{
|
||||
var collectionPath = _traversalContextUnpacker.GetCollectionPath(traversalContext).ToArray();
|
||||
allComponents.Add((collectionPath, instanceComponent));
|
||||
}
|
||||
}
|
||||
|
||||
// Add definition proxies from metadata (these don't have collection paths)
|
||||
if (definitionProxies != null)
|
||||
{
|
||||
foreach (var definitionProxy in definitionProxies)
|
||||
{
|
||||
allComponents.Add((Array.Empty<Collection>(), definitionProxy));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by depth (deepest first) then by type (definitions before instances)
|
||||
return allComponents
|
||||
.OrderByDescending(x => x.component.maxDepth)
|
||||
.ThenBy(x => x.component is InstanceDefinitionProxy ? 0 : 1)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates definitions and instances in dependency order, populating convertedObjectsMap
|
||||
/// with instances as they're created (following Rhino's applicationIdMap pattern).
|
||||
/// </summary>
|
||||
private void CreateBlocksInDependencyOrder(
|
||||
List<(Collection[] path, IInstanceComponent component)> sortedComponents,
|
||||
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder,
|
||||
HashSet<string> consumedObjectIds
|
||||
)
|
||||
{
|
||||
var definitions = new Dictionary<string, SpeckleBlockDefinitionWrapper>();
|
||||
|
||||
// NOTE: This relies on ExtractAndSortBlocks to have done its job correctly!
|
||||
foreach (var (collectionPath, component) in sortedComponents)
|
||||
{
|
||||
if (component is InstanceDefinitionProxy definitionProxy)
|
||||
{
|
||||
// Create definition using current state of convertedObjectsMap
|
||||
var definitionId = definitionProxy.applicationId ?? definitionProxy.id ?? Guid.NewGuid().ToString();
|
||||
var definition = CreateBlockDefinitionWrapper(
|
||||
definitionProxy,
|
||||
definitionId,
|
||||
convertedObjectsMap,
|
||||
consumedObjectIds
|
||||
);
|
||||
if (definition != null)
|
||||
{
|
||||
definitions[definitionId] = definition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: throw?
|
||||
}
|
||||
}
|
||||
else if (component is InstanceProxy instanceProxy)
|
||||
{
|
||||
// Create instance using available definitions
|
||||
string instanceId = instanceProxy.applicationId ?? instanceProxy.id ?? Guid.NewGuid().ToString();
|
||||
SpeckleBlockInstanceWrapper? instance = CreateBlockInstanceWrapper(
|
||||
instanceProxy,
|
||||
instanceId,
|
||||
definitions,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
if (instance != null)
|
||||
{
|
||||
AddInstanceToCollection(instance, collectionPath, collectionRebuilder);
|
||||
convertedObjectsMap[instanceId] = instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: throw?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SpeckleBlockDefinitionWrapper"/> from its proxy using pre-converted defining objects.
|
||||
/// Tracks consumed object IDs to prevent duplication in collection hierarchy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Objects used in block definitions are considered "consumed" and should not appear as standalone objects,
|
||||
/// matching Rhino's behavior where doc.Objects.Delete() removes consumed objects after block creation.
|
||||
/// </remarks>
|
||||
private SpeckleBlockDefinitionWrapper? CreateBlockDefinitionWrapper(
|
||||
InstanceDefinitionProxy definitionProxy,
|
||||
string definitionId,
|
||||
Dictionary<string, SpeckleObjectWrapper> convertedObjectsMap,
|
||||
HashSet<string> consumedObjectIds
|
||||
)
|
||||
{
|
||||
var definitionObjects = new List<SpeckleObjectWrapper>();
|
||||
var currentDefinitionObjectIds = new HashSet<string>();
|
||||
|
||||
foreach (var objectId in definitionProxy.objects)
|
||||
{
|
||||
if (convertedObjectsMap.TryGetValue(objectId, out var convertedObject))
|
||||
{
|
||||
definitionObjects.Add(convertedObject);
|
||||
currentDefinitionObjectIds.Add(objectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: throw?
|
||||
}
|
||||
}
|
||||
|
||||
// Only create definition if we have objects
|
||||
if (definitionObjects.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Track consumed objects (matches Rhino's consumedObjectIds.UnionWith pattern)
|
||||
consumedObjectIds.UnionWith(currentDefinitionObjectIds);
|
||||
|
||||
return new SpeckleBlockDefinitionWrapper
|
||||
{
|
||||
Base = definitionProxy,
|
||||
Name = definitionProxy.name,
|
||||
Objects = definitionObjects,
|
||||
ApplicationId = definitionId
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SpeckleBlockInstanceWrapper"/> from its proxy using.
|
||||
/// </summary>
|
||||
private SpeckleBlockInstanceWrapper? CreateBlockInstanceWrapper(
|
||||
InstanceProxy instanceProxy,
|
||||
string instanceId,
|
||||
Dictionary<string, SpeckleBlockDefinitionWrapper> definitions,
|
||||
GrasshopperColorUnpacker colorUnpacker,
|
||||
GrasshopperMaterialUnpacker materialUnpacker
|
||||
)
|
||||
{
|
||||
// Find the referenced definition
|
||||
if (!definitions.TryGetValue(instanceProxy.definitionId, out var definition))
|
||||
{
|
||||
return null; // Definition not found or failed to build
|
||||
}
|
||||
|
||||
Transform transform = GrasshopperHelpers.MatrixToTransform(instanceProxy.transform, instanceProxy.units);
|
||||
return new SpeckleBlockInstanceWrapper
|
||||
{
|
||||
Base = instanceProxy,
|
||||
Name = instanceProxy["name"] as string ?? "",
|
||||
ApplicationId = instanceId,
|
||||
Transform = transform,
|
||||
Definition = definition,
|
||||
GeometryBase = new InstanceReferenceGeometry(Guid.Empty, transform), //Instances shouldn't be using this except for the filter objects node,
|
||||
Color = colorUnpacker.Cache.TryGetValue(instanceProxy.applicationId ?? "", out var cachedInstanceColor)
|
||||
? cachedInstanceColor
|
||||
: null,
|
||||
Material = materialUnpacker.Cache.TryGetValue(instanceProxy.applicationId ?? "", out var cachedInstanceMaterial)
|
||||
? cachedInstanceMaterial
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an instance to the collection and sets up hierarchy relationships.
|
||||
/// </summary>
|
||||
private void AddInstanceToCollection(
|
||||
SpeckleBlockInstanceWrapper instance,
|
||||
Collection[] collectionPath,
|
||||
GrasshopperCollectionRebuilder collectionRebuilder
|
||||
)
|
||||
{
|
||||
var pathList = collectionPath.ToList();
|
||||
|
||||
// Get or create the target collection
|
||||
var targetCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
pathList,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
// Set up instance hierarchy properties
|
||||
instance.Path = pathList.Select(c => c.name).ToList();
|
||||
instance.Parent = targetCollection;
|
||||
|
||||
// Add to collection
|
||||
targetCollection.Elements.Add(instance);
|
||||
}
|
||||
}
|
||||
+32
-15
@@ -31,21 +31,6 @@ internal sealed class GrasshopperCollectionRebuilder
|
||||
GrasshopperMaterialUnpacker materialUnpacker
|
||||
)
|
||||
{
|
||||
// add the object color and material
|
||||
speckleGrasshopperObjectWrapper.Color = colorUnpacker.Cache.TryGetValue(
|
||||
speckleGrasshopperObjectWrapper.Base.applicationId ?? "",
|
||||
out var cachedColor
|
||||
)
|
||||
? cachedColor
|
||||
: null;
|
||||
|
||||
speckleGrasshopperObjectWrapper.Material = materialUnpacker.Cache.TryGetValue(
|
||||
speckleGrasshopperObjectWrapper.Base.applicationId ?? "",
|
||||
out var cachedMaterial
|
||||
)
|
||||
? cachedMaterial
|
||||
: null;
|
||||
|
||||
var collWrapper = GetOrCreateSpeckleCollectionFromPath(collectionPath, colorUnpacker, materialUnpacker);
|
||||
collWrapper.Elements.Add(speckleGrasshopperObjectWrapper);
|
||||
}
|
||||
@@ -110,4 +95,36 @@ internal sealed class GrasshopperCollectionRebuilder
|
||||
|
||||
return previousCollectionWrapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes consumed objects from the collection hierarchy.
|
||||
/// Matches Rhino's pattern: createdObjectIds.RemoveWhere(id => consumedObjectIds.Contains(id))
|
||||
/// </summary>
|
||||
/// <param name="consumedObjectIds">Set of object IDs that have been consumed by block definitions</param>
|
||||
public void RemoveConsumedObjects(HashSet<string> consumedObjectIds)
|
||||
{
|
||||
if (consumedObjectIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveConsumedObjectsFromCollection(RootCollectionWrapper, consumedObjectIds);
|
||||
}
|
||||
|
||||
private static void RemoveConsumedObjectsFromCollection(
|
||||
SpeckleCollectionWrapper collection,
|
||||
HashSet<string> consumedObjectIds
|
||||
)
|
||||
{
|
||||
// Remove consumed objects from this level
|
||||
collection.Elements.RemoveAll(element =>
|
||||
element is SpeckleObjectWrapper obj && obj.ApplicationId != null && consumedObjectIds.Contains(obj.ApplicationId)
|
||||
);
|
||||
|
||||
// Recurse into child collections
|
||||
foreach (var childCollection in collection.Elements.OfType<SpeckleCollectionWrapper>())
|
||||
{
|
||||
RemoveConsumedObjectsFromCollection(childCollection, consumedObjectIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+79
-47
@@ -1,17 +1,27 @@
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// Handles conversion of atomic objects from TraversalContexts into Grasshopper wrapper objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Follows Rhino's approach: atomic objects are converted directly without pre-transformation,
|
||||
/// with instance transformations handled separately during block reconstruction. Implements consumedObjectIds
|
||||
/// tracking to prevent objects consumed by block definitions from appearing as standalone objects.
|
||||
/// </remarks>
|
||||
internal sealed class LocalToGlobalMapHandler
|
||||
{
|
||||
private readonly TraversalContextUnpacker _traversalContextUnpacker;
|
||||
public Dictionary<string, SpeckleObjectWrapper> ConvertedObjectsMap { get; } = new();
|
||||
public readonly GrasshopperCollectionRebuilder CollectionRebuilder;
|
||||
|
||||
private readonly TraversalContextUnpacker _traversalContextUnpacker;
|
||||
private readonly GrasshopperColorUnpacker _colorUnpacker;
|
||||
private readonly GrasshopperMaterialUnpacker _materialUnpacker;
|
||||
|
||||
@@ -29,72 +39,61 @@ internal sealed class LocalToGlobalMapHandler
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a grasshopper speckle object from a local to global map, and appends it to the collection rebuilder.
|
||||
/// POC: TODO: this should decimate dataobjects into their display values, while storing the same properties etc
|
||||
/// This is because we don't want to be storing one-to-many maps in the object wrapper, this will complicate mutations
|
||||
/// Converts atomic object from TraversalContext to SpeckleObjectWrapper.
|
||||
/// </summary>
|
||||
/// <param name="map"></param>
|
||||
///
|
||||
public void CreateGrasshopperObjectFromMap(LocalToGlobalMap map)
|
||||
public void ConvertAtomicObject(TraversalContext atomicContext)
|
||||
{
|
||||
var obj = atomicContext.Current;
|
||||
var objId = obj.applicationId ?? obj.id;
|
||||
|
||||
if (objId == null || ConvertedObjectsMap.ContainsKey(objId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
List<(GeometryBase, Base)> converted = SpeckleConversionContext.ConvertToHost(map.AtomicObject);
|
||||
List<(GeometryBase, Base)> converted = SpeckleConversionContext.ConvertToHost(obj);
|
||||
|
||||
if (converted.Count == 0)
|
||||
{
|
||||
return; // TODO: throw?
|
||||
return;
|
||||
}
|
||||
|
||||
// get the units and transform by matrices in the map
|
||||
string units = map.AtomicObject["units"] is string u
|
||||
? u
|
||||
: converted.First().Item2["units"] is string convertedU
|
||||
? convertedU
|
||||
: "none";
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(atomicContext).ToList();
|
||||
|
||||
foreach (var matrix in map.Matrix)
|
||||
{
|
||||
var mat = GrasshopperHelpers.MatrixToTransform(matrix, units);
|
||||
converted.ForEach(res => res.Item1.Transform(mat));
|
||||
}
|
||||
|
||||
// get the collection
|
||||
var path = _traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
|
||||
SpeckleCollectionWrapper objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
// Always create collection - consumed objects will be cleaned up later
|
||||
var objectCollection = CollectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path,
|
||||
_colorUnpacker,
|
||||
_materialUnpacker
|
||||
);
|
||||
|
||||
// get the name and properties
|
||||
// Extract name and properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
string name = "";
|
||||
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
|
||||
|
||||
if (obj is Speckle.Objects.Data.DataObject dataObject)
|
||||
{
|
||||
propertyGroup.CastFrom(da.properties);
|
||||
name = da.name;
|
||||
propertyGroup.CastFrom(dataObject.properties);
|
||||
name = dataObject.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (map.AtomicObject[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
if (obj[Constants.PROPERTIES_PROP] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
if (map.AtomicObject[Constants.NAME_PROP] is string n)
|
||||
if (obj[Constants.NAME_PROP] is string objName)
|
||||
{
|
||||
name = n;
|
||||
name = objName;
|
||||
}
|
||||
}
|
||||
|
||||
// create objects for every value in converted. This is where one to many is not handled very nicely.
|
||||
// we will decimate dataobjects and multi-object display values here
|
||||
// meaning for every value in the display value, we will create a grasshopper wrapper, while preserving app id, name, props, etc
|
||||
// similar objects will be re-packaged on send
|
||||
foreach ((GeometryBase geometryBase, Base original) in converted)
|
||||
{
|
||||
var gh = new SpeckleObjectWrapper()
|
||||
var wrapper = new SpeckleObjectWrapper()
|
||||
{
|
||||
Base = original,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
@@ -102,18 +101,51 @@ internal sealed class LocalToGlobalMapHandler
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = name,
|
||||
Color = null,
|
||||
Material = null,
|
||||
WrapperGuid = map.AtomicObject.applicationId,
|
||||
ApplicationId = original.applicationId ?? Guid.NewGuid().ToString() // create if none
|
||||
Color = _colorUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjColor)
|
||||
? cachedObjColor
|
||||
: null,
|
||||
Material = _materialUnpacker.Cache.TryGetValue(original.applicationId ?? "", out var cachedObjMaterial)
|
||||
? cachedObjMaterial
|
||||
: null,
|
||||
ApplicationId = objId
|
||||
};
|
||||
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(gh, path, _colorUnpacker, _materialUnpacker);
|
||||
// Always add to both map and collections
|
||||
ConvertedObjectsMap[objId] = wrapper;
|
||||
CollectionRebuilder.AppendSpeckleGrasshopperObject(wrapper, path, _colorUnpacker, _materialUnpacker);
|
||||
}
|
||||
}
|
||||
catch (ConversionException)
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// TODO
|
||||
// TODO: throw?
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts block instances and definitions from traversal contexts into Grasshopper wrapper objects.
|
||||
/// Automatically handles cleanup of consumed objects from the collection hierarchy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deliberately handles both block conversion AND consumed object cleanup in a single operation.
|
||||
/// Too much, I know, BUT it ensures the cleanup always occurs immediately after block processing without
|
||||
/// requiring receive components to call a separate cleanup method in the correct order.
|
||||
/// </remarks>
|
||||
public void ConvertBlockInstances(
|
||||
IReadOnlyCollection<TraversalContext> blocks,
|
||||
IReadOnlyCollection<InstanceDefinitionProxy>? definitionProxies
|
||||
)
|
||||
{
|
||||
var blockUnpacker = new GrasshopperBlockUnpacker(_traversalContextUnpacker, _colorUnpacker, _materialUnpacker);
|
||||
|
||||
// Get consumed object IDs from unpacker
|
||||
var consumedObjectIds = blockUnpacker.UnpackBlocks(
|
||||
blocks,
|
||||
definitionProxies,
|
||||
ConvertedObjectsMap,
|
||||
CollectionRebuilder
|
||||
);
|
||||
|
||||
// Clean up consumed objects from collections
|
||||
CollectionRebuilder.RemoveConsumedObjects(consumedObjectIds);
|
||||
}
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
|
||||
|
||||
/// <summary>
|
||||
/// Processes block instances and their nested definitions for publish.
|
||||
/// Handles nested definitions and depth tracking (injected InstanceObjectsManager).
|
||||
/// </summary>
|
||||
internal sealed class GrasshopperBlockPacker
|
||||
{
|
||||
private readonly IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> _instanceObjectsManager;
|
||||
|
||||
public GrasshopperBlockPacker(IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> instanceObjectsManager)
|
||||
{
|
||||
_instanceObjectsManager = instanceObjectsManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a map of instance definition id to instance definition proxy
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Storing <see cref="InstanceDefinitionProxy"/> directly and not the wrapper (matching Rhino).
|
||||
/// </remarks>
|
||||
public Dictionary<string, InstanceDefinitionProxy> InstanceDefinitionProxies { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Processes a <see cref="SpeckleBlockInstanceWrapper"/> by tracking it in InstanceObjectsManager and recursively
|
||||
/// processing its definition. Handles depth calculation for nested block hierarchies.
|
||||
/// </summary>
|
||||
/// <param name="blockInstance">The block instance to process</param>
|
||||
/// <param name="depth">Current nesting depth (0 = top level, increases for nested instances)</param>
|
||||
public List<SpeckleObjectWrapper>? ProcessInstance(SpeckleBlockInstanceWrapper? blockInstance, int depth = 0)
|
||||
{
|
||||
if (blockInstance?.Definition == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
blockInstance.ApplicationId ??= Guid.NewGuid().ToString();
|
||||
var instanceId = blockInstance.ApplicationId;
|
||||
|
||||
blockInstance.InstanceProxy.maxDepth = depth;
|
||||
_instanceObjectsManager.AddInstanceProxy(instanceId, blockInstance.InstanceProxy);
|
||||
|
||||
return ProcessDefinition(blockInstance.Definition, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a block definition, adding it and its objects to InstanceObjectsManager.
|
||||
/// Updates maxDepth for existing definitions when encountered at greater depths.
|
||||
/// </summary>
|
||||
private List<SpeckleObjectWrapper>? ProcessDefinition(SpeckleBlockDefinitionWrapper definition, int depth = 0)
|
||||
{
|
||||
// Use wrapper's id as definitive identifier. Create if empty.
|
||||
definition.ApplicationId ??= Guid.NewGuid().ToString();
|
||||
string definitionId = definition.ApplicationId;
|
||||
|
||||
// Check if already processed using InstanceObjectsManager
|
||||
if (
|
||||
_instanceObjectsManager.TryGetInstanceDefinitionProxy(definitionId, out InstanceDefinitionProxy? definitionProxy)
|
||||
)
|
||||
{
|
||||
int depthDifference = depth - definitionProxy.maxDepth;
|
||||
if (depthDifference > 0)
|
||||
{
|
||||
// Use InstanceObjectsManager to update max depth
|
||||
_instanceObjectsManager.UpdateChildrenMaxDepth(definitionProxy, depthDifference);
|
||||
}
|
||||
|
||||
return null; // this prevents infinite recursion
|
||||
}
|
||||
|
||||
// Process objects recursively
|
||||
var objectsToAdd = new List<SpeckleObjectWrapper>();
|
||||
var currentObjectIds = new List<string>(); // Track current object IDs for proxy update
|
||||
|
||||
foreach (var obj in definition.Objects)
|
||||
{
|
||||
if (obj.ApplicationId == null) // we should be loud about this. If gone through all casting etc. this should be complete!
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Object in block definition '{definition.Name}' missing ApplicationId during send operation. This indicates a processing pipeline error."
|
||||
);
|
||||
}
|
||||
|
||||
objectsToAdd.Add(obj);
|
||||
currentObjectIds.Add(obj.ApplicationId); // Collect current ID
|
||||
_instanceObjectsManager.AddAtomicObject(obj.ApplicationId, obj);
|
||||
|
||||
if (obj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
var nestedObjects = ProcessInstance(nestedInstance, depth + 1);
|
||||
if (nestedObjects != null)
|
||||
{
|
||||
objectsToAdd.AddRange(nestedObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add definition to InstanceObjectsManager
|
||||
definition.InstanceDefinitionProxy.objects = currentObjectIds;
|
||||
definition.InstanceDefinitionProxy.maxDepth = depth;
|
||||
_instanceObjectsManager.AddDefinitionProxy(definitionId, definition.InstanceDefinitionProxy);
|
||||
InstanceDefinitionProxies[definitionId] = definition.InstanceDefinitionProxy;
|
||||
|
||||
return objectsToAdd;
|
||||
}
|
||||
}
|
||||
+94
-40
@@ -1,14 +1,25 @@
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using DataObject = Speckle.Objects.Data.DataObject;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Operations.Send;
|
||||
|
||||
public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollectionWrapperGoo>
|
||||
public class GrasshopperRootObjectBuilder : IRootObjectBuilder<SpeckleCollectionWrapperGoo>
|
||||
{
|
||||
private readonly IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> _instanceObjectsManager;
|
||||
|
||||
// each Build() call gets a fresh scoped IInstanceObjectsManager
|
||||
public GrasshopperRootObjectBuilder(
|
||||
IInstanceObjectsManager<SpeckleObjectWrapper, List<string>> instanceObjectsManager
|
||||
)
|
||||
{
|
||||
_instanceObjectsManager = instanceObjectsManager;
|
||||
}
|
||||
|
||||
// Keeps track of the wrapper applicationId of processed objects for send.
|
||||
// This is used to keep track of the following situations:
|
||||
// 1 - objects with the same name, properties, and application id are packaged into a data object. this can happen when receiving data objects.
|
||||
@@ -33,13 +44,15 @@ public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollecti
|
||||
// create packers for colors and render materials
|
||||
GrasshopperColorPacker colorPacker = new();
|
||||
GrasshopperMaterialPacker materialPacker = new();
|
||||
GrasshopperBlockPacker blockPacker = new(_instanceObjectsManager);
|
||||
|
||||
// unwrap the input collection to remove all wrappers
|
||||
Collection root = Unwrap(inputCollectionGoo.Value, colorPacker, materialPacker);
|
||||
Collection root = Unwrap(inputCollectionGoo.Value, colorPacker, materialPacker, blockPacker);
|
||||
|
||||
// add proxies
|
||||
root[ProxyKeys.COLOR] = colorPacker.ColorProxies.Values.ToList();
|
||||
root[ProxyKeys.RENDER_MATERIAL] = materialPacker.RenderMaterialProxies.Values.ToList();
|
||||
root[ProxyKeys.INSTANCE_DEFINITION] = blockPacker.InstanceDefinitionProxies.Values.ToList();
|
||||
|
||||
// TODO: Not getting any conversion results yet
|
||||
var result = new RootObjectBuilderResult(root, []);
|
||||
@@ -47,46 +60,52 @@ public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollecti
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
// Unwraps collection wrappers and object wrapppers.
|
||||
// Also packs colors and Render Materials into proxies while unwrapping.
|
||||
// Unwraps collection wrappers and object wrappers.
|
||||
// Also packs colors, render materials and block definitions into proxies while unwrapping.
|
||||
private Collection Unwrap(
|
||||
SpeckleCollectionWrapper wrapper,
|
||||
GrasshopperColorPacker colorPacker,
|
||||
GrasshopperMaterialPacker materialPacker
|
||||
GrasshopperMaterialPacker materialPacker,
|
||||
GrasshopperBlockPacker blockPacker
|
||||
)
|
||||
{
|
||||
Collection currentColl = wrapper.Collection;
|
||||
|
||||
// unpack color and render material
|
||||
// unpack color, render material and block definitions
|
||||
colorPacker.ProcessColor(wrapper.ApplicationId, wrapper.Color);
|
||||
materialPacker.ProcessMaterial(wrapper.ApplicationId, wrapper.Material);
|
||||
|
||||
// iterate through this wrapper's elements to unwrap children
|
||||
// HashSet<string> collObjectIds = new();
|
||||
foreach (SpeckleWrapper wrapperElement in wrapper.Elements)
|
||||
foreach (ISpeckleCollectionObject element in wrapper.Elements)
|
||||
{
|
||||
if (wrapperElement is SpeckleCollectionWrapper collWrapper)
|
||||
switch (element)
|
||||
{
|
||||
// create an application id for this collection if none exists. This will be used for color and render material proxies
|
||||
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
|
||||
case SpeckleCollectionWrapper collWrapper:
|
||||
// create an application id for this collection if none exists. This will be used for color and render material proxies
|
||||
collWrapper.ApplicationId ??= collWrapper.GetSpeckleApplicationId();
|
||||
|
||||
// add to collection and continue unwrap
|
||||
currentColl.elements.Add(collWrapper.Collection);
|
||||
Unwrap(collWrapper, colorPacker, materialPacker);
|
||||
}
|
||||
else if (wrapperElement is SpeckleObjectWrapper so)
|
||||
{
|
||||
// process the object first. This may result in application id mutations, so this must be done before processing color and materials.
|
||||
//ProcessObjectWrapper(so, ref collObjectIds);
|
||||
DataObject dataObject = ConvertWrappersToDataObject(
|
||||
new List<SpeckleObjectWrapper>() { so },
|
||||
Guid.NewGuid().ToString() // note: we are always generating a new id here, do *not* use the Base appid as this will cause conflicts in viewer for color and material proxy application
|
||||
);
|
||||
currentColl.elements.Add(dataObject);
|
||||
// add to collection and continue unwrap
|
||||
currentColl.elements.Add(collWrapper.Collection);
|
||||
Unwrap(collWrapper, colorPacker, materialPacker, blockPacker);
|
||||
break;
|
||||
|
||||
// unpack color and render material
|
||||
colorPacker.ProcessColor(so.ApplicationId, so.Color);
|
||||
materialPacker.ProcessMaterial(so.ApplicationId, so.Material);
|
||||
case SpeckleObjectWrapper so: // handles both SpeckleObjectWrapper and SpeckleBlockInstanceWrapper (inheritance)
|
||||
// convert wrapper to base and add to collection - common for all object wrappers
|
||||
Base objectBase = Unwrap(so);
|
||||
string applicationId = objectBase.applicationId!;
|
||||
currentColl.elements.Add(objectBase);
|
||||
|
||||
// do block instance specific stuff (if this object wrapper is actually a block instance)
|
||||
if (so is SpeckleBlockInstanceWrapper blockInstance)
|
||||
{
|
||||
ProcessBlockInstanceDefinition(blockInstance, colorPacker, materialPacker, blockPacker, currentColl);
|
||||
}
|
||||
|
||||
// process color and material for all object wrappers (including block instances)
|
||||
colorPacker.ProcessColor(applicationId, so.Color);
|
||||
materialPacker.ProcessMaterial(applicationId, so.Material);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,21 +124,56 @@ public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollecti
|
||||
return currentColl;
|
||||
}
|
||||
|
||||
// creates a data object from the input wrappers.
|
||||
// assumes these wrappers have been processed for similarity, so that the name and props of all wrappers are the same.
|
||||
private DataObject ConvertWrappersToDataObject(List<SpeckleObjectWrapper> wrappers, string appId)
|
||||
/// <summary>
|
||||
/// Converts a <see cref="SpeckleObjectWrapper"/> to underlying Base object with dynamically attached properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POC: if we move properties assignment to auto set the wrapped base, we can get rid of this entirely!
|
||||
/// </remarks>
|
||||
private Base Unwrap(SpeckleObjectWrapper wrapper)
|
||||
{
|
||||
Dictionary<string, object?> props = new();
|
||||
|
||||
wrappers.First().Properties.CastTo<Dictionary<string, object?>>(ref props);
|
||||
|
||||
return new()
|
||||
Dictionary<string, object?> props = [];
|
||||
Base baseObject = wrapper.Base;
|
||||
if (wrapper.Properties.CastTo(ref props))
|
||||
{
|
||||
displayValue = wrappers.Select(o => o.Base).ToList(),
|
||||
name = wrappers.First().Name,
|
||||
properties = props,
|
||||
applicationId = appId
|
||||
};
|
||||
baseObject["properties"] = props; // setting props here on base since it's not auto-set, like name and appid
|
||||
}
|
||||
|
||||
return baseObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a block instance's definition and adds the defining objects to the current collection.
|
||||
/// Handles nested block hierarchies and depth calculation via GrasshopperBlockPacker.
|
||||
/// </summary>
|
||||
private void ProcessBlockInstanceDefinition(
|
||||
SpeckleBlockInstanceWrapper blockInstance,
|
||||
GrasshopperColorPacker colorPacker,
|
||||
GrasshopperMaterialPacker materialPacker,
|
||||
GrasshopperBlockPacker blockPacker,
|
||||
Collection currentColl
|
||||
)
|
||||
{
|
||||
// NOTE: Depth calculation handled by GrasshopperBlockPacker.ProcessInstance()
|
||||
// Objects start with maxDepth=0, then updated during processing
|
||||
|
||||
// process block for definition collection and get defining objects
|
||||
var definitionObjects = blockPacker.ProcessInstance(blockInstance);
|
||||
|
||||
if (definitionObjects != null)
|
||||
{
|
||||
foreach (var definitionObject in definitionObjects)
|
||||
{
|
||||
Base defObjectBase = Unwrap(definitionObject);
|
||||
string applicationId = defObjectBase.applicationId!;
|
||||
|
||||
// just add to current collection for now
|
||||
currentColl.elements.Add(defObjectBase);
|
||||
|
||||
colorPacker.ProcessColor(applicationId, definitionObject.Color);
|
||||
materialPacker.ProcessMaterial(applicationId, definitionObject.Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,3 +4,10 @@ public interface ISpecklePropertyGoo
|
||||
{
|
||||
bool Equals(ISpecklePropertyGoo other);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"Design",
|
||||
"CA1040:Avoid empty interfaces",
|
||||
Justification = "Needed to identify acceptable values of objects in collections"
|
||||
)]
|
||||
public interface ISpeckleCollectionObject { }
|
||||
|
||||
-383
@@ -1,383 +0,0 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Layer = Rhino.DocObjects.Layer;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// A Wrapper class representing a Speckle Collection to Rhino Layer relationship.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When constructing, the following properties need to be set in order:
|
||||
/// <see cref="SpeckleWrapper.Base"/>, then <see cref="SpeckleWrapper.Name"/> and <see cref="SpeckleWrapper.ApplicationId"/>
|
||||
/// This is because chanbging the Name or ApplicationId will update Collection.
|
||||
/// </remarks>
|
||||
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
|
||||
public class SpeckleCollectionWrapper : SpeckleWrapper
|
||||
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
|
||||
{
|
||||
public override required Base Base
|
||||
{
|
||||
get => Collection;
|
||||
set
|
||||
{
|
||||
if (value is not Collection coll)
|
||||
{
|
||||
throw new ArgumentException("Cannot create collection wrapper from a non-Collection Base");
|
||||
}
|
||||
|
||||
Collection = coll;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection Collection { get; set; }
|
||||
|
||||
private List<string> StoredPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of Collection names that build up the path to this collection (inclusive of <see cref="SpeckleWrapper.Name"/>;
|
||||
/// </summary>
|
||||
/// <remarks>Setting this property will update all element paths inside <see cref="Elements"/></remarks>
|
||||
public required List<string> Path
|
||||
{
|
||||
get => StoredPath;
|
||||
set
|
||||
{
|
||||
StoredPath = value;
|
||||
OnPathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public List<SpeckleWrapper> Elements { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The Grasshopper Topology of this collection. This setter also sets the "topology" prop dynamicall on <see cref="Collection"/>
|
||||
/// </summary>
|
||||
public string? Topology
|
||||
{
|
||||
get => Collection[Constants.TOPOLOGY_PROP] as string;
|
||||
set => Collection[Constants.TOPOLOGY_PROP] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public required Color? Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The material of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public required SpeckleMaterialWrapper? Material { get; set; }
|
||||
|
||||
public override string ToString() => $"{Name} [{Elements.Count}]";
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to retrieve an existing Layer from the <see cref="Path"/>.
|
||||
/// </summary>
|
||||
/// <returns>Index of existing layer if found, or -1 if not.</returns>
|
||||
public int GetLayerIndex() => RhinoDoc.ActiveDoc.Layers.FindByFullPath(string.Join("::", Path), -1);
|
||||
|
||||
// updates the elements' paths inside this collection
|
||||
private void OnPathChanged()
|
||||
{
|
||||
var newPath = StoredPath.ToList();
|
||||
|
||||
// then update paths and parents of all children
|
||||
foreach (var element in Elements)
|
||||
{
|
||||
if (element is SpeckleObjectWrapper o)
|
||||
{
|
||||
o.Path = newPath;
|
||||
o.Parent = this;
|
||||
}
|
||||
else if (element is SpeckleCollectionWrapper c)
|
||||
{
|
||||
// don't forget to add the child collection name to the path
|
||||
newPath.Add(c.Name);
|
||||
c.Path = newPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SpeckleCollectionWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = new Collection(Collection.name) { applicationId = Collection.applicationId, id = Collection.id },
|
||||
Color = Color,
|
||||
Material = Material,
|
||||
ApplicationId = ApplicationId,
|
||||
Name = Name,
|
||||
Path = Path,
|
||||
Topology = Topology,
|
||||
Elements = Elements
|
||||
.Select(e =>
|
||||
e is SpeckleCollectionWrapper c
|
||||
? c.DeepCopy()
|
||||
: e is SpeckleObjectWrapper o
|
||||
? o.DeepCopy()
|
||||
: e
|
||||
)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Bakes this collection as a layer, in its path structure.
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="obj_ids"></param>
|
||||
/// <param name="bakeObjects"></param>
|
||||
/// <returns>The index of the baked layer</returns>
|
||||
public int Bake(RhinoDoc doc, List<Guid> obj_ids, bool bakeObjects, int parentLayerIndex = -1)
|
||||
{
|
||||
if (!LayerExists(doc, Path, out int currentLayerIndex))
|
||||
{
|
||||
if (parentLayerIndex != -1)
|
||||
{
|
||||
Guid parentLayerId = doc.Layers[parentLayerIndex].Id;
|
||||
currentLayerIndex = CreateLayer(doc, Collection.name, parentLayerId, Color);
|
||||
Guid currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
|
||||
obj_ids.Add(currentLayerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLayerIndex = CreateLayerByPath(doc, Path, Color, obj_ids);
|
||||
}
|
||||
}
|
||||
|
||||
// then bake elements in this collection
|
||||
foreach (var obj in Elements)
|
||||
{
|
||||
if (obj is SpeckleObjectWrapper so)
|
||||
{
|
||||
if (bakeObjects)
|
||||
{
|
||||
so.Bake(doc, obj_ids, currentLayerIndex, true);
|
||||
}
|
||||
}
|
||||
else if (obj is SpeckleCollectionWrapper c)
|
||||
{
|
||||
c.Bake(doc, obj_ids, bakeObjects, currentLayerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return currentLayerIndex;
|
||||
}
|
||||
|
||||
private bool LayerExists(RhinoDoc doc, List<string> path, out int layerIndex)
|
||||
{
|
||||
var fullPath = string.Join("::", path);
|
||||
layerIndex = doc.Layers.FindByFullPath(fullPath, -1);
|
||||
return layerIndex != -1;
|
||||
}
|
||||
|
||||
private int CreateLayer(RhinoDoc doc, string name, Guid parentId, Color? color)
|
||||
{
|
||||
Layer layer = new() { Name = name, ParentLayerId = parentId };
|
||||
if (color is not null)
|
||||
{
|
||||
layer.Color = color.Value;
|
||||
}
|
||||
|
||||
return doc.Layers.Add(layer);
|
||||
}
|
||||
|
||||
private int CreateLayerByPath(RhinoDoc doc, List<string> path, Color? color, List<Guid> obj_ids)
|
||||
{
|
||||
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, color);
|
||||
currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
|
||||
obj_ids.Add(currentLayerId);
|
||||
}
|
||||
|
||||
parentLayerIndex = currentLayerIndex;
|
||||
}
|
||||
|
||||
return parentLayerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SpeckleCollectionWrapperGoo : GH_Goo<SpeckleCollectionWrapper> //, IGH_PreviewData // can be made previewable later
|
||||
{
|
||||
public override IGH_Goo Duplicate() => new SpeckleCollectionWrapperGoo(Value.DeepCopy());
|
||||
|
||||
public override string ToString() => $@"Speckle Collection Goo [{m_value.Name} ({Value.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;
|
||||
}
|
||||
|
||||
// Handle case of model objects in rhino 8
|
||||
return CastFromModelLayer(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelLayer(object _) => false;
|
||||
|
||||
private bool CastToModelLayer<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
return CastToModelLayer(ref target);
|
||||
}
|
||||
|
||||
public SpeckleCollectionWrapperGoo() { }
|
||||
|
||||
public SpeckleCollectionWrapperGoo(SpeckleCollectionWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH_BakeAwareObject, IGH_PreviewObject
|
||||
{
|
||||
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",
|
||||
"A Speckle collection, corresponding to layers in Rhino",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("6E871D5B-B221-4992-882A-EFE6796F3010");
|
||||
protected override Bitmap Icon => Resources.speckle_param_collection;
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
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, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BoundingBox _clippingBox;
|
||||
public BoundingBox ClippingBox => _clippingBox;
|
||||
|
||||
bool IGH_PreviewObject.Hidden { get; set; }
|
||||
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
private List<SpeckleObjectWrapper> _previewObjects = new();
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
_previewObjects = new();
|
||||
_clippingBox = new();
|
||||
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleCollectionWrapperGoo goo)
|
||||
{
|
||||
FlattenForPreview(goo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (_previewObjects.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var elem in _previewObjects)
|
||||
{
|
||||
elem.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected()
|
||||
{
|
||||
return Attributes?.Parent?.Selected ?? false;
|
||||
}
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// todo?
|
||||
}
|
||||
|
||||
private void FlattenForPreview(SpeckleCollectionWrapper collWrapper)
|
||||
{
|
||||
foreach (var element in collWrapper.Elements)
|
||||
{
|
||||
if (element is SpeckleCollectionWrapper subCollWrapper)
|
||||
{
|
||||
FlattenForPreview(subCollWrapper);
|
||||
}
|
||||
|
||||
if (element is SpeckleObjectWrapper objWrapper)
|
||||
{
|
||||
_previewObjects.Add(objWrapper);
|
||||
var box = objWrapper.GeometryBase is null ? new() : objWrapper.GeometryBase.GetBoundingBox(false);
|
||||
_clippingBox.Union(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-287
@@ -1,287 +0,0 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models;
|
||||
using SpeckleRenderMaterial = Speckle.Objects.Other.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around a render material base object and its converted speckle equivalent.
|
||||
/// </summary>
|
||||
public class SpeckleMaterialWrapper : SpeckleWrapper
|
||||
{
|
||||
public override required Base Base
|
||||
{
|
||||
get => Material;
|
||||
set
|
||||
{
|
||||
if (value is not SpeckleRenderMaterial mat)
|
||||
{
|
||||
throw new ArgumentException("Cannot create material wrapper from a non-SpeckleRenderMaterial Base");
|
||||
}
|
||||
|
||||
Material = mat;
|
||||
}
|
||||
}
|
||||
|
||||
public SpeckleRenderMaterial Material { get; set; }
|
||||
|
||||
public required Material RhinoMaterial { get; set; }
|
||||
|
||||
// The guid of the rhino render material that corresponds to the rhino material, if it exists.
|
||||
public required Guid RhinoRenderMaterialId { get; set; }
|
||||
|
||||
public override string ToString() => $"Speckle Wrapper [{typeof(Material)}]";
|
||||
|
||||
/// <summary>
|
||||
/// Creates the material in the document
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="name">The name override, if any. Used for param baking where the nickname is changed by the user, or no name is available.</param>
|
||||
/// <returns>The index of the created material in the material table</returns>
|
||||
public int Bake(RhinoDoc doc, string? name = null)
|
||||
{
|
||||
Material bakeMaterial = new();
|
||||
bakeMaterial.CopyFrom(RhinoMaterial);
|
||||
|
||||
// set the material name
|
||||
// this should be the given name in the rhino material *unless* an override name is passed in
|
||||
if (name != null)
|
||||
{
|
||||
bakeMaterial.Name = name;
|
||||
}
|
||||
|
||||
return doc.Materials.Add(bakeMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SpeckleMaterialWrapperGoo : GH_Goo<SpeckleMaterialWrapper>
|
||||
{
|
||||
public override IGH_Goo Duplicate() => throw new NotImplementedException();
|
||||
|
||||
public override string ToString() => $@"Speckle Material Goo [{Value.Name}]";
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "Speckle render material wrapper";
|
||||
public override string TypeDescription => "A wrapper around speckle render materials.";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleMaterialWrapper speckleGrasshopperMaterial:
|
||||
Value = speckleGrasshopperMaterial;
|
||||
return true;
|
||||
case GH_Goo<SpeckleMaterialWrapper> speckleGrasshopperMaterialGoo:
|
||||
Value = speckleGrasshopperMaterialGoo.Value;
|
||||
return true;
|
||||
case GH_Material materialGoo:
|
||||
var gooMaterial = ToRhinoMaterial(materialGoo.Value);
|
||||
Value = new()
|
||||
{
|
||||
Base = ToSpeckleRenderMaterial(materialGoo.Value),
|
||||
Name = gooMaterial.Name,
|
||||
RhinoMaterial = gooMaterial,
|
||||
RhinoRenderMaterialId = Guid.Empty,
|
||||
};
|
||||
return true;
|
||||
case Material material:
|
||||
Value = new()
|
||||
{
|
||||
Base = ToSpeckleRenderMaterial(material),
|
||||
Name = material.Name,
|
||||
RhinoMaterial = material,
|
||||
RhinoRenderMaterialId = Guid.Empty
|
||||
};
|
||||
return true;
|
||||
case SpeckleRenderMaterial speckleMaterial:
|
||||
Value = new()
|
||||
{
|
||||
Base = speckleMaterial,
|
||||
Name = speckleMaterial.name,
|
||||
RhinoMaterial = ToRhinoMaterial(speckleMaterial),
|
||||
RhinoRenderMaterialId = Guid.Empty,
|
||||
ApplicationId = speckleMaterial.applicationId,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastFromModelRenderMaterial(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelRenderMaterial(object _) => false;
|
||||
|
||||
private bool CastToModelRenderMaterial<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GH_Material))
|
||||
{
|
||||
target = (T)(object)(new GH_Material() { Value = new(Value.RhinoMaterial) });
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastToModelRenderMaterial(ref target);
|
||||
}
|
||||
|
||||
public SpeckleMaterialWrapperGoo(SpeckleMaterialWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public SpeckleMaterialWrapperGoo() { }
|
||||
|
||||
private SpeckleRenderMaterial ToSpeckleRenderMaterial(Rhino.Display.DisplayMaterial mat)
|
||||
{
|
||||
SpeckleRenderMaterial speckleRenderMaterial =
|
||||
new()
|
||||
{
|
||||
name = mat.GetSpeckleApplicationId(),
|
||||
opacity = 1 - mat.Transparency,
|
||||
metalness = mat.Shine,
|
||||
diffuse = mat.Diffuse.ToArgb(),
|
||||
emissive = mat.Emission.ToArgb(),
|
||||
applicationId = mat.GetSpeckleApplicationId(),
|
||||
};
|
||||
|
||||
// add additional dynamic props for rhino material receive
|
||||
speckleRenderMaterial["specular"] = mat.Specular.ToArgb();
|
||||
speckleRenderMaterial["shine"] = mat.Shine;
|
||||
|
||||
return speckleRenderMaterial;
|
||||
}
|
||||
|
||||
private SpeckleRenderMaterial ToSpeckleRenderMaterial(Material mat)
|
||||
{
|
||||
SpeckleRenderMaterial speckleRenderMaterial =
|
||||
new()
|
||||
{
|
||||
name = mat.Name,
|
||||
opacity = 1 - mat.Transparency,
|
||||
diffuse = mat.DiffuseColor.ToArgb(),
|
||||
emissive = mat.EmissionColor.ToArgb(),
|
||||
applicationId = mat.Name,
|
||||
["specular"] = mat.SpecularColor.ToArgb(),
|
||||
["shine"] = mat.AmbientColor,
|
||||
["ior"] = mat.IndexOfRefraction
|
||||
};
|
||||
|
||||
return speckleRenderMaterial;
|
||||
}
|
||||
|
||||
private Material ToRhinoMaterial(Rhino.Display.DisplayMaterial mat) =>
|
||||
new()
|
||||
{
|
||||
DiffuseColor = mat.Diffuse,
|
||||
EmissionColor = mat.Emission,
|
||||
Transparency = mat.Transparency,
|
||||
SpecularColor = mat.Specular,
|
||||
Shine = mat.Shine,
|
||||
};
|
||||
|
||||
private Material ToRhinoMaterial(SpeckleRenderMaterial mat) =>
|
||||
new()
|
||||
{
|
||||
Name = mat.name,
|
||||
DiffuseColor = mat.diffuseColor,
|
||||
EmissionColor = mat.emissiveColor,
|
||||
Transparency = 1 - mat.opacity,
|
||||
Shine = mat["shine"] is double shine ? shine : default,
|
||||
IndexOfRefraction = mat["ior"] is double ior ? ior : default
|
||||
};
|
||||
}
|
||||
|
||||
public class SpeckleMaterialParam : GH_Param<SpeckleMaterialWrapperGoo>, IGH_BakeAwareObject
|
||||
{
|
||||
private const string NICKNAME = "Speckle Material";
|
||||
|
||||
public SpeckleMaterialParam()
|
||||
: this(GH_ParamAccess.item) { }
|
||||
|
||||
public SpeckleMaterialParam(IGH_InstanceDescription tag)
|
||||
: base(tag) { }
|
||||
|
||||
public SpeckleMaterialParam(IGH_InstanceDescription tag, GH_ParamAccess access)
|
||||
: base(tag, access) { }
|
||||
|
||||
public SpeckleMaterialParam(GH_ParamAccess access)
|
||||
: base(
|
||||
NICKNAME,
|
||||
"SM",
|
||||
"Represents a Speckle material",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("1A08CF79-2072-4B14-9430-E4465FF0C0FE");
|
||||
protected override Bitmap Icon => Resources.speckle_param_material;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
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 SpeckleMaterialWrapperGoo goo)
|
||||
{
|
||||
// get the param nickname if it is a custom name.
|
||||
// this is used to override the name of the material.
|
||||
// the nickname should also be used in case of an empty name on the rhino material
|
||||
string? name =
|
||||
NickName != NICKNAME
|
||||
? NickName
|
||||
: string.IsNullOrEmpty(goo.Value.Name)
|
||||
? NickName
|
||||
: null;
|
||||
|
||||
int bakeIndex = goo.Value.Bake(doc, name);
|
||||
|
||||
if (bakeIndex == -1)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Failed to add material {name} to document.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 SpeckleMaterialWrapperGoo goo)
|
||||
{
|
||||
// get the param nickname if it is a custom name.
|
||||
// this is used to override the name of the material.
|
||||
// the nickname should also be used in case of an empty name on the rhino material
|
||||
string? name =
|
||||
NickName != NICKNAME
|
||||
? NickName
|
||||
: string.IsNullOrEmpty(goo.Value.Name)
|
||||
? NickName
|
||||
: null;
|
||||
|
||||
int bakeIndex = goo.Value.Bake(doc, name);
|
||||
if (bakeIndex == -1)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Failed to add material {name} to document.");
|
||||
}
|
||||
|
||||
obj_ids.Add(doc.Materials[bakeIndex].Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
#if RHINO8_OR_GREATER
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Geometry;
|
||||
using Grasshopper.Rhinoceros.Model;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
using Rhino.DocObjects;
|
||||
using Grasshopper.Rhinoceros.Render;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
|
||||
{
|
||||
public SpeckleObjectWrapperGoo(ModelObject mo)
|
||||
{
|
||||
CastFrom(mo);
|
||||
}
|
||||
|
||||
private bool TryCastToExtrusion<T>(ref T target)
|
||||
{
|
||||
Extrusion? extrusion = null;
|
||||
if (GH_Convert.ToExtrusion(Value.GeometryBase, ref extrusion, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Extrusion(extrusion);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToPointcloud<T>(ref T target)
|
||||
{
|
||||
PointCloud? pointCloud = null;
|
||||
if (GH_Convert.ToPointCloud(Value.GeometryBase, ref pointCloud, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_PointCloud(pointCloud);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToHatch<T>(ref T target)
|
||||
{
|
||||
Hatch? hatch = null;
|
||||
if (GH_Convert.ToHatch(Value.GeometryBase, ref hatch, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Hatch(hatch);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToSubD<T>(ref T target)
|
||||
{
|
||||
SubD? subd = null;
|
||||
if (GH_Convert.ToSubD(Value.GeometryBase, ref subd, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_SubD(subd);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CastToModelObject<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(ModelObject))
|
||||
{
|
||||
// create attributes
|
||||
ObjectAttributes atts = new();
|
||||
CastTo<ObjectAttributes>(ref atts);
|
||||
|
||||
// create model object
|
||||
ModelObject modelObject = new(RhinoDoc.ActiveDoc, atts, Value.GeometryBase);
|
||||
target = (T)(object)modelObject;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == typeof(ObjectAttributes))
|
||||
{
|
||||
ObjectAttributes atts = new() { Name = Value.Name };
|
||||
|
||||
if (Value.Color is Color color)
|
||||
{
|
||||
atts.ObjectColor = color;
|
||||
atts.ColorSource = ObjectColorSource.ColorFromObject;
|
||||
}
|
||||
|
||||
// POC: only set material if it exists in the doc. Avoiding baking during cast.
|
||||
// ModelObject.Render.Material has no setter, so we are handling it here.
|
||||
if (
|
||||
Value.Material is SpeckleMaterialWrapper materialWrapper
|
||||
&& materialWrapper.RhinoRenderMaterialId != Guid.Empty
|
||||
)
|
||||
{
|
||||
Rhino.Render.RenderMaterial renderMaterial = RhinoDoc.ActiveDoc.RenderMaterials.Find(
|
||||
materialWrapper.RhinoRenderMaterialId
|
||||
);
|
||||
|
||||
atts.RenderMaterial = renderMaterial;
|
||||
atts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
}
|
||||
|
||||
// POC: only set layer if it exists in the doc. Avoid baking during cast.
|
||||
// ModelObject.Layer has no setter, so we are handling it here.
|
||||
if (Value.Parent is SpeckleCollectionWrapper collectionWrapper)
|
||||
{
|
||||
int layerIndex = collectionWrapper.GetLayerIndex();
|
||||
if (layerIndex != -1)
|
||||
{
|
||||
atts.LayerIndex = layerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// add props
|
||||
Value.Properties.AssignToObjectAttributes(atts);
|
||||
|
||||
target = (T)(object)atts;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == typeof(ModelRenderMaterial))
|
||||
{
|
||||
if (Value.Material is SpeckleMaterialWrapper matWrapper)
|
||||
{
|
||||
SpeckleMaterialWrapperGoo matWrapperGoo = new(matWrapper);
|
||||
ModelRenderMaterial modelMat = new();
|
||||
if (matWrapperGoo.CastTo<ModelRenderMaterial>(ref modelMat))
|
||||
{
|
||||
target = (T)(object)modelMat;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == typeof(ModelLayer))
|
||||
{
|
||||
if (Value.Parent is SpeckleCollectionWrapper collWrapper)
|
||||
{
|
||||
SpeckleCollectionWrapperGoo collWrapperGoo = new(collWrapper);
|
||||
ModelLayer modelLayer = new();
|
||||
if (collWrapperGoo.CastTo<ModelLayer>(ref modelLayer))
|
||||
{
|
||||
target = (T)(object)modelLayer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CastFromModelObject(object source)
|
||||
{
|
||||
if (source is ModelObject modelObject)
|
||||
{
|
||||
if (GetGeometryFromModelObject(modelObject) is GeometryBase modelGB)
|
||||
{
|
||||
Base modelConverted = SpeckleConversionContext.ConvertToSpeckle(modelGB);
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(modelObject.UserText);
|
||||
|
||||
// get the object layer
|
||||
SpeckleCollectionWrapperGoo collWrapperGoo = new();
|
||||
SpeckleCollectionWrapper? collWrapper = collWrapperGoo.CastFrom(modelObject.Layer)
|
||||
? collWrapperGoo.Value
|
||||
: null;
|
||||
|
||||
// update the converted Base with props as well
|
||||
modelConverted.applicationId = modelObject.Id?.ToString();
|
||||
modelConverted[Constants.NAME_PROP] = modelObject.Name.ToString();
|
||||
Dictionary<string, object?> propertyDict = new();
|
||||
foreach (var entry in modelObject.UserText)
|
||||
{
|
||||
propertyDict.Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
modelConverted[Constants.PROPERTIES_PROP] = propertyDict;
|
||||
|
||||
// get the object color and material
|
||||
Color? color = GetColorFromModelObject(modelObject);
|
||||
SpeckleMaterialWrapperGoo? materialWrapper = new();
|
||||
if (GetMaterialFromModelObject(modelObject) is Rhino.Render.RenderMaterial renderMat)
|
||||
{
|
||||
materialWrapper.CastFrom(renderMat);
|
||||
}
|
||||
|
||||
SpeckleObjectWrapper so =
|
||||
new()
|
||||
{
|
||||
GeometryBase = modelGB,
|
||||
Base = modelConverted,
|
||||
Parent = collWrapper,
|
||||
Name = modelObject.Name.ToString(),
|
||||
Color = color,
|
||||
Material = materialWrapper.Value,
|
||||
Properties = propertyGroup,
|
||||
WrapperGuid = null // keep this null, processed on send
|
||||
};
|
||||
|
||||
Value = so;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Could not retrieve geometry from Model Object {modelObject.ObjectType}. Did you forget to bake these objects in your document?"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private GeometryBase? GetGeometryFromModelObject(ModelObject modelObject) =>
|
||||
RhinoDoc.ActiveDoc.Objects.FindId(modelObject.Id ?? Guid.Empty)?.Geometry;
|
||||
|
||||
private Color? GetColorFromModelObject(ModelObject modelObject)
|
||||
{
|
||||
// we need to retrieve the actual color by the color source (otherwise will return default color for anything other than by object)
|
||||
int? argb = null;
|
||||
switch (modelObject.Display.Color?.Source)
|
||||
{
|
||||
case ObjectColorSource.ColorFromLayer:
|
||||
argb = modelObject.Layer.DisplayColor?.ToArgb();
|
||||
break;
|
||||
case ObjectColorSource.ColorFromObject:
|
||||
argb = modelObject.Display.Color?.Color.ToArgb();
|
||||
break;
|
||||
case ObjectColorSource.ColorFromMaterial:
|
||||
Rhino.Render.RenderMaterial? mat = GetMaterialFromModelObject(modelObject);
|
||||
argb = mat?.ToMaterial(Rhino.Render.RenderTexture.TextureGeneration.Skip)?.DiffuseColor.ToArgb();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return argb is int validArgb ? Color.FromArgb(validArgb) : null;
|
||||
}
|
||||
|
||||
private Rhino.Render.RenderMaterial? GetMaterialFromModelObject(ModelObject modelObject)
|
||||
{
|
||||
// we need to retrieve the actual material by the material source (otherwise will return default material for anything other than by object)
|
||||
Guid? matId = null;
|
||||
switch (modelObject.Render.Material?.Source)
|
||||
{
|
||||
case ObjectMaterialSource.MaterialFromLayer:
|
||||
matId = modelObject.Layer.Material.Id;
|
||||
break;
|
||||
case ObjectMaterialSource.MaterialFromObject:
|
||||
matId = modelObject.Render.Material?.Material?.Id;
|
||||
break;
|
||||
case ObjectMaterialSource.MaterialFromParent: // POC: too complicated for now
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return matId is Guid validId ? RhinoDoc.ActiveDoc.RenderMaterials.Find(validId) : null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
-550
@@ -1,550 +0,0 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Display;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around a geometry base object and its converted speckle equivalent.
|
||||
/// </summary>
|
||||
public class SpeckleObjectWrapper : SpeckleWrapper
|
||||
{
|
||||
public override required Base Base { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GeometryBase corresponding to the <see cref="SpeckleWrapper.Base"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POC: how will we send intervals and other gh native objects? do we? maybe not for now?
|
||||
/// Objects using fallback conversion (eg DataObjects) will create one wrapper per geometry in the display value.
|
||||
/// </remarks>
|
||||
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; }
|
||||
public SpecklePropertyGroupGoo Properties { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public Color? Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The material of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public SpeckleMaterialWrapper? Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents the guid of this <see cref="SpeckleObjectWrapper"/>
|
||||
/// </summary>
|
||||
/// <remarks>This property will usually be assigned in create components, or in publish components, and may differ from <see cref="Base.applicationId"/></remarks>
|
||||
public required string? WrapperGuid { get; set; }
|
||||
|
||||
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.DrawExtrusionWires(e, isSelected ? args.WireColour_Selected : args.WireColour);
|
||||
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.Bake(doc, obj_ids, false);
|
||||
}
|
||||
|
||||
if (bakeLayerIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// create attributes
|
||||
using ObjectAttributes att = new() { Name = Name, LayerIndex = bakeLayerIndex };
|
||||
|
||||
if (Color is Color color)
|
||||
{
|
||||
att.ObjectColor = color;
|
||||
att.ColorSource = ObjectColorSource.ColorFromObject;
|
||||
}
|
||||
|
||||
if (Material is SpeckleMaterialWrapper materialWrapper)
|
||||
{
|
||||
int matIndex = materialWrapper.Bake(doc, materialWrapper.Name);
|
||||
if (matIndex >= 0)
|
||||
{
|
||||
att.MaterialIndex = matIndex;
|
||||
att.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
}
|
||||
}
|
||||
|
||||
// add props
|
||||
Properties.AssignToObjectAttributes(att);
|
||||
|
||||
// add to doc
|
||||
Guid guid = doc.Objects.Add(GeometryBase, att);
|
||||
obj_ids.Add(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines similarity of two SpeckleObjectWrappers.
|
||||
/// If the path, name, and properties of the wrappers are the same, they should be considered similar.
|
||||
/// This should be used to pack similar objects into one `DataObject` on send.
|
||||
/// </summary>
|
||||
/// <param name="objWrapper">The object wrapper to compare to</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks> Application Id is not considered in similarity, because these can be unique to objects inside the same displayvalue for proxy reasons</remarks>
|
||||
public bool SmellsLike(SpeckleObjectWrapper objWrapper)
|
||||
{
|
||||
if (Path != objWrapper.Path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Name != objWrapper.Name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (!Properties.Equals(objWrapper.Properties))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public SpeckleObjectWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = Base.ShallowCopy(),
|
||||
GeometryBase = GeometryBase?.Duplicate(),
|
||||
Color = Color,
|
||||
Material = Material,
|
||||
WrapperGuid = WrapperGuid,
|
||||
ApplicationId = ApplicationId,
|
||||
Parent = Parent,
|
||||
Properties = Properties,
|
||||
Name = Name,
|
||||
Path = Path
|
||||
};
|
||||
}
|
||||
|
||||
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
|
||||
{
|
||||
public override IGH_Goo Duplicate()
|
||||
{
|
||||
return new SpeckleObjectWrapperGoo(Value.DeepCopy());
|
||||
}
|
||||
|
||||
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.";
|
||||
|
||||
/// <summary>
|
||||
/// Casts from Speckle objects, geometry base, and model objects.
|
||||
/// All non-Speckle objects will be converted to its geometry equivalent.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleObjectWrapper wrapper:
|
||||
Value = wrapper.DeepCopy();
|
||||
return true;
|
||||
case GH_Goo<SpeckleObjectWrapper> speckleGrasshopperObjectGoo:
|
||||
Value = speckleGrasshopperObjectGoo.Value.DeepCopy();
|
||||
return true;
|
||||
case IGH_GeometricGoo geometricGoo:
|
||||
var gooGB = geometricGoo.GeometricGooToGeometryBase();
|
||||
var gooConverted = SpeckleConversionContext.ConvertToSpeckle(gooGB);
|
||||
Value = new SpeckleObjectWrapper()
|
||||
{
|
||||
GeometryBase = gooGB,
|
||||
Base = gooConverted,
|
||||
Name = "",
|
||||
Color = null,
|
||||
Material = null,
|
||||
WrapperGuid = null,
|
||||
ApplicationId = Guid.NewGuid().ToString()
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle case of model objects in rhino 8
|
||||
return CastFromModelObject(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelObject(object _) => false;
|
||||
|
||||
private bool CastToModelObject<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
if (Value.GeometryBase == null)
|
||||
{
|
||||
return CastToModelObject(ref target);
|
||||
}
|
||||
|
||||
return target switch
|
||||
{
|
||||
GH_Surface => TryCastToSurface(ref target),
|
||||
GH_Mesh => TryCastToMesh(ref target),
|
||||
GH_Brep => TryCastToBrep(ref target),
|
||||
GH_Line => TryCastToLine(ref target),
|
||||
GH_Curve => TryCastToCurve(ref target),
|
||||
GH_Point => TryCastToPoint(ref target),
|
||||
GH_Circle => TryCastToCircle(ref target),
|
||||
GH_Arc => TryCastToArc(ref target),
|
||||
#if RHINO8_OR_GREATER
|
||||
GH_Extrusion => TryCastToExtrusion(ref target),
|
||||
GH_PointCloud => TryCastToPointcloud(ref target),
|
||||
GH_SubD => TryCastToSubD(ref target),
|
||||
GH_Hatch => TryCastToHatch(ref target),
|
||||
#endif
|
||||
IGH_GeometricGoo => TryCastToGeometricGoo(ref target),
|
||||
_ => CastToModelObject(ref target)
|
||||
};
|
||||
}
|
||||
|
||||
private bool TryCastToSurface<T>(ref T target)
|
||||
{
|
||||
Surface? surface = null;
|
||||
if (GH_Convert.ToSurface(Value.GeometryBase, ref surface, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Surface(surface);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToMesh<T>(ref T target)
|
||||
{
|
||||
Mesh? mesh = null;
|
||||
if (GH_Convert.ToMesh(Value.GeometryBase, ref mesh, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Mesh(mesh);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToBrep<T>(ref T target)
|
||||
{
|
||||
Brep? brep = null;
|
||||
if (GH_Convert.ToBrep(Value.GeometryBase, ref brep, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Brep(brep);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToLine<T>(ref T target)
|
||||
{
|
||||
Line line = new();
|
||||
if (GH_Convert.ToLine(Value.GeometryBase, ref line, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Line(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToCurve<T>(ref T target)
|
||||
{
|
||||
Curve? curve = null;
|
||||
if (GH_Convert.ToCurve(Value.GeometryBase, ref curve, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Curve(curve);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToPoint<T>(ref T target)
|
||||
{
|
||||
Point3d point = new();
|
||||
if (GH_Convert.ToPoint3d(Value.GeometryBase, ref point, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Point(point);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToGeometricGoo<T>(ref T target)
|
||||
{
|
||||
var geometricGoo = GH_Convert.ToGeometricGoo(Value.GeometryBase);
|
||||
if (geometricGoo != null && geometricGoo is T convertedGoo)
|
||||
{
|
||||
target = convertedGoo;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToCircle<T>(ref T target)
|
||||
{
|
||||
var circle = new Rhino.Geometry.Circle();
|
||||
if (GH_Convert.ToCircle(Value.GeometryBase, ref circle, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Circle(circle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToArc<T>(ref T target)
|
||||
{
|
||||
var arc = new Arc();
|
||||
if (GH_Convert.ToArc(Value.GeometryBase, ref arc, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Arc(arc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DrawViewportWires(GH_PreviewWireArgs args)
|
||||
{
|
||||
// TODO ?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(GH_PreviewMeshArgs args)
|
||||
{
|
||||
Value.DrawPreviewRaw(args.Pipeline, args.Material);
|
||||
}
|
||||
|
||||
BoundingBox IGH_PreviewData.ClippingBox =>
|
||||
Value.GeometryBase is null ? new() : Value.GeometryBase.GetBoundingBox(false);
|
||||
|
||||
public SpeckleObjectWrapperGoo(SpeckleObjectWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public SpeckleObjectWrapperGoo()
|
||||
{
|
||||
Value = new()
|
||||
{
|
||||
Base = new(),
|
||||
GeometryBase = null,
|
||||
Color = null,
|
||||
Material = null,
|
||||
WrapperGuid = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SpeckleObjectParam : GH_Param<SpeckleObjectWrapperGoo>, IGH_BakeAwareObject, IGH_PreviewObject
|
||||
{
|
||||
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 => Resources.speckle_param_object;
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes the object
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="att"></param>
|
||||
/// <param name="obj_ids"></param>
|
||||
/// <remarks>
|
||||
/// The attributes come from the user dialog after calling bake.
|
||||
/// The selected layer from the dialog will only be user if no path is already present on the object.
|
||||
/// </remarks>
|
||||
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)
|
||||
{
|
||||
int layerIndex = goo.Value.Path.Count == 0 ? att.LayerIndex : -1;
|
||||
bool layerCreated = goo.Value.Path.Count == 0;
|
||||
goo.Value.Bake(doc, obj_ids, layerIndex, layerCreated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
public BoundingBox ClippingBox
|
||||
{
|
||||
get
|
||||
{
|
||||
BoundingBox clippingBox = new();
|
||||
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo && goo.Value.GeometryBase is GeometryBase gb)
|
||||
{
|
||||
var box = gb.GetBoundingBox(false);
|
||||
clippingBox.Union(box);
|
||||
}
|
||||
}
|
||||
return clippingBox;
|
||||
}
|
||||
}
|
||||
bool IGH_PreviewObject.Hidden { get; set; }
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// todo?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo)
|
||||
{
|
||||
goo.Value.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected()
|
||||
{
|
||||
return Attributes?.Parent?.Selected ?? false;
|
||||
}
|
||||
}
|
||||
+23
-2
@@ -126,7 +126,28 @@ public class SpecklePropertyGoo : GH_Goo<object>, ISpecklePropertyGoo
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Value == prop.Value;
|
||||
switch (Value)
|
||||
{
|
||||
case string s:
|
||||
return s == prop.Value.ToString();
|
||||
case bool b:
|
||||
return prop.Value is bool otherBool
|
||||
? b == otherBool
|
||||
: bool.TryParse(prop.Value.ToString(), out bool parsedBool) && b == parsedBool;
|
||||
case double d:
|
||||
return prop.Value is double otherDouble
|
||||
? d == otherDouble
|
||||
: double.TryParse(prop.Value.ToString(), out double parsedDouble) && d == parsedDouble;
|
||||
case float f:
|
||||
return prop.Value is float otherFloat
|
||||
? f == otherFloat
|
||||
: float.TryParse(prop.Value.ToString(), out float parsedFloat) && f == parsedFloat;
|
||||
case int i:
|
||||
return prop.Value is int otherInt
|
||||
? i == otherInt
|
||||
: int.TryParse(prop.Value.ToString(), out int parsedInt) && i == parsedInt;
|
||||
default:
|
||||
return Value == prop.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,11 +15,11 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
|
||||
{
|
||||
public override IGH_Goo Duplicate() => throw new NotImplementedException();
|
||||
|
||||
public override string ToString() => $"PropertyGroup ({Value.Count})";
|
||||
public override string ToString() => $"Speckle Properties : ({Value.Count})";
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "Speckle property group wrapper";
|
||||
public override string TypeDescription => "Speckle property group wrapper";
|
||||
public override string TypeName => "Speckle property group goo";
|
||||
public override string TypeDescription => "Speckle property group goo";
|
||||
|
||||
public SpecklePropertyGroupGoo()
|
||||
{
|
||||
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Display;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around a block definition and its converted Speckle equivalent.
|
||||
/// </summary>
|
||||
public class SpeckleBlockDefinitionWrapper : SpeckleWrapper
|
||||
{
|
||||
public InstanceDefinitionProxy InstanceDefinitionProxy { get; set; }
|
||||
private const int MAX_DISPLAY_DEPTH = 3;
|
||||
|
||||
public override required Base Base
|
||||
{
|
||||
get => InstanceDefinitionProxy;
|
||||
set
|
||||
{
|
||||
if (value is not InstanceDefinitionProxy def)
|
||||
{
|
||||
throw new ArgumentException("Cannot create block definition wrapper from a non-InstanceDefinitionProxy Base");
|
||||
}
|
||||
InstanceDefinitionProxy = def;
|
||||
}
|
||||
}
|
||||
|
||||
private List<SpeckleObjectWrapper> _objects = new();
|
||||
|
||||
public List<SpeckleObjectWrapper> Objects
|
||||
{
|
||||
get => _objects;
|
||||
set
|
||||
{
|
||||
ValidateObjects(value);
|
||||
_objects = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateObjects(List<SpeckleObjectWrapper> objects)
|
||||
{
|
||||
// SpeckleBlockInstanceWrapper inherits from SpeckleObjectWrapper, check if it's assignable, not exact type match
|
||||
var invalidObjects = objects.Where(o => !typeof(SpeckleObjectWrapper).IsAssignableFrom(o.GetType())).ToList();
|
||||
|
||||
if (invalidObjects.Count > 0)
|
||||
{
|
||||
var invalidTypes = string.Join(", ", invalidObjects.Select(o => o.GetType().Name));
|
||||
throw new ArgumentException(
|
||||
$"Block definitions can only contain objects and instances. Found invalid types: {invalidTypes}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"Speckle Block Definition : {Name} ({Objects.Count})";
|
||||
|
||||
public override IGH_Goo CreateGoo() => new SpeckleBlockDefinitionWrapperGoo(this);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a preview of the block definition by displaying all contained objects
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Leveraging already defined preview logic for the objects which make up this block. Refer to <see cref="SpeckleObjectWrapper.DrawPreview"/>.
|
||||
/// </remarks>
|
||||
public void DrawPreview(IGH_PreviewArgs args, bool isSelected = false) =>
|
||||
DrawDepthLimitedPreview(args, isSelected, 0);
|
||||
|
||||
private void DrawDepthLimitedPreview(IGH_PreviewArgs args, bool isSelected, int depth)
|
||||
{
|
||||
if (depth > MAX_DISPLAY_DEPTH)
|
||||
{
|
||||
return; // Stop early if too deep
|
||||
}
|
||||
|
||||
foreach (var obj in Objects)
|
||||
{
|
||||
if (obj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
nestedInstance.DrawDepthLimitedPreview(args, isSelected, depth + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawPreviewRaw(DisplayPipeline display, DisplayMaterial material) =>
|
||||
DrawDepthLimitedPreviewRaw(display, material, 0);
|
||||
|
||||
private void DrawDepthLimitedPreviewRaw(DisplayPipeline display, DisplayMaterial material, int depth)
|
||||
{
|
||||
if (depth > MAX_DISPLAY_DEPTH)
|
||||
{
|
||||
return; // Stop early if too deep
|
||||
}
|
||||
|
||||
foreach (var obj in Objects)
|
||||
{
|
||||
if (obj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
nestedInstance.DrawDepthLimitedPreviewRaw(display, material, depth + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.DrawPreviewRaw(display, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes this block definition to Rhino, automatically handing nested block dependencies.
|
||||
/// Nested definitions are baked first, to ensure that InstanceReferenceGeometry can find them
|
||||
/// </summary>
|
||||
public (int index, bool existingDefinitionUpdated) Bake(RhinoDoc doc, List<Guid> objIds, string? name = null)
|
||||
{
|
||||
// Collect and bake nested dependencies first
|
||||
var visited = new HashSet<string>();
|
||||
BakeNestedDefinitions(this, doc, objIds, visited);
|
||||
|
||||
// Now bake this definition
|
||||
return BakeCurrentDefinition(doc, objIds, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively bakes nested block definitions in dependency order.
|
||||
/// </summary>
|
||||
private static void BakeNestedDefinitions(
|
||||
SpeckleBlockDefinitionWrapper definition,
|
||||
RhinoDoc doc,
|
||||
List<Guid> objIds,
|
||||
HashSet<string> visited
|
||||
)
|
||||
{
|
||||
var defId = definition.ApplicationId ?? definition.Name;
|
||||
if (visited.Contains(defId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
visited.Add(defId);
|
||||
|
||||
// Bake nested dependencies first
|
||||
foreach (var obj in definition.Objects)
|
||||
{
|
||||
if (obj is SpeckleBlockInstanceWrapper { Definition: not null } blockInstance)
|
||||
{
|
||||
BakeNestedDefinitions(blockInstance.Definition, doc, objIds, visited);
|
||||
|
||||
// Bake the nested definition if not already in document
|
||||
if (doc.InstanceDefinitions.Find(blockInstance.Definition.Name) == null)
|
||||
{
|
||||
blockInstance.Definition.BakeCurrentDefinition(doc, objIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Rhino InstanceDefinition, converting nested instances to InstanceReferenceGeometry.
|
||||
/// </summary>
|
||||
private (int index, bool existingDefinitionUpdated) BakeCurrentDefinition(
|
||||
RhinoDoc doc,
|
||||
List<Guid> objIds,
|
||||
string? name = null
|
||||
)
|
||||
{
|
||||
string definitionName = name ?? Name;
|
||||
var geometries = new List<GeometryBase>();
|
||||
var attributes = new List<ObjectAttributes>();
|
||||
|
||||
foreach (var obj in Objects)
|
||||
{
|
||||
if (obj is SpeckleBlockInstanceWrapper blockInstance && blockInstance.Definition != null)
|
||||
{
|
||||
// Convert to InstanceReferenceGeometry (nested definition should exist by now)
|
||||
var referenceDefinition = doc.InstanceDefinitions.Find(blockInstance.Definition.Name);
|
||||
if (referenceDefinition != null)
|
||||
{
|
||||
geometries.Add(new InstanceReferenceGeometry(referenceDefinition.Id, blockInstance.Transform));
|
||||
attributes.Add(blockInstance.CreateObjectAttributes(bakeMaterial: true));
|
||||
}
|
||||
}
|
||||
else if (obj.GeometryBase != null)
|
||||
{
|
||||
geometries.Add(obj.GeometryBase);
|
||||
attributes.Add(obj.CreateObjectAttributes(bakeMaterial: true));
|
||||
}
|
||||
}
|
||||
|
||||
if (geometries.Count == 0)
|
||||
{
|
||||
return (-1, false);
|
||||
}
|
||||
|
||||
var documentDefinition = doc.InstanceDefinitions.Find(definitionName);
|
||||
|
||||
if (documentDefinition != null)
|
||||
{
|
||||
// Update existing
|
||||
bool success = doc.InstanceDefinitions.ModifyGeometry(
|
||||
documentDefinition.Index,
|
||||
geometries.ToArray(),
|
||||
attributes.ToArray()
|
||||
);
|
||||
if (success)
|
||||
{
|
||||
objIds.Add(documentDefinition.Id);
|
||||
return (documentDefinition.Index, true);
|
||||
}
|
||||
return (-1, true);
|
||||
}
|
||||
|
||||
// Create new
|
||||
int index = doc.InstanceDefinitions.Add(definitionName, string.Empty, Point3d.Origin, geometries, attributes);
|
||||
if (index >= 0)
|
||||
{
|
||||
objIds.Add(doc.InstanceDefinitions[index].Id);
|
||||
return (index, false);
|
||||
}
|
||||
return (-1, false);
|
||||
}
|
||||
|
||||
public SpeckleBlockDefinitionWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = InstanceDefinitionProxy.ShallowCopy(),
|
||||
ApplicationId = ApplicationId,
|
||||
Name = Name,
|
||||
Objects = Objects.Select(o => o.DeepCopy()).ToList()
|
||||
};
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#if RHINO8_OR_GREATER
|
||||
using Grasshopper.Rhinoceros.Model;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleBlockDefinitionWrapperGoo
|
||||
{
|
||||
private bool CastFromModelObject(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case InstanceDefinition instanceDefinition:
|
||||
List<SpeckleObjectWrapper> objects = new();
|
||||
foreach (var defObj in instanceDefinition.GetObjects())
|
||||
{
|
||||
SpeckleObjectWrapperGoo defObjGoo = new();
|
||||
if (defObjGoo.CastFrom(defObj))
|
||||
{
|
||||
objects.Add(defObjGoo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (objects.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value = new SpeckleBlockDefinitionWrapper()
|
||||
{
|
||||
Base = new InstanceDefinitionProxy
|
||||
{
|
||||
name = instanceDefinition.Name,
|
||||
objects = objects.Select(o => o.ApplicationId!).ToList(),
|
||||
maxDepth = 0 // represent newly created, top-level objects. actual depth calculation happens in GrasshopperBlockPacker
|
||||
},
|
||||
Name = instanceDefinition.Name,
|
||||
Objects = objects,
|
||||
ApplicationId = instanceDefinition.Id.ToString()
|
||||
};
|
||||
|
||||
return true;
|
||||
|
||||
case ModelInstanceDefinition modelInstanceDef:
|
||||
InstanceDefinition? instanceDef = RhinoDoc.ActiveDoc?.InstanceDefinitions.Find(modelInstanceDef.Name);
|
||||
if (instanceDef == null)
|
||||
{
|
||||
// Rhino → Model → Model Block Definition passthrough component returns type ModelInstanceDefinition
|
||||
// .Objects of a ModelInstanceDefinition returns ModelObjects
|
||||
// ModelObject.Geometry is internal and cannot be accessed directly.
|
||||
// Only way to get geometry from a ModelObject is through RhinoDoc.Objects.FindId(), which only works for baked objects.
|
||||
// Unbaked Grasshopper geometry cannot be processed through the ModelObject workflow until we get a public geometry accessor 😓
|
||||
// ⚠️ So if user defines a Model Block Definition in Grasshopper with Grasshopper (unbaked) geometry, we're stuck.
|
||||
// That's why we're intercepting this case early → if the instanceDef == null don't go further
|
||||
throw new InvalidOperationException(
|
||||
$"Block definition '{modelInstanceDef.Name}' not found in Rhino document. Please bake the definition first or use Speckle Block Definition components instead."
|
||||
);
|
||||
}
|
||||
|
||||
return CastFromModelObject(instanceDef);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CastToModelObject<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(ModelInstanceDefinition))
|
||||
{
|
||||
var doc = RhinoDoc.ActiveDoc;
|
||||
var instanceDef = doc?.InstanceDefinitions.Find(Value.Name); // POC: this seems dangerous as users can change rhino block names
|
||||
if (instanceDef != null)
|
||||
{
|
||||
// ⚠️ ModelInstanceDefinition(InstanceDefinition) constructor strips .Id and we can't set it afterward
|
||||
var modelInstanceDef = new ModelInstanceDefinition(instanceDef);
|
||||
target = (T)(object)modelInstanceDef;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// The goo of <see cref="SpeckleBlockDefinitionWrapper"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Casting </remarks>
|
||||
public partial class SpeckleBlockDefinitionWrapperGoo : GH_Goo<SpeckleBlockDefinitionWrapper>
|
||||
{
|
||||
public override bool IsValid => Value?.InstanceDefinitionProxy is not null && Value.ApplicationId is not null;
|
||||
public override string TypeName => "Speckle Block Definition";
|
||||
public override string TypeDescription => "Represents an instance definition proxy from Speckle";
|
||||
|
||||
public SpeckleBlockDefinitionWrapperGoo(SpeckleBlockDefinitionWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public SpeckleBlockDefinitionWrapperGoo()
|
||||
{
|
||||
Value = new()
|
||||
{
|
||||
Base = new InstanceDefinitionProxy
|
||||
{
|
||||
name = "Unnamed Block",
|
||||
objects = new List<string>(),
|
||||
maxDepth = 0, // represent newly created, top-level objects. actual depth calculation happens in GrasshopperBlockPacker
|
||||
},
|
||||
|
||||
ApplicationId = Guid.NewGuid().ToString(),
|
||||
Name = "Unnamed Block"
|
||||
};
|
||||
}
|
||||
|
||||
public override IGH_Goo Duplicate() => new SpeckleBlockDefinitionWrapperGoo(Value.DeepCopy());
|
||||
|
||||
public override string ToString() => $"Speckle Block Definition : {m_value.Name}";
|
||||
|
||||
// POC: need to verify deep copies are needed, for memory reasons!!
|
||||
// May not be needed on GH_Goo if eg passing from param to param.
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleBlockDefinitionWrapper sourceWrapper:
|
||||
Value = sourceWrapper;
|
||||
return true;
|
||||
case SpeckleBlockDefinitionWrapperGoo wrapperGoo:
|
||||
Value = wrapperGoo.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rhino 8 Model Objects
|
||||
return CastFromModelObject(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelObject(object _) => false;
|
||||
|
||||
private bool CastToModelObject<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
return CastToModelObject(ref target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep copy of this block definition wrapper for proper data handling.
|
||||
/// Follows the same pattern as other Goo implementations in the codebase.
|
||||
/// </summary>
|
||||
/// <returns>A new instance with copied data</returns>
|
||||
public SpeckleBlockDefinitionWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = Value.InstanceDefinitionProxy.ShallowCopy(),
|
||||
Name = Value.Name,
|
||||
Objects = Value.Objects.Select(o => o.DeepCopy()).ToList(),
|
||||
ApplicationId = Value.ApplicationId
|
||||
};
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleBlockDefinitionWrapperParam
|
||||
: GH_Param<SpeckleBlockDefinitionWrapperGoo>,
|
||||
IGH_BakeAwareObject,
|
||||
IGH_PreviewObject
|
||||
{
|
||||
public SpeckleBlockDefinitionWrapperParam()
|
||||
: this(GH_ParamAccess.item) { }
|
||||
|
||||
public SpeckleBlockDefinitionWrapperParam(IGH_InstanceDescription tag)
|
||||
: base(tag) { }
|
||||
|
||||
public SpeckleBlockDefinitionWrapperParam(IGH_InstanceDescription tag, GH_ParamAccess access)
|
||||
: base(tag, access) { }
|
||||
|
||||
public SpeckleBlockDefinitionWrapperParam(GH_ParamAccess access)
|
||||
: base(
|
||||
"Speckle Block Definition",
|
||||
"SBD",
|
||||
"Returns a Speckle Block definition.",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("C71BE6AD-E27B-4E7F-87DA-569D4DEE77BE");
|
||||
|
||||
protected override Bitmap Icon => Resources.speckle_param_block_def;
|
||||
|
||||
public override void RegisterRemoteIDs(GH_GuidTable idList)
|
||||
{
|
||||
// Register Rhino InstanceDefinition GUIDs so Grasshopper can track when
|
||||
// block definitions change in the Rhino document and auto-expire this parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (
|
||||
item is SpeckleBlockDefinitionWrapperGoo goo
|
||||
&& goo.Value?.ApplicationId != null
|
||||
&& Guid.TryParse(goo.Value.ApplicationId, out Guid id)
|
||||
)
|
||||
{
|
||||
idList.Add(id, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBakeCapable => !VolatileData.IsEmpty;
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
public void BakeGeometry(RhinoDoc doc, List<Guid> objIds) => BakeAllItems(doc, objIds);
|
||||
|
||||
public void BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> objIds) =>
|
||||
// we "ignore" the ObjectAttributes parameter because definitions manage their own internal object attributes
|
||||
// atts aren't on a definition level, but either on an instance level and/or objects within a definition (right?)
|
||||
BakeAllItems(doc, objIds);
|
||||
|
||||
private void BakeAllItems(RhinoDoc doc, List<Guid> objIds)
|
||||
{
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockDefinitionWrapperGoo goo)
|
||||
{
|
||||
var (index, wasUpdated) = goo.Value.Bake(doc, objIds);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
string message = wasUpdated
|
||||
? $"Updated existing block definition: '{goo.Value.Name}'"
|
||||
: $"Created new block definition: '{goo.Value.Name}'";
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Failed to bake block definition: '{goo.Value.Name}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// TODO?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockDefinitionWrapperGoo goo)
|
||||
{
|
||||
goo.Value.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected() => Attributes?.Parent?.Selected ?? false;
|
||||
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
public BoundingBox ClippingBox
|
||||
{
|
||||
get
|
||||
{
|
||||
BoundingBox clippingBox = new();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockDefinitionWrapperGoo goo && goo.Value?.Objects != null)
|
||||
{
|
||||
foreach (var obj in goo.Value.Objects)
|
||||
{
|
||||
if (obj.GeometryBase != null)
|
||||
{
|
||||
clippingBox.Union(obj.GeometryBase.GetBoundingBox(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return clippingBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Display;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleBlockInstanceWrapper : SpeckleObjectWrapper
|
||||
{
|
||||
private InstanceProxy _instanceProxy;
|
||||
private Transform _transform = Transform.Identity;
|
||||
private List<SpeckleObjectWrapper>? _cachedTransformedObjects;
|
||||
private Transform _lastCachedTransform = Transform.Unset;
|
||||
private const int MAX_DISPLAY_DEPTH = 3;
|
||||
private SpeckleBlockDefinitionWrapper? _definition;
|
||||
|
||||
public SpeckleBlockInstanceWrapper() { }
|
||||
|
||||
/// <summary>
|
||||
/// A default constructor for speckle block instances, with default values
|
||||
/// </summary>
|
||||
/// <param name="transform">This should be the identity transform, and will be set as identity regardless of value passed in.</param>
|
||||
[SetsRequiredMembers]
|
||||
public SpeckleBlockInstanceWrapper(Transform transform)
|
||||
{
|
||||
// gross af but override the incoming transform to be identity, since this constructor should be a default constructor
|
||||
Transform identity = transform == Transform.Identity ? transform : Transform.Identity;
|
||||
|
||||
var units = RhinoDoc.ActiveDoc?.ModelUnitSystem.ToSpeckleString();
|
||||
_instanceProxy = new()
|
||||
{
|
||||
definitionId = "placeholder",
|
||||
maxDepth = 0, // represent newly created, top-level objects. actual depth calculation happens in GrasshopperBlockPacker
|
||||
transform = GrasshopperHelpers.TransformToMatrix(identity, units),
|
||||
units = units ?? Units.None
|
||||
};
|
||||
|
||||
Base = _instanceProxy; // set required base
|
||||
ApplicationId = Guid.NewGuid().ToString();
|
||||
GeometryBase = new InstanceReferenceGeometry(Guid.Empty, identity);
|
||||
}
|
||||
|
||||
public InstanceProxy InstanceProxy
|
||||
{
|
||||
get => _instanceProxy;
|
||||
set
|
||||
{
|
||||
_instanceProxy = value ?? throw new ArgumentNullException(nameof(value));
|
||||
Base = _instanceProxy; // keep base in sync
|
||||
UpdateTransformFromProxy();
|
||||
}
|
||||
}
|
||||
|
||||
public SpeckleBlockDefinitionWrapper? Definition
|
||||
{
|
||||
get => _definition;
|
||||
set
|
||||
{
|
||||
_definition = value;
|
||||
|
||||
if (_definition != null)
|
||||
{
|
||||
_instanceProxy.definitionId =
|
||||
_definition.ApplicationId
|
||||
?? throw new InvalidOperationException(
|
||||
"Block definition must have ApplicationId before being assigned to instance"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public required Transform Transform
|
||||
{
|
||||
get => _transform;
|
||||
set
|
||||
{
|
||||
_transform = value;
|
||||
UpdateProxyFromTransform();
|
||||
}
|
||||
}
|
||||
|
||||
public override required Base Base
|
||||
{
|
||||
get => _instanceProxy;
|
||||
set
|
||||
{
|
||||
if (value is not InstanceProxy proxy)
|
||||
{
|
||||
throw new ArgumentException("Cannot create block instance wrapper from a non-InstanceProxy Base");
|
||||
}
|
||||
|
||||
_instanceProxy = proxy;
|
||||
UpdateTransformFromProxy();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"Speckle Block Instance : {(string.IsNullOrWhiteSpace(Name) ? Definition?.Name : Name)}";
|
||||
|
||||
public override IGH_Goo CreateGoo() => new SpeckleBlockInstanceWrapperGoo(this);
|
||||
|
||||
public override void DrawPreview(IGH_PreviewArgs args, bool isSelected = false) =>
|
||||
DrawDepthLimitedPreview(args, isSelected, 0);
|
||||
|
||||
internal void DrawDepthLimitedPreview(IGH_PreviewArgs args, bool isSelected, int depth)
|
||||
{
|
||||
if (depth > MAX_DISPLAY_DEPTH)
|
||||
{
|
||||
return; // Just stop
|
||||
}
|
||||
|
||||
if (Definition?.Objects == null || Definition.Objects.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var transformedObj in GetTransformedObjectsForDisplay())
|
||||
{
|
||||
if (transformedObj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
nestedInstance.DrawDepthLimitedPreview(args, isSelected, depth + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
transformedObj.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new void DrawPreviewRaw(DisplayPipeline display, DisplayMaterial material) =>
|
||||
DrawDepthLimitedPreviewRaw(display, material, 0);
|
||||
|
||||
internal void DrawDepthLimitedPreviewRaw(DisplayPipeline display, DisplayMaterial material, int depth)
|
||||
{
|
||||
if (depth > MAX_DISPLAY_DEPTH)
|
||||
{
|
||||
return; // Just stop
|
||||
}
|
||||
|
||||
if (Definition?.Objects == null || Definition.Objects.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var transformedObj in GetTransformedObjectsForDisplay())
|
||||
{
|
||||
if (transformedObj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
nestedInstance.DrawDepthLimitedPreviewRaw(display, material, depth + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
transformedObj.DrawPreviewRaw(display, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Bake(RhinoDoc doc, List<Guid> objIds, int bakeLayerIndex = -1, bool layersAlreadyCreated = false)
|
||||
{
|
||||
if (Definition?.Objects == null)
|
||||
{
|
||||
return; // can't bake an instance without a definition
|
||||
}
|
||||
|
||||
// check if the definition already exists in the document
|
||||
// this prevents multiple instances from overwriting the same definition
|
||||
var existingDef = doc.InstanceDefinitions.Find(Definition.Name);
|
||||
|
||||
if (existingDef == null)
|
||||
{
|
||||
// definition doesn't exist yet, create it
|
||||
// this should only happen for the first instance with this definition name
|
||||
var (index, _) = Definition.Bake(doc, objIds);
|
||||
if (index == -1)
|
||||
{
|
||||
return; // definition creation failed
|
||||
}
|
||||
existingDef = doc.InstanceDefinitions[index];
|
||||
}
|
||||
|
||||
var attributes = CreateObjectAttributes(bakeLayerIndex, true);
|
||||
|
||||
// create the instance with our specific transform
|
||||
var instanceRef = doc.Objects.AddInstanceObject(existingDef.Index, Transform, attributes);
|
||||
if (instanceRef != Guid.Empty)
|
||||
{
|
||||
objIds.Add(instanceRef);
|
||||
}
|
||||
}
|
||||
|
||||
public override SpeckleObjectWrapper DeepCopy() =>
|
||||
new SpeckleBlockInstanceWrapper()
|
||||
{
|
||||
Base = InstanceProxy.ShallowCopy(),
|
||||
GeometryBase = GeometryBase?.Duplicate(),
|
||||
Color = Color,
|
||||
Material = Material,
|
||||
ApplicationId = ApplicationId,
|
||||
Parent = Parent,
|
||||
Properties = Properties,
|
||||
Name = Name,
|
||||
Path = Path,
|
||||
Transform = Transform,
|
||||
Definition = Definition?.DeepCopy(),
|
||||
};
|
||||
|
||||
private void UpdateTransformFromProxy() =>
|
||||
_transform = GrasshopperHelpers.MatrixToTransform(_instanceProxy.transform, _instanceProxy.units);
|
||||
|
||||
private void UpdateProxyFromTransform()
|
||||
{
|
||||
var units = _instanceProxy.units;
|
||||
_instanceProxy.transform = GrasshopperHelpers.TransformToMatrix(_transform, units);
|
||||
_instanceProxy.units = units;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or builds a cached list of transformed objects for displaying.
|
||||
/// Only rebuilds the cache when the transform changes, dramatically improving performance.
|
||||
/// </summary>
|
||||
private List<SpeckleObjectWrapper> GetTransformedObjectsForDisplay()
|
||||
{
|
||||
// Check if cache is valid (transform hasn't changed)
|
||||
if (_cachedTransformedObjects != null && Transform.Equals(_lastCachedTransform))
|
||||
{
|
||||
return _cachedTransformedObjects;
|
||||
}
|
||||
|
||||
// Rebuild cache
|
||||
_cachedTransformedObjects = new List<SpeckleObjectWrapper>();
|
||||
_lastCachedTransform = Transform;
|
||||
|
||||
if (Definition?.Objects == null)
|
||||
{
|
||||
return _cachedTransformedObjects;
|
||||
}
|
||||
|
||||
foreach (var obj in Definition.Objects)
|
||||
{
|
||||
if (obj is SpeckleBlockInstanceWrapper nestedInstance)
|
||||
{
|
||||
var copiedNestedInstance = (SpeckleBlockInstanceWrapper)nestedInstance.DeepCopy(); // don't mutate original
|
||||
copiedNestedInstance.Transform = Transform * nestedInstance.Transform; // combine transforms for nested blocks
|
||||
_cachedTransformedObjects.Add(copiedNestedInstance);
|
||||
}
|
||||
else if (obj.GeometryBase != null)
|
||||
{
|
||||
var copiedObj = obj.DeepCopy(); // don't mutate original
|
||||
copiedObj.GeometryBase!.Transform(Transform);
|
||||
_cachedTransformedObjects.Add(copiedObj);
|
||||
}
|
||||
}
|
||||
|
||||
return _cachedTransformedObjects;
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
#if RHINO8_OR_GREATER
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Grasshopper.Rhinoceros.Model;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleBlockInstanceWrapperGoo
|
||||
{
|
||||
private bool CastFromModelObject(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case InstanceReferenceGeometry instanceRef:
|
||||
SpeckleObjectWrapperGoo objGoo = new();
|
||||
objGoo.CastFrom(instanceRef);
|
||||
if (objGoo.Value is SpeckleBlockInstanceWrapper instanceWrapper)
|
||||
{
|
||||
Value = instanceWrapper;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case GH_InstanceReference ghInstanceRef:
|
||||
return ghInstanceRef.Value != null && CastFromModelObject(ghInstanceRef.Value);
|
||||
|
||||
case RhinoObject rhinoObject:
|
||||
return CastFromModelObject((ModelObject)rhinoObject); // use this casting method to handle rhinoobjects: using constructor will result in a null guid!!
|
||||
|
||||
// Rhino model objects can be instances
|
||||
case ModelObject modelObject:
|
||||
if (modelObject.ObjectType == ObjectType.InstanceReference)
|
||||
{
|
||||
SpeckleObjectWrapperGoo modelObjGoo = new();
|
||||
modelObjGoo.CastFrom(modelObject); // handles all model object casting like geo conversion, model object name and props and color and mat
|
||||
|
||||
if (modelObjGoo.Value is SpeckleBlockInstanceWrapper modelInstanceWrapper)
|
||||
{
|
||||
Value = modelInstanceWrapper;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CastToModelObject<T>(ref T target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case GH_InstanceReference:
|
||||
if (Value == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Value.Definition == null)
|
||||
{
|
||||
// No definition available - create minimal instance reference for compatibility
|
||||
// This handles edge cases where we have a transform but no block definition
|
||||
var minimalInstanceRef = new InstanceReferenceGeometry(Guid.Empty, Value.Transform);
|
||||
target = (T)(object)new GH_InstanceReference(minimalInstanceRef);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create or find the block definition in the Rhino document
|
||||
// This either finds existing definition or creates temporary one for pure GH workflows
|
||||
var modelInstanceDef = CreateModelInstanceDefinition(Value.Definition);
|
||||
if (modelInstanceDef == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ModelInstanceDefinition.Id contains the real definition ID from document (either found or just created)
|
||||
// Fallback to Guid.Empty only for theoretical edge cases where ID might be null
|
||||
var definitionId = modelInstanceDef.Id ?? Guid.Empty;
|
||||
|
||||
// Create InstanceReferenceGeometry with the actual definition ID
|
||||
// This preserves the link to the real block definition in the Rhino document (if any)
|
||||
var instanceRefGeo = new InstanceReferenceGeometry(definitionId, Value.Transform);
|
||||
var ghInstanceRef = new GH_InstanceReference(instanceRefGeo, modelInstanceDef);
|
||||
target = (T)(object)ghInstanceRef;
|
||||
return true;
|
||||
|
||||
case InstanceReferenceGeometry:
|
||||
return CreateInstanceReferenceGeometry(ref target);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ModelInstanceDefinition? CreateModelInstanceDefinition(SpeckleBlockDefinitionWrapper definition)
|
||||
{
|
||||
SpeckleBlockDefinitionWrapperGoo modelInstanceDefGoo = new(definition);
|
||||
ModelInstanceDefinition existingModelDef = new();
|
||||
if (modelInstanceDefGoo.CastTo(ref existingModelDef))
|
||||
{
|
||||
return existingModelDef;
|
||||
}
|
||||
|
||||
var doc = RhinoDoc.ActiveDoc;
|
||||
|
||||
if (doc == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rhinoInstanceDef = doc.InstanceDefinitions.Find(definition.Name);
|
||||
|
||||
if (rhinoInstanceDef != null)
|
||||
{
|
||||
return new ModelInstanceDefinition(rhinoInstanceDef);
|
||||
}
|
||||
|
||||
var geometries = new List<GeometryBase>();
|
||||
var attributes = new List<ObjectAttributes>();
|
||||
|
||||
foreach (var obj in definition.Objects)
|
||||
{
|
||||
if (obj.GeometryBase != null)
|
||||
{
|
||||
geometries.Add(obj.GeometryBase.Duplicate());
|
||||
ObjectAttributes att = obj.CreateObjectAttributes();
|
||||
attributes.Add(att);
|
||||
}
|
||||
}
|
||||
|
||||
if (geometries.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var defIndex = doc.InstanceDefinitions.Add(
|
||||
definition.Name,
|
||||
"Temporary for Grasshopper workflow - objects will appear as point on bake",
|
||||
Point3d.Origin,
|
||||
geometries,
|
||||
attributes
|
||||
);
|
||||
|
||||
if (defIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
InstanceDefinition? tempRhinoDef = doc.InstanceDefinitions[defIndex];
|
||||
ModelInstanceDefinition modelDef = new(tempRhinoDef);
|
||||
|
||||
return modelDef;
|
||||
}
|
||||
|
||||
private bool CreateInstanceReferenceGeometry<T>(ref T target)
|
||||
{
|
||||
// Only works if the block definition exists in the Rhino document
|
||||
// Will fail for pure Grasshopper workflows
|
||||
if (Value?.Definition == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var doc = RhinoDoc.ActiveDoc;
|
||||
var instanceDef = doc?.InstanceDefinitions.Find(Value.Definition.Name);
|
||||
|
||||
if (instanceDef != null)
|
||||
{
|
||||
var instanceRefGeo = new InstanceReferenceGeometry(instanceDef.Id, Value.Transform);
|
||||
target = (T)(object)instanceRefGeo;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleBlockInstanceWrapperGoo : GH_Goo<SpeckleBlockInstanceWrapper>, IGH_PreviewData
|
||||
{
|
||||
public override bool IsValid => Value?.InstanceProxy != null && Value.ApplicationId is not null;
|
||||
public override string TypeName => "Speckle Block Instance";
|
||||
public override string TypeDescription => "Represents an instance object from Speckle";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default Instance Goo with default values. Only use this for casting.
|
||||
/// </summary>
|
||||
public SpeckleBlockInstanceWrapperGoo()
|
||||
{
|
||||
Value = new SpeckleBlockInstanceWrapper(Transform.Identity);
|
||||
}
|
||||
|
||||
public SpeckleBlockInstanceWrapperGoo(SpeckleBlockInstanceWrapper value)
|
||||
{
|
||||
Value = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
public override IGH_Goo Duplicate() =>
|
||||
new SpeckleBlockInstanceWrapperGoo((SpeckleBlockInstanceWrapper)Value.DeepCopy());
|
||||
|
||||
public override string ToString() =>
|
||||
$"Speckle Block Instance : {(string.IsNullOrWhiteSpace(Value.Name) ? Value.Base.speckle_type : Value.Name)}";
|
||||
|
||||
//POC: we probably shouldn't be deep copying here!!! do so in each component that mutates inputs...
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleBlockInstanceWrapper sourceWrapper:
|
||||
Value = sourceWrapper;
|
||||
return true;
|
||||
case SpeckleBlockInstanceWrapperGoo wrapperGoo:
|
||||
Value = wrapperGoo.Value;
|
||||
return true;
|
||||
case GH_Goo<SpeckleBlockInstanceWrapper> goo:
|
||||
Value = goo.Value;
|
||||
return true;
|
||||
case SpeckleObjectWrapperGoo objWrapperGoo:
|
||||
if (objWrapperGoo.Value is SpeckleBlockInstanceWrapper objWrapper)
|
||||
{
|
||||
Value = objWrapper;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case GH_Goo<SpeckleObjectWrapper> goo:
|
||||
if (goo.Value is SpeckleBlockInstanceWrapper wrapper)
|
||||
{
|
||||
Value = wrapper;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case IGH_GeometricGoo geometricGoo:
|
||||
// this happens when you assign instances in rhino to a model isntance param
|
||||
// need to get the id of the referenced geometry here and pass the retrieved object
|
||||
if (geometricGoo.IsReferencedGeometry)
|
||||
{
|
||||
return RhinoDoc.ActiveDoc?.Objects.FindId(geometricGoo.ReferenceID) is RhinoObject rhinoObj
|
||||
&& CastFromModelObject(rhinoObj);
|
||||
}
|
||||
|
||||
if (geometricGoo is not InstanceReferenceGeometry instance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Base converted = SpeckleConversionContext.ConvertToSpeckle(instance);
|
||||
Value = new SpeckleBlockInstanceWrapper()
|
||||
{
|
||||
GeometryBase = instance,
|
||||
Base = converted,
|
||||
Transform = instance.Xform,
|
||||
ApplicationId = Guid.NewGuid().ToString(),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastFromModelObject(source);
|
||||
}
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case SpeckleObjectWrapperGoo:
|
||||
target = (T)(object)Value;
|
||||
return true;
|
||||
case Transform:
|
||||
target = (T)(object)Value.Transform;
|
||||
return true;
|
||||
default:
|
||||
return CastToModelObject(ref target);
|
||||
}
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelObject(object _) => false;
|
||||
|
||||
private bool CastToModelObject<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public void DrawViewportWires(GH_PreviewWireArgs args)
|
||||
{
|
||||
// TODO?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(GH_PreviewMeshArgs args) => Value?.DrawPreviewRaw(args.Pipeline, args.Material);
|
||||
|
||||
public BoundingBox ClippingBox
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Value?.Definition?.Objects == null)
|
||||
{
|
||||
return new BoundingBox();
|
||||
}
|
||||
|
||||
var clippingBox = new BoundingBox();
|
||||
foreach (var obj in Value.Definition.Objects)
|
||||
{
|
||||
if (obj.GeometryBase != null)
|
||||
{
|
||||
var transformedGeometry = obj.GeometryBase.Duplicate();
|
||||
transformedGeometry.Transform(Value.Transform);
|
||||
clippingBox.Union(transformedGeometry.GetBoundingBox(false));
|
||||
}
|
||||
}
|
||||
|
||||
return clippingBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleBlockInstanceParam
|
||||
: GH_Param<SpeckleBlockInstanceWrapperGoo>,
|
||||
IGH_BakeAwareObject,
|
||||
IGH_PreviewObject
|
||||
{
|
||||
public SpeckleBlockInstanceParam()
|
||||
: this(GH_ParamAccess.item) { }
|
||||
|
||||
public SpeckleBlockInstanceParam(IGH_InstanceDescription tag)
|
||||
: base(tag) { }
|
||||
|
||||
public SpeckleBlockInstanceParam(IGH_InstanceDescription tag, GH_ParamAccess access)
|
||||
: base(tag, access) { }
|
||||
|
||||
public SpeckleBlockInstanceParam(GH_ParamAccess access)
|
||||
: base(
|
||||
"Speckle Block Instance",
|
||||
"SBI",
|
||||
"Represents a Speckle block instance",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("938CCD6E-B202-4A0C-9D68-ABD7683B0EDE");
|
||||
protected override Bitmap Icon => Resources.speckle_param_block_instance;
|
||||
|
||||
public override void RegisterRemoteIDs(GH_GuidTable idList)
|
||||
{
|
||||
// Register both the block definition and instance GUIDs so Grasshopper
|
||||
// auto-expires when either the definition or instance changes in Rhino
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockInstanceWrapperGoo goo)
|
||||
{
|
||||
// Track the referenced block definition
|
||||
if (
|
||||
goo.Value?.Definition?.ApplicationId != null
|
||||
&& Guid.TryParse(goo.Value.Definition.ApplicationId, out Guid defId)
|
||||
)
|
||||
{
|
||||
idList.Add(defId, this);
|
||||
}
|
||||
|
||||
// Track the instance itself if it references a Rhino object
|
||||
if (goo.Value?.ApplicationId != null && Guid.TryParse(goo.Value.ApplicationId, out Guid instId))
|
||||
{
|
||||
idList.Add(instId, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBakeCapable => !VolatileData.IsEmpty;
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
public void BakeGeometry(RhinoDoc doc, List<Guid> objIds)
|
||||
{
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockInstanceWrapperGoo goo)
|
||||
{
|
||||
goo.Value.Bake(doc, objIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> objIds) => BakeGeometry(doc, objIds); // Instances manage their own attributes
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// TODO ?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockInstanceWrapperGoo goo)
|
||||
{
|
||||
goo.Value.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected()
|
||||
{
|
||||
return Attributes?.Parent?.Selected ?? false;
|
||||
}
|
||||
|
||||
public bool Hidden { get; set; }
|
||||
public BoundingBox ClippingBox
|
||||
{
|
||||
get
|
||||
{
|
||||
var clippingBox = new BoundingBox();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleBlockInstanceWrapperGoo goo)
|
||||
{
|
||||
clippingBox.Union(goo.ClippingBox);
|
||||
}
|
||||
}
|
||||
return clippingBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Layer = Rhino.DocObjects.Layer;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// A Wrapper class representing a Speckle Collection to Rhino Layer relationship.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When constructing, the following properties need to be set in order:
|
||||
/// <see cref="SpeckleWrapper.Base"/>, then <see cref="SpeckleWrapper.Name"/> and <see cref="SpeckleWrapper.ApplicationId"/>
|
||||
/// This is because changing the Name or ApplicationId will update Collection.
|
||||
/// </remarks>
|
||||
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
|
||||
public class SpeckleCollectionWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
|
||||
{
|
||||
public override required Base Base
|
||||
{
|
||||
get => Collection;
|
||||
set
|
||||
{
|
||||
if (value is not Collection coll)
|
||||
{
|
||||
throw new ArgumentException("Cannot create collection wrapper from a non-Collection Base");
|
||||
}
|
||||
|
||||
Collection = coll;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection Collection { get; set; }
|
||||
|
||||
private List<string> StoredPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of Collection names that build up the path to this collection (inclusive of <see cref="SpeckleWrapper.Name"/>;
|
||||
/// </summary>
|
||||
/// <remarks>Setting this property will update all element paths inside <see cref="Elements"/></remarks>
|
||||
public required List<string> Path
|
||||
{
|
||||
get => StoredPath;
|
||||
set
|
||||
{
|
||||
StoredPath = value;
|
||||
OnPathChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ISpeckleCollectionObject> Elements { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The Grasshopper Topology of this collection. This setter also sets the "topology" prop dynamically on <see cref="Collection"/>
|
||||
/// </summary>
|
||||
public string? Topology
|
||||
{
|
||||
get => Collection[Constants.TOPOLOGY_PROP] as string;
|
||||
set => Collection[Constants.TOPOLOGY_PROP] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public required Color? Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The material of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public required SpeckleMaterialWrapper? Material { get; set; }
|
||||
|
||||
public override string ToString() => $"Speckle Collection : {Name} ({Elements.Count})";
|
||||
|
||||
public override IGH_Goo CreateGoo() => new SpeckleCollectionWrapperGoo(this);
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to retrieve an existing Layer from the <see cref="Path"/>.
|
||||
/// </summary>
|
||||
/// <returns>Index of existing layer if found, or -1 if not.</returns>
|
||||
public int GetLayerIndex() => RhinoDoc.ActiveDoc.Layers.FindByFullPath(string.Join("::", Path), -1);
|
||||
|
||||
// updates the elements' paths inside this collection
|
||||
private void OnPathChanged()
|
||||
{
|
||||
var newPath = StoredPath.ToList();
|
||||
|
||||
// then update paths and parents of all children
|
||||
foreach (var element in Elements)
|
||||
{
|
||||
switch (element)
|
||||
{
|
||||
case SpeckleObjectWrapper o:
|
||||
o.Path = newPath;
|
||||
o.Parent = this;
|
||||
break;
|
||||
case SpeckleCollectionWrapper c:
|
||||
// don't forget to add the child collection name to the path
|
||||
var childPath = newPath.ToList();
|
||||
childPath.Add(c.Name);
|
||||
c.Path = childPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SpeckleCollectionWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = new Collection(Collection.name) { applicationId = Collection.applicationId, id = Collection.id },
|
||||
Color = Color,
|
||||
Material = Material,
|
||||
ApplicationId = ApplicationId,
|
||||
Name = Name,
|
||||
Path = Path,
|
||||
Topology = Topology,
|
||||
Elements = Elements
|
||||
.Select(e =>
|
||||
e switch
|
||||
{
|
||||
SpeckleCollectionWrapper c => c.DeepCopy(),
|
||||
SpeckleBlockInstanceWrapper b => b.DeepCopy(),
|
||||
SpeckleObjectWrapper o => o.DeepCopy(),
|
||||
_ => e
|
||||
}
|
||||
)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Bakes this collection as a layer, in its path structure.
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="objIds"></param>
|
||||
/// <param name="bakeObjects"></param>
|
||||
/// <returns>The index of the baked layer</returns>
|
||||
public int Bake(RhinoDoc doc, List<Guid> objIds, bool bakeObjects, int parentLayerIndex = -1)
|
||||
{
|
||||
if (!LayerExists(doc, Path, out int currentLayerIndex))
|
||||
{
|
||||
if (parentLayerIndex != -1)
|
||||
{
|
||||
Guid parentLayerId = doc.Layers[parentLayerIndex].Id;
|
||||
currentLayerIndex = CreateLayer(doc, Collection.name, parentLayerId, Color);
|
||||
Guid currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
|
||||
objIds.Add(currentLayerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLayerIndex = CreateLayerByPath(doc, Path, Color, objIds);
|
||||
}
|
||||
}
|
||||
|
||||
// then bake elements in this collection
|
||||
foreach (var obj in Elements)
|
||||
{
|
||||
if (obj is SpeckleObjectWrapper so)
|
||||
{
|
||||
if (bakeObjects)
|
||||
{
|
||||
so.Bake(doc, objIds, currentLayerIndex, true);
|
||||
}
|
||||
}
|
||||
else if (obj is SpeckleCollectionWrapper c)
|
||||
{
|
||||
c.Bake(doc, objIds, bakeObjects, currentLayerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return currentLayerIndex;
|
||||
}
|
||||
|
||||
private bool LayerExists(RhinoDoc doc, List<string> path, out int layerIndex)
|
||||
{
|
||||
var fullPath = string.Join("::", path);
|
||||
layerIndex = doc.Layers.FindByFullPath(fullPath, -1);
|
||||
return layerIndex != -1;
|
||||
}
|
||||
|
||||
private int CreateLayer(RhinoDoc doc, string name, Guid parentId, Color? color)
|
||||
{
|
||||
Layer layer = new() { Name = name, ParentLayerId = parentId };
|
||||
if (color is not null)
|
||||
{
|
||||
layer.Color = color.Value;
|
||||
}
|
||||
|
||||
return doc.Layers.Add(layer);
|
||||
}
|
||||
|
||||
private int CreateLayerByPath(RhinoDoc doc, List<string> path, Color? color, List<Guid> objIds)
|
||||
{
|
||||
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, color);
|
||||
currentLayerId = doc.Layers.FindIndex(currentLayerIndex).Id;
|
||||
objIds.Add(currentLayerId);
|
||||
}
|
||||
|
||||
parentLayerIndex = currentLayerIndex;
|
||||
}
|
||||
|
||||
return parentLayerIndex;
|
||||
}
|
||||
}
|
||||
+33
-32
@@ -55,42 +55,43 @@ public partial class SpeckleCollectionWrapperGoo : GH_Goo<SpeckleCollectionWrapp
|
||||
|
||||
private bool CastFromModelLayer(object source)
|
||||
{
|
||||
if (source is ModelLayer modelLayer)
|
||||
switch (source)
|
||||
{
|
||||
Collection modelCollection =
|
||||
new()
|
||||
case ModelLayer modelLayer:
|
||||
Collection modelCollection =
|
||||
new()
|
||||
{
|
||||
name = modelLayer.Name,
|
||||
elements = new(),
|
||||
applicationId = modelLayer.Id?.ToString()
|
||||
};
|
||||
|
||||
// get color and material
|
||||
Color? layerColor = null;
|
||||
if (modelLayer.DisplayColor is ModelColor color)
|
||||
{
|
||||
name = modelLayer.Name,
|
||||
elements = new(),
|
||||
applicationId = modelLayer.Id?.ToString()
|
||||
layerColor = Color.FromArgb(color.ToArgb());
|
||||
}
|
||||
|
||||
SpeckleMaterialWrapper? layerMaterial = null;
|
||||
if (modelLayer.Material?.Id is Guid id)
|
||||
{
|
||||
var mat = RhinoDoc.ActiveDoc.RenderMaterials.Find(id);
|
||||
SpeckleMaterialWrapperGoo materialGoo = new();
|
||||
materialGoo.CastFrom(mat);
|
||||
layerMaterial = materialGoo.Value;
|
||||
}
|
||||
|
||||
Value = new SpeckleCollectionWrapper()
|
||||
{
|
||||
Base = modelCollection,
|
||||
Name = modelLayer.Name,
|
||||
Color = layerColor,
|
||||
Material = layerMaterial,
|
||||
Path = GetModelLayerPath(modelLayer)
|
||||
};
|
||||
|
||||
// get color and material
|
||||
Color? layerColor = null;
|
||||
if (modelLayer.DisplayColor is ModelColor color)
|
||||
{
|
||||
layerColor = Color.FromArgb(color.ToArgb());
|
||||
}
|
||||
|
||||
SpeckleMaterialWrapper? layerMaterial = null;
|
||||
if (modelLayer.Material?.Id is Guid id)
|
||||
{
|
||||
var mat = RhinoDoc.ActiveDoc.RenderMaterials.Find(id);
|
||||
SpeckleMaterialWrapperGoo materialGoo = new();
|
||||
materialGoo.CastFrom(mat);
|
||||
layerMaterial = materialGoo.Value;
|
||||
}
|
||||
|
||||
Value = new SpeckleCollectionWrapper()
|
||||
{
|
||||
Base = modelCollection,
|
||||
Name = modelLayer.Name,
|
||||
Color = layerColor,
|
||||
Material = layerMaterial,
|
||||
Path = GetModelLayerPath(modelLayer)
|
||||
};
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleCollectionWrapperGoo : GH_Goo<SpeckleCollectionWrapper> //, IGH_PreviewData // can be made previewable later
|
||||
{
|
||||
public override bool IsValid => Value.Collection is not null;
|
||||
public override string TypeName => "Speckle Collection";
|
||||
public override string TypeDescription => "Represents a collection from Speckle";
|
||||
|
||||
public SpeckleCollectionWrapperGoo() { }
|
||||
|
||||
public SpeckleCollectionWrapperGoo(SpeckleCollectionWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override IGH_Goo Duplicate() => new SpeckleCollectionWrapperGoo(Value.DeepCopy());
|
||||
|
||||
public override string ToString() => $"Speckle Collection : {Value.Name} ({Value.Elements.Count})";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleCollectionWrapper sourceWrapper:
|
||||
Value = sourceWrapper;
|
||||
return true;
|
||||
case SpeckleCollectionWrapperGoo wrapperGoo:
|
||||
Value = wrapperGoo.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle case of model objects in rhino 8
|
||||
return CastFromModelLayer(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelLayer(object _) => false;
|
||||
|
||||
private bool CastToModelLayer<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
return CastToModelLayer(ref target);
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleCollectionParam : GH_Param<SpeckleCollectionWrapperGoo>, IGH_BakeAwareObject, IGH_PreviewObject
|
||||
{
|
||||
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",
|
||||
"A Speckle collection, corresponding to layers in Rhino",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("6E871D5B-B221-4992-882A-EFE6796F3010");
|
||||
protected override Bitmap Icon => Resources.speckle_param_collection;
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
bool IGH_BakeAwareObject.IsBakeCapable => // False if no data
|
||||
!VolatileData.IsEmpty;
|
||||
|
||||
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, List<Guid> objIds)
|
||||
{
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleCollectionWrapperGoo goo)
|
||||
{
|
||||
goo.Value.Bake(doc, objIds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IGH_BakeAwareObject.BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> objIds)
|
||||
{
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleCollectionWrapperGoo goo)
|
||||
{
|
||||
goo.Value.Bake(doc, objIds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BoundingBox _clippingBox;
|
||||
public BoundingBox ClippingBox => _clippingBox;
|
||||
|
||||
bool IGH_PreviewObject.Hidden { get; set; }
|
||||
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
private readonly List<SpeckleObjectWrapper> _previewObjects = new();
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
if (_previewObjects.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var elem in _previewObjects)
|
||||
{
|
||||
elem.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected()
|
||||
{
|
||||
return Attributes?.Parent?.Selected ?? false;
|
||||
}
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// todo?
|
||||
}
|
||||
|
||||
// Called when volatile data has been collected.
|
||||
// post-process or analyze the volatile data here.
|
||||
// this is where we will recompute and store the objects for preview
|
||||
protected override void OnVolatileDataCollected()
|
||||
{
|
||||
base.OnVolatileDataCollected();
|
||||
_previewObjects.Clear();
|
||||
_clippingBox = new();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleCollectionWrapperGoo goo)
|
||||
{
|
||||
FlattenForPreview(goo.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlattenForPreview(SpeckleCollectionWrapper collWrapper)
|
||||
{
|
||||
foreach (var element in collWrapper.Elements)
|
||||
{
|
||||
if (element is SpeckleCollectionWrapper subCollWrapper)
|
||||
{
|
||||
FlattenForPreview(subCollWrapper);
|
||||
}
|
||||
|
||||
if (element is SpeckleObjectWrapper objWrapper)
|
||||
{
|
||||
_previewObjects.Add(objWrapper);
|
||||
var box = objWrapper.GeometryBase is null ? new() : objWrapper.GeometryBase.GetBoundingBox(false);
|
||||
_clippingBox.Union(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Sdk.Models;
|
||||
using SpeckleRenderMaterial = Speckle.Objects.Other.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around a render material base object and its converted speckle equivalent.
|
||||
/// </summary>
|
||||
public class SpeckleMaterialWrapper : SpeckleWrapper
|
||||
{
|
||||
public override required Base Base
|
||||
{
|
||||
get => Material;
|
||||
set
|
||||
{
|
||||
if (value is not SpeckleRenderMaterial mat)
|
||||
{
|
||||
throw new ArgumentException("Cannot create material wrapper from a non-SpeckleRenderMaterial Base");
|
||||
}
|
||||
|
||||
Material = mat;
|
||||
}
|
||||
}
|
||||
|
||||
public SpeckleRenderMaterial Material { get; set; }
|
||||
|
||||
public required Material RhinoMaterial { get; set; }
|
||||
|
||||
// The guid of the rhino render material that corresponds to the rhino material, if it exists.
|
||||
public required Guid RhinoRenderMaterialId { get; set; }
|
||||
|
||||
public override string ToString() => $"Speckle Material : {Name}";
|
||||
|
||||
public override IGH_Goo CreateGoo() => new SpeckleMaterialWrapperGoo(this);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the material in the document
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="name">The name override, if any. Used for param baking where the nickname is changed by the user, or no name is available.</param>
|
||||
/// <returns>The index of the created material in the material table</returns>
|
||||
public int Bake(RhinoDoc doc, string? name = null)
|
||||
{
|
||||
Material bakeMaterial = new();
|
||||
bakeMaterial.CopyFrom(RhinoMaterial);
|
||||
|
||||
// set the material name
|
||||
// this should be the given name in the rhino material *unless* an override name is passed in
|
||||
if (name != null)
|
||||
{
|
||||
bakeMaterial.Name = name;
|
||||
}
|
||||
|
||||
return doc.Materials.Add(bakeMaterial);
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using SpeckleRenderMaterial = Speckle.Objects.Other.RenderMaterial;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleMaterialWrapperGoo : GH_Goo<SpeckleMaterialWrapper>
|
||||
{
|
||||
public override bool IsValid => Value.Base is not null;
|
||||
public override string TypeName => "Speckle Material";
|
||||
public override string TypeDescription => "Represents a render material from Speckle";
|
||||
|
||||
public SpeckleMaterialWrapperGoo(SpeckleMaterialWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty constructor should only be used for casting
|
||||
/// </summary>
|
||||
public SpeckleMaterialWrapperGoo() { }
|
||||
|
||||
public override IGH_Goo Duplicate() => throw new NotImplementedException();
|
||||
|
||||
public override string ToString() => $"Speckle Material : {Value.Name}";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleMaterialWrapper sourceWrapper:
|
||||
Value = sourceWrapper;
|
||||
return true;
|
||||
case SpeckleMaterialWrapperGoo wrapperGoo:
|
||||
Value = wrapperGoo.Value;
|
||||
return true;
|
||||
case GH_Material materialGoo:
|
||||
var gooMaterial = ToRhinoMaterial(materialGoo.Value);
|
||||
Value = new()
|
||||
{
|
||||
Base = ToSpeckleRenderMaterial(materialGoo.Value),
|
||||
Name = gooMaterial.Name,
|
||||
RhinoMaterial = gooMaterial,
|
||||
RhinoRenderMaterialId = Guid.Empty,
|
||||
};
|
||||
return true;
|
||||
case Material material:
|
||||
Value = new()
|
||||
{
|
||||
Base = ToSpeckleRenderMaterial(material),
|
||||
Name = material.Name,
|
||||
RhinoMaterial = material,
|
||||
RhinoRenderMaterialId = Guid.Empty
|
||||
};
|
||||
return true;
|
||||
case SpeckleRenderMaterial speckleMaterial:
|
||||
Value = new()
|
||||
{
|
||||
Base = speckleMaterial,
|
||||
Name = speckleMaterial.name,
|
||||
RhinoMaterial = ToRhinoMaterial(speckleMaterial),
|
||||
RhinoRenderMaterialId = Guid.Empty,
|
||||
ApplicationId = speckleMaterial.applicationId,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastFromModelRenderMaterial(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelRenderMaterial(object _) => false;
|
||||
|
||||
private bool CastToModelRenderMaterial<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(GH_Material))
|
||||
{
|
||||
target = (T)(object)(new GH_Material() { Value = new(Value.RhinoMaterial) });
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastToModelRenderMaterial(ref target);
|
||||
}
|
||||
|
||||
private SpeckleRenderMaterial ToSpeckleRenderMaterial(Rhino.Display.DisplayMaterial mat)
|
||||
{
|
||||
SpeckleRenderMaterial speckleRenderMaterial =
|
||||
new()
|
||||
{
|
||||
name = mat.GetSpeckleApplicationId(),
|
||||
opacity = 1 - mat.Transparency,
|
||||
metalness = mat.Shine,
|
||||
diffuse = mat.Diffuse.ToArgb(),
|
||||
emissive = mat.Emission.ToArgb(),
|
||||
applicationId = mat.GetSpeckleApplicationId(),
|
||||
};
|
||||
|
||||
// add additional dynamic props for rhino material receive
|
||||
speckleRenderMaterial["specular"] = mat.Specular.ToArgb();
|
||||
speckleRenderMaterial["shine"] = mat.Shine;
|
||||
|
||||
return speckleRenderMaterial;
|
||||
}
|
||||
|
||||
private SpeckleRenderMaterial ToSpeckleRenderMaterial(Material mat)
|
||||
{
|
||||
SpeckleRenderMaterial speckleRenderMaterial =
|
||||
new()
|
||||
{
|
||||
name = mat.Name,
|
||||
opacity = 1 - mat.Transparency,
|
||||
diffuse = mat.DiffuseColor.ToArgb(),
|
||||
emissive = mat.EmissionColor.ToArgb(),
|
||||
applicationId = mat.Name,
|
||||
["specular"] = mat.SpecularColor.ToArgb(),
|
||||
["shine"] = mat.AmbientColor,
|
||||
["ior"] = mat.IndexOfRefraction
|
||||
};
|
||||
|
||||
return speckleRenderMaterial;
|
||||
}
|
||||
|
||||
private Material ToRhinoMaterial(Rhino.Display.DisplayMaterial mat) =>
|
||||
new()
|
||||
{
|
||||
DiffuseColor = mat.Diffuse,
|
||||
EmissionColor = mat.Emission,
|
||||
Transparency = mat.Transparency,
|
||||
SpecularColor = mat.Specular,
|
||||
Shine = mat.Shine,
|
||||
};
|
||||
|
||||
private Material ToRhinoMaterial(SpeckleRenderMaterial mat) =>
|
||||
new()
|
||||
{
|
||||
Name = mat.name,
|
||||
DiffuseColor = mat.diffuseColor,
|
||||
EmissionColor = mat.emissiveColor,
|
||||
Transparency = 1 - mat.opacity,
|
||||
Shine = mat["shine"] is double shine ? shine : default,
|
||||
IndexOfRefraction = mat["ior"] is double ior ? ior : default
|
||||
};
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleMaterialParam : GH_Param<SpeckleMaterialWrapperGoo>, IGH_BakeAwareObject
|
||||
{
|
||||
private const string NICKNAME = "Speckle Material";
|
||||
|
||||
public SpeckleMaterialParam()
|
||||
: this(GH_ParamAccess.item) { }
|
||||
|
||||
public SpeckleMaterialParam(IGH_InstanceDescription tag)
|
||||
: base(tag) { }
|
||||
|
||||
public SpeckleMaterialParam(IGH_InstanceDescription tag, GH_ParamAccess access)
|
||||
: base(tag, access) { }
|
||||
|
||||
public SpeckleMaterialParam(GH_ParamAccess access)
|
||||
: base(
|
||||
NICKNAME,
|
||||
"SM",
|
||||
"Represents a Speckle material",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.PARAMETERS,
|
||||
access
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("1A08CF79-2072-4B14-9430-E4465FF0C0FE");
|
||||
protected override Bitmap Icon => Resources.speckle_param_material;
|
||||
public override GH_Exposure Exposure => GH_Exposure.secondary;
|
||||
|
||||
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 SpeckleMaterialWrapperGoo goo)
|
||||
{
|
||||
// get the param nickname if it is a custom name.
|
||||
// this is used to override the name of the material.
|
||||
// the nickname should also be used in case of an empty name on the rhino material
|
||||
string? name =
|
||||
NickName != NICKNAME
|
||||
? NickName
|
||||
: string.IsNullOrEmpty(goo.Value.Name)
|
||||
? NickName
|
||||
: null;
|
||||
|
||||
int bakeIndex = goo.Value.Bake(doc, name);
|
||||
|
||||
if (bakeIndex == -1)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Failed to add material {name} to document.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 SpeckleMaterialWrapperGoo goo)
|
||||
{
|
||||
// get the param nickname if it is a custom name.
|
||||
// this is used to override the name of the material.
|
||||
// the nickname should also be used in case of an empty name on the rhino material
|
||||
string? name =
|
||||
NickName != NICKNAME
|
||||
? NickName
|
||||
: string.IsNullOrEmpty(goo.Value.Name)
|
||||
? NickName
|
||||
: null;
|
||||
|
||||
int bakeIndex = goo.Value.Bake(doc, name);
|
||||
if (bakeIndex == -1)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Failed to add material {name} to document.");
|
||||
}
|
||||
|
||||
obj_ids.Add(doc.Materials[bakeIndex].Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Display;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around a geometry base object and its converted speckle equivalent.
|
||||
/// </summary>
|
||||
public class SpeckleObjectWrapper : SpeckleWrapper, ISpeckleCollectionObject
|
||||
{
|
||||
public override required Base Base { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GeometryBase corresponding to the <see cref="SpeckleWrapper.Base"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// POC: how will we send intervals and other gh native objects? do we? maybe not for now?
|
||||
/// Objects using fallback conversion (eg DataObjects) will create one wrapper per geometry in the display value.
|
||||
/// </remarks>
|
||||
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; }
|
||||
public SpecklePropertyGroupGoo Properties { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public Color? Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The material of the <see cref="Base"/>
|
||||
/// </summary>
|
||||
public SpeckleMaterialWrapper? Material { get; set; }
|
||||
|
||||
public override string ToString() =>
|
||||
$"Speckle Object : {(string.IsNullOrWhiteSpace(Name) ? Base.speckle_type : Name)}";
|
||||
|
||||
public virtual 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.DrawExtrusionWires(e, isSelected ? args.WireColour_Selected : args.WireColour);
|
||||
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 virtual void Bake(RhinoDoc doc, List<Guid> objIds, int bakeLayerIndex = -1, bool layersAlreadyCreated = false)
|
||||
{
|
||||
if (!layersAlreadyCreated && bakeLayerIndex < 0 && Path.Count > 0 && Parent != null)
|
||||
{
|
||||
bakeLayerIndex = Parent.Bake(doc, objIds, false);
|
||||
if (bakeLayerIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using var attributes = CreateObjectAttributes(bakeLayerIndex, true);
|
||||
Guid guid = doc.Objects.Add(GeometryBase, attributes);
|
||||
objIds.Add(guid);
|
||||
}
|
||||
|
||||
public virtual SpeckleObjectWrapper DeepCopy() =>
|
||||
new()
|
||||
{
|
||||
Base = Base.ShallowCopy(),
|
||||
GeometryBase = GeometryBase?.Duplicate(),
|
||||
Color = Color,
|
||||
Material = Material,
|
||||
ApplicationId = ApplicationId,
|
||||
Parent = Parent,
|
||||
Properties = Properties,
|
||||
Name = Name,
|
||||
Path = Path
|
||||
};
|
||||
|
||||
public virtual ObjectAttributes CreateObjectAttributes(int layerIndex = -1, bool bakeMaterial = false)
|
||||
{
|
||||
var attributes = new ObjectAttributes { Name = Name };
|
||||
|
||||
if (layerIndex >= 0)
|
||||
{
|
||||
attributes.LayerIndex = layerIndex;
|
||||
}
|
||||
|
||||
AddColorToAttributes(attributes);
|
||||
AddMaterialToAttributes(attributes, bakeMaterial);
|
||||
AddPropertiesToAttributes(attributes);
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public override IGH_Goo CreateGoo() => new SpeckleObjectWrapperGoo(this);
|
||||
|
||||
protected virtual void AddPropertiesToAttributes(ObjectAttributes attributes) =>
|
||||
Properties?.AssignToObjectAttributes(attributes);
|
||||
|
||||
protected virtual void AddColorToAttributes(ObjectAttributes attributes)
|
||||
{
|
||||
if (Color is Color validColor)
|
||||
{
|
||||
attributes.ObjectColor = validColor;
|
||||
attributes.ColorSource = ObjectColorSource.ColorFromObject;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void AddMaterialToAttributes(ObjectAttributes attributes, bool bakeMaterial)
|
||||
{
|
||||
if (Material is SpeckleMaterialWrapper materialWrapper && bakeMaterial)
|
||||
{
|
||||
// Only handle the baking scenario here
|
||||
// Existing baking logic from BakingHelpers (works in all Rhino versions)
|
||||
int matIndex = materialWrapper.Bake(RhinoDoc.ActiveDoc, materialWrapper.Name);
|
||||
if (matIndex >= 0)
|
||||
{
|
||||
attributes.MaterialIndex = matIndex;
|
||||
attributes.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: bakeMaterial: false scenario (casting) is handled in ModelObjects.cs
|
||||
// where it belongs, with proper Rhino 8+ conditional compilation
|
||||
}
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
#if RHINO8_OR_GREATER
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Grasshopper.Rhinoceros.Model;
|
||||
using Grasshopper.Rhinoceros.Render;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
|
||||
{
|
||||
public SpeckleObjectWrapperGoo(ModelObject mo)
|
||||
{
|
||||
CastFrom(mo);
|
||||
}
|
||||
|
||||
private bool CastFromModelObject(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case RhinoObject rhinoObject:
|
||||
return CastFromModelObject((ModelObject)rhinoObject); // use this casting method to handle rhinoobjects: using constructor will result in a null guid!!
|
||||
|
||||
case ModelObject modelObject:
|
||||
return HandleModelObject(modelObject);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleModelObject(ModelObject modelObject)
|
||||
{
|
||||
if (RhinoDoc.ActiveDoc.Objects.FindId(modelObject.Id ?? Guid.Empty)?.Geometry is not GeometryBase geometryBase)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Could not retrieve geometry from Model Object {modelObject.ObjectType}. Did you forget to bake these objects in your document?"
|
||||
);
|
||||
}
|
||||
|
||||
Base converted = SpeckleConversionContext.ConvertToSpeckle(geometryBase);
|
||||
|
||||
// get layer, props, color, and mat
|
||||
SpeckleCollectionWrapper? collection = GetLayerCollectionFromModelObject(modelObject);
|
||||
SpecklePropertyGroupGoo? props = GetPropsFromModelObjectAndAssignToBase(modelObject, converted);
|
||||
Color? color = GetColorFromModelObject(modelObject);
|
||||
SpeckleMaterialWrapper? material = GetMaterialFromModelObject(modelObject);
|
||||
|
||||
// get the definition if this is an instance
|
||||
SpeckleBlockDefinitionWrapper? definition = GetBlockDefinition(geometryBase);
|
||||
|
||||
return SetValueAsObjectOrInstanceWrapper(
|
||||
geometryBase,
|
||||
converted,
|
||||
modelObject.Name.ToString(),
|
||||
props,
|
||||
collection,
|
||||
color,
|
||||
material,
|
||||
modelObject.Id.ToString(),
|
||||
definition
|
||||
);
|
||||
}
|
||||
|
||||
private bool CastToModelObject<T>(ref T target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case ModelObject:
|
||||
// create attributes
|
||||
ObjectAttributes modelObjectAtts = new();
|
||||
CastTo(ref modelObjectAtts);
|
||||
|
||||
// create model object
|
||||
ModelObject modelObject = new(RhinoDoc.ActiveDoc, modelObjectAtts, Value.GeometryBase);
|
||||
target = (T)(object)modelObject;
|
||||
return true;
|
||||
|
||||
case ObjectAttributes:
|
||||
ObjectAttributes objectAtts = new() { Name = Value.Name };
|
||||
|
||||
if (Value.Color is Color color)
|
||||
{
|
||||
objectAtts.ObjectColor = color;
|
||||
objectAtts.ColorSource = ObjectColorSource.ColorFromObject;
|
||||
}
|
||||
|
||||
// POC: only set material if it exists in the doc. Avoiding baking during cast.
|
||||
if (
|
||||
Value.Material is SpeckleMaterialWrapper materialWrapper
|
||||
&& materialWrapper.RhinoRenderMaterialId != Guid.Empty
|
||||
)
|
||||
{
|
||||
Rhino.Render.RenderMaterial renderMaterial = RhinoDoc.ActiveDoc.RenderMaterials.Find(
|
||||
materialWrapper.RhinoRenderMaterialId
|
||||
);
|
||||
|
||||
objectAtts.RenderMaterial = renderMaterial;
|
||||
objectAtts.MaterialSource = ObjectMaterialSource.MaterialFromObject;
|
||||
}
|
||||
|
||||
// POC: only set layer if it exists in the doc. Avoid baking during cast.
|
||||
if (Value.Parent is SpeckleCollectionWrapper collectionWrapper)
|
||||
{
|
||||
int layerIndex = collectionWrapper.GetLayerIndex();
|
||||
if (layerIndex != -1)
|
||||
{
|
||||
objectAtts.LayerIndex = layerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// add props
|
||||
Value.Properties.AssignToObjectAttributes(objectAtts);
|
||||
|
||||
target = (T)(object)objectAtts;
|
||||
return true;
|
||||
|
||||
case ModelRenderMaterial:
|
||||
if (Value.Material is SpeckleMaterialWrapper matWrapper)
|
||||
{
|
||||
SpeckleMaterialWrapperGoo matWrapperGoo = new(matWrapper);
|
||||
ModelRenderMaterial modelMat = new();
|
||||
if (matWrapperGoo.CastTo(ref modelMat))
|
||||
{
|
||||
target = (T)(object)modelMat;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case ModelLayer:
|
||||
if (Value.Parent is SpeckleCollectionWrapper collWrapper)
|
||||
{
|
||||
SpeckleCollectionWrapperGoo collWrapperGoo = new(collWrapper);
|
||||
ModelLayer modelLayer = new();
|
||||
if (collWrapperGoo.CastTo(ref modelLayer))
|
||||
{
|
||||
target = (T)(object)modelLayer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Rhino.Render.RenderMaterial? GetRenderMaterial(ModelObject modelObject)
|
||||
{
|
||||
// we need to retrieve the actual material by the material source (otherwise will return default material for anything other than by object)
|
||||
Guid? matId = null;
|
||||
switch (modelObject.Render.Material?.Source)
|
||||
{
|
||||
case ObjectMaterialSource.MaterialFromLayer:
|
||||
matId = modelObject.Layer.Material.Id;
|
||||
break;
|
||||
case ObjectMaterialSource.MaterialFromObject:
|
||||
matId = modelObject.Render.Material?.Material?.Id;
|
||||
break;
|
||||
case ObjectMaterialSource.MaterialFromParent: // POC: too complicated for now
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return matId is Guid validId ? RhinoDoc.ActiveDoc.RenderMaterials.Find(validId) : null;
|
||||
}
|
||||
|
||||
private bool SetValueAsObjectOrInstanceWrapper(
|
||||
GeometryBase geometryBase,
|
||||
Base @base,
|
||||
string name,
|
||||
SpecklePropertyGroupGoo props,
|
||||
SpeckleCollectionWrapper? parent,
|
||||
Color? color,
|
||||
SpeckleMaterialWrapper? mat,
|
||||
string appId,
|
||||
SpeckleBlockDefinitionWrapper? definition = null
|
||||
)
|
||||
{
|
||||
Value = geometryBase is InstanceReferenceGeometry instance
|
||||
? new SpeckleBlockInstanceWrapper()
|
||||
{
|
||||
GeometryBase = instance,
|
||||
Base = @base,
|
||||
Transform = instance.Xform,
|
||||
Definition = definition, // May be null in pure Grasshopper workflows
|
||||
Parent = parent,
|
||||
Name = name,
|
||||
Color = color,
|
||||
Material = mat,
|
||||
Properties = props,
|
||||
ApplicationId = appId
|
||||
}
|
||||
: new SpeckleObjectWrapper()
|
||||
{
|
||||
GeometryBase = geometryBase,
|
||||
Base = @base,
|
||||
Parent = parent,
|
||||
Name = name,
|
||||
Color = color,
|
||||
Material = mat,
|
||||
Properties = props,
|
||||
ApplicationId = appId
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SpeckleBlockDefinitionWrapper? GetBlockDefinition(GeometryBase geometryBase)
|
||||
{
|
||||
SpeckleBlockDefinitionWrapper? definition = null;
|
||||
if (geometryBase is InstanceReferenceGeometry instance)
|
||||
{
|
||||
var instanceDef = RhinoDoc.ActiveDoc?.InstanceDefinitions.FindId(instance.ParentIdefId);
|
||||
if (instanceDef != null)
|
||||
{
|
||||
var defGoo = new SpeckleBlockDefinitionWrapperGoo();
|
||||
if (defGoo.CastFrom(instanceDef))
|
||||
{
|
||||
definition = defGoo.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
private SpeckleCollectionWrapper? GetLayerCollectionFromModelObject(ModelObject modelObject)
|
||||
{
|
||||
SpeckleCollectionWrapperGoo collWrapperGoo = new();
|
||||
return collWrapperGoo.CastFrom(modelObject.Layer) ? collWrapperGoo.Value : null;
|
||||
}
|
||||
|
||||
private SpecklePropertyGroupGoo GetPropsFromModelObjectAndAssignToBase(ModelObject modelObject, Base @base)
|
||||
{
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
if (propertyGroup.CastFrom(modelObject.UserText))
|
||||
{
|
||||
Dictionary<string, object?> propertyDict = new();
|
||||
foreach (var entry in modelObject.UserText)
|
||||
{
|
||||
propertyDict.Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
@base[Constants.PROPERTIES_PROP] = propertyDict;
|
||||
}
|
||||
|
||||
return propertyGroup;
|
||||
}
|
||||
|
||||
private SpeckleMaterialWrapper? GetMaterialFromModelObject(ModelObject modelObject)
|
||||
{
|
||||
Rhino.Render.RenderMaterial? mat = GetRenderMaterial(modelObject);
|
||||
|
||||
if (mat is Rhino.Render.RenderMaterial renderMat)
|
||||
{
|
||||
var wrapper = new SpeckleMaterialWrapperGoo();
|
||||
if (wrapper.CastFrom(renderMat))
|
||||
{
|
||||
return wrapper.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Color? GetColorFromModelObject(ModelObject modelObject)
|
||||
{
|
||||
// we need to retrieve the actual color by the color source (otherwise will return default color for anything other than by object)
|
||||
int? argb = null;
|
||||
switch (modelObject.Display.Color?.Source)
|
||||
{
|
||||
case ObjectColorSource.ColorFromLayer:
|
||||
argb = modelObject.Layer.DisplayColor?.ToArgb();
|
||||
break;
|
||||
case ObjectColorSource.ColorFromObject:
|
||||
argb = modelObject.Display.Color?.Color.ToArgb();
|
||||
break;
|
||||
case ObjectColorSource.ColorFromMaterial:
|
||||
Rhino.Render.RenderMaterial? mat = GetRenderMaterial(modelObject);
|
||||
argb = mat?.ToMaterial(Rhino.Render.RenderTexture.TextureGeneration.Skip)?.DiffuseColor.ToArgb();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return argb is int validArgb ? Color.FromArgb(validArgb) : null;
|
||||
}
|
||||
|
||||
private bool TryCastToExtrusion<T>(ref T target)
|
||||
{
|
||||
Extrusion? extrusion = null;
|
||||
if (GH_Convert.ToExtrusion(Value.GeometryBase, ref extrusion, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Extrusion(extrusion);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToPointcloud<T>(ref T target)
|
||||
{
|
||||
PointCloud? pointCloud = null;
|
||||
if (GH_Convert.ToPointCloud(Value.GeometryBase, ref pointCloud, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_PointCloud(pointCloud);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToHatch<T>(ref T target)
|
||||
{
|
||||
Hatch? hatch = null;
|
||||
if (GH_Convert.ToHatch(Value.GeometryBase, ref hatch, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Hatch(hatch);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToSubD<T>(ref T target)
|
||||
{
|
||||
SubD? subd = null;
|
||||
if (GH_Convert.ToSubD(Value.GeometryBase, ref subd, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_SubD(subd);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public partial class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_PreviewData
|
||||
{
|
||||
public override bool IsValid => Value.Base is not null && Value.ApplicationId is not null;
|
||||
public override string TypeName => "Speckle Object";
|
||||
public override string TypeDescription => "Represents a geometry object from Speckle";
|
||||
|
||||
public SpeckleObjectWrapperGoo(SpeckleObjectWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Parameterless constructor</summary>
|
||||
/// <remarks>Should only be used for casting!</remarks>
|
||||
public SpeckleObjectWrapperGoo()
|
||||
{
|
||||
Value = new()
|
||||
{
|
||||
Base = new(),
|
||||
GeometryBase = null,
|
||||
Color = null,
|
||||
Material = null,
|
||||
};
|
||||
}
|
||||
|
||||
public override IGH_Goo Duplicate() => new SpeckleObjectWrapperGoo(Value.DeepCopy());
|
||||
|
||||
public override string ToString() =>
|
||||
$"Speckle Object : {(string.IsNullOrWhiteSpace(Value.Name) ? Value.Base.speckle_type : Value.Name)}";
|
||||
|
||||
/// <summary>
|
||||
/// Casts from Speckle objects, geometry base, and model objects.
|
||||
/// All non-Speckle objects will be converted to its geometry equivalent.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleObjectWrapper wrapper:
|
||||
Value = wrapper;
|
||||
return true;
|
||||
case SpeckleObjectWrapperGoo wrapperGoo:
|
||||
Value = wrapperGoo.Value;
|
||||
return true;
|
||||
case SpeckleBlockInstanceWrapperGoo instanceWrapperGoo:
|
||||
Value = instanceWrapperGoo.Value;
|
||||
return true;
|
||||
case IGH_GeometricGoo geometricGoo:
|
||||
GeometryBase gb = geometricGoo.ToGeometryBase();
|
||||
Base converted = SpeckleConversionContext.ConvertToSpeckle(gb);
|
||||
string appId = Guid.NewGuid().ToString();
|
||||
Value = gb is InstanceReferenceGeometry instance
|
||||
? new SpeckleBlockInstanceWrapper()
|
||||
{
|
||||
GeometryBase = gb,
|
||||
Base = converted,
|
||||
Transform = instance.Xform,
|
||||
ApplicationId = appId,
|
||||
}
|
||||
: new SpeckleObjectWrapper()
|
||||
{
|
||||
GeometryBase = gb,
|
||||
Base = converted,
|
||||
ApplicationId = appId
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
return CastFromModelObject(source);
|
||||
}
|
||||
|
||||
#if !RHINO8_OR_GREATER
|
||||
private bool CastFromModelObject(object _) => false;
|
||||
|
||||
private bool CastToModelObject<T>(ref T _) => false;
|
||||
#endif
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
if (Value.GeometryBase == null)
|
||||
{
|
||||
return CastToModelObject(ref target);
|
||||
}
|
||||
|
||||
return target switch
|
||||
{
|
||||
GH_Surface => TryCastToSurface(ref target),
|
||||
GH_Mesh => TryCastToMesh(ref target),
|
||||
GH_Brep => TryCastToBrep(ref target),
|
||||
GH_Line => TryCastToLine(ref target),
|
||||
GH_Curve => TryCastToCurve(ref target),
|
||||
GH_Point => TryCastToPoint(ref target),
|
||||
GH_Circle => TryCastToCircle(ref target),
|
||||
GH_Arc => TryCastToArc(ref target),
|
||||
#if RHINO8_OR_GREATER
|
||||
GH_Extrusion => TryCastToExtrusion(ref target),
|
||||
GH_PointCloud => TryCastToPointcloud(ref target),
|
||||
GH_SubD => TryCastToSubD(ref target),
|
||||
GH_Hatch => TryCastToHatch(ref target),
|
||||
#endif
|
||||
IGH_GeometricGoo => TryCastToGeometricGoo(ref target),
|
||||
_ => CastToModelObject(ref target)
|
||||
};
|
||||
}
|
||||
|
||||
private bool TryCastToSurface<T>(ref T target)
|
||||
{
|
||||
Surface? surface = null;
|
||||
if (GH_Convert.ToSurface(Value.GeometryBase, ref surface, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Surface(surface);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToMesh<T>(ref T target)
|
||||
{
|
||||
Mesh? mesh = null;
|
||||
if (GH_Convert.ToMesh(Value.GeometryBase, ref mesh, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Mesh(mesh);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToBrep<T>(ref T target)
|
||||
{
|
||||
Brep? brep = null;
|
||||
if (GH_Convert.ToBrep(Value.GeometryBase, ref brep, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Brep(brep);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToLine<T>(ref T target)
|
||||
{
|
||||
Line line = new();
|
||||
if (GH_Convert.ToLine(Value.GeometryBase, ref line, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Line(line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToCurve<T>(ref T target)
|
||||
{
|
||||
Curve? curve = null;
|
||||
if (GH_Convert.ToCurve(Value.GeometryBase, ref curve, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Curve(curve);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToPoint<T>(ref T target)
|
||||
{
|
||||
Point3d point = new();
|
||||
if (GH_Convert.ToPoint3d(Value.GeometryBase, ref point, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Point(point);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToGeometricGoo<T>(ref T target)
|
||||
{
|
||||
var geometricGoo = GH_Convert.ToGeometricGoo(Value.GeometryBase);
|
||||
if (geometricGoo != null && geometricGoo is T convertedGoo)
|
||||
{
|
||||
target = convertedGoo;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToCircle<T>(ref T target)
|
||||
{
|
||||
var circle = new Circle();
|
||||
if (GH_Convert.ToCircle(Value.GeometryBase, ref circle, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Circle(circle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryCastToArc<T>(ref T target)
|
||||
{
|
||||
var arc = new Arc();
|
||||
if (GH_Convert.ToArc(Value.GeometryBase, ref arc, GH_Conversion.Both))
|
||||
{
|
||||
target = (T)(object)new GH_Arc(arc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DrawViewportWires(GH_PreviewWireArgs args)
|
||||
{
|
||||
// TODO ?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(GH_PreviewMeshArgs args)
|
||||
{
|
||||
Value.DrawPreviewRaw(args.Pipeline, args.Material);
|
||||
}
|
||||
|
||||
BoundingBox IGH_PreviewData.ClippingBox =>
|
||||
Value.GeometryBase is null ? new() : Value.GeometryBase.GetBoundingBox(false);
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.GrasshopperShared.Components;
|
||||
using Speckle.Connectors.GrasshopperShared.Properties;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
public class SpeckleObjectParam : GH_Param<SpeckleObjectWrapperGoo>, IGH_BakeAwareObject, IGH_PreviewObject
|
||||
{
|
||||
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 => Resources.speckle_param_object;
|
||||
public override GH_Exposure Exposure => GH_Exposure.primary;
|
||||
|
||||
public bool IsBakeCapable =>
|
||||
// False if no data
|
||||
!VolatileData.IsEmpty;
|
||||
|
||||
public void BakeGeometry(RhinoDoc doc, List<Guid> objIds)
|
||||
{
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo)
|
||||
{
|
||||
goo.Value.Bake(doc, objIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes the object
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="att"></param>
|
||||
/// <param name="objIds"></param>
|
||||
/// <remarks>
|
||||
/// The attributes come from the user dialog after calling bake.
|
||||
/// The selected layer from the dialog will only be user if no path is already present on the object.
|
||||
/// </remarks>
|
||||
public void BakeGeometry(RhinoDoc doc, ObjectAttributes att, List<Guid> objIds)
|
||||
{
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo)
|
||||
{
|
||||
int layerIndex = goo.Value.Path.Count == 0 ? att.LayerIndex : -1;
|
||||
bool layerCreated = goo.Value.Path.Count == 0;
|
||||
goo.Value.Bake(doc, objIds, layerIndex, layerCreated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPreviewCapable => !VolatileData.IsEmpty;
|
||||
|
||||
public BoundingBox ClippingBox
|
||||
{
|
||||
get
|
||||
{
|
||||
BoundingBox clippingBox = new();
|
||||
|
||||
// Iterate over all data stored in the parameter
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo && goo.Value.GeometryBase is GeometryBase gb)
|
||||
{
|
||||
var box = gb.GetBoundingBox(false);
|
||||
clippingBox.Union(box);
|
||||
}
|
||||
}
|
||||
return clippingBox;
|
||||
}
|
||||
}
|
||||
bool IGH_PreviewObject.Hidden { get; set; }
|
||||
|
||||
public void DrawViewportWires(IGH_PreviewArgs args)
|
||||
{
|
||||
// todo?
|
||||
}
|
||||
|
||||
public void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this) || OwnerSelected();
|
||||
foreach (var item in VolatileData.AllData(true))
|
||||
{
|
||||
if (item is SpeckleObjectWrapperGoo goo)
|
||||
{
|
||||
goo.Value.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool OwnerSelected() => Attributes?.Parent?.Selected ?? false;
|
||||
}
|
||||
+13
@@ -1,8 +1,12 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.GrasshopperShared.HostApp;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.GrasshopperShared.Parameters;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class for Speckle <see cref="Base"/>
|
||||
/// </summary>
|
||||
public abstract class SpeckleWrapper
|
||||
{
|
||||
/// <summary>
|
||||
@@ -14,6 +18,9 @@ public abstract class SpeckleWrapper
|
||||
set => Base[Constants.NAME_PROP] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wrapped <see cref="Base"/>
|
||||
/// </summary>
|
||||
public abstract Base Base { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -24,4 +31,10 @@ public abstract class SpeckleWrapper
|
||||
get => Base.applicationId;
|
||||
set => Base.applicationId = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IGH_Goo"/> from this wrapper
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract IGH_Goo CreateGoo();
|
||||
}
|
||||
+40
@@ -140,6 +140,26 @@ namespace Speckle.Connectors.GrasshopperShared.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap speckle_objects_block_inst {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("speckle_objects_block_inst", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap speckle_objects_block_def {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("speckle_objects_block_def", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
@@ -209,6 +229,26 @@ namespace Speckle.Connectors.GrasshopperShared.Properties {
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap speckle_param_block_def {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("speckle_param_block_def", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
public static System.Drawing.Bitmap speckle_param_block_instance {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("speckle_param_block_instance", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@@ -139,6 +139,12 @@
|
||||
<data name="speckle_inputs_property" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_inputs_property.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="speckle_objects_block_inst" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_objects_block_inst.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="speckle_objects_block_def" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_objects_block_def.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="speckle_inputs_token" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_inputs_token.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
@@ -184,4 +190,10 @@
|
||||
<data name="speckle_properties_query" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_properties_query.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="speckle_param_block_def" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_param_block_def.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="speckle_param_block_instance" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\speckle_param_block_instance.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
@@ -60,6 +61,10 @@ public class PriorityLoader : GH_AssemblyPriority
|
||||
services.AddTransient<IRootObjectBuilder<SpeckleCollectionWrapperGoo>, GrasshopperRootObjectBuilder>();
|
||||
services.AddTransient<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
services.AddSingleton<IThreadContext>(new DefaultThreadContext());
|
||||
services.AddScoped<
|
||||
IInstanceObjectsManager<SpeckleObjectWrapper, List<string>>,
|
||||
InstanceObjectsManager<SpeckleObjectWrapper, List<string>>
|
||||
>(); // each send operation gets its own InstanceObjectsManager instance (scoped = per-operation)
|
||||
|
||||
Container = services.BuildServiceProvider();
|
||||
return GH_LoadingInstruction.Proceed;
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
+26
-17
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' < '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
@@ -16,6 +16,8 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\CollectionPathsSelector.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\CreateCollection.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Collections\ExpandCollection.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\SpeckleBlockInstancePassthrough.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\SpeckleBlockDefinitionPassthrough.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Dev\TokenUrlComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\IGH_StructureExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\SpecklePropertiesPassthrough.cs" />
|
||||
@@ -36,6 +38,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Wizard\WorkspaceMenuHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Wizard\SpeckleOperationWizard.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\KeyWatcher.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\GrasshopperBlockUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\GrasshopperMaterialUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\GetObjectProperties.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Objects\PropertyGroupPathsSelector.cs" />
|
||||
@@ -44,6 +47,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\GrasshopperCollectionRebuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Receive\ReceiveAsyncComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Receive\ReceiveComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperBlockPacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperMaterialPacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperColorPacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Components\Operations\Send\SendAsyncComponent.cs" />
|
||||
@@ -56,19 +60,33 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Helpers.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SpeckleResource.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SpeckleResourceBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapperParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapperGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockDefinitionWrapperParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockDefinitionWrapperGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockInstanceWrapperGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockDefinitionWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleBlockDefinitionWrapperGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Interfaces.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleVariableParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleMaterialWrapper.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleMaterialWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleCollectionWrapperGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleCollectionWrapperParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleObjectWrapperParam.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleObjectWrapperGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapperGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleMaterialWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Properties\Resources.Designer.cs" />
|
||||
<Compile Include="..\Speckle.Connectors.GrasshopperShared\HostApp\SpeckleConversionContext.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\GrasshopperReceiveOperation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\GrasshopperSendOperation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleCollectionWrapper.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleCollectionWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleObjectWrapper.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpeckleObjectWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleCollectionWrapperGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleCollectionWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleObjectWrapperGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\Wrappers\SpeckleObjectWrapper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpecklePropertyGroupGoo.ModelObjects.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpecklePropertyGroupGoo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Parameters\SpecklePropertyGoo.cs" />
|
||||
@@ -84,15 +102,6 @@
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)Resources\speckle_objects_filter.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)Resources\speckle_deconstruct.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)Resources\speckle_objects_object.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="$(MSBuildThisFileDirectory)Resources\logo.png" />
|
||||
</ItemGroup>
|
||||
|
||||
+5
@@ -2,6 +2,7 @@ using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.Grasshopper;
|
||||
|
||||
@@ -11,6 +12,7 @@ public class GeometryBaseConverter : IToSpeckleTopLevelConverter
|
||||
private readonly ITypedConverter<RG.Point, SOG.Point> _pointConverter;
|
||||
private readonly ITypedConverter<RG.ArcCurve, Base> _arcCurveConverter;
|
||||
private readonly ITypedConverter<RG.Hatch, SOG.Region> _hatchConverter;
|
||||
private readonly ITypedConverter<RG.InstanceReferenceGeometry, InstanceProxy> _instanceConverter;
|
||||
private readonly ITypedConverter<RG.LineCurve, SOG.Line> _lineCurveConverter;
|
||||
private readonly ITypedConverter<RG.NurbsCurve, SOG.Curve> _nurbsCurveConverter;
|
||||
private readonly ITypedConverter<RG.PointCloud, SOG.Pointcloud> _pointcloudConverter;
|
||||
@@ -26,6 +28,7 @@ public class GeometryBaseConverter : IToSpeckleTopLevelConverter
|
||||
ITypedConverter<RG.Point, SOG.Point> pointConverter,
|
||||
ITypedConverter<RG.ArcCurve, Base> arcCurveConverter,
|
||||
ITypedConverter<RG.Hatch, SOG.Region> hatchConverter,
|
||||
ITypedConverter<RG.InstanceReferenceGeometry, InstanceProxy> instanceConverter,
|
||||
ITypedConverter<RG.LineCurve, SOG.Line> lineCurveConverter,
|
||||
ITypedConverter<RG.NurbsCurve, SOG.Curve> nurbsCurveConverter,
|
||||
ITypedConverter<RG.PointCloud, SOG.Pointcloud> pointcloudConverter,
|
||||
@@ -41,6 +44,7 @@ public class GeometryBaseConverter : IToSpeckleTopLevelConverter
|
||||
_pointConverter = pointConverter;
|
||||
_arcCurveConverter = arcCurveConverter;
|
||||
_hatchConverter = hatchConverter;
|
||||
_instanceConverter = instanceConverter;
|
||||
_lineCurveConverter = lineCurveConverter;
|
||||
_nurbsCurveConverter = nurbsCurveConverter;
|
||||
_pointcloudConverter = pointcloudConverter;
|
||||
@@ -60,6 +64,7 @@ public class GeometryBaseConverter : IToSpeckleTopLevelConverter
|
||||
RG.Point pt => _pointConverter.Convert(pt),
|
||||
RG.ArcCurve ac => _arcCurveConverter.Convert(ac),
|
||||
RG.Hatch hatch => _hatchConverter.Convert(hatch),
|
||||
RG.InstanceReferenceGeometry instance => _instanceConverter.Convert(instance),
|
||||
RG.LineCurve ln => _lineCurveConverter.Convert(ln),
|
||||
RG.NurbsCurve nurbsCurve => _nurbsCurveConverter.Convert(nurbsCurve),
|
||||
RG.PointCloud pointcloud => _pointcloudConverter.Convert(pointcloud),
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.Raw;
|
||||
|
||||
public class InstanceReferenceGeometryToSpeckleConverter : ITypedConverter<RG.InstanceReferenceGeometry, InstanceProxy>
|
||||
{
|
||||
private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;
|
||||
|
||||
public InstanceReferenceGeometryToSpeckleConverter(IConverterSettingsStore<RhinoConversionSettings> settingsStore)
|
||||
{
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a instance reference geometry object to a Speckle Instance proxy.
|
||||
/// </summary>
|
||||
/// <param name="target">The instance reference geometry object to convert.</param>
|
||||
/// <returns>The converted Speckle Instance proxy.</returns>
|
||||
/// <remarks>
|
||||
/// ⚠️ This conversion does not produce correct depth.
|
||||
/// The def id on the instance may not be reliable for proxies.
|
||||
/// </remarks>
|
||||
public InstanceProxy Convert(RG.InstanceReferenceGeometry target)
|
||||
{
|
||||
var t = target.Xform;
|
||||
var m = new Matrix4x4()
|
||||
{
|
||||
M11 = t.M00,
|
||||
M12 = t.M01,
|
||||
M13 = t.M02,
|
||||
M14 = t.M03,
|
||||
|
||||
M21 = t.M10,
|
||||
M22 = t.M11,
|
||||
M23 = t.M12,
|
||||
M24 = t.M13,
|
||||
|
||||
M31 = t.M20,
|
||||
M32 = t.M21,
|
||||
M33 = t.M22,
|
||||
M34 = t.M23,
|
||||
|
||||
M41 = t.M30,
|
||||
M42 = t.M31,
|
||||
M43 = t.M32,
|
||||
M44 = t.M33
|
||||
};
|
||||
|
||||
return new InstanceProxy()
|
||||
{
|
||||
definitionId = target.ParentIdefId.ToString(),
|
||||
maxDepth = 0, // default value since this is too much to calculate and will be done in connectors
|
||||
transform = m,
|
||||
units = _settingsStore.Current.SpeckleUnits
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user