diff --git a/src/RhinoInside.Revit.External/DB/Extensions/Mesh.cs b/src/RhinoInside.Revit.External/DB/Extensions/Mesh.cs index e55b9fbcb..a20062e67 100644 --- a/src/RhinoInside.Revit.External/DB/Extensions/Mesh.cs +++ b/src/RhinoInside.Revit.External/DB/Extensions/Mesh.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Linq; using Autodesk.Revit.DB; namespace RhinoInside.Revit.External.DB.Extensions @@ -207,5 +210,125 @@ public static double ComputeSurfaceArea(this Mesh mesh) return area.Value * 0.5; } #endif + + #region Naked Edges + public static bool TryGetNakedEdges(this Mesh mesh, out PolyLine[] edges) + { + if (mesh.NumTriangles > 0) + { + var polylines = JoinLines(GetNakedEdges(mesh)); + if (polylines.Count > 0) + { + edges = new PolyLine[polylines.Count]; + + var vertices = mesh.Vertices; + var index = 0; + foreach (var pline in polylines) + edges[index++] = PolyLine.Create(pline.Select(x => vertices[x]).ToArray()); + } + else edges = Array.Empty(); // A totally closed mesh with no naked edges. + + return true; + } + + edges = Array.Empty(); + return false; + } + + static ISet<(int V0, int V1)> GetNakedEdges(this Mesh mesh) + { + static (int V0, int V1) Edge(int v0, int v1) => v0 < v1 ? (v0, v1) : (v1, v0); + var edges = new SortedSet<(int V0, int V1)>(); + + var numTriangles = mesh.NumTriangles; + for (int t = 0; t < numTriangles; ++t) + { + var triangle = mesh.get_Triangle(t); + var v0 = (int) triangle.get_Index(0); + var v1 = (int) triangle.get_Index(1); + var v2 = (int) triangle.get_Index(2); + + var edge0 = Edge(v0, v1); + if (!edges.Remove(edge0)) edges.Add(edge0); + var edge1 = Edge(v1, v2); + if (!edges.Remove(edge1)) edges.Add(edge1); + var edge2 = Edge(v2, v0); + if (!edges.Remove(edge2)) edges.Add(edge2); + } + + return edges; + } + + static List> JoinLines(ISet<(int A, int B)> segments) + { + var adjacency = new Dictionary>(); + { + foreach (var segment in segments) + { + if (!adjacency.ContainsKey(segment.A)) adjacency[segment.A] = new List<(int A, int B)>(); + if (!adjacency.ContainsKey(segment.B)) adjacency[segment.B] = new List<(int A, int B)>(); + + adjacency[segment.A].Add(segment); + adjacency[segment.B].Add(segment); + } + } + + var polylines = new List>(); + + // Open polylines + { + foreach (int start in adjacency.Where(x => x.Value.Count != 2).Select(x => x.Key)) + { + if (adjacency[start].All(segments.Contains)) + continue; + + var poly = new List(); + + int current = start; + while (true) + { + poly.Add(current); + + var next = adjacency[current].FirstOrDefault(segments.Contains); + if (next.Equals(default) && !segments.Contains(next)) + break; + + segments.Remove(next); + + current = (next.A == current) ? next.B : next.A; + if (adjacency[current].Count != 2) + { + poly.Add(current); + break; + } + } + + polylines.Add(poly); + } + } + + // Closed polylines + { + while (segments.Count > 0) + { + var first = segments.First(); + segments.Remove(first); + int start = first.A; + int current = first.B; + var loop = new List { start, current }; + while (current != start) + { + var next = adjacency[current].First(segments.Contains); + segments.Remove(next); + current = (next.A == current) ? next.B : next.A; + loop.Add(current); + } + polylines.Add(loop); + } + } + + return polylines; + } + #endregion } } diff --git a/src/RhinoInside.Revit.External/Extensions/RhinoCommon.cs b/src/RhinoInside.Revit.External/Extensions/RhinoCommon.cs index b43e9674e..fb0645b55 100644 --- a/src/RhinoInside.Revit.External/Extensions/RhinoCommon.cs +++ b/src/RhinoInside.Revit.External/Extensions/RhinoCommon.cs @@ -218,6 +218,28 @@ public static BoundingBox GetBoundingBox(this Box value, Transform xform) return new BoundingBox(value.GetCorners(), xform); } + + public static IEnumerable ToPlanes(this Box box) + { + var plane = box.Plane; + yield return new Plane(plane.PointAt(0.0, 0.0, box.Z.T0), plane.YAxis, plane.XAxis); + yield return new Plane(plane.PointAt(0.0, box.Y.T0, 0.0), plane.XAxis, plane.ZAxis); + yield return new Plane(plane.PointAt(box.X.T0, 0.0, 0.0), plane.ZAxis, plane.YAxis); + yield return new Plane(plane.PointAt(box.X.T1, 0.0, 0.0), plane.YAxis, plane.ZAxis); + yield return new Plane(plane.PointAt(0.0, box.Y.T1, 0.0), plane.ZAxis, plane.XAxis); + yield return new Plane(plane.PointAt(0.0, 0.0, box.Z.T1), plane.XAxis, plane.YAxis); + } + + public static IEnumerable ToSurfaces(this Box box) + { + var plane = box.Plane; + yield return new PlaneSurface(new Plane(plane.PointAt(0.0, 0.0, box.Z.T0), plane.YAxis, plane.XAxis), box.Y, box.X); + yield return new PlaneSurface(new Plane(plane.PointAt(0.0, box.Y.T0, 0.0), plane.XAxis, plane.ZAxis), box.X, box.Z); + yield return new PlaneSurface(new Plane(plane.PointAt(box.X.T0, 0.0, 0.0), plane.ZAxis, plane.YAxis), box.Z, box.Y); + yield return new PlaneSurface(new Plane(plane.PointAt(box.X.T1, 0.0, 0.0), plane.YAxis, plane.ZAxis), box.Y, box.Z); + yield return new PlaneSurface(new Plane(plane.PointAt(0.0, box.Y.T1, 0.0), plane.ZAxis, plane.XAxis), box.Z, box.X); + yield return new PlaneSurface(new Plane(plane.PointAt(0.0, 0.0, box.Z.T1), plane.XAxis, plane.YAxis), box.X, box.Y); + } } static class ExtrusionExtension @@ -1537,11 +1559,13 @@ public static bool Scale(this RhinoViewport viewport, double scaleFactor) projection.SetFrustum(left * scaleFactor, right * scaleFactor, bottom * scaleFactor, top * scaleFactor, near * scaleFactor, far * scaleFactor); } - if (!viewport.SetViewProjection(projection, updateTargetLocation: true)) + if (!viewport.SetViewProjection(projection, updateTargetLocation: false)) return false; var cplane = viewport.GetConstructionPlane(); - cplane.Plane.Transform(scaleTransform); + var location = cplane.Plane; + location.Transform(scaleTransform); + cplane.Plane = location; cplane.GridSpacing *= scaleFactor; cplane.SnapSpacing *= scaleFactor; viewport.SetConstructionPlane(cplane); diff --git a/src/RhinoInside.Revit/Convert/Display/PreviewConverter.cs b/src/RhinoInside.Revit/Convert/Display/PreviewConverter.cs index af2401ee2..2ef974a92 100644 --- a/src/RhinoInside.Revit/Convert/Display/PreviewConverter.cs +++ b/src/RhinoInside.Revit/Convert/Display/PreviewConverter.cs @@ -222,6 +222,16 @@ this IEnumerable geometries } break; } + case ARDB.Mesh mesh: + { + if (mesh.TryGetNakedEdges(out var edges)) + { + foreach (var edge in edges) + yield return edge.ToPolylineCurve(); + } + + break; + } case ARDB.Solid solid: { if (solid.Faces.IsEmpty) diff --git a/src/RhinoInside.Revit/Convert/Geometry/MeshDecoder.cs b/src/RhinoInside.Revit/Convert/Geometry/MeshDecoder.cs index cf9d32133..5fb3d8759 100644 --- a/src/RhinoInside.Revit/Convert/Geometry/MeshDecoder.cs +++ b/src/RhinoInside.Revit/Convert/Geometry/MeshDecoder.cs @@ -25,7 +25,6 @@ internal static Mesh FromRawMesh(Mesh mesh, double scaleFactor) } mesh.Vertices.Align(2.0 * GeometryTolerance.Model.VertexTolerance); // MeshEncoder.ShortEdgeTolerance in model units - mesh.Ngons.AddPlanarNgons(GeometryTolerance.Model.VertexTolerance, 4, 2, true); return mesh; } } diff --git a/src/RhinoInside.Revit/Convert/Geometry/Raw/RawDecoder.cs b/src/RhinoInside.Revit/Convert/Geometry/Raw/RawDecoder.cs index 1081b62fb..fe6e267a3 100644 --- a/src/RhinoInside.Revit/Convert/Geometry/Raw/RawDecoder.cs +++ b/src/RhinoInside.Revit/Convert/Geometry/Raw/RawDecoder.cs @@ -302,7 +302,7 @@ public static Curve ToRhino(ARDB.Curve curve) public static PolylineCurve ToRhino(ARDB.PolyLine polyline) { - return new PolylineCurve(polyline.GetCoordinates().Select(x => AsPoint3d(x))); + return new PolylineCurve(polyline.GetCoordinates().Select(AsPoint3d)); } #endregion