From 56f336633ffa6d33dfad4f6c6eab917ba962ec3c Mon Sep 17 00:00:00 2001 From: Luiz Henrique Cassettari Date: Mon, 23 Sep 2024 14:32:53 -0300 Subject: [PATCH] Add `BRepBuilderExtension` --- CHANGELOG.md | 2 +- .../Extensions/BRepBuilderExtension.cs | 244 ++++++++++++++++++ .../Extensions/FaceExtension.cs | 60 ----- 3 files changed, 245 insertions(+), 61 deletions(-) create mode 100644 ricaun.Revit.DB.Shape/Extensions/BRepBuilderExtension.cs delete mode 100644 ricaun.Revit.DB.Shape/Extensions/FaceExtension.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 42d266c..4d39f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.4.0] / 2024-09-23 ### Shapes -- Add `FaceExtension` convert face to solid. +- Add `BRepBuilderExtension` with extension for face and solid. ## [0.3.2] / 2024-06-14 ### Shapes diff --git a/ricaun.Revit.DB.Shape/Extensions/BRepBuilderExtension.cs b/ricaun.Revit.DB.Shape/Extensions/BRepBuilderExtension.cs new file mode 100644 index 0000000..c2494d4 --- /dev/null +++ b/ricaun.Revit.DB.Shape/Extensions/BRepBuilderExtension.cs @@ -0,0 +1,244 @@ +#if NET47_OR_GREATER || NET8_0_OR_GREATER +using Autodesk.Revit.DB; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ricaun.Revit.DB.Shape.Extensions +{ + /// + /// BRepBuilder extension + /// + public static class BRepBuilderExtension + { + /// + /// Create and copy the face to a new solid instance. + /// + /// The face to convert. + /// The material ID to assign to the solid. + /// The converted solid. + /// Failed to create the solid. + /// This method uses to create the solid. + public static Solid CreateSolid(this Face face, ElementId materialId = null) + { + BRepBuilder brepBuilder = new BRepBuilder(BRepType.OpenShell); + + var surface = face.GetSurface(); + var faceIsReversed = !face.OrientationMatchesSurfaceOrientation; + BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(surface, null), faceIsReversed); + + var curveLoops = face.GetEdgesAsCurveLoops(); + foreach (var curveLoop in curveLoops) + { + BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId); + var curves = curveLoop.OfType(); + + var edgeIds = curves.Select(curve => + { + BRepBuilderEdgeGeometry edgeId = BRepBuilderEdgeGeometry.Create(curve); + BRepBuilderGeometryId geoEdgeId = brepBuilder.AddEdge(edgeId); + return geoEdgeId; + }).ToList(); + + foreach (var edgeId in edgeIds) + { + brepBuilder.AddCoEdge(loopId, edgeId, false); + } + + brepBuilder.FinishLoop(loopId); + } + + brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId); + + if (materialId != null) + brepBuilder.SetFaceMaterialId(faceId, materialId); + + brepBuilder.FinishFace(faceId); + + var outcome = brepBuilder.Finish(); + + if (outcome == BRepBuilderOutcome.Failure) + throw new InvalidOperationException("Failed to create the solid."); + + return brepBuilder.GetResult(); + } + + /// + /// Create and copy the solid to a new solid instance. + /// + /// The solid to convert. + /// The material ID to assign to the solid. + /// The converted solid. + /// Failed to create the solid. + /// This method uses to create the solid. + public static Solid CreateSolid(this Solid solid, ElementId materialId = null) + { + BRepBuilder brepBuilder = new BRepBuilder(BRepType.Solid); + + var bRepBuilderGeometryId = new Dictionary(); + var solidEdges = solid.Edges.OfType(); + + foreach (var edge in solidEdges) + { + var curve = edge.AsCurve(); + BRepBuilderEdgeGeometry edgeId = BRepBuilderEdgeGeometry.Create(curve); + BRepBuilderGeometryId geoEdgeId = brepBuilder.AddEdge(edgeId); + bRepBuilderGeometryId.Add(curve, geoEdgeId); + } + + foreach (Face face in solid.Faces.OfType()) + { + var surface = face.GetSurface(); + + if (BRepBuilder.IsPermittedSurfaceType(surface) == false) + continue; + + var faceIsReversed = !face.OrientationMatchesSurfaceOrientation; + BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(surface, null), faceIsReversed); + + var curveLoops = face.GetEdgesAsCurveLoops(); + foreach (var curveLoop in curveLoops) + { + BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId); + foreach (var curveFace in curveLoop.OfType()) + { + var edgeCurve = bRepBuilderGeometryId.Keys.FirstOrDefault(e => IsAlmostEqualTo(e, curveFace)); + var edgeId = bRepBuilderGeometryId[edgeCurve]; + + var edgeIsReversed = IsAlmostEqualTo(edgeCurve, curveFace, false); + + brepBuilder.AddCoEdge(loopId, edgeId, !edgeIsReversed); + } + brepBuilder.FinishLoop(loopId); + } + + brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId); + + if (materialId != null) + brepBuilder.SetFaceMaterialId(faceId, materialId); + + brepBuilder.FinishFace(faceId); + } + + var outcome = brepBuilder.Finish(); + + if (outcome == BRepBuilderOutcome.Failure) + throw new InvalidOperationException("Failed to create the solid."); + + return brepBuilder.GetResult(); + } + + /// + /// Create and copy the solid to a new solid instance. + /// + /// The solid to convert. + /// The material ID to assign to the solid. + /// The converted solid. + /// Failed to create the solid. + /// This method uses to create the solid. + public static Solid CreateOpenShell(this Solid solid, ElementId materialId = null) + { + BRepBuilder brepBuilder = new BRepBuilder(BRepType.OpenShell); + + foreach (Face face in solid.Faces.OfType()) + { + var surface = face.GetSurface(); + var faceIsReversed = !face.OrientationMatchesSurfaceOrientation; + BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(surface, null), faceIsReversed); + + var curveLoops = face.GetEdgesAsCurveLoops(); + foreach (var curveLoop in curveLoops) + { + BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId); + var curves = curveLoop.OfType(); + + var edgeIds = curves.Select(curve => + { + BRepBuilderEdgeGeometry edgeId = BRepBuilderEdgeGeometry.Create(curve); + BRepBuilderGeometryId geoEdgeId = brepBuilder.AddEdge(edgeId); + return geoEdgeId; + }).ToList(); + + foreach (var edgeId in edgeIds) + { + brepBuilder.AddCoEdge(loopId, edgeId, false); + } + + brepBuilder.FinishLoop(loopId); + } + + brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId); + + if (materialId != null) + brepBuilder.SetFaceMaterialId(faceId, materialId); + + + brepBuilder.FinishFace(faceId); + } + + var outcome = brepBuilder.Finish(); + + if (outcome == BRepBuilderOutcome.Failure) + throw new InvalidOperationException("Failed to create the solid."); + + return brepBuilder.GetResult(); + } + + internal static bool IsAlmostEqualTo(Curve curve1, Curve curve2, bool similarReversed = true) + { + if (curve1.GetType() != curve2.GetType()) + return false; + + if (curve1.ApproximateLength.IsAlmostEqualTo(curve2.ApproximateLength) == false) + return false; + + var t1 = curve1.Tessellate(); + var t2 = curve2.Tessellate(); + + if (t1.Count != t2.Count) + return false; + + var equalPointFirst = (t1[0] - t2[0]).IsZeroLength(); + var equalPointLast = (t1[0] - t2[t2.Count - 1]).IsZeroLength(); + + if ((equalPointFirst || equalPointLast) == false) + return false; + + if (similarReversed == false) + { + return equalPointFirst; + } + + return true; + } + + /// + /// Tolerance of the Almost + /// + private const double Tolerance = 1.0E-9; + + /// + /// is almost Zero by + /// + /// + /// + /// + internal static bool IsAlmostZero(this double a, double tolerance = Tolerance) + { + return tolerance > Math.Abs(a); + } + + /// + /// is almost Equal to by + /// + /// + /// + /// + /// + internal static bool IsAlmostEqualTo(this double a, double b, double tolerance = Tolerance) + { + return IsAlmostZero(b - a, tolerance); + } + } +} +#endif \ No newline at end of file diff --git a/ricaun.Revit.DB.Shape/Extensions/FaceExtension.cs b/ricaun.Revit.DB.Shape/Extensions/FaceExtension.cs deleted file mode 100644 index ec30add..0000000 --- a/ricaun.Revit.DB.Shape/Extensions/FaceExtension.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Autodesk.Revit.DB; -using System.Linq; - -namespace ricaun.Revit.DB.Shape.Extensions -{ - /// - /// Face extension - /// - public static class FaceExtension - { -#if NET47_OR_GREATER || NET8_0_OR_GREATER - /// - /// Converts a Revit face to a solid. - /// - /// The face to convert. - /// The material ID to assign to the solid. - /// The converted solid. - public static Solid ToSolid(this Face face, ElementId materialId = null) - { - BRepBuilder brepBuilder = new BRepBuilder(BRepType.OpenShell); - - var surface = face.GetSurface(); - var faceIsReversed = !face.OrientationMatchesSurfaceOrientation; - BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(surface, null), faceIsReversed); - - var curveLoops = face.GetEdgesAsCurveLoops(); - foreach (var curveLoop in curveLoops) - { - BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId); - var curves = curveLoop.OfType(); - - var edgeIds = curves.Select(curve => - { - BRepBuilderEdgeGeometry edgeId = BRepBuilderEdgeGeometry.Create(curve); - BRepBuilderGeometryId geoEdgeId = brepBuilder.AddEdge(edgeId); - return geoEdgeId; - }).ToList(); - - foreach (var edgeId in edgeIds) - { - brepBuilder.AddCoEdge(loopId, edgeId, false); - } - - brepBuilder.FinishLoop(loopId); - } - - brepBuilder.SetFaceMaterialId(faceId, face.MaterialElementId); - - if (materialId != null) - brepBuilder.SetFaceMaterialId(faceId, materialId); - - brepBuilder.FinishFace(faceId); - - var outcome = brepBuilder.Finish(); - - return brepBuilder.GetResult(); - } -#endif - } -}