feat: Working initial nodes
Receive, Collection, URL parsing for models, root object unpacking
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
|
||||
public abstract class SpeckleScopedTaskCapableComponent<TInput, TOutput>(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
string category,
|
||||
string subCategory
|
||||
) : SpeckleTaskCapableComponent<TInput, TOutput>(name, nickname, description, category, subCategory)
|
||||
{
|
||||
protected override Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var scope = PriorityLoader.Container.CreateScope();
|
||||
return PerformScopedTask(input, scope, cancellationToken);
|
||||
}
|
||||
|
||||
protected abstract Task<TOutput> PerformScopedTask(
|
||||
TInput input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
|
||||
public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
string category,
|
||||
string subCategory
|
||||
) : GH_TaskCapableComponent<TOutput>(name, nickname, description, category, subCategory)
|
||||
{
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
//TODO: We're missing activity and logging here. Will enable it for all inherited classes.
|
||||
|
||||
if (InPreSolve)
|
||||
{
|
||||
// Collect the data and create the task
|
||||
try
|
||||
{
|
||||
var input = GetInput(da);
|
||||
TaskList.Add(PerformTask(input, CancelToken));
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetSolveResults(da, out TOutput result))
|
||||
{
|
||||
// INFO: This will run synchronously. Useful for Rhino.Compute runs, but can also be enabled by user.
|
||||
try
|
||||
{
|
||||
TInput input = GetInput(da);
|
||||
var syncResult = PerformTask(input).Result;
|
||||
result = syncResult;
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
SetOutput(da, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract TInput GetInput(IGH_DataAccess da);
|
||||
|
||||
protected abstract void SetOutput(IGH_DataAccess da, TOutput result);
|
||||
|
||||
protected abstract Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
public record CreateCollectionComponentInput(
|
||||
Collection? Collection,
|
||||
string? Name,
|
||||
List<Base>? Elements,
|
||||
List<Collection>? Collections
|
||||
);
|
||||
|
||||
public record CreateCollectionComponentOutput(Collection Collection);
|
||||
|
||||
public class CreateCollectionComponent
|
||||
: SpeckleTaskCapableComponent<CreateCollectionComponentInput, CreateCollectionComponentOutput>
|
||||
{
|
||||
public CreateCollectionComponent()
|
||||
: base("Create Collection", "CrCol", "Creates a new collection", "Speckle", "Collections") { }
|
||||
|
||||
public override Guid ComponentGuid => new("6A9EDFDE-8AC4-4E28-B455-45DF42E2172B");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
var colIndex = pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"collection",
|
||||
"Collection",
|
||||
"Collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
var nameIndex = pManager.AddTextParameter("Name", "Name", "Name of the collection", GH_ParamAccess.item);
|
||||
|
||||
var elementsIndex = pManager.AddParameter(
|
||||
new SpeckleObjectParam(GH_ParamAccess.list),
|
||||
"elements",
|
||||
"Elements",
|
||||
"Elements of the collection",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
var collectionsIndex = pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.list),
|
||||
"collections",
|
||||
"Collections",
|
||||
"Sub-collections of the collection",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
|
||||
pManager[colIndex].Optional = true;
|
||||
pManager[nameIndex].Optional = true;
|
||||
pManager[elementsIndex].Optional = true;
|
||||
pManager[collectionsIndex].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
var colIndex = pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"collection",
|
||||
"Collection",
|
||||
"Collection",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
var nameIndex = pManager.AddTextParameter("Name", "Name", "Name of the collection", GH_ParamAccess.item);
|
||||
|
||||
var elementsIndex = pManager.AddParameter(
|
||||
new SpeckleObjectParam(GH_ParamAccess.list),
|
||||
"elements",
|
||||
"Elements",
|
||||
"Elements of the collection",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
var collectionsIndex = pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.list),
|
||||
"collections",
|
||||
"Collections",
|
||||
"Sub-collections of the collection",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
|
||||
pManager[colIndex].Optional = true;
|
||||
pManager[nameIndex].Optional = true;
|
||||
pManager[elementsIndex].Optional = true;
|
||||
pManager[collectionsIndex].Optional = true;
|
||||
}
|
||||
|
||||
protected override CreateCollectionComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
Collection? collection = null;
|
||||
string? name = "";
|
||||
List<Base>? elements = new List<Base>();
|
||||
List<Collection>? collections = new List<Collection>();
|
||||
|
||||
da.GetData(0, ref collection);
|
||||
da.GetData(1, ref name);
|
||||
da.GetDataList(2, elements);
|
||||
da.GetDataList(3, collections);
|
||||
|
||||
return new CreateCollectionComponentInput(collection, name, elements, collections);
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, CreateCollectionComponentOutput result)
|
||||
{
|
||||
da.SetData(0, result.Collection);
|
||||
da.SetData(1, result.Collection.name);
|
||||
da.SetDataList(2, result.Collection.elements.Where(e => e is not Collection));
|
||||
da.SetDataList(3, result.Collection.elements.Where(e => e is Collection));
|
||||
}
|
||||
|
||||
protected override Task<CreateCollectionComponentOutput> PerformTask(
|
||||
CreateCollectionComponentInput input,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (input.Collection is null)
|
||||
{
|
||||
// Create new collection
|
||||
if (input.Name is null)
|
||||
{
|
||||
throw new SpeckleException("New collections must have a name");
|
||||
}
|
||||
|
||||
var collection = new Collection(input.Name) { elements = input.Elements ?? new List<Base>() };
|
||||
var result = new CreateCollectionComponentOutput(collection);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
var collection = new Collection(input.Collection.name) { elements = input.Collection.elements };
|
||||
|
||||
// Create new collection
|
||||
if (input.Name is not null && input.Name.Length != 0)
|
||||
{
|
||||
collection.name = input.Name;
|
||||
}
|
||||
var elements = new List<Base>();
|
||||
if (input.Elements is not null && input.Elements.Count != 0)
|
||||
{
|
||||
elements.AddRange(input.Elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
elements.AddRange(collection.elements.Where(e => e is not Collection));
|
||||
}
|
||||
|
||||
if (input.Collections is not null && input.Collections.Count != 0)
|
||||
{
|
||||
elements.AddRange(input.Collections);
|
||||
}
|
||||
else
|
||||
{
|
||||
elements.AddRange(collection.elements.Where(e => e is Collection));
|
||||
}
|
||||
|
||||
var result = new CreateCollectionComponentOutput(collection);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Conversion;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
public record UnpackRootObjectComponentInput(Base RootObject) { }
|
||||
|
||||
public record UnpackRootObjectComponentOutput(
|
||||
List<Base> Elements,
|
||||
List<string> ElementPaths,
|
||||
List<IInstanceComponent> Instances,
|
||||
List<string> InstancePaths
|
||||
) { }
|
||||
|
||||
public class UnpackRootObjectComponent
|
||||
: SpeckleScopedTaskCapableComponent<UnpackRootObjectComponentInput, UnpackRootObjectComponentOutput>
|
||||
{
|
||||
public UnpackRootObjectComponent()
|
||||
: base("Unpack Root Object", "SURO", "Unpacks the root object from a receive operation", "Speckle", "Collections")
|
||||
{ }
|
||||
|
||||
public override Guid ComponentGuid => new Guid("3C770686-20D5-434C-99E3-BDE735E8267F");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleObjectParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter("Element Paths", "EP", "Path to the element in the collection tree", GH_ParamAccess.list);
|
||||
pManager.AddParameter(new SpeckleObjectParam(), "Elements", "E", "Elements", GH_ParamAccess.list);
|
||||
pManager.AddTextParameter(
|
||||
"Instance Paths",
|
||||
"IP",
|
||||
"Path to the instance in the collection tree",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
pManager.AddParameter(new SpeckleObjectParam(), "Instances", "I", "Instances", GH_ParamAccess.list);
|
||||
}
|
||||
|
||||
protected override UnpackRootObjectComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
Base? baseObject = null;
|
||||
da.GetData(0, ref baseObject);
|
||||
if (baseObject == null)
|
||||
{
|
||||
throw new SpeckleException("No base object provided");
|
||||
}
|
||||
return new UnpackRootObjectComponentInput(baseObject);
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, UnpackRootObjectComponentOutput result)
|
||||
{
|
||||
da.SetDataList(0, result.ElementPaths);
|
||||
da.SetDataList(1, result.Elements);
|
||||
da.SetDataList(2, result.InstancePaths);
|
||||
da.SetDataList(3, result.Instances);
|
||||
}
|
||||
|
||||
protected override async Task<UnpackRootObjectComponentOutput> PerformScopedTask(
|
||||
UnpackRootObjectComponentInput input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetRequiredService<RootObjectUnpacker>();
|
||||
var contextUnpacker = scope.ServiceProvider.GetRequiredService<TraversalContextUnpacker>();
|
||||
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(input.RootObject);
|
||||
|
||||
// 2 - Split atomic objects and instance components with their path
|
||||
var (atomicObjects, instanceComponents) = rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
var atomicObjectsWithPath = contextUnpacker.GetAtomicObjectsWithPath(atomicObjects);
|
||||
var instanceComponentsWithPath = contextUnpacker.GetInstanceComponentsWithPath(instanceComponents);
|
||||
|
||||
// 2.1 - these are not captured by traversal, so we need to re-add them here
|
||||
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
|
||||
{
|
||||
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
|
||||
(Array.Empty<Collection>(), proxy as IInstanceComponent)
|
||||
);
|
||||
instanceComponentsWithPath.AddRange(transformed);
|
||||
}
|
||||
|
||||
var applicationIdMap = new Dictionary<string, Base>();
|
||||
atomicObjectsWithPath.ForEach(a => applicationIdMap.Add(a.current.applicationId ?? a.current.id, a.current));
|
||||
|
||||
var instanceResult = await ProcessInstances(instanceComponentsWithPath, applicationIdMap).ConfigureAwait(false);
|
||||
|
||||
foreach (string objId in instanceResult.ConsumedObjectIds)
|
||||
{
|
||||
var indexAtomic = atomicObjectsWithPath.FindIndex(o => o.current.id == objId);
|
||||
if (indexAtomic != -1)
|
||||
{
|
||||
atomicObjectsWithPath.RemoveAt(indexAtomic);
|
||||
}
|
||||
// HACK: Why is instancecomponent not ISpeckleObject?
|
||||
var indexInstance = instanceComponentsWithPath.FindIndex(o => ((Base)o.instance).id == objId);
|
||||
if (indexInstance != -1)
|
||||
{
|
||||
instanceComponentsWithPath.RemoveAt(indexInstance);
|
||||
}
|
||||
}
|
||||
|
||||
var elements = new List<Base>();
|
||||
var instances = new List<IInstanceComponent>();
|
||||
var elementPaths = new List<string>();
|
||||
var instancePaths = new List<string>();
|
||||
|
||||
atomicObjectsWithPath.ForEach(atomicObj =>
|
||||
{
|
||||
var names = atomicObj.path.Select(p => p.name);
|
||||
elements.Add(atomicObj.current);
|
||||
elementPaths.Add(string.Join("::", names));
|
||||
});
|
||||
|
||||
instanceComponentsWithPath.ForEach(instanceObj =>
|
||||
{
|
||||
var names = instanceObj.path.Select(p => p.name);
|
||||
instances.Add(instanceObj.instance);
|
||||
instancePaths.Add(string.Join("::", names));
|
||||
});
|
||||
|
||||
var output = new UnpackRootObjectComponentOutput(elements, elementPaths, instances, instancePaths);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public Task<BakeResult> ProcessInstances(
|
||||
IReadOnlyCollection<(Collection[] collectionPath, IInstanceComponent obj)> instanceComponents,
|
||||
Dictionary<string, Base> applicationIdMap
|
||||
)
|
||||
{
|
||||
var sortedInstanceComponents = instanceComponents
|
||||
.OrderByDescending(x => x.obj.maxDepth) // Sort by max depth, so we start baking from the deepest element first
|
||||
.ThenBy(x => x.obj is InstanceDefinitionProxy ? 0 : 1) // Ensure we bake the deepest definition first, then any instances that depend on it
|
||||
.ToList();
|
||||
|
||||
var definitionObjectsMap = new Dictionary<string, (InstanceDefinitionProxy, List<Base>)>();
|
||||
|
||||
var consumedObjectIds = new List<string>();
|
||||
foreach (var (layerCollection, instanceOrDefinition) in sortedInstanceComponents)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instanceOrDefinition is InstanceDefinitionProxy definitionProxy)
|
||||
{
|
||||
var currentSpeckleObjects = definitionProxy
|
||||
.objects.Where(applicationIdMap.ContainsKey)
|
||||
.Select(x => applicationIdMap[x])
|
||||
.ToList();
|
||||
|
||||
definitionObjectsMap.Add(
|
||||
definitionProxy.applicationId ?? definitionProxy.id,
|
||||
(definitionProxy, currentSpeckleObjects)
|
||||
);
|
||||
|
||||
consumedObjectIds.AddRange(currentSpeckleObjects.Select(o => o.id));
|
||||
consumedObjectIds.Add(definitionProxy.id);
|
||||
}
|
||||
|
||||
if (
|
||||
instanceOrDefinition is InstanceProxy instanceProxy
|
||||
&& definitionObjectsMap.TryGetValue(instanceProxy.definitionId, out var definition)
|
||||
)
|
||||
{
|
||||
instanceProxy["__geometry"] = definition.Item2;
|
||||
instanceProxy["__definition"] = definition.Item1;
|
||||
applicationIdMap[instanceProxy.applicationId ?? instanceProxy.id] = instanceProxy;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
//_logger.LogError(ex, "Failed to create an instance from proxy");
|
||||
}
|
||||
}
|
||||
|
||||
//await Task.Yield();
|
||||
BakeResult processInstances = new(new List<string>(), consumedObjectIds, new List<ReceiveConversionResult>());
|
||||
return Task.FromResult(processInstances);
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Conversion;
|
||||
|
||||
public static class RhinoUnitsExtension
|
||||
{
|
||||
public static string ToSpeckleString(this UnitSystem unitSystem)
|
||||
{
|
||||
switch (unitSystem)
|
||||
{
|
||||
case UnitSystem.None:
|
||||
return Units.Meters;
|
||||
case UnitSystem.Millimeters:
|
||||
return Units.Millimeters;
|
||||
case UnitSystem.Centimeters:
|
||||
return Units.Centimeters;
|
||||
case UnitSystem.Meters:
|
||||
return Units.Meters;
|
||||
case UnitSystem.Kilometers:
|
||||
return Units.Kilometers;
|
||||
case UnitSystem.Inches:
|
||||
return Units.Inches;
|
||||
case UnitSystem.Feet:
|
||||
return Units.Feet;
|
||||
case UnitSystem.Yards:
|
||||
return Units.Yards;
|
||||
case UnitSystem.Miles:
|
||||
return Units.Miles;
|
||||
case UnitSystem.Unset:
|
||||
return Units.Meters;
|
||||
default:
|
||||
throw new UnitNotSupportedException($"The Unit System \"{unitSystem}\" is unsupported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ToNativeConversion()
|
||||
: SpeckleScopedTaskCapableComponent<Base, List<GeometryBase>>(
|
||||
"ToNativeConversion",
|
||||
"STN",
|
||||
"Converts a speckle object to rhino",
|
||||
"Speckle",
|
||||
"Conversion"
|
||||
)
|
||||
{
|
||||
public override Guid ComponentGuid => new Guid("38BAB10C-4D80-4E0C-8235-A87C3E66F55F");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleObjectParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddGeometryParameter("Geometry", "Geometry", "Geometry", GH_ParamAccess.list);
|
||||
}
|
||||
|
||||
protected override Base GetInput(IGH_DataAccess da)
|
||||
{
|
||||
Base? input = null;
|
||||
if (!da.GetData(0, ref input) || input is null)
|
||||
{
|
||||
throw new SpeckleException("Input is not valid");
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, List<GeometryBase> result)
|
||||
{
|
||||
da.SetDataList(0, result);
|
||||
}
|
||||
|
||||
protected override Task<List<GeometryBase>> PerformScopedTask(
|
||||
Base input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var rhinoConversionSettingsFactory = scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
|
||||
|
||||
scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
|
||||
|
||||
var rootConverter = scope.ServiceProvider.GetRequiredService<IRootToHostConverter>();
|
||||
|
||||
if (input is InstanceProxy proxy)
|
||||
{
|
||||
var geometries = proxy["__geometry"] as List<Base>;
|
||||
var converted = geometries.SelectMany(g => Convert(g, rootConverter)).ToList();
|
||||
var transform = MatrixToTransform(proxy.transform, proxy.units);
|
||||
converted.ForEach(c => c.Transform(transform));
|
||||
return Task.FromResult(converted);
|
||||
}
|
||||
|
||||
return Task.FromResult(Convert(input, rootConverter));
|
||||
}
|
||||
|
||||
private List<GeometryBase> Convert(Base input, IRootToHostConverter rootConverter)
|
||||
{
|
||||
var result = rootConverter.Convert(input);
|
||||
|
||||
if (result is GeometryBase geometry)
|
||||
{
|
||||
return new List<GeometryBase> { geometry };
|
||||
}
|
||||
else if (result is List<GeometryBase> geometryList)
|
||||
{
|
||||
return geometryList;
|
||||
}
|
||||
|
||||
throw new SpeckleException("Failed to convert input to rhino");
|
||||
}
|
||||
|
||||
private Transform MatrixToTransform(Matrix4x4 matrix, string units)
|
||||
{
|
||||
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
|
||||
var conversionFactor = Units.GetConversionFactor(units, currentDoc.ModelUnitSystem.ToSpeckleString());
|
||||
|
||||
var t = Transform.Identity;
|
||||
t.M00 = matrix.M11;
|
||||
t.M01 = matrix.M12;
|
||||
t.M02 = matrix.M13;
|
||||
t.M03 = matrix.M14 * conversionFactor;
|
||||
|
||||
t.M10 = matrix.M21;
|
||||
t.M11 = matrix.M22;
|
||||
t.M12 = matrix.M23;
|
||||
t.M13 = matrix.M24 * conversionFactor;
|
||||
|
||||
t.M20 = matrix.M31;
|
||||
t.M21 = matrix.M32;
|
||||
t.M22 = matrix.M33;
|
||||
t.M23 = matrix.M34 * conversionFactor;
|
||||
|
||||
t.M30 = matrix.M41;
|
||||
t.M31 = matrix.M42;
|
||||
t.M32 = matrix.M43;
|
||||
t.M33 = matrix.M44;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
|
||||
|
||||
public class ReceiveComponentOutput
|
||||
{
|
||||
public Base RootObject { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlModelResource, ReceiveComponentOutput>
|
||||
{
|
||||
public ReceiveComponent()
|
||||
: base("Receive from Speckle", "RFS", "Receive objects from speckle", "Speckle", "Operations") { }
|
||||
|
||||
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("R");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(GH_ParamAccess.item),
|
||||
"Model",
|
||||
"model",
|
||||
"The model object for the received version",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override SpeckleUrlModelResource GetInput(IGH_DataAccess da)
|
||||
{
|
||||
SpeckleUrlModelResource? url = null;
|
||||
da.GetData(0, ref url);
|
||||
if (url is null)
|
||||
{
|
||||
throw new SpeckleException("Speckle url is null");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, ReceiveComponentOutput result)
|
||||
{
|
||||
da.SetData(0, result.RootObject);
|
||||
Message = "Done";
|
||||
}
|
||||
|
||||
protected override async Task<ReceiveComponentOutput> PerformScopedTask(
|
||||
SpeckleUrlModelResource input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// TODO: Resolving dependencies here may be overkill in most cases. Must re-evaluate.
|
||||
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
// Do the thing 👇🏼
|
||||
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Server));
|
||||
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new ReceiveComponentOutput { RootObject = root };
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -3,14 +3,14 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8;
|
||||
namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
|
||||
public class SpeckleFirstComponent : GH_TaskCapableComponent<List<object?>>
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public class SpeckleFirstComponent : GH_TaskCapableComponent<List<object?>>
|
||||
/// new tabs/panels will automatically be created.
|
||||
/// </summary>
|
||||
public SpeckleFirstComponent()
|
||||
: base("Send to Speckle", "STP", "Sends objects to speckle", "Speckle", "Operations")
|
||||
: base("First Speckle Component", "STP", "Sends objects to speckle", "Speckle", "Other")
|
||||
{
|
||||
_accountManager = PriorityLoader.Container.NotNull().GetRequiredService<AccountManager>();
|
||||
}
|
||||
@@ -45,8 +45,8 @@ public class SpeckleFirstComponent : GH_TaskCapableComponent<List<object?>>
|
||||
{
|
||||
// Collect the data and create the task
|
||||
string url = GetInput(da);
|
||||
TaskList.Add(PerformReceiveOperation(url, CancelToken));
|
||||
Message = "Receiving...";
|
||||
TaskList.Add(PerformReceiveOperation(url, CancelToken));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,9 +125,10 @@ public class SpeckleFirstComponent : GH_TaskCapableComponent<List<object?>>
|
||||
throw new NotSupportedException($"Unsupported conversion result type: {conversionResult}");
|
||||
}
|
||||
|
||||
//results.Add(ghConversionResult.Source);
|
||||
if (ghConversionResult.Result is GeometryBase geometryBase)
|
||||
{
|
||||
//var guid = BakeObject(geometryBase, obj, atts);
|
||||
results.Add(geometryBase);
|
||||
}
|
||||
else if (ghConversionResult.Result is List<GeometryBase> geometryBases) // one to many raw encoding case
|
||||
{
|
||||
@@ -137,7 +138,6 @@ public class SpeckleFirstComponent : GH_TaskCapableComponent<List<object?>>
|
||||
{
|
||||
results.AddRange(fallbackConversionResult.Select(o => o.Item1));
|
||||
}
|
||||
results.Add(ghConversionResult.Result);
|
||||
}
|
||||
|
||||
return results;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
|
||||
public class SpeckleResourceFromUrlComponent : SpeckleTaskCapableComponent<string, SpeckleUrlModelResource[]>
|
||||
{
|
||||
public SpeckleResourceFromUrlComponent()
|
||||
: base("Speckle Resource From Url", "spcklUrl", "Speckle resource from url", "Speckle", "Resources") { }
|
||||
|
||||
public override Guid ComponentGuid => new("A55C74C6-D955-4822-84BB-2266A2B965EE");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddTextParameter("URL", "URL", "URL to send to resource", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.list));
|
||||
}
|
||||
|
||||
protected override string GetInput(IGH_DataAccess da)
|
||||
{
|
||||
string url = string.Empty;
|
||||
da.GetData(0, ref url);
|
||||
return url;
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, SpeckleUrlModelResource[] result)
|
||||
{
|
||||
da.SetDataList(0, result);
|
||||
}
|
||||
|
||||
protected override Task<SpeckleUrlModelResource[]> PerformTask(
|
||||
string input,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var resources = SpeckleResourceBuilder.FromUrlString(input);
|
||||
|
||||
// TODO: Here's where we can validate the resources and throw or not?
|
||||
|
||||
return Task.FromResult(resources);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public static class BitmapBuilder
|
||||
{
|
||||
public static Bitmap CreateSquareIconBitmap(string text, int width = 24, int height = 24)
|
||||
{
|
||||
Bitmap bitmap = new(width, height);
|
||||
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||
|
||||
// Enable high-quality rendering
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Set background to transparent
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
// Rectangle with a 1px offset
|
||||
Rectangle squareRect = new(1, 1, width - 2, height - 2);
|
||||
|
||||
using (Brush blueBrush = new SolidBrush(Color.Blue))
|
||||
{
|
||||
graphics.FillRectangle(blueBrush, squareRect);
|
||||
}
|
||||
|
||||
// Draw white letters in the center
|
||||
using (Font font = new("Arial", 8, FontStyle.Bold, GraphicsUnit.Pixel))
|
||||
using (Brush whiteBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
|
||||
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
graphics.DrawString(text, font, whiteBrush, new RectangleF(1, 1, width - 2, height - 2), format);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap CreateHexagonalBitmap(string text, int width = 24, int height = 24)
|
||||
{
|
||||
Bitmap bitmap = new(width, height);
|
||||
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||
|
||||
// Enable high-quality rendering
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Set background to transparent
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
// Calculate hexagon points centered within the bitmap
|
||||
float side = (width - 2) / 2.236f; // 2.236f approximates 4 / √3 for regular hex dimensions
|
||||
float h = side * (float)Math.Sqrt(3) / 2;
|
||||
float centerX = width / 2f;
|
||||
float centerY = height / 2f;
|
||||
|
||||
Point[] hexagonPoints =
|
||||
[
|
||||
new((int)(centerX - side / 2), (int)(centerY - h)),
|
||||
new((int)(centerX + side / 2), (int)(centerY - h)),
|
||||
new((int)(centerX + side), (int)centerY),
|
||||
new((int)(centerX + side / 2), (int)(centerY + h)),
|
||||
new((int)(centerX - side / 2), (int)(centerY + h)),
|
||||
new((int)(centerX - side), (int)centerY)
|
||||
];
|
||||
|
||||
using (Brush blueBrush = new SolidBrush(Color.Blue))
|
||||
{
|
||||
graphics.FillPolygon(blueBrush, hexagonPoints);
|
||||
}
|
||||
|
||||
// Draw white letters in the center
|
||||
using Font font = new("Monospace", 10, FontStyle.Bold, GraphicsUnit.Pixel);
|
||||
using Brush whiteBrush = new SolidBrush(Color.White);
|
||||
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
|
||||
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
graphics.DrawString(text, font, whiteBrush, new RectangleF(0, 1, width, height), format);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -11,11 +11,12 @@ using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public sealed class GrasshopperReceiveConversionResult : ReceiveConversionResult
|
||||
{
|
||||
public object? Result { get; set; }
|
||||
public Base Source { get; set; }
|
||||
|
||||
public GrasshopperReceiveConversionResult(
|
||||
Status status,
|
||||
@@ -28,6 +29,7 @@ public sealed class GrasshopperReceiveConversionResult : ReceiveConversionResult
|
||||
: base(status, source, resultId, resultType, exception)
|
||||
{
|
||||
Result = result;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +148,8 @@ public class GrasshopperHostObjectBuilder : IHostObjectBuilder
|
||||
conversionResults.Add(
|
||||
new GrasshopperReceiveConversionResult(Status.SUCCESS, obj, result, null, result.GetType().ToString())
|
||||
);
|
||||
|
||||
// applicationIdMap[obj.applicationId ?? obj.id] = conversionIds;
|
||||
convertActivity?.SetStatus(SdkActivityStatusCode.Ok);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Logging;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public class GrasshopperReceiveOperation
|
||||
{
|
||||
private readonly AccountService _accountService;
|
||||
private readonly IServerTransportFactory _serverTransportFactory;
|
||||
private readonly IProgressDisplayManager _progressDisplayManager;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
private readonly IOperations _operations;
|
||||
private readonly IClientFactory _clientFactory;
|
||||
|
||||
public GrasshopperReceiveOperation(
|
||||
AccountService accountService,
|
||||
IServerTransportFactory serverTransportFactory,
|
||||
IProgressDisplayManager progressDisplayManager,
|
||||
ISdkActivityFactory activityFactory,
|
||||
IOperations operations,
|
||||
IClientFactory clientFactory
|
||||
)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_serverTransportFactory = serverTransportFactory;
|
||||
_progressDisplayManager = progressDisplayManager;
|
||||
_activityFactory = activityFactory;
|
||||
_operations = operations;
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
public async Task<Base> ReceiveCommitObject(
|
||||
ReceiveInfo receiveInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using var execute = _activityFactory.Start("Receive Operation");
|
||||
execute?.SetTag("receiveInfo", receiveInfo);
|
||||
// 2 - Check account exist
|
||||
Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl);
|
||||
using Client apiClient = _clientFactory.Create(account);
|
||||
using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail());
|
||||
|
||||
var version = await apiClient
|
||||
.Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
using var transport = _serverTransportFactory.Create(account, receiveInfo.ProjectId);
|
||||
|
||||
double? previousPercentage = null;
|
||||
_progressDisplayManager.Begin();
|
||||
Base commitObject = await _operations
|
||||
.Receive2(
|
||||
new Uri(account.serverInfo.url),
|
||||
receiveInfo.ProjectId,
|
||||
version.referencedObject,
|
||||
account.token,
|
||||
onProgressAction: new PassthroughProgress(args =>
|
||||
{
|
||||
if (args.ProgressEvent == ProgressEvent.CacheCheck || args.ProgressEvent == ProgressEvent.DownloadBytes)
|
||||
{
|
||||
switch (args.ProgressEvent)
|
||||
{
|
||||
case ProgressEvent.CacheCheck:
|
||||
previousPercentage = _progressDisplayManager.CalculatePercentage(args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!_progressDisplayManager.ShouldUpdate())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.ProgressEvent)
|
||||
{
|
||||
case ProgressEvent.CacheCheck:
|
||||
case ProgressEvent.DownloadBytes:
|
||||
onOperationProgressed.Report(new("Checking and Downloading... ", previousPercentage));
|
||||
break;
|
||||
case ProgressEvent.DeserializeObject:
|
||||
onOperationProgressed.Report(new("Deserializing ...", _progressDisplayManager.CalculatePercentage(args)));
|
||||
break;
|
||||
}
|
||||
}),
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await apiClient
|
||||
.Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return commitObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Common;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public abstract record SpeckleUrlModelResource(string Server, string ProjectId)
|
||||
{
|
||||
public abstract Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public record SpeckleUrlLatestModelVersionResource(string Server, string ProjectId, string ModelId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
ModelWithVersions model = await client
|
||||
.Model.GetWithVersions(ModelId, ProjectId, 1, null, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Version version = model.versions.items[0];
|
||||
|
||||
var info = new ReceiveInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
project.name,
|
||||
ModelId,
|
||||
model.name,
|
||||
version.id,
|
||||
version.sourceApplication.NotNull()
|
||||
);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelVersionResource(string Server, string ProjectId, string ModelId, string VersionId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
Model model = await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
Version version = await client.Version.Get(VersionId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var info = new ReceiveInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
project.name,
|
||||
ModelId,
|
||||
model.name,
|
||||
VersionId,
|
||||
version.sourceApplication.NotNull()
|
||||
);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelObjectResource(string Server, string ProjectId, string ObjectId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException("Object Resources are not supported yet");
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public record SpeckleResourceBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The ReGex pattern to determine if a URL's AbsolutePath is a Frontend2 URL or not.
|
||||
/// </summary>
|
||||
private static readonly Regex s_fe2UrlRegex =
|
||||
new(
|
||||
@"/projects/(?<projectId>[\w\d]+)(?:/models/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?"
|
||||
);
|
||||
|
||||
public static SpeckleUrlModelResource[] FromUrlString(string speckleModel)
|
||||
{
|
||||
var uri = new Uri(speckleModel);
|
||||
var serverUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
var match = s_fe2UrlRegex.Match(speckleModel);
|
||||
var result = ParseFe2RegexMatch(serverUrl, match);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static SpeckleUrlModelResource[] ParseFe2RegexMatch(string serverUrl, Match match)
|
||||
{
|
||||
var projectId = match.Groups["projectId"];
|
||||
var model = match.Groups["model"];
|
||||
var additionalModels = match.Groups["additionalModels"];
|
||||
|
||||
if (!projectId.Success)
|
||||
{
|
||||
throw new SpeckleException("The provided url is not a valid Speckle url");
|
||||
}
|
||||
|
||||
if (!model.Success)
|
||||
{
|
||||
throw new SpeckleException("The provided url is not pointing to any model in the project.");
|
||||
}
|
||||
|
||||
if (model.Value == "all")
|
||||
{
|
||||
throw new NotSupportedException("Fetching all models is not supported.");
|
||||
}
|
||||
|
||||
if (model.Value.StartsWith("$"))
|
||||
{
|
||||
throw new NotSupportedException("Federation model urls are not supported");
|
||||
}
|
||||
|
||||
var modelRes = GetUrlModelResource(serverUrl, projectId.Value, model.Value);
|
||||
|
||||
var result = new List<SpeckleUrlModelResource> { modelRes };
|
||||
|
||||
if (additionalModels.Success)
|
||||
{
|
||||
foreach (Capture additionalModelsCapture in additionalModels.Captures)
|
||||
{
|
||||
var extraModel = GetUrlModelResource(serverUrl, projectId.Value, additionalModelsCapture.Value);
|
||||
result.Add(extraModel);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static SpeckleUrlModelResource GetUrlModelResource(string serverUrl, string projectId, string modelValue)
|
||||
{
|
||||
if (modelValue.Length == 32)
|
||||
{
|
||||
return new SpeckleUrlModelObjectResource(serverUrl, projectId, modelValue); // Model value is an ObjectID
|
||||
}
|
||||
|
||||
if (!modelValue.Contains('@'))
|
||||
{
|
||||
return new SpeckleUrlLatestModelVersionResource(serverUrl, projectId, modelValue); // Model has no version attached
|
||||
}
|
||||
|
||||
var res = modelValue.Split('@');
|
||||
return new SpeckleUrlModelVersionResource(serverUrl, projectId, res[0], res[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleCollectionGoo : GH_Goo<Collection>
|
||||
{
|
||||
// TODO: Massive hack for setup only!!! We need some sort of `ShallowCopy` or a transparent wrapper for Speckle Objects
|
||||
// to prevent backwards propagation of changes of the same instance.
|
||||
public override IGH_Goo Duplicate() => new SpeckleCollectionGoo { Value = m_value };
|
||||
|
||||
public override string ToString() => $"Speckle Collection [{m_value.name}]";
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "SpeckleCollection";
|
||||
public override string TypeDescription => "A Speckle Collection";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
Collection? obj = null;
|
||||
switch (source)
|
||||
{
|
||||
case Collection speckleCollection:
|
||||
obj = speckleCollection;
|
||||
break;
|
||||
case SpeckleObjectGoo speckleObjectGoo:
|
||||
if (speckleObjectGoo.Value is Collection collection)
|
||||
{
|
||||
obj = collection;
|
||||
}
|
||||
break;
|
||||
case SpeckleCollectionGoo speckleCollectionGoo:
|
||||
obj = speckleCollectionGoo.Value;
|
||||
break;
|
||||
case GH_Goo<ISpeckleObject> speckleObjectGoo:
|
||||
if (speckleObjectGoo.Value is Collection collection2)
|
||||
{
|
||||
obj = collection2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
obj = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value = obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CastTo<TOut>(ref TOut target)
|
||||
{
|
||||
var type = typeof(TOut);
|
||||
var success = false;
|
||||
if (type == typeof(SpeckleObjectGoo))
|
||||
{
|
||||
target = (TOut)(object)new SpeckleObjectGoo { Value = Value };
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(SpeckleCollectionGoo))
|
||||
{
|
||||
target = (TOut)(object)new SpeckleObjectGoo { Value = Value };
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(Collection))
|
||||
{
|
||||
target = (TOut)(object)Value;
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(Base))
|
||||
{
|
||||
target = (TOut)(object)Value;
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleCollectionParam : GH_Param<SpeckleCollectionGoo>
|
||||
{
|
||||
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", "SpcklCol", "XXX", "Speckle", "Params", access) { }
|
||||
|
||||
public override Guid ComponentGuid => new("F397D941-6B4D-4143-B535-A11F7F776BA1");
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateHexagonalBitmap("C");
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleObjectGoo : GH_Goo<Base>
|
||||
{
|
||||
// TODO: Massive hack for setup only!!! We need some sort of `ShallowCopy` or a transparent wrapper for Speckle Objects
|
||||
// to prevent backwards propagation of changes of the same instance.
|
||||
public override IGH_Goo Duplicate() => new SpeckleObjectGoo { Value = m_value };
|
||||
|
||||
public override string ToString() => $"Speckle Object [{m_value.GetType().Name}]";
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "SpeckleObject";
|
||||
public override string TypeDescription => "A Speckle Object";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
Base? obj = source switch
|
||||
{
|
||||
Base speckleObject => speckleObject,
|
||||
SpeckleObjectGoo speckleObjectGoo => speckleObjectGoo.Value,
|
||||
SpeckleCollectionGoo speckleCollectionGoo => speckleCollectionGoo.Value,
|
||||
GH_Goo<Base> speckleObjectGoo => speckleObjectGoo.Value,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Value = obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CastTo<TOut>(ref TOut target)
|
||||
{
|
||||
var type = typeof(TOut);
|
||||
var success = false;
|
||||
if (type == typeof(SpeckleObjectGoo))
|
||||
{
|
||||
target = (TOut)(object)new SpeckleObjectGoo { Value = Value };
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(SpeckleCollectionGoo) && Value is Collection collection)
|
||||
{
|
||||
target = (TOut)(object)new SpeckleObjectGoo { Value = collection };
|
||||
success = true;
|
||||
}
|
||||
else if (type == typeof(Base))
|
||||
{
|
||||
target = (TOut)(object)Value;
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Grasshopper.Kernel;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleObjectParam : GH_Param<SpeckleObjectGoo>
|
||||
{
|
||||
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", "SpklObj", "XXXXX", "Speckle", "Params", access) { }
|
||||
|
||||
public override Guid ComponentGuid => new("F708F88C-FE00-44EF-8D30-02AB6CF5F728");
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleUrlModelResourceGoo : GH_Goo<SpeckleUrlModelResource>
|
||||
{
|
||||
public override IGH_Goo Duplicate() => new SpeckleUrlModelResourceGoo() { Value = Value };
|
||||
|
||||
public override string ToString() => Value.ToString();
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "SpeckleUrlModelResource";
|
||||
public override string TypeDescription => "Points to a model/version/object in a Speckle server";
|
||||
|
||||
public override bool CastFrom(object source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case SpeckleUrlModelResource resource:
|
||||
Value = resource;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CastTo<TOut>(ref TOut target)
|
||||
{
|
||||
var type = typeof(TOut);
|
||||
var success = false;
|
||||
if (type == typeof(SpeckleUrlModelResource))
|
||||
{
|
||||
target = (TOut)(object)Value;
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using Grasshopper.Kernel;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
public class SpeckleUrlModelResourceParam : GH_Param<SpeckleUrlModelResourceGoo>
|
||||
{
|
||||
public SpeckleUrlModelResourceParam()
|
||||
: this(GH_ParamAccess.item) { }
|
||||
|
||||
public SpeckleUrlModelResourceParam(IGH_InstanceDescription tag)
|
||||
: base(tag) { }
|
||||
|
||||
public SpeckleUrlModelResourceParam(IGH_InstanceDescription tag, GH_ParamAccess access)
|
||||
: base(tag, access) { }
|
||||
|
||||
public SpeckleUrlModelResourceParam(GH_ParamAccess access)
|
||||
: base("Speckle URL", "spcklUrl", "A Speckle resource", "Speckle", "Resources", access) { }
|
||||
|
||||
public override Guid ComponentGuid => new Guid("E5421FC2-F10D-447F-BF23-5C934ABDB2D3");
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Credentials;
|
||||
@@ -26,6 +26,7 @@ public class PriorityLoader : GH_AssemblyPriority
|
||||
services.AddRhinoConverters().AddConnectorUtils();
|
||||
|
||||
services.AddTransient<IHostObjectBuilder, GrasshopperHostObjectBuilder>();
|
||||
services.AddTransient<GrasshopperReceiveOperation>();
|
||||
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
services.AddTransient<TraversalContextUnpacker>();
|
||||
services.AddTransient<AccountManager>();
|
||||
|
||||
-4
@@ -30,8 +30,4 @@
|
||||
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="HostApp\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8;
|
||||
|
||||
public class SpeckleObjectParam : GH_Param<SpeckleObjectGoo>
|
||||
{
|
||||
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", "SpklObj", "XXXXX", "Speckle", "Params", access) { }
|
||||
|
||||
public override Guid ComponentGuid => new("F708F88C-FE00-44EF-8D30-02AB6CF5F728");
|
||||
}
|
||||
|
||||
public class SpeckleObjectGoo : GH_Goo<ISpeckleObject>
|
||||
{
|
||||
// TODO: Massive hack for setup only!!! We need some sort of `ShallowCopy` or a transparent wrapper for Speckle Objects
|
||||
// to prevent backwards propagation of changes of the same instance.
|
||||
public override IGH_Goo Duplicate() => new SpeckleObjectGoo { Value = m_value };
|
||||
|
||||
public override string ToString() => m_value.ToString();
|
||||
|
||||
public override bool IsValid => true;
|
||||
public override string TypeName => "SpeckleObject";
|
||||
public override string TypeDescription => "A Speckle Object";
|
||||
}
|
||||
@@ -273,9 +273,36 @@
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.191, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.191, )",
|
||||
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Web.WebView2": "[1.0.1938.49, )",
|
||||
"Speckle.Connectors.DUI": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
"type": "Project"
|
||||
},
|
||||
"speckle.connectors.rhino7": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[7.13.21348.13001, )",
|
||||
"RhinoWindows": "[7.13.21348.13001, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
|
||||
"Speckle.Converters.Rhino7": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
@@ -283,6 +310,13 @@
|
||||
"Speckle.Objects": "[3.1.0-dev.191, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino7": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[7.13.21348.13001, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
@@ -323,6 +357,21 @@
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
|
||||
},
|
||||
"Microsoft.Web.WebView2": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.0.1938.49, )",
|
||||
"resolved": "1.0.1938.49",
|
||||
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
|
||||
},
|
||||
"RhinoWindows": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.9.24194.18121, )",
|
||||
"resolved": "7.13.21348.13001",
|
||||
"contentHash": "V94T8emmJmFfmbd5cu+uTNS0neZApx1Q5MXvsQGFtt/mEGEbdHE+dFOETNgbOOJXSdNboAnCR3uo0GosOFX+/g==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[7.13.21348.13001]"
|
||||
}
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.191, )",
|
||||
@@ -354,6 +403,12 @@
|
||||
"requested": "[3.1.0-dev.191, )",
|
||||
"resolved": "3.1.0-dev.191",
|
||||
"contentHash": "EmEOyjsGsNi56Z/ZoBOn8WirTmIT2yqWvlUeUh0BSPX2TDMZXHTKOM/kHmP6HSd10KVFn2Zo/ItY7/K9iRtL1Q=="
|
||||
},
|
||||
"System.Threading.Tasks.Dataflow": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<!-- Logging -->
|
||||
CA1848;CA2254;CA1727;
|
||||
<!-- Others we don't want -->
|
||||
CA1815;CA1725;
|
||||
CA1815;CA1725;CA1501;
|
||||
<!-- Package using wrong RIDs (Net8 changed them). Usually fixable by updating. -->
|
||||
NETSDK1206;
|
||||
$(NoWarn)
|
||||
|
||||
Reference in New Issue
Block a user