feat(grasshopper): adds create object node (#724)
* Avoid multiple enumeration issues when saving if we copy the list first (#713) * add create data object component * fixes extrusion display * adds name and user strings dynamically to output model objects * undo geometry list to geometry in object goo * Update SpeckleGrasshopperObject.cs --------- Co-authored-by: Adam Hathcock <adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -3,11 +3,12 @@ namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
// NOTE: The number of spaces determines the order in which they display in the ribbon (nice hack)
|
||||
public static class ComponentCategories
|
||||
{
|
||||
public const string OPERATIONS = " Operations";
|
||||
public const string MODELS = " Model Management";
|
||||
public const string OPERATIONS = " Operations";
|
||||
public const string MODELS = " Model Management";
|
||||
public const string PARAMETERS = "Parameters";
|
||||
public const string COLLECTIONS = "Collections";
|
||||
public const string COLLECTIONS = " Collections";
|
||||
public const string PRIMARY_RIBBON = "Speckle";
|
||||
public const string OBJECTS = " Objects";
|
||||
}
|
||||
|
||||
public enum ComponentState
|
||||
|
||||
+27
-14
@@ -472,28 +472,41 @@ public class ReceiveComponentWorker : WorkerInstance
|
||||
converted.ForEach(res => res.Transform(mat));
|
||||
}
|
||||
|
||||
// note one to many not handled too nice here
|
||||
// get the collection
|
||||
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
|
||||
|
||||
// get the name and properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
string name = "";
|
||||
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
|
||||
{
|
||||
propertyGroup.CastFrom(da.properties);
|
||||
name = da.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
if (map.AtomicObject["name"] is string n)
|
||||
{
|
||||
name = n;
|
||||
}
|
||||
}
|
||||
|
||||
// create objects for every value in converted. This is where one to many is not handled very nicely.
|
||||
foreach (var geometryBase in converted)
|
||||
{
|
||||
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(
|
||||
path
|
||||
);
|
||||
|
||||
// get properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(
|
||||
map.AtomicObject is Speckle.Objects.Data.DataObject da
|
||||
? da.properties
|
||||
: map.AtomicObject["properties"] as Dictionary<string, object?> ?? new()
|
||||
);
|
||||
|
||||
var gh = new SpeckleObjectWrapper()
|
||||
{
|
||||
Base = map.AtomicObject,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup
|
||||
Properties = propertyGroup,
|
||||
Name = name
|
||||
};
|
||||
|
||||
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
|
||||
|
||||
+36
-6
@@ -117,7 +117,9 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlMode
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
var collGen = new GrasshopperCollectionRebuilder((root as Collection) ?? new Collection() { name = "unnamed" });
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection() { name = "unnamed" }
|
||||
);
|
||||
|
||||
foreach (var map in localToGlobalMaps)
|
||||
{
|
||||
@@ -132,16 +134,44 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlMode
|
||||
converted.ForEach(res => res.Transform(mat));
|
||||
}
|
||||
|
||||
// note one to many not handled too nice here
|
||||
// get the collection
|
||||
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
|
||||
|
||||
// get the name and properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
string name = "";
|
||||
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
|
||||
{
|
||||
propertyGroup.CastFrom(da.properties);
|
||||
name = da.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
if (map.AtomicObject["name"] is string n)
|
||||
{
|
||||
name = n;
|
||||
}
|
||||
}
|
||||
|
||||
// create objects for every value in converted. This is where one to many is not handled very nicely.
|
||||
foreach (var geometryBase in converted)
|
||||
{
|
||||
var gh = new SpeckleObjectWrapper()
|
||||
{
|
||||
Base = map.AtomicObject,
|
||||
Path = path.Select(o => o.name).ToList(),
|
||||
GeometryBase = geometryBase
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = name
|
||||
};
|
||||
collGen.AppendSpeckleGrasshopperObject(gh, path);
|
||||
|
||||
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
|
||||
}
|
||||
}
|
||||
catch (ConversionException)
|
||||
@@ -151,7 +181,7 @@ public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlMode
|
||||
}
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collGen.RootCollectionWrapper);
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo };
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -48,7 +48,7 @@ public class SendAsyncComponent : GH_AsyncComponent
|
||||
public string? Url { get; set; }
|
||||
public Client ApiClient { get; set; }
|
||||
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleCollectionWrapperGoo? RootCollection { get; set; }
|
||||
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
|
||||
|
||||
public SpeckleUrlModelResource? OutputParam { get; set; }
|
||||
public SendOperation<SpeckleCollectionWrapperGoo> SendOperation { get; private set; }
|
||||
@@ -253,11 +253,11 @@ public class SendAsyncComponent : GH_AsyncComponent
|
||||
da.GetData(1, ref rootCollectionWrapper);
|
||||
if (rootCollectionWrapper is null)
|
||||
{
|
||||
RootCollection = null;
|
||||
RootCollectionWrapper = null;
|
||||
TriggerAutoSave();
|
||||
return;
|
||||
}
|
||||
RootCollection = rootCollectionWrapper;
|
||||
RootCollectionWrapper = rootCollectionWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ public class SendComponentWorker : WorkerInstance
|
||||
{
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
$"Successfully sent {((SendAsyncComponent)Parent).RootCollection?.Value.GetTotalChildrenCount()} objects to Speckle."
|
||||
$"Successfully sent {((SendAsyncComponent)Parent).RootCollectionWrapper?.Value.GetTotalChildrenCount()} objects to Speckle."
|
||||
);
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
@@ -347,8 +347,8 @@ public class SendComponentWorker : WorkerInstance
|
||||
throw new InvalidOperationException("Url Resource was null");
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapperGoo? rootCollection = sendComponent.RootCollection;
|
||||
if (rootCollection is null)
|
||||
SpeckleCollectionWrapperGoo? rootCollectionWrapper = sendComponent.RootCollectionWrapper;
|
||||
if (rootCollectionWrapper is null)
|
||||
{
|
||||
throw new InvalidOperationException("Root Collection was null");
|
||||
}
|
||||
@@ -374,7 +374,7 @@ public class SendComponentWorker : WorkerInstance
|
||||
|
||||
var result = await sendComponent
|
||||
.SendOperation.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo>() { rootCollection },
|
||||
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
|
||||
sendInfo,
|
||||
progress,
|
||||
CancellationToken
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
|
||||
|
||||
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
|
||||
public class CreateSpeckleObject : GH_Component
|
||||
{
|
||||
public CreateSpeckleObject()
|
||||
: base(
|
||||
"Create Speckle Object",
|
||||
"CSO",
|
||||
"Creates a Speckle Object",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OBJECTS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("cO");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Geometry", "G", "The geometry of the new Speckle Object", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the new Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[1].Optional = true;
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"The properties of the new Speckle Object",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[2].Optional = true;
|
||||
|
||||
// TODO: add render material and color
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Speckle Object", "SO", "The created Speckle Object", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
object gooGeometry = new();
|
||||
da.GetData(0, ref gooGeometry);
|
||||
GeometryBase geometry = ((IGH_GeometricGoo)gooGeometry).GeometricGooToGeometryBase();
|
||||
|
||||
string name = "";
|
||||
da.GetData(1, ref name);
|
||||
|
||||
SpecklePropertyGroupGoo properties = new();
|
||||
da.GetData(2, ref properties);
|
||||
|
||||
// convert the properties
|
||||
Dictionary<string, object?> props = new();
|
||||
properties.CastTo(ref props);
|
||||
|
||||
// convert the geometries
|
||||
Base converted = ToSpeckleConversionContext.ToSpeckleConverter.Convert(geometry);
|
||||
|
||||
Objects.Data.DataObject grasshopperObject =
|
||||
new()
|
||||
{
|
||||
name = name,
|
||||
displayValue = new() { converted },
|
||||
properties = props
|
||||
};
|
||||
|
||||
SpeckleObjectWrapper so =
|
||||
new()
|
||||
{
|
||||
Base = grasshopperObject,
|
||||
GeometryBase = geometry,
|
||||
Properties = properties,
|
||||
Name = name
|
||||
};
|
||||
|
||||
da.SetData(0, new SpeckleObjectWrapperGoo(so));
|
||||
}
|
||||
}
|
||||
+22
-6
@@ -17,7 +17,11 @@ namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
public class SpeckleObjectWrapper : Base
|
||||
{
|
||||
public required Base Base { get; set; }
|
||||
public required GeometryBase GeometryBase { get; set; } // note: how will we send intervals and other gh native objects? do we? maybe not for now
|
||||
|
||||
// note: how will we send intervals and other gh native objects? do we? maybe not for now
|
||||
// note: this does not handle on to many relationship well.
|
||||
// For receiving data objects, we are wrapping every value in the data object display value, and storing a reference to the same data object in each wrapped object.
|
||||
public required GeometryBase GeometryBase { get; set; }
|
||||
|
||||
// The list of layer/collection names that forms the full path to this object
|
||||
public List<string> Path { get; set; } = new();
|
||||
@@ -99,7 +103,9 @@ public class SpeckleObjectWrapper : Base
|
||||
display.DrawBrepWires(b, material.Diffuse);
|
||||
break;
|
||||
case Extrusion e:
|
||||
display.DrawMeshShaded(e.GetMesh(MeshType.Any), material);
|
||||
var eBrep = e.ToBrep();
|
||||
display.DrawBrepShaded(eBrep, material);
|
||||
display.DrawBrepWires(eBrep, material.Diffuse);
|
||||
break;
|
||||
case SubD d:
|
||||
display.DrawSubDShaded(d, material);
|
||||
@@ -162,6 +168,8 @@ public class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_Preview
|
||||
public override string TypeName => "Speckle object wrapper";
|
||||
public override string TypeDescription => "A wrapper around speckle grasshopper objects.";
|
||||
|
||||
BoundingBox IGH_PreviewData.ClippingBox => Value.GeometryBase.GetBoundingBox(false);
|
||||
|
||||
public SpeckleObjectWrapperGoo(ModelObject mo)
|
||||
{
|
||||
CastFrom(mo);
|
||||
@@ -189,12 +197,21 @@ public class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_Preview
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
propertyGroup.CastFrom(modelObject.UserText);
|
||||
|
||||
// update the converted Base with props as well
|
||||
modelConverted["name"] = modelObject.Name.ToString();
|
||||
Dictionary<string, object?> propertyDict = new();
|
||||
foreach (var entry in propertyGroup.Value)
|
||||
{
|
||||
propertyDict.Add(entry.Key, entry.Value.Value);
|
||||
}
|
||||
modelConverted["properties"] = propertyDict;
|
||||
|
||||
SpeckleObjectWrapper so =
|
||||
new()
|
||||
{
|
||||
GeometryBase = modelGB,
|
||||
Base = modelConverted,
|
||||
Name = modelObject.Name,
|
||||
Name = modelObject.Name.ToString(),
|
||||
Color = modelObject.Display.Color?.Color.ToArgb(),
|
||||
RenderMaterialName = modelObject.Render.Material?.Material?.Name,
|
||||
Properties = propertyGroup
|
||||
@@ -215,13 +232,14 @@ public class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_Preview
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(IGH_GeometricGoo))
|
||||
{
|
||||
target = (T)(object)GH_Convert.ToGeometricGoo(Value.GeometryBase);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: cast to material, modle object, etc.
|
||||
// TODO: cast to material, etc.
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -236,8 +254,6 @@ public class SpeckleObjectWrapperGoo : GH_Goo<SpeckleObjectWrapper>, IGH_Preview
|
||||
Value.DrawPreviewRaw(args.Pipeline, args.Material);
|
||||
}
|
||||
|
||||
public BoundingBox ClippingBox => Value.GeometryBase.GetBoundingBox(false);
|
||||
|
||||
public SpeckleObjectWrapperGoo(SpeckleObjectWrapper value)
|
||||
{
|
||||
Value = value;
|
||||
|
||||
+24
-2
@@ -20,7 +20,10 @@ public class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, SpeckleProperty
|
||||
public override string TypeName => "Speckle property group wrapper";
|
||||
public override string TypeDescription => "Speckle property group wrapper";
|
||||
|
||||
public SpecklePropertyGroupGoo() { }
|
||||
public SpecklePropertyGroupGoo()
|
||||
{
|
||||
Value = new();
|
||||
}
|
||||
|
||||
public SpecklePropertyGroupGoo(Dictionary<string, SpecklePropertyGoo> value)
|
||||
{
|
||||
@@ -42,7 +45,7 @@ public class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, SpeckleProperty
|
||||
|
||||
case ModelUserText userText:
|
||||
Dictionary<string, SpecklePropertyGoo> dictionary = new();
|
||||
foreach (var entry in userText)
|
||||
foreach (KeyValuePair<string, string> entry in userText)
|
||||
{
|
||||
string key = entry.Key;
|
||||
SpecklePropertyGoo value = new() { Path = key, Value = entry.Value };
|
||||
@@ -67,6 +70,25 @@ public class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, SpeckleProperty
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CastTo<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (type == typeof(Dictionary<string, object?>))
|
||||
{
|
||||
Dictionary<string, object?> dictionary = new();
|
||||
foreach (var entry in Value)
|
||||
{
|
||||
dictionary.Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
target = (T)(object)dictionary;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: cast to material, model object, etc.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flattens a dictionary that may contain more dictionaries of the same type
|
||||
private void FlattenDictionary(
|
||||
Dictionary<string, object?> dict,
|
||||
|
||||
@@ -10,9 +10,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{85A13E
|
||||
CodeMetricsConfig.txt = CodeMetricsConfig.txt
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
.config\dotnet-tools.json = .config\dotnet-tools.json
|
||||
global.json = global.json
|
||||
README.md = README.md
|
||||
.config\dotnet-tools.json = .config\dotnet-tools.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Revit", "Revit", "{D92751C8-1039-4005-90B2-913E55E0B8BD}"
|
||||
@@ -200,6 +200,7 @@ EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converter.Tekla2023", "Converters\Tekla\Speckle.Converter.Tekla2023\Speckle.Converter.Tekla2023.csproj", "{8F9181C2-1808-44C0-A33A-5BAE40C49E63}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Grasshopper8", "Connectors\Rhino\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj", "{8F6AD59B-FE43-41AE-8F47-7B9752BA0085}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSi", "CSi", "{073F40A8-6C95-41C1-A2F3-369FFFCB9520}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Connectors.ETABS22", "Connectors\CSi\Speckle.Connectors.ETABS22\Speckle.Connectors.ETABS22.csproj", "{7C49337A-6F7B-47AB-B549-42E799E89CF2}"
|
||||
|
||||
Reference in New Issue
Block a user