Compare commits

...

30 Commits

Author SHA1 Message Date
Jedd Morgan 27a7d72de3 Merge pull request #1051 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev -> main
2025-08-26 17:20:06 +01:00
Jedd Morgan 678f113d05 Merge pull request #1050 from specklesystems/jrm/main-to-dev2
Update dev with main
2025-08-26 16:54:05 +01:00
Jedd Morgan 92da66bbbb Merge branch 'dev' into jrm/main-to-dev2 2025-08-26 16:53:25 +01:00
Oğuzhan Koral 79a5228899 Fix: invert boolean flag (#1049)
* Introduce global config

* invert boolean flag
2025-08-26 18:52:21 +03:00
Adam Hathcock 4d9411de42 fix(revit): Revit files persist model card data to a file like Tekla instead of into the file (#1045)
* Revit files persist model card data to a file like Tekla instead of into the file

* fmt

* fixes logger

* Update Connectors/Revit/Speckle.Connectors.RevitShared/HostApp/RevitDocumentStore.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 16:31:48 +01:00
Oğuzhan Koral 3780747992 Introduce global config (#1041) 2025-08-26 09:54:50 +00:00
Mucahit Bilal GOKER 4514b1b831 Merge pull request #1039 from specklesystems/bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit
feat(revit): Add Area Scheme
2025-08-24 18:29:02 +03:00
Björn Steinhagen 2bbbbf6204 Merge branch 'dev' into bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit 2025-08-24 17:16:49 +02:00
Björn Steinhagen e1b5dea3f7 fix: csharpier 2025-08-24 17:15:29 +02:00
Mucahit Bilal GOKER 2d2c274030 Merge pull request #1040 from specklesystems/bilal/cnx-2121-duplicated-geometry-when-parts-visibility-is-on
fix(revit): Exclude host element from view filter when parts are enabled
2025-08-24 18:15:11 +03:00
Björn Steinhagen 81dd72a281 Merge branch 'dev' into bilal/cnx-2354-add-area-scheme-property-to-areas-from-revit 2025-08-24 17:12:17 +02:00
Björn Steinhagen b82349478c Merge branch 'dev' into bilal/cnx-2121-duplicated-geometry-when-parts-visibility-is-on 2025-08-24 17:09:08 +02:00
bimgeek 7d0690f7a0 bjorn pasha asked for these changes 2025-08-24 18:08:55 +03:00
bimgeek 62a0cb895d pasha bjorns comments 2025-08-24 17:56:21 +03:00
Claire Kuang f28ce73d33 Merge pull request #1035 from specklesystems/claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision
fix(rhino/grasshopper): always use double precision meshes and current doc mesh settings
2025-08-22 16:56:26 +01:00
bimgeek 15425c5328 no need for 2 db queries 2025-08-22 15:12:33 +03:00
bimgeek 7c645e3c51 collector disposal 2025-08-22 15:05:35 +03:00
bimgeek 795d068175 exclude parts from view filter 2025-08-22 14:57:43 +03:00
Claire Kuang 90c2bd2873 Merge branch 'dev' into claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision 2025-08-22 12:45:36 +01:00
bimgeek 4fba12f966 add area scheme switch statement 2025-08-21 19:47:52 +03:00
Claire Kuang 348975c33d Merge branch 'dev' into claire/cnx-2310-grasshopper-and-rhino-not-sending-correct-double-precision 2025-08-21 10:33:54 +01:00
Claire Kuang 9a6dda629b also fixes an issue with sending low res meshes.
uses current doc settings to convert display meshes for breps etc
2025-08-19 16:32:03 +01:00
Claire Kuang bc0fe17d08 Update MeshToSpeckleConverter.cs 2025-08-19 14:38:33 +01:00
Claire Kuang 2e52409db6 Update DisplayMeshExtractor.cs 2025-08-19 14:31:21 +01:00
Claire Kuang f434cde7b3 removes model far from origin logic from rhino 2025-08-19 14:23:12 +01:00
Claire Kuang 3e596cac29 Update MeshToSpeckleConverter.cs 2025-08-19 10:00:10 +01:00
Oğuzhan Koral 3424de9130 Merge pull request #1032 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Update dev to main
2025-08-18 11:33:50 +01:00
Jedd Morgan f1f17eea3d Merge pull request #1021 from specklesystems/jrm/dev-to-main
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
Dev -> Main
2025-08-07 15:16:38 +01:00
Jedd Morgan 642607acad Merge branch 'main' into jrm/dev-to-main 2025-08-07 15:09:09 +01:00
Claire Kuang 2cb7211734 Merge pull request #1009 from specklesystems/dev
.NET Build and Publish / build-windows (push) Has been cancelled
.NET Build and Publish / build-linux (push) Has been cancelled
.NET Build and Publish / deploy-installers (push) Has been cancelled
dev to main
2025-07-30 12:42:01 +01:00
14 changed files with 156 additions and 230 deletions
@@ -3,48 +3,45 @@ using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Utils;
using Speckle.Connectors.Revit.Plugin;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.SQLite;
namespace Speckle.Connectors.Revit.HostApp;
// POC: should be interfaced out
internal sealed class RevitDocumentStore : DocumentModelStore
{
// POC: move to somewhere central?
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
private readonly ILogger<RevitDocumentStore> _logger;
private readonly IAppIdleManager _idleManager;
private readonly RevitContext _revitContext;
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
private readonly IdStorageSchema _idStorageSchema;
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
private readonly IThreadContext _threadContext;
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
public RevitDocumentStore(
ILogger<DocumentModelStore> logger,
IAppIdleManager idleManager,
RevitContext revitContext,
IJsonSerializer jsonSerializer,
DocumentModelStorageSchema documentModelStorageSchema,
IdStorageSchema idStorageSchema,
ITopLevelExceptionHandler topLevelExceptionHandler,
IThreadContext threadContext,
IRevitTask revitTask
IRevitTask revitTask,
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
ILogger<RevitDocumentStore> logger
)
: base(logger, jsonSerializer)
{
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
_idleManager = idleManager;
_revitContext = revitContext;
_documentModelStorageSchema = documentModelStorageSchema;
_idStorageSchema = idStorageSchema;
_topLevelExceptionHandler = topLevelExceptionHandler;
_threadContext = threadContext;
_logger = logger;
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
@@ -101,80 +98,36 @@ internal sealed class RevitDocumentStore : DocumentModelStore
return;
}
_threadContext
.RunOnMain(() =>
{
//if not the same active document then don't save the current cards to a bad document!
if (!EnsureActiveDocumentIsSame(document))
{
return;
}
using Transaction t = new(document, "Speckle Write State");
t.Start();
using DataStorage ds = GetSettingsDataStorage(document) ?? DataStorage.Create(document);
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
string serializedModels = Serialize();
stateEntity.Set("contents", serializedModels);
using Entity idEntity = new(_idStorageSchema.GetSchema());
idEntity.Set("Id", s_revitDocumentStoreId);
ds.SetEntity(idEntity);
ds.SetEntity(stateEntity);
t.Commit();
})
.FireAndForget();
}
private bool EnsureActiveDocumentIsSame(Document document)
{
var localDoc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
if (localDoc == null)
try
{
return false;
var key = document.ProjectInformation.UniqueId.NotNull();
_jsonCacheManager.UpdateObject(key, modelCardState);
}
catch (Exception ex) when (!ex.IsFatal())
{
var key = document.ProjectInformation.UniqueId.NotNull();
_logger.LogError(ex, "Failed to save model card state for document {DocumentId}", key);
}
return localDoc.Equals(document);
}
protected override void LoadState()
{
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
// POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save)
if (document == null)
{
return;
}
var stateEntity = GetSpeckleEntity(document);
if (stateEntity == null || !stateEntity.IsValid())
{
ClearAndSave();
return;
}
string modelsString = stateEntity.Get<string>("contents");
LoadFromString(modelsString);
}
private DataStorage? GetSettingsDataStorage(Document doc)
{
using FilteredElementCollector collector = new(doc);
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
foreach (Element element in dataStorages)
{
DataStorage dataStorage = (DataStorage)element;
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
if (!settingIdEntity.IsValid())
{
continue;
}
Guid id = settingIdEntity.Get<Guid>("Id");
if (!id.Equals(s_revitDocumentStoreId))
{
continue;
}
return dataStorage;
}
return null;
var key = document.ProjectInformation.UniqueId.NotNull();
var state = _jsonCacheManager.GetObject(key);
LoadFromString(state);
}
private Entity? GetSpeckleEntity(Document? doc)
@@ -2,7 +2,9 @@ using Autodesk.Revit.DB;
using Speckle.Connectors.DUI.Exceptions;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Utils;
using Speckle.Converters.RevitShared.Extensions;
using Speckle.Converters.RevitShared.Helpers;
using Speckle.Sdk;
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
@@ -75,8 +77,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
//this used to throw an exception, but we don't want to fail loudly if the view is not found
return [];
}
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var elementsInView = viewCollector.ToElements();
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
@@ -125,4 +127,52 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
_revitContext = revitContext;
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
}
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
// Below function collects list of ids to exclude from final list.
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
{
var elementsToExclude = new HashSet<ElementId>();
foreach (var element in elements)
{
// check if element is a part
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
{
try
{
// get source element ids from the part
var sourceIds = part.GetSourceElementIds();
if (sourceIds != null)
{
foreach (var sourceId in sourceIds)
{
elementsToExclude.Add(sourceId.HostElementId);
}
}
}
catch (Exception e) when (!e.IsFatal())
{
// silently continue processing other Parts if one fails
// this follows the pattern used elsewhere in the codebase
}
}
}
return elementsToExclude;
}
private IEnumerable<Element> GetFilteredElementsForView(View view)
{
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
var allElements = viewCollector.ToElements();
// parts filtering when view is set to show Parts only (and overwrites allElements)
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
{
var idsToExclude = GetSourceElementIdsToExclude(allElements);
return allElements.Where(e => !idsToExclude.Contains(e.Id));
}
return allElements;
}
}
@@ -22,15 +22,14 @@ public class ClassPropertiesExtractor
{
Dictionary<string, object?> elementPropertiesDict = ExtractElementProperties(element);
// add type specific props not included in parameters.
// so far, no extra props are needed
/*
// type specific properties
switch (element)
{
default:
// area scheme for area elements
case DB.Area area:
elementPropertiesDict.Add("areaScheme", area.AreaScheme?.Name);
break;
}
*/
return elementPropertiesDict;
}
@@ -1,24 +0,0 @@
namespace Speckle.Converters.Rhino.Extensions;
public static class GeometryBaseExtensions
{
/// <summary>
/// Getting translation vector from origin to the Geometry bbox Center (if geometry is far from origin and translation needed)
/// This is needed for some objects, because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
/// </summary>
/// <returns>
/// Vector from origin to Geometry bbox center (if translation needed), otherwise zero-length vector.
/// </returns>
public static bool IsFarFromOrigin(this RG.GeometryBase geometry, out RG.Vector3d vectorToGeometry)
{
var geometryBbox = geometry.GetBoundingBox(false); // 'false' for 'accurate' parameter to accelerate bbox calculation
if (geometryBbox.Min.DistanceTo(RG.Point3d.Origin) > 1e5 || geometryBbox.Max.DistanceTo(RG.Point3d.Origin) > 1e5)
{
vectorToGeometry = new RG.Vector3d(geometryBbox.Center);
return true;
}
vectorToGeometry = new RG.Vector3d();
return false;
}
}
@@ -1,13 +1,8 @@
using Rhino;
using Rhino;
namespace Speckle.Converters.Rhino;
/// <summary>
/// Represents the settings used for Rhino and Grasshopper conversions.
/// </summary>
public record RhinoConversionSettings(
RhinoDoc Document,
string SpeckleUnits,
bool ModelFarFromOrigin,
bool AddVisualizationProperties
);
public record RhinoConversionSettings(RhinoDoc Document, string SpeckleUnits, bool AddVisualizationProperties);
@@ -1,4 +1,4 @@
using Rhino;
using Rhino;
using Speckle.Converters.Common;
using Speckle.InterfaceGenerator;
@@ -13,24 +13,5 @@ public class RhinoConversionSettingsFactory(
public RhinoConversionSettings Current => settingsStore.Current;
public RhinoConversionSettings Create(RhinoDoc document, bool addVisualizationProperties) =>
new(
document,
unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem),
ModelFarFromOrigin(),
addVisualizationProperties
);
/// <summary>
/// Quick check whether any of the objects in the scene might be located too far from origin and cause precision issues during meshing.
/// It prevents 'normal' Rhino models (not too far from origin) from unnecessary Bbox calculations on every object on Send.
/// </summary>
private bool ModelFarFromOrigin()
{
RG.BoundingBox bbox = RhinoDoc.ActiveDoc.Objects.BoundingBox;
if (bbox.Min.DistanceTo(RG.Point3d.Origin) > 1e5 || bbox.Max.DistanceTo(RG.Point3d.Origin) > 1e5)
{
return true;
}
return false;
}
new(document, unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem), addVisualizationProperties);
}
@@ -1,14 +1,13 @@
using Rhino;
using Rhino.DocObjects;
using Speckle.Converters.Common.Objects;
using Speckle.Converters.Rhino.Extensions;
using Speckle.DoubleNumerics;
using Speckle.Sdk.Common.Exceptions;
namespace Speckle.Converters.Rhino.ToSpeckle.Meshing;
public static class DisplayMeshExtractor
{
public static RG.Mesh GetDisplayMesh(RhinoObject obj)
public static RG.Mesh GetDisplayMesh(RhinoObject obj, RhinoDoc doc)
{
// note: unsure this is nice, we get bigger meshes - we should to benchmark (conversion time vs size tradeoffs)
var joinedMesh = new RG.Mesh();
@@ -23,15 +22,15 @@ public static class DisplayMeshExtractor
switch (obj)
{
case BrepObject brep:
joinedMesh.Append(GetGeometryDisplayMesh(brep.BrepGeometry));
joinedMesh.Append(GetGeometryDisplayMesh(brep.BrepGeometry, doc));
break;
case ExtrusionObject extrusion:
joinedMesh.Append(GetGeometryDisplayMesh(extrusion.ExtrusionGeometry.ToBrep()));
joinedMesh.Append(GetGeometryDisplayMesh(extrusion.ExtrusionGeometry.ToBrep(), doc));
break;
case SubDObject subDObject:
if (subDObject.Geometry is RG.SubD subdGeometry)
{
joinedMesh.Append(GetGeometryDisplayMesh(subdGeometry));
joinedMesh.Append(GetGeometryDisplayMesh(subdGeometry, doc));
}
else
{
@@ -49,18 +48,16 @@ public static class DisplayMeshExtractor
/// <summary>
/// Extracting Rhino Mesh from Rhino GeometryBase using specified MeshingParameters settings, e.g. minimumEdgeLength.
/// </summary>
public static RG.Mesh GetGeometryDisplayMesh(RG.GeometryBase geometry, bool highPrecision = false)
public static RG.Mesh GetGeometryDisplayMesh(RG.GeometryBase geometry, RhinoDoc doc)
{
double minEdgeLength = highPrecision ? GetAccurateMinEdgeLegth(geometry) : 0.05;
// declare "renderMeshes" as a separate var, because it needs to be checked for null after each Mesh.Create method
RG.Mesh[] renderMeshes;
var joinedMesh = new RG.Mesh();
RG.MeshingParameters meshParams = RG.MeshingParameters.DocumentCurrentSetting(doc);
switch (geometry)
{
case RG.Brep brep:
renderMeshes = RG.Mesh.CreateFromBrep(brep, new(0.05, minEdgeLength));
renderMeshes = RG.Mesh.CreateFromBrep(brep, meshParams);
break;
case RG.SubD subd:
#pragma warning disable CA2000
@@ -69,7 +66,7 @@ public static class DisplayMeshExtractor
renderMeshes = [subdMesh];
break;
case RG.Extrusion extrusion:
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ToBrep(), new(0.05, minEdgeLength));
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ToBrep(), meshParams);
break;
default:
throw new ConversionException($"Unsupported object for display mesh generation {geometry.GetType().FullName}");
@@ -93,61 +90,17 @@ public static class DisplayMeshExtractor
}
/// <summary>
/// Calculating optimal meshing parameter 'minimumEdgeLength' for the given geometry.
/// </summary>
private static double GetAccurateMinEdgeLegth(RG.GeometryBase geometry)
{
// adjust meshing parameters if Brep edges are too close to the document tolerance
double minEdgeLength = 0.05;
if (geometry is RG.Brep brep && brep.Edges.Any(x => x.GetLength() < minEdgeLength))
{
return 0;
}
return minEdgeLength;
}
/// <summary>
/// Extracting Rhino Mesh and converting to Speckle with the most suitable settings (e.g. moving to origin first, if needed)
/// This is needed because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
/// Extracting Rhino Mesh and converting to Speckle with the most suitable settings
/// </summary>
/// <returns>List of converted Speckle meshes</returns>
public static List<SOG.Mesh> GetSpeckleMeshes(
RG.GeometryBase geometry,
bool modelFarFromOrigin,
string units,
ITypedConverter<RG.Mesh, SOG.Mesh> meshConverter
ITypedConverter<RG.Mesh, SOG.Mesh> meshConverter,
RhinoDoc doc
)
{
RG.GeometryBase geometryToMesh = geometry;
RG.Vector3d? vector = null;
// 1.1. If needed, move geometry to origin
if (modelFarFromOrigin && geometry.IsFarFromOrigin(out RG.Vector3d vectorToGeometry))
{
geometryToMesh = geometry.Duplicate();
geometryToMesh.Transform(RG.Transform.Translation(-vectorToGeometry));
vector = vectorToGeometry;
}
// 1.2. Extract Rhino Mesh
RG.Mesh movedDisplayMesh = GetGeometryDisplayMesh(geometryToMesh, true);
// 2. Convert extracted Mesh to Speckle. We don't move geometry back yet, because 'far from origin' geometry is causing Speckle conversion issues too
List<SOG.Mesh> displayValue = new() { meshConverter.Convert(movedDisplayMesh) };
// 3. Move Speckle geometry back from origin, if translation was applied
MoveSpeckleMeshes(displayValue, vector, units);
RG.Mesh displayMesh = GetGeometryDisplayMesh(geometry, doc);
List<SOG.Mesh> displayValue = new() { meshConverter.Convert(displayMesh) };
return displayValue;
}
public static void MoveSpeckleMeshes(List<SOG.Mesh> displayValue, RG.Vector3d? vectorToGeometry, string units)
{
if (vectorToGeometry is RG.Vector3d vector)
{
Matrix4x4 matrix = new(1, 0, 0, vector.X, 0, 1, 0, vector.Y, 0, 0, 1, vector.Z, 0, 0, 0, 1);
SO.Transform transform = new() { matrix = matrix, units = units };
displayValue.ForEach(x => x.Transform(transform));
}
}
}
@@ -30,9 +30,8 @@ public class BrepToSpeckleConverter : ITypedConverter<RG.Brep, SOG.BrepX>
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
target,
_settingsStore.Current.ModelFarFromOrigin,
_settingsStore.Current.SpeckleUnits,
_meshConverter
_meshConverter,
_settingsStore.Current.Document
);
var bx = new SOG.BrepX()
@@ -30,9 +30,8 @@ public class ExtrusionToSpeckleConverter : ITypedConverter<RG.Extrusion, SOG.Ext
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
target,
_settingsStore.Current.ModelFarFromOrigin,
_settingsStore.Current.SpeckleUnits,
_meshConverter
_meshConverter,
_settingsStore.Current.Document
);
var bx = new SOG.ExtrusionX()
@@ -41,9 +41,8 @@ public class HatchToSpeckleConverter : ITypedConverter<RG.Hatch, SOG.Region>
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
brep,
_settingsStore.Current.ModelFarFromOrigin,
_settingsStore.Current.SpeckleUnits,
_meshConverter
_meshConverter,
_settingsStore.Current.Document
);
return new SOG.Region
@@ -1,7 +1,5 @@
using Speckle.Converters.Common;
using Speckle.Converters.Common.Objects;
using Speckle.Converters.Rhino.Extensions;
using Speckle.Converters.Rhino.ToSpeckle.Meshing;
using Speckle.Sdk.Common.Exceptions;
namespace Speckle.Converters.Rhino.ToSpeckle.Raw;
@@ -34,39 +32,34 @@ public class MeshToSpeckleConverter : ITypedConverter<RG.Mesh, SOG.Mesh>
throw new ValidationException("Cannot convert a mesh with 0 vertices/faces");
}
// Extracting Rhino Mesh and converting to Speckle with the most suitable settings (e.g. moving to origin first, if needed)
// This is needed because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
RG.Mesh meshToConvert = target;
RG.Vector3d? vector = null;
// 1. If needed, move geometry to origin
if (_settingsStore.Current.ModelFarFromOrigin && target.IsFarFromOrigin(out RG.Vector3d vectorToGeometry))
{
meshToConvert = (RG.Mesh)target.Duplicate();
meshToConvert.Transform(RG.Transform.Translation(-vectorToGeometry));
vector = vectorToGeometry;
}
// 2. Convert extracted Mesh to Speckle. We don't move geometry back yet, because 'far from origin' geometry is causing Speckle conversion issues too
SOG.Mesh convertedMesh = ConvertMesh(meshToConvert);
// 3. Move Speckle geometry back from origin, if translation was applied
DisplayMeshExtractor.MoveSpeckleMeshes([convertedMesh], vector, _settingsStore.Current.SpeckleUnits);
SOG.Mesh convertedMesh = ConvertMesh(target);
return convertedMesh;
}
private SOG.Mesh ConvertMesh(RG.Mesh target)
// Rhino common is casting mesh vertex coords from doubles to float: by default the api returns `Vertices` as float instead of double precision
// https://github.com/mcneel/rhino3dm/blob/71c63a8c1c87782a13a1b76c825e4b792b36fd09/src/dotnet/opennurbs/opennurbs_mesh.cs#L6990-L7000
// We need to use double precision or else meshes far from origin will come out distorted: do *not* access `Vertices` directly - use `ToPoint3dArray`
private double[] ConvertDoublePrecisionVertices(RG.Mesh target)
{
var vertexCoordinates = new double[target.Vertices.Count * 3];
RG.Point3d[] vertices = target.Vertices.ToPoint3dArray();
var x = 0;
for (int i = 0; i < target.Vertices.Count; i++)
for (int i = 0; i < vertices.Length; i++)
{
var v = target.Vertices[i];
var v = vertices[i];
vertexCoordinates[x++] = v.X;
vertexCoordinates[x++] = v.Y;
vertexCoordinates[x++] = v.Z;
}
return vertexCoordinates;
}
private SOG.Mesh ConvertMesh(RG.Mesh target)
{
var vertexCoordinates = ConvertDoublePrecisionVertices(target);
List<int> faces = new();
foreach (RG.MeshNgon polygon in target.GetNgonAndFacesEnumerable())
@@ -81,7 +74,7 @@ public class MeshToSpeckleConverter : ITypedConverter<RG.Mesh, SOG.Mesh>
}
var colors = new int[target.VertexColors.Count];
x = 0;
int x = 0;
foreach (var c in target.VertexColors)
{
colors[x++] = c.ToArgb();
@@ -30,9 +30,8 @@ public class SubDToSpeckleConverter : ITypedConverter<RG.SubD, SOG.SubDX>
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
target,
_settingsStore.Current.ModelFarFromOrigin,
_settingsStore.Current.SpeckleUnits,
_meshConverter
_meshConverter,
_settingsStore.Current.Document
);
var bx = new SOG.SubDX()
@@ -115,6 +115,30 @@ public class ConfigBinding : IBinding
}
}
public GlobalConfig? GetGlobalConfig()
{
var rawConfig = _jsonCacheManager.GetObject("global");
if (rawConfig is null)
{
return null;
}
try
{
var config = _serializer.Deserialize<GlobalConfig>(rawConfig);
if (config is null)
{
throw new SerializationException("Failed to deserialize global config");
}
return config;
}
catch (SerializationException)
{
return null;
}
}
public AccountsConfig? GetAccountsConfig()
{
var rawConfig = _jsonCacheManager.GetObject("accounts");
@@ -182,6 +206,11 @@ public class ConnectorConfig
public bool DarkTheme { get; set; } = true;
}
public class GlobalConfig
{
public bool IsUpdateNotificationDisabled { get; set; }
}
public class AccountsConfig
{
public string? UserSelectedAccountId { get; set; }
+1
View File
@@ -968,6 +968,7 @@ Global
Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{4459f2b1-a340-488e-a856-eb2ae9c72ad4}*SharedItemsImports = 5
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5
Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{52666479-5401-47d6-b7ba-d554784439ea}*SharedItemsImports = 13
Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{5422f2c8-1e00-4dae-bb01-65a17be8cd68}*SharedItemsImports = 5
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{5505b953-d434-49ce-8ebd-efd7b3c378f7}*SharedItemsImports = 5
Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{56680ea7-3599-4d88-83a5-b43ba93ac046}*SharedItemsImports = 5
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{56a909ae-6e99-4d4d-a22e-38bdc5528b8e}*SharedItemsImports = 5