diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs index b5e36a8b0d..bf6402e50d 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Bindings/RhinoReceiveBinding.cs @@ -3,6 +3,8 @@ using Speckle.Connectors.Common.Cancellation; using Speckle.Connectors.DUI.Bindings; using Speckle.Connectors.DUI.Bridge; +using Speckle.Connectors.DUI.Settings; +using Speckle.Connectors.Rhino.Operations.Receive.Settings; using Speckle.Converters.Common; using Speckle.Converters.Rhino; @@ -12,7 +14,8 @@ public class RhinoReceiveBinding( ICancellationManager cancellationManager, IBrowserBridge parent, IRhinoConversionSettingsFactory rhinoConversionSettingsFactory, - IReceiveOperationManagerFactory receiveOperationManagerFactory + IReceiveOperationManagerFactory receiveOperationManagerFactory, + IToHostSettingsManager toHostSettingsManager ) : IReceiveBinding { public string Name => "receiveBinding"; @@ -20,6 +23,10 @@ IReceiveOperationManagerFactory receiveOperationManagerFactory private ReceiveBindingUICommands Commands { get; } = new(parent); +#pragma warning disable CA1024 + public List GetReceiveSettings() => [new ConvertMeshesToPolysurfacesSetting()]; +#pragma warning restore CA1024 + public void CancelReceive(string modelCardId) => cancellationManager.CancelOperation(modelCardId); public async Task Receive(string modelCardId) @@ -32,7 +39,13 @@ await manager.Process( (sp, card) => { sp.GetRequiredService>() - .Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc, true)); + .Initialize( + rhinoConversionSettingsFactory.Create( + RhinoDoc.ActiveDoc, + true, + toHostSettingsManager.GetConvertMeshesToPolysurfacesSetting(card) + ) + ); }, async (modelName, processor) => { diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ConvertMeshesToPolysurfacesSetting.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ConvertMeshesToPolysurfacesSetting.cs new file mode 100644 index 0000000000..f395722cbc --- /dev/null +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ConvertMeshesToPolysurfacesSetting.cs @@ -0,0 +1,14 @@ +using Speckle.Connectors.DUI.Settings; + +namespace Speckle.Connectors.Rhino.Operations.Receive.Settings; + +public class ConvertMeshesToPolysurfacesSetting(bool value = false) : ICardSetting +{ + public const string SETTING_ID = "convertMeshesToPolysurfaces"; + + public string? Id { get; set; } = SETTING_ID; + public string? Title { get; set; } = "Convert meshes to polysurfaces"; + public string? Type { get; set; } = "boolean"; + public object? Value { get; set; } = value; + public List? Enum { get; set; } +} diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ToHostSettingsManager.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ToHostSettingsManager.cs new file mode 100644 index 0000000000..0b11a1683e --- /dev/null +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Operations/Receive/Settings/ToHostSettingsManager.cs @@ -0,0 +1,15 @@ +using Speckle.Connectors.DUI.Models.Card; +using Speckle.InterfaceGenerator; + +namespace Speckle.Connectors.Rhino.Operations.Receive.Settings; + +[GenerateAutoInterface] +public class ToHostSettingsManager : IToHostSettingsManager +{ + public bool GetConvertMeshesToPolysurfacesSetting(ModelCard modelCard) + { + var value = + modelCard.Settings?.FirstOrDefault(s => s.Id == ConvertMeshesToPolysurfacesSetting.SETTING_ID)?.Value as bool?; + return value is true; + } +} diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs index 6369b4b29d..3546dc73f4 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Registration/ServiceRegistration.cs @@ -19,6 +19,7 @@ using Speckle.Connectors.Rhino.HostApp.Properties; using Speckle.Connectors.Rhino.Mapper.Revit; using Speckle.Connectors.Rhino.Operations.Receive; +using Speckle.Connectors.Rhino.Operations.Receive.Settings; using Speckle.Connectors.Rhino.Operations.Send; using Speckle.Connectors.Rhino.Operations.Send.Settings; using Speckle.Connectors.Rhino.Plugin; @@ -62,6 +63,9 @@ public static void AddRhino(this IServiceCollection serviceCollection, bool isCo // register send settings serviceCollection.AddScoped(); + // register receive settings + serviceCollection.AddSingleton(); + // register send conversion cache serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems index 8da708e54e..838e7ae61b 100644 --- a/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems +++ b/Connectors/Rhino/Speckle.Connectors.RhinoShared/Speckle.Connectors.RhinoShared.projitems @@ -38,6 +38,8 @@ + + diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettings.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettings.cs index 5b53e888c3..dcf2d4140e 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettings.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettings.cs @@ -5,4 +5,9 @@ namespace Speckle.Converters.Rhino; /// /// Represents the settings used for Rhino and Grasshopper conversions. /// -public record RhinoConversionSettings(RhinoDoc Document, string SpeckleUnits, bool AddVisualizationProperties); +public record RhinoConversionSettings( + RhinoDoc Document, + string SpeckleUnits, + bool AddVisualizationProperties, + bool ConvertMeshesToPolysurfaces = false +); diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettingsFactory.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettingsFactory.cs index 002664dd9f..8e04cd0a93 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettingsFactory.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/RhinoConversionSettingsFactory.cs @@ -14,4 +14,16 @@ IConverterSettingsStore settingsStore public RhinoConversionSettings Create(RhinoDoc document, bool addVisualizationProperties) => new(document, unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem), addVisualizationProperties); + + public RhinoConversionSettings Create( + RhinoDoc document, + bool addVisualizationProperties, + bool convertMeshesToPolysurfaces + ) => + new( + document, + unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem), + addVisualizationProperties, + convertMeshesToPolysurfaces + ); } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs index 50b5a28736..7f7c0c93e8 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DataObjectConverter.cs @@ -107,7 +107,7 @@ IDataObjectInstanceRegistry dataObjectInstanceRegistry SOG.Ellipse ellipse => new() { _ellipseConverter.Convert(ellipse) }, SOG.ExtrusionX extrusion => _extrusionConverter.Convert(extrusion), SOG.Line line => new() { _lineConverter.Convert(line) }, - SOG.Mesh mesh => new() { _meshConverter.Convert(mesh) }, + SOG.Mesh mesh => new() { ConvertMesh(mesh) }, SOG.Pointcloud pointcloud => new() { _pointcloudConverter.Convert(pointcloud) }, SOG.Point point => new() { _pointConverter.Convert(point) }, SOG.Polycurve polycurve => new() { _polycurveConverter.Convert(polycurve) }, @@ -133,4 +133,26 @@ private RG.Transform GetUnitsTransform(Base speckleObject) return RG.Transform.Identity; } + +#pragma warning disable CA1508 // Brep.CreateFromMesh can return null for degenerate meshes + private RG.GeometryBase ConvertMesh(SOG.Mesh mesh) + { + var rhinoMesh = _meshConverter.Convert(mesh); + + if (_settingsStore.Current.ConvertMeshesToPolysurfaces) + { + var brep = RG.Brep.CreateFromMesh(rhinoMesh, true); + if (brep is not null) + { + brep.MergeCoplanarFaces( + _settingsStore.Current.Document.ModelAbsoluteTolerance, + _settingsStore.Current.Document.ModelAngleToleranceRadians + ); + return brep; + } + } + + return rhinoMesh; + } +#pragma warning restore CA1508 } diff --git a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs index 24bde1354a..d53815f79a 100644 --- a/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs +++ b/Converters/Rhino/Speckle.Converters.RhinoShared/ToHost/TopLevel/DisplayableObjectToHostTopLevelConverter.cs @@ -47,7 +47,7 @@ IConverterSettingsStore settingsStore SOG.Line line => _lineConverter.Convert(line), SOG.Polyline polyline => _polylineConverter.Convert(polyline), SOG.Arc arc => _arcConverter.Convert(arc), - SOG.Mesh mesh => _meshConverter.Convert(mesh), + SOG.Mesh mesh => ConvertMesh(mesh), SOG.Point point => _pointConverter.Convert(point), _ => throw new ConversionException($"Found unsupported fallback geometry: {item.GetType()}") }; @@ -74,4 +74,26 @@ private RG.Transform GetUnitsTransform(Base speckleObject) return RG.Transform.Identity; } + +#pragma warning disable CA1508 // Brep.CreateFromMesh can return null for degenerate meshes + private RG.GeometryBase ConvertMesh(SOG.Mesh mesh) + { + var rhinoMesh = _meshConverter.Convert(mesh); + + if (_settingsStore.Current.ConvertMeshesToPolysurfaces) + { + var brep = RG.Brep.CreateFromMesh(rhinoMesh, true); + if (brep is not null) + { + brep.MergeCoplanarFaces( + _settingsStore.Current.Document.ModelAbsoluteTolerance, + _settingsStore.Current.Document.ModelAngleToleranceRadians + ); + return brep; + } + } + + return rhinoMesh; + } +#pragma warning restore CA1508 }