feat: POC Send node
This commit is contained in:
+24
-3
@@ -27,11 +27,28 @@ public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetSolveResults(da, out TOutput result))
|
||||
bool solveResults = false;
|
||||
TOutput? result = default;
|
||||
|
||||
try
|
||||
{
|
||||
solveResults = GetSolveResults(da, out result);
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
foreach (var inner in e.InnerExceptions)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, inner.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!solveResults)
|
||||
{
|
||||
// INFO: This will run synchronously. Useful for Rhino.Compute runs, but can also be enabled by user.
|
||||
try
|
||||
@@ -40,9 +57,12 @@ public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
|
||||
var syncResult = PerformTask(input).Result;
|
||||
result = syncResult;
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
catch (AggregateException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
foreach (var inner in e.InnerExceptions)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, inner.Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -56,6 +76,7 @@ public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap(IconText);
|
||||
|
||||
protected string IconText => string.Join("", Name.Split(' ').Select(s => s.First()));
|
||||
|
||||
protected abstract TInput GetInput(IGH_DataAccess da);
|
||||
|
||||
protected abstract void SetOutput(IGH_DataAccess da, TOutput result);
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Data;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Credentials;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Send;
|
||||
|
||||
public class SendComponentInput
|
||||
{
|
||||
public SpeckleUrlModelResource Resource { get; }
|
||||
public Dictionary<string, GH_Structure<IGH_Goo>> Inputs { get; }
|
||||
|
||||
public SendComponentInput(SpeckleUrlModelResource resource, Dictionary<string, GH_Structure<IGH_Goo>> inputs)
|
||||
{
|
||||
Resource = resource;
|
||||
Inputs = inputs;
|
||||
}
|
||||
}
|
||||
|
||||
public class SendComponentOutput(SpeckleUrlModelObjectResource resource)
|
||||
{
|
||||
public SpeckleUrlModelObjectResource Resource { get; } = resource;
|
||||
}
|
||||
|
||||
public class SendComponent()
|
||||
: SpeckleScopedTaskCapableComponent<SendComponentInput, SendComponentOutput>(
|
||||
"Send",
|
||||
"SS",
|
||||
"Speckle Send",
|
||||
"Speckle",
|
||||
"Operations"
|
||||
)
|
||||
{
|
||||
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
pManager.AddGenericParameter("A", "A", "A", GH_ParamAccess.tree);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
}
|
||||
|
||||
protected override SendComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
if (da.Iteration != 0)
|
||||
{
|
||||
throw new SpeckleException("No more than 1 resource allowed");
|
||||
}
|
||||
|
||||
SpeckleUrlModelResource? resource = null;
|
||||
if (!da.GetData(0, ref resource))
|
||||
{
|
||||
throw new SpeckleException("Failed to get resource");
|
||||
}
|
||||
|
||||
var name = Params.Input[1].Name;
|
||||
da.GetDataTree(1, out GH_Structure<IGH_Goo> tree);
|
||||
|
||||
var inputDict = new Dictionary<string, GH_Structure<IGH_Goo>> { { name, tree } };
|
||||
|
||||
return new SendComponentInput(resource.NotNull(), inputDict);
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, SendComponentOutput result)
|
||||
{
|
||||
da.SetData(0, result.Resource);
|
||||
}
|
||||
|
||||
protected override async Task<SendComponentOutput> PerformScopedTask(
|
||||
SendComponentInput input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var rhinoConversionSettingsFactory = scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
|
||||
scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
|
||||
|
||||
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var sendOperation = scope.ServiceProvider.GetRequiredService<GrasshopperSendOperation>();
|
||||
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server));
|
||||
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = await sendOperation
|
||||
.Execute(input.Inputs, receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new SendComponentOutput(
|
||||
new SpeckleUrlModelObjectResource(receiveInfo.ServerUrl.ToString(), receiveInfo.ProjectId, result.RootId)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public static class GrasshopperGooExtensions
|
||||
{
|
||||
public static T UnwrapGoo<T>(this IGH_Goo goo)
|
||||
{
|
||||
if (goo is GH_Goo<T> specificGoo)
|
||||
{
|
||||
return specificGoo.Value;
|
||||
}
|
||||
|
||||
var valuePropInfo = goo.GetType()
|
||||
.GetField("m_value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
if (valuePropInfo != null)
|
||||
{
|
||||
var tempValue = valuePropInfo.GetValue(goo);
|
||||
if (tempValue is T value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// TODO: Potentially unwrap new rhino objects
|
||||
|
||||
throw new SpeckleException(
|
||||
$"Internal value of goo {goo.GetType().Name} was not the provided type {typeof(T).Name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Grasshopper.Kernel.Data;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public class GrasshopperSendOperation
|
||||
{
|
||||
private readonly IRootObjectSender _baseObjectSender;
|
||||
private readonly IRootToSpeckleConverter _converter;
|
||||
|
||||
public GrasshopperSendOperation(IRootObjectSender baseObjectSender, IRootToSpeckleConverter converter)
|
||||
{
|
||||
_baseObjectSender = baseObjectSender;
|
||||
_converter = converter;
|
||||
}
|
||||
|
||||
public async Task<GrashopperSendOperationResult> Execute(
|
||||
IReadOnlyDictionary<string, GH_Structure<IGH_Goo>> objects,
|
||||
SendInfo sendInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var elements = new List<Base>();
|
||||
foreach (var keypair in objects)
|
||||
{
|
||||
elements.Add(ConvertGhStructureToCollection(keypair.Key, keypair.Value));
|
||||
}
|
||||
|
||||
var buildResult = new Collection("Grasshopper Model") { elements = elements, ["version"] = 3 };
|
||||
|
||||
var result = await _baseObjectSender.Send(buildResult, sendInfo, onOperationProgressed, ct).ConfigureAwait(false);
|
||||
|
||||
return new(result.RootId);
|
||||
}
|
||||
|
||||
private Collection ConvertGhStructureToCollection(string name, GH_Structure<IGH_Goo> tree)
|
||||
{
|
||||
var result = tree.Paths.Select(path =>
|
||||
{
|
||||
var pathElements = tree.get_Branch(path) as IList<IGH_Goo>;
|
||||
var convertedElements = new List<Base>();
|
||||
|
||||
pathElements.NotNull();
|
||||
|
||||
foreach (var pathElement in pathElements)
|
||||
{
|
||||
try
|
||||
{
|
||||
var goo = pathElement.UnwrapGoo<GeometryBase>();
|
||||
var converted = _converter.Convert(goo);
|
||||
convertedElements.Add(converted);
|
||||
}
|
||||
catch (Exception e) when (!e.IsFatal())
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection(path.ToString()) { elements = convertedElements };
|
||||
});
|
||||
|
||||
return new Collection(name) { elements = result.Cast<Base>().ToList() };
|
||||
}
|
||||
}
|
||||
|
||||
public record GrashopperSendOperationResult(string RootId);
|
||||
@@ -9,6 +9,8 @@ namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
public abstract record SpeckleUrlModelResource(string Server, string ProjectId)
|
||||
{
|
||||
public abstract Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default);
|
||||
|
||||
public abstract Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public record SpeckleUrlLatestModelVersionResource(string Server, string ProjectId, string ModelId)
|
||||
@@ -35,6 +37,21 @@ public record SpeckleUrlLatestModelVersionResource(string Server, string Project
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// We don't care about the return info, we just want to be sure we have access and everything exists.
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new SendInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelVersionResource(string Server, string ProjectId, string ModelId, string VersionId)
|
||||
@@ -59,6 +76,21 @@ public record SpeckleUrlModelVersionResource(string Server, string ProjectId, st
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// We don't care about the return info, we just want to be sure we have access and everything exists.
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new SendInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelObjectResource(string Server, string ProjectId, string ObjectId)
|
||||
@@ -66,4 +98,7 @@ public record SpeckleUrlModelObjectResource(string Server, string ProjectId, str
|
||||
{
|
||||
public override Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException("Object Resources are not supported yet");
|
||||
|
||||
public override Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException("Object Resources are not supported yet");
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Host;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8;
|
||||
|
||||
@@ -23,15 +18,9 @@ public class PriorityLoader : GH_AssemblyPriority
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
_disposableLogger = services.Initialize(HostApplications.Grasshopper, GetVersion());
|
||||
services.AddRhinoConverters().AddConnectorUtils();
|
||||
|
||||
services.AddTransient<IHostObjectBuilder, GrasshopperHostObjectBuilder>();
|
||||
services.AddTransient<GrasshopperReceiveOperation>();
|
||||
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
services.AddScoped<RootObjectUnpacker>();
|
||||
services.AddRhinoConverters().AddGrasshopper().AddConnectorUtils();
|
||||
|
||||
services.AddTransient<TraversalContextUnpacker>();
|
||||
services.AddTransient<AccountManager>();
|
||||
Container = services.BuildServiceProvider();
|
||||
return GH_LoadingInstruction.Proceed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8;
|
||||
|
||||
public static class ServiceRegistration
|
||||
{
|
||||
public static IServiceCollection AddGrasshopper(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IHostObjectBuilder, GrasshopperHostObjectBuilder>();
|
||||
services.AddTransient<GrasshopperReceiveOperation>();
|
||||
services.AddTransient<GrasshopperSendOperation>();
|
||||
services.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
services.AddScoped<RootObjectUnpacker>();
|
||||
|
||||
services.AddTransient<TraversalContextUnpacker>();
|
||||
services.AddTransient<AccountManager>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
+34
-17
@@ -11,27 +11,44 @@ public static class DisplayMeshExtractor
|
||||
var renderMeshes = obj.GetMeshes(RG.MeshType.Render);
|
||||
if (renderMeshes.Length == 0)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case BrepObject brep:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(brep.BrepGeometry, new(0.05, 0.05));
|
||||
break;
|
||||
case ExtrusionObject extrusion:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ExtrusionGeometry.ToBrep(), new(0.05, 0.05));
|
||||
break;
|
||||
case SubDObject subDObject:
|
||||
#pragma warning disable CA2000
|
||||
var mesh = RG.Mesh.CreateFromSubD(subDObject.Geometry as RG.SubD, 0);
|
||||
#pragma warning restore CA2000
|
||||
renderMeshes = [mesh];
|
||||
break;
|
||||
default:
|
||||
throw new ConversionException($"Unsupported object for display mesh generation {obj.GetType().FullName}");
|
||||
}
|
||||
renderMeshes = GetDisplayMeshes(obj.Geometry);
|
||||
}
|
||||
|
||||
var joinedMesh = new RG.Mesh();
|
||||
joinedMesh.Append(renderMeshes);
|
||||
return joinedMesh;
|
||||
}
|
||||
|
||||
public static RG.Mesh GetDisplayMesh(RG.GeometryBase obj)
|
||||
{
|
||||
// note: unsure this is nice, we get bigger meshes - we should to benchmark (conversion time vs size tradeoffs)
|
||||
var renderMeshes = GetDisplayMeshes(obj);
|
||||
var joinedMesh = new RG.Mesh();
|
||||
joinedMesh.Append(renderMeshes);
|
||||
return joinedMesh;
|
||||
}
|
||||
|
||||
private static RG.Mesh[] GetDisplayMeshes(RG.GeometryBase obj)
|
||||
{
|
||||
RG.Mesh[] renderMeshes;
|
||||
switch (obj)
|
||||
{
|
||||
case RG.Brep brep:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(brep, new(0.05, 0.05));
|
||||
break;
|
||||
case RG.Extrusion extrusion:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ToBrep(), new(0.05, 0.05));
|
||||
break;
|
||||
case RG.SubD subDObject:
|
||||
#pragma warning disable CA2000
|
||||
var mesh = RG.Mesh.CreateFromSubD(subDObject, 0);
|
||||
#pragma warning restore CA2000
|
||||
renderMeshes = [mesh];
|
||||
break;
|
||||
default:
|
||||
throw new ConversionException($"Unsupported object for display mesh generation {obj.GetType().FullName}");
|
||||
}
|
||||
|
||||
return renderMeshes;
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.Rhino.ToSpeckle.Encoding;
|
||||
using Speckle.Converters.Rhino.ToSpeckle.Meshing;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(RG.Brep), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class BrepToSpeckleTopLevelConverter : IToSpeckleTopLevelConverter
|
||||
{
|
||||
private readonly ITypedConverter<RG.Mesh, SOG.Mesh> _meshConverter;
|
||||
private readonly IConverterSettingsStore<RhinoConversionSettings> _settingsStore;
|
||||
|
||||
public BrepToSpeckleTopLevelConverter(
|
||||
ITypedConverter<RG.Mesh, SOG.Mesh> meshConverter,
|
||||
IConverterSettingsStore<RhinoConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_meshConverter = meshConverter;
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
public Base Convert(object target)
|
||||
{
|
||||
var brepObject = (RG.Brep)target;
|
||||
var brepEncoding = RawEncodingCreator.Encode(brepObject, _settingsStore.Current.Document);
|
||||
|
||||
var mesh = DisplayMeshExtractor.GetDisplayMesh(brepObject);
|
||||
var displayValue = new List<SOG.Mesh> { _meshConverter.Convert(mesh) };
|
||||
|
||||
var bx = new SOG.BrepX()
|
||||
{
|
||||
displayValue = displayValue,
|
||||
encodedValue = brepEncoding,
|
||||
units = _settingsStore.Current.SpeckleUnits
|
||||
};
|
||||
|
||||
return bx;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user