From 4829135a6ff6a2d6596fbd8c863e57f9d5cff7cf Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 12:03:31 -0700 Subject: [PATCH 01/15] Don't create individual tesselators. --- .../Tessellation/SolidFaceTessAdapter.cs | 48 +++++++++++++++++++ .../SolidTessellationTargetProvider.cs | 5 +- .../src/Geometry/Tessellation/Tessellation.cs | 4 +- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs b/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs index 12c7925c8..e6ff19d69 100644 --- a/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs +++ b/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs @@ -3,6 +3,54 @@ namespace Elements.Geometry.Tessellation { + /// + /// An object which provides a tessellation for a solid. + /// + internal class SolidTessAdapter : ITessAdapter + { + private readonly Solid solid; + private readonly Transform transform; + + /// + /// Construct a solid tessellation adapter. + /// + /// + /// + public SolidTessAdapter(Solid solid, Transform transform = null) + { + this.solid = solid; + this.transform = transform; + } + + public Tess GetTess() + { + var tess = new Tess + { + NoEmptyPolygons = true + }; + foreach (var face in solid.Faces.Values) + { + tess.AddContour(face.Outer.ToContourVertexArray(transform)); + + if (face.Inner != null) + { + foreach (var loop in face.Inner) + { + tess.AddContour(loop.ToContourVertexArray(transform)); + } + } + } + + tess.Tessellate(WindingRule.Positive, ElementType.Polygons, 3); + return tess; + } + + public bool RequiresTessellation() + { + return true; + } + } + /// /// An object which provides a tessellation for a solid face. /// diff --git a/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs b/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs index 9d246ade7..9834ed03c 100644 --- a/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs +++ b/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs @@ -27,10 +27,7 @@ public SolidTesselationTargetProvider(Solid solid, Transform transform = null) /// public IEnumerable GetTessellationTargets() { - foreach (var f in solid.Faces.Values) - { - yield return new SolidFaceTessAdapter(f, transform); - } + yield return new SolidTessAdapter(solid, transform); } } } \ No newline at end of file diff --git a/Elements/src/Geometry/Tessellation/Tessellation.cs b/Elements/src/Geometry/Tessellation/Tessellation.cs index 2087b48db..f66ab0f04 100644 --- a/Elements/src/Geometry/Tessellation/Tessellation.cs +++ b/Elements/src/Geometry/Tessellation/Tessellation.cs @@ -25,7 +25,7 @@ internal static void Tessellate(IEnumerable provide { foreach (var target in provider.GetTessellationTargets()) { - TessellatePolygon(target.GetTess(), buffers, allVertices, mergeVertices); + PackTessellationIntoBuffers(target.GetTess(), buffers, allVertices, mergeVertices); } } @@ -43,7 +43,7 @@ internal static void Tessellate(IEnumerable provide } } - private static void TessellatePolygon(Tess tess, + private static void PackTessellationIntoBuffers(Tess tess, IGraphicsBuffers buffers, List<(Vector3 position, Vector3 normal, UV uv, Color color)> allVertices, bool mergeVertices = false) From 488f564cf069230a2b2050f970c657672d61af77 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 12:53:02 -0700 Subject: [PATCH 02/15] Don't allocate a rotated transform during solid operations. --- Elements/src/Geometry/Curve.cs | 9 ++++++++- Elements/src/Geometry/Interfaces/ICurve.cs | 3 ++- Elements/src/Geometry/Kernel.cs | 4 ++-- Elements/src/Geometry/Solids/Solid.cs | 6 ++++-- Elements/src/Geometry/Solids/Sweep.cs | 4 +--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Elements/src/Geometry/Curve.cs b/Elements/src/Geometry/Curve.cs index dcd5dce44..4f97e61de 100644 --- a/Elements/src/Geometry/Curve.cs +++ b/Elements/src/Geometry/Curve.cs @@ -27,14 +27,21 @@ public abstract partial class Curve : ICurve, ITransformable /// /// The offset parameter from the start of the curve. /// The offset parameter from the end of the curve. + /// An additional rotation of the frame at each point. /// A collection of transforms. - public virtual Transform[] Frames(double startSetback = 0.0, double endSetback = 0.0) + public virtual Transform[] Frames(double startSetback = 0.0, + double endSetback = 0.0, + double additionalRotation = 0.0) { var parameters = GetSampleParameters(startSetback, endSetback); var transforms = new Transform[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { transforms[i] = TransformAt(parameters[i]); + if (additionalRotation != 0.0) + { + transforms[i].Rotate(transforms[i].ZAxis, additionalRotation); + } } return transforms; } diff --git a/Elements/src/Geometry/Interfaces/ICurve.cs b/Elements/src/Geometry/Interfaces/ICurve.cs index b92669b94..518aed1ee 100644 --- a/Elements/src/Geometry/Interfaces/ICurve.cs +++ b/Elements/src/Geometry/Interfaces/ICurve.cs @@ -29,8 +29,9 @@ public interface ICurve /// /// The offset from the start of the ICurve. /// The offset from the end of the ICurve. + /// An additional rotation of the frame at each point. /// A collection of Transforms. - Transform[] Frames(double startSetback = 0.0, double endSetback = 0.0); + Transform[] Frames(double startSetback = 0.0, double endSetback = 0.0, double additionalRotation = 0.0); /// /// Get the bounding box of this curve. diff --git a/Elements/src/Geometry/Kernel.cs b/Elements/src/Geometry/Kernel.cs index ebd588fa3..cd5ffeb81 100644 --- a/Elements/src/Geometry/Kernel.cs +++ b/Elements/src/Geometry/Kernel.cs @@ -29,9 +29,9 @@ public static Kernel Instance /// Create a sweep along a curve. /// /// A solid. - public Solid CreateSweepAlongCurve(Profile profile, Curve curve, double startSetback, double endSetback) + public Solid CreateSweepAlongCurve(Profile profile, Curve curve, double startSetback, double endSetback, double profileRotation) { - return Solid.SweepFaceAlongCurve(profile.Perimeter, profile.Voids, curve, startSetback, endSetback); + return Solid.SweepFaceAlongCurve(profile.Perimeter, profile.Voids, curve, startSetback, endSetback, profileRotation); } /// diff --git a/Elements/src/Geometry/Solids/Solid.cs b/Elements/src/Geometry/Solids/Solid.cs index b72b4213e..536f1f5a4 100644 --- a/Elements/src/Geometry/Solids/Solid.cs +++ b/Elements/src/Geometry/Solids/Solid.cs @@ -105,12 +105,14 @@ public static Solid SweepFace(Polygon perimeter, /// The curve along which to sweep. /// The setback distance of the sweep from the start of the curve. /// The setback distance of the sweep from the end of the curve. + /// The rotation of the profile. /// A solid. public static Solid SweepFaceAlongCurve(Polygon perimeter, IList holes, ICurve curve, double startSetback = 0, - double endSetback = 0) + double endSetback = 0, + double profileRotation = 0) { var solid = new Solid(); @@ -130,7 +132,7 @@ public static Solid SweepFaceAlongCurve(Polygon perimeter, var ssb = startSetback / l; var esb = endSetback / l; - var transforms = curve.Frames(ssb, esb); + var transforms = curve.Frames(ssb, esb, profileRotation); if (curve is Polygon) { diff --git a/Elements/src/Geometry/Solids/Sweep.cs b/Elements/src/Geometry/Solids/Sweep.cs index 1e0f641b4..7301a0182 100644 --- a/Elements/src/Geometry/Solids/Sweep.cs +++ b/Elements/src/Geometry/Solids/Sweep.cs @@ -111,9 +111,7 @@ public double ProfileRotation private void UpdateGeometry() { - var profileTrans = new Transform(); - profileTrans.Rotate(profileTrans.ZAxis, this.ProfileRotation); - this._solid = Kernel.Instance.CreateSweepAlongCurve(profileTrans.OfProfile(this._profile), this._curve, this._startSetback, this._endSetback); + this._solid = Kernel.Instance.CreateSweepAlongCurve(this._profile, this._curve, this._startSetback, this._endSetback, this._profileRotation); } } } \ No newline at end of file From b0b656d4d882a2db8db4b643e029c74112fdc0fb Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 12:53:16 -0700 Subject: [PATCH 03/15] Don't validate for known polygon types. --- Elements/src/Geometry/Bezier.cs | 10 ++++++-- Elements/src/Geometry/Circle.cs | 2 +- Elements/src/Geometry/Polygon.cs | 19 ++++++++++++-- Elements/src/Geometry/Polygons.cs | 12 ++++----- Elements/src/Geometry/Polyline.cs | 25 ++++++++++++++++--- .../Geometry/Profiles/ParametricProfile.cs | 2 +- .../Geometry/Profiles/WideFlangeProfile.cs | 2 +- Elements/src/Validators/Validators.cs | 8 +++--- 8 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Elements/src/Geometry/Bezier.cs b/Elements/src/Geometry/Bezier.cs index 00a3f1c94..19c36a5f4 100644 --- a/Elements/src/Geometry/Bezier.cs +++ b/Elements/src/Geometry/Bezier.cs @@ -74,13 +74,19 @@ public override BBox3 Bounds() /// /// /// - /// - public override Transform[] Frames(double startSetback = 0, double endSetback = 0) + /// + public override Transform[] Frames(double startSetback = 0, + double endSetback = 0, + double additionalRotation = 0.0) { var transforms = new Transform[_samples + 1]; for (var i = 0; i <= _samples; i++) { transforms[i] = TransformAt(i * 1.0 / _samples); + if (additionalRotation != 0.0) + { + transforms[i].Rotate(transforms[i].ZAxis, additionalRotation); + } } return transforms; } diff --git a/Elements/src/Geometry/Circle.cs b/Elements/src/Geometry/Circle.cs index 46805a5e9..3a1e2d296 100644 --- a/Elements/src/Geometry/Circle.cs +++ b/Elements/src/Geometry/Circle.cs @@ -34,7 +34,7 @@ public Polygon ToPolygon(int divisions = 10) { pts[i] = this.PointAt((double)i / (double)divisions); } - return new Polygon(pts); + return new Polygon(pts, true); } } } \ No newline at end of file diff --git a/Elements/src/Geometry/Polygon.cs b/Elements/src/Geometry/Polygon.cs index 1d161d772..058ee4b2e 100644 --- a/Elements/src/Geometry/Polygon.cs +++ b/Elements/src/Geometry/Polygon.cs @@ -28,8 +28,9 @@ public partial class Polygon : Polyline /// Construct a polygon. /// /// A collection of vertex locations. + /// Should self-intersection testing be disabled? [JsonConstructor] - public Polygon(IList @vertices) : base(vertices) + public Polygon(IList @vertices, bool disableValidation = false) : base(vertices, disableValidation) { _plane = Plane(); } @@ -79,6 +80,13 @@ protected override void ValidateVertices() /// The vertices of the polygon. public Polygon(params Vector3[] vertices) : this(new List(vertices)) { } + /// + /// Construct a polygon from points. + /// + /// Should self-intersection testing be disabled? + /// The vertices of the polygon. + public Polygon(bool disableValidation, params Vector3[] vertices) : this(new List(vertices), disableValidation) { } + /// /// Construct a transformed copy of this Polygon. /// @@ -1975,7 +1983,10 @@ public Polygon CollinearPointsRemoved() /// /// /// - public override Transform[] Frames(double startSetback, double endSetback) + /// + public override Transform[] Frames(double startSetback = 0.0, + double endSetback = 0.0, + double additionalRotation = 0.0) { // Create an array of transforms with the same // number of items as the vertices. @@ -1988,6 +1999,10 @@ public override Transform[] Frames(double startSetback, double endSetback) { var a = this.Vertices[i]; result[i] = CreateMiterTransform(i, a, up); + if (additionalRotation != 0.0) + { + result[i].Rotate(result[i].ZAxis, additionalRotation); + } } return result; } diff --git a/Elements/src/Geometry/Polygons.cs b/Elements/src/Geometry/Polygons.cs index fc2e1db18..d2e4f368c 100644 --- a/Elements/src/Geometry/Polygons.cs +++ b/Elements/src/Geometry/Polygons.cs @@ -21,7 +21,7 @@ public static Polygon Rectangle(double width, double height) var c = new Vector3(width / 2, height / 2); var d = new Vector3(-width / 2, height / 2); - return new Polygon(new[] { a, b, c, d }); + return new Polygon(true, new[] { a, b, c, d }); } /// @@ -37,7 +37,7 @@ public static Polygon Rectangle(Vector3 min, Vector3 max) var c = max; var d = new Vector3(min.X, max.Y); - return new Polygon(new[] { a, b, c, d }); + return new Polygon(true, new[] { a, b, c, d }); } /// @@ -55,7 +55,7 @@ public static Polygon Circle(double radius = 1.0, int divisions = 10) var t = i * (Math.PI * 2 / divisions); verts[i] = new Vector3(radius * Math.Cos(t), radius * Math.Sin(t)); } - return new Polygon(verts); + return new Polygon(verts, true); } /// @@ -84,7 +84,7 @@ public static Polygon Ngon(int sides, double radius = 0.5) var t = i * (Math.PI * 2 / sides); verts[i] = new Vector3(radius * Math.Cos(t), radius * Math.Sin(t)); } - return new Polygon(verts); + return new Polygon(verts, true); } /// @@ -111,7 +111,7 @@ public static Polygon L(double width, double length, double thickness) var d = new Vector3(thickness, thickness, 0); var e = new Vector3(thickness, length, 0); var f = new Vector3(0, length, 0); - return new Polygon(new[] { a, b, c, d, e, f }); + return new Polygon(true, new[] { a, b, c, d, e, f }); } /// @@ -139,7 +139,7 @@ public static Polygon Star(double outerRadius, double innerRadius, int points) verts.Add(c1.PointAt(t)); } } - return new Polygon(verts); + return new Polygon(verts, true); } } } \ No newline at end of file diff --git a/Elements/src/Geometry/Polyline.cs b/Elements/src/Geometry/Polyline.cs index 1e037aad7..1a0c5123b 100644 --- a/Elements/src/Geometry/Polyline.cs +++ b/Elements/src/Geometry/Polyline.cs @@ -32,12 +32,13 @@ public class Polyline : Curve, IEquatable /// Construct a polyline. /// /// A collection of vertex locations. + /// Should self intersection testing be disabled? [JsonConstructor] - public Polyline(IList @vertices) : base() + public Polyline(IList @vertices, bool disableValidation = false) : base() { this.Vertices = @vertices; - if (!Validator.DisableValidationOnConstruction) + if (!Validator.DisableValidationOnConstruction && !disableValidation) { ValidateVertices(); } @@ -63,6 +64,17 @@ public Polyline(params Vector3[] vertices) : this(new List(vertices)) } + /// + /// Construct a polyline from points. This is a convenience constructor + /// that can be used like this: `new Polyline((0,0,0), (10,0,0), (10,10,0))` + /// + /// Should self intersection testing be disabled? + /// The vertices of the polyline. + public Polyline(bool disableValidation, params Vector3[] vertices) : this(new List(vertices), disableValidation) + { + + } + /// /// Calculate the length of the polygon. /// @@ -353,7 +365,10 @@ protected virtual Vector3[] NormalsAtVertices() /// /// /// - public override Transform[] Frames(double startSetback, double endSetback) + /// + public override Transform[] Frames(double startSetback = 0.0, + double endSetback = 0.0, + double additionalRotation = 0.0) { var normals = this.NormalsAtVertices(); @@ -363,6 +378,10 @@ public override Transform[] Frames(double startSetback, double endSetback) { var a = this.Vertices[i]; result[i] = CreateOrthogonalTransform(i, a, normals[i]); + if (additionalRotation != 0.0) + { + result[i].Rotate(result[i].ZAxis, additionalRotation); + } } return result; } diff --git a/Elements/src/Geometry/Profiles/ParametricProfile.cs b/Elements/src/Geometry/Profiles/ParametricProfile.cs index e3248fcac..d452a7bc9 100644 --- a/Elements/src/Geometry/Profiles/ParametricProfile.cs +++ b/Elements/src/Geometry/Profiles/ParametricProfile.cs @@ -73,7 +73,7 @@ public ParametricProfile(List perimeterVectorExpressions, private string CompilePolygonScriptFromExpressions(List expressions) { var sb = new StringBuilder(); - sb.Append("new Polygon(new[]{"); + sb.Append("new Polygon(true, new[]{"); foreach (var expr in expressions) { diff --git a/Elements/src/Geometry/Profiles/WideFlangeProfile.cs b/Elements/src/Geometry/Profiles/WideFlangeProfile.cs index bf01d7490..49b7bffcb 100644 --- a/Elements/src/Geometry/Profiles/WideFlangeProfile.cs +++ b/Elements/src/Geometry/Profiles/WideFlangeProfile.cs @@ -227,7 +227,7 @@ private static Polygon CreateProfile(double bf, var l = new Vector3(o.X + width / 2 + horizontalOffset, o.Y + height / 2 - thicknessFlange + verticalOffset); var m = new Vector3(o.X + width / 2 + horizontalOffset, o.Y + height / 2 + verticalOffset); - return new Polygon(new[] { a, b, c, e, f, g, h, i, j, k, l, m }); + return new Polygon(false, new[] { a, b, c, e, f, g, h, i, j, k, l, m }); } } } \ No newline at end of file diff --git a/Elements/src/Validators/Validators.cs b/Elements/src/Validators/Validators.cs index f31bd88c1..189f83f8c 100644 --- a/Elements/src/Validators/Validators.cs +++ b/Elements/src/Validators/Validators.cs @@ -339,9 +339,11 @@ public void PostConstruct(object obj) private void UpdateGeometry(Sweep sweep) { - var profileTrans = new Transform(); - profileTrans.Rotate(profileTrans.ZAxis, sweep.ProfileRotation); - sweep._solid = Kernel.Instance.CreateSweepAlongCurve(profileTrans.OfProfile(sweep.Profile), sweep.Curve, sweep.StartSetback, sweep.EndSetback); + sweep._solid = Kernel.Instance.CreateSweepAlongCurve(sweep.Profile, + sweep.Curve, + sweep.StartSetback, + sweep.EndSetback, + sweep.ProfileRotation); } public void PreConstruct(object[] args) From 7dba1ecbc507d5f11036481c800187cee4e30ebc Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 14:03:41 -0700 Subject: [PATCH 04/15] Don't mess with existing serialization constructors. --- Elements/src/Geometry/Polygon.cs | 11 ++++++++++- Elements/src/Geometry/Polyline.cs | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Elements/src/Geometry/Polygon.cs b/Elements/src/Geometry/Polygon.cs index 058ee4b2e..dfe9f5a18 100644 --- a/Elements/src/Geometry/Polygon.cs +++ b/Elements/src/Geometry/Polygon.cs @@ -28,8 +28,17 @@ public partial class Polygon : Polyline /// Construct a polygon. /// /// A collection of vertex locations. - /// Should self-intersection testing be disabled? [JsonConstructor] + public Polygon(IList @vertices) : base(vertices) + { + _plane = Plane(); + } + + /// + /// Construct a polygon. + /// + /// A collection of vertex locations. + /// Should self-intersection testing be disabled? public Polygon(IList @vertices, bool disableValidation = false) : base(vertices, disableValidation) { _plane = Plane(); diff --git a/Elements/src/Geometry/Polyline.cs b/Elements/src/Geometry/Polyline.cs index 1a0c5123b..b3c9e5624 100644 --- a/Elements/src/Geometry/Polyline.cs +++ b/Elements/src/Geometry/Polyline.cs @@ -32,8 +32,24 @@ public class Polyline : Curve, IEquatable /// Construct a polyline. /// /// A collection of vertex locations. - /// Should self intersection testing be disabled? [JsonConstructor] + public Polyline(IList @vertices) : base() + { + this.Vertices = @vertices; + + if (!Validator.DisableValidationOnConstruction) + { + ValidateVertices(); + } + _bounds = new BBox3(Vertices); + } + + + /// + /// Construct a polyline. + /// + /// A collection of vertex locations. + /// Should self intersection testing be disabled? public Polyline(IList @vertices, bool disableValidation = false) : base() { this.Vertices = @vertices; From 4627315f880d63de0b7e7427014791405134634b Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 14:15:35 -0700 Subject: [PATCH 05/15] Update changelog. --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a913d013b..2a7b16525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,15 @@ ## 1.0.1 +### Added + +- `Polygon(IList @vertices, bool disableValidation = false)` +- `Polygon(bool disableValidation, params Vector3[] vertices)` +- `Polyline(IList @vertices, bool disableValidation = false)` +- `Polyline(bool disableValidation, params Vector3[] vertices)` + ### Fixed - #805 - -### Fixed - `Line.IsCollinear(Line line)` would return `false` if lines are close to each other but not collinear - `Vector3.AreCollinear(Vector3 a, Vector3 b, Vector3 c)` would return `false` if points coordinates difference is larger than `Vector3.EPSILON` - `EdgeDisplaySettings` for materials to control the display of lines in supported viewers (like Hypar.io). @@ -13,10 +18,10 @@ - `Line.MergeCollinearLine(Line line)` creates new line containing all four collinear vertices - `Line.Projected(Plane plane)` create new line projected onto plane - ### Changed - Simplified `IEnumerable.ToGraphicsBuffers()` - `TryToGraphicsBuffers` is now public +- `Solid SweepFaceAlongCurve` now has an additional parameter, `profileRotation`, which enables the caller to pass a profile rotation into sweep creation. ## 1.0.0 From f48342999c673e48ba07d3aa3b8012625c2433c0 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 15:49:40 -0700 Subject: [PATCH 06/15] Avoid creating empty faces. --- Elements/src/Geometry/Kernel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Elements/src/Geometry/Kernel.cs b/Elements/src/Geometry/Kernel.cs index cd5ffeb81..7ce3584f0 100644 --- a/Elements/src/Geometry/Kernel.cs +++ b/Elements/src/Geometry/Kernel.cs @@ -31,7 +31,7 @@ public static Kernel Instance /// A solid. public Solid CreateSweepAlongCurve(Profile profile, Curve curve, double startSetback, double endSetback, double profileRotation) { - return Solid.SweepFaceAlongCurve(profile.Perimeter, profile.Voids, curve, startSetback, endSetback, profileRotation); + return Solid.SweepFaceAlongCurve(profile.Perimeter, profile.Voids != null && profile.Voids.Count > 0 ? profile.Voids : null, curve, startSetback, endSetback, profileRotation); } /// From 30cb88817e44d8c34573abd612b63afbabe3f871 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 15:49:56 -0700 Subject: [PATCH 07/15] Output memory as well. --- Elements.Benchmarks/Trace.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Elements.Benchmarks/Trace.cs b/Elements.Benchmarks/Trace.cs index 5aaf7e45c..4d0cd5b02 100644 --- a/Elements.Benchmarks/Trace.cs +++ b/Elements.Benchmarks/Trace.cs @@ -22,6 +22,7 @@ public void TraceModelCreation() } [EventPipeProfiler(EventPipeProfile.CpuSampling)] + [MemoryDiagnoser] [SimpleJob] public class TraceGltfSerialization { From e5efe5d991571d09255ebbb0cd08bbaf11f8bb5f Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 15:50:40 -0700 Subject: [PATCH 08/15] It's a 2D tessellator. We can't do it per solid. --- .../Tessellation/SolidFaceTessAdapter.cs | 50 +------------------ .../SolidTessellationTargetProvider.cs | 5 +- 2 files changed, 6 insertions(+), 49 deletions(-) diff --git a/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs b/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs index e6ff19d69..cd94e6aa1 100644 --- a/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs +++ b/Elements/src/Geometry/Tessellation/SolidFaceTessAdapter.cs @@ -3,54 +3,6 @@ namespace Elements.Geometry.Tessellation { - /// - /// An object which provides a tessellation for a solid. - /// - internal class SolidTessAdapter : ITessAdapter - { - private readonly Solid solid; - private readonly Transform transform; - - /// - /// Construct a solid tessellation adapter. - /// - /// - /// - public SolidTessAdapter(Solid solid, Transform transform = null) - { - this.solid = solid; - this.transform = transform; - } - - public Tess GetTess() - { - var tess = new Tess - { - NoEmptyPolygons = true - }; - foreach (var face in solid.Faces.Values) - { - tess.AddContour(face.Outer.ToContourVertexArray(transform)); - - if (face.Inner != null) - { - foreach (var loop in face.Inner) - { - tess.AddContour(loop.ToContourVertexArray(transform)); - } - } - } - - tess.Tessellate(WindingRule.Positive, ElementType.Polygons, 3); - return tess; - } - - public bool RequiresTessellation() - { - return true; - } - } - /// /// An object which provides a tessellation for a solid face. /// @@ -77,8 +29,10 @@ public Tess GetTess() { var tess = new Tess { + UsePooling = true, NoEmptyPolygons = true }; + tess.AddContour(face.Outer.ToContourVertexArray(transform)); if (face.Inner != null) diff --git a/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs b/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs index 9834ed03c..9d246ade7 100644 --- a/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs +++ b/Elements/src/Geometry/Tessellation/SolidTessellationTargetProvider.cs @@ -27,7 +27,10 @@ public SolidTesselationTargetProvider(Solid solid, Transform transform = null) /// public IEnumerable GetTessellationTargets() { - yield return new SolidTessAdapter(solid, transform); + foreach (var f in solid.Faces.Values) + { + yield return new SolidFaceTessAdapter(f, transform); + } } } } \ No newline at end of file From 26b004846b96d4e212ca886078f51ff3380a7843 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 26 May 2022 21:24:45 -0700 Subject: [PATCH 09/15] Transform rotation around a point. --- CHANGELOG.md | 1 + Elements/src/Geometry/Bezier.cs | 2 +- Elements/src/Geometry/CsgExtensions.cs | 10 +--- Elements/src/Geometry/Curve.cs | 2 +- Elements/src/Geometry/GraphicsBuffers.cs | 29 +++++++++- Elements/src/Geometry/Polygon.cs | 2 +- Elements/src/Geometry/Polyline.cs | 2 +- .../src/Geometry/Tessellation/Tessellation.cs | 57 +++++++++++-------- Elements/src/Geometry/Transform.cs | 22 ++++++- .../src/Serialization/glTF/GltfExtensions.cs | 9 +-- Elements/test/CsgTests.cs | 57 +++++++++---------- 11 files changed, 119 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7b16525..97a0a734d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `Polygon(bool disableValidation, params Vector3[] vertices)` - `Polyline(IList @vertices, bool disableValidation = false)` - `Polyline(bool disableValidation, params Vector3[] vertices)` +- `Transform.Rotate(Vector3 axis, double angle, Vector3 origin)` ### Fixed - #805 diff --git a/Elements/src/Geometry/Bezier.cs b/Elements/src/Geometry/Bezier.cs index 19c36a5f4..86a8b7f42 100644 --- a/Elements/src/Geometry/Bezier.cs +++ b/Elements/src/Geometry/Bezier.cs @@ -85,7 +85,7 @@ public override Transform[] Frames(double startSetback = 0, transforms[i] = TransformAt(i * 1.0 / _samples); if (additionalRotation != 0.0) { - transforms[i].Rotate(transforms[i].ZAxis, additionalRotation); + transforms[i].Rotate(transforms[i].ZAxis, additionalRotation, transforms[i].Origin); } } return transforms; diff --git a/Elements/src/Geometry/CsgExtensions.cs b/Elements/src/Geometry/CsgExtensions.cs index 8cc3af9d4..965b71855 100644 --- a/Elements/src/Geometry/CsgExtensions.cs +++ b/Elements/src/Geometry/CsgExtensions.cs @@ -25,10 +25,9 @@ internal static void Tessellate(this Csg.Solid csg, ref Mesh mesh) /// appropriate for use with gltf. /// internal static GraphicsBuffers Tessellate(this Csg.Solid csg, - bool mergeVertices = false, Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) { - return Tessellate(new[] { csg }, mergeVertices, modifyVertexAttributes); + return Tessellate(new[] { csg }, modifyVertexAttributes); } /// @@ -36,14 +35,9 @@ internal static GraphicsBuffers Tessellate(this Csg.Solid csg, /// buffers appropriate for use with gltf. /// internal static GraphicsBuffers Tessellate(this Csg.Solid[] csgs, - bool mergeVertices = false, Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) { - var buffers = new GraphicsBuffers(); - - Tessellation.Tessellation.Tessellate(csgs.Select(csg => new CsgTessellationTargetProvider(csg)), - buffers, - mergeVertices, + var buffers = Tessellation.Tessellation.Tessellate(csgs.Select(csg => new CsgTessellationTargetProvider(csg)), modifyVertexAttributes); return buffers; } diff --git a/Elements/src/Geometry/Curve.cs b/Elements/src/Geometry/Curve.cs index 4f97e61de..6597793e9 100644 --- a/Elements/src/Geometry/Curve.cs +++ b/Elements/src/Geometry/Curve.cs @@ -40,7 +40,7 @@ public virtual Transform[] Frames(double startSetback = 0.0, transforms[i] = TransformAt(parameters[i]); if (additionalRotation != 0.0) { - transforms[i].Rotate(transforms[i].ZAxis, additionalRotation); + transforms[i].Rotate(transforms[i].ZAxis, additionalRotation, transforms[i].Origin); } } return transforms; diff --git a/Elements/src/Geometry/GraphicsBuffers.cs b/Elements/src/Geometry/GraphicsBuffers.cs index 35ef48918..952d52c91 100644 --- a/Elements/src/Geometry/GraphicsBuffers.cs +++ b/Elements/src/Geometry/GraphicsBuffers.cs @@ -145,6 +145,33 @@ public GraphicsBuffers() this.UVMax = new double[2] { double.MinValue, double.MinValue }; } + /// + /// Construct an empty graphics buffers object with pre-allocated collections. + /// + /// + /// + public GraphicsBuffers(int vertexCount, int indexCount) + { + // Initialize everything + this.Vertices = new List(sizeof(float) * 3 * vertexCount); + this.Normals = new List(sizeof(float) * 3 * vertexCount); + this.Indices = new List(sizeof(ushort) * indexCount); + this.UVs = new List(sizeof(float) * 2 * vertexCount); + this.Colors = new List(sizeof(float) * 3 * vertexCount); + + this.CMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + this.CMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + + this.VMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + this.VMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + + this.NMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + this.NMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + + this.UVMin = new double[2] { double.MaxValue, double.MaxValue }; + this.UVMax = new double[2] { double.MinValue, double.MinValue }; + } + /// /// Add a vertex to the graphics buffers. /// @@ -201,7 +228,7 @@ public void AddVertex(double x, double y, double z, double nx, double ny, double this.UVMin[0] = Math.Min(this.UVMin[0], u); this.UVMin[1] = Math.Min(this.UVMin[1], v); - if (color.HasValue && color.Value != default(Color)) + if (color.HasValue && color.Value != default) { this.CMax[0] = Math.Max(this.CMax[0], color.Value.Red); this.CMax[1] = Math.Max(this.CMax[1], color.Value.Green); diff --git a/Elements/src/Geometry/Polygon.cs b/Elements/src/Geometry/Polygon.cs index dfe9f5a18..0d046938d 100644 --- a/Elements/src/Geometry/Polygon.cs +++ b/Elements/src/Geometry/Polygon.cs @@ -2010,7 +2010,7 @@ public override Transform[] Frames(double startSetback = 0.0, result[i] = CreateMiterTransform(i, a, up); if (additionalRotation != 0.0) { - result[i].Rotate(result[i].ZAxis, additionalRotation); + result[i].Rotate(result[i].ZAxis, additionalRotation, result[i].Origin); } } return result; diff --git a/Elements/src/Geometry/Polyline.cs b/Elements/src/Geometry/Polyline.cs index b3c9e5624..bd4a18684 100644 --- a/Elements/src/Geometry/Polyline.cs +++ b/Elements/src/Geometry/Polyline.cs @@ -396,7 +396,7 @@ public override Transform[] Frames(double startSetback = 0.0, result[i] = CreateOrthogonalTransform(i, a, normals[i]); if (additionalRotation != 0.0) { - result[i].Rotate(result[i].ZAxis, additionalRotation); + result[i].Rotate(result[i].ZAxis, additionalRotation, result[i].Origin); } } return result; diff --git a/Elements/src/Geometry/Tessellation/Tessellation.cs b/Elements/src/Geometry/Tessellation/Tessellation.cs index f66ab0f04..36aa6f95c 100644 --- a/Elements/src/Geometry/Tessellation/Tessellation.cs +++ b/Elements/src/Geometry/Tessellation/Tessellation.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using LibTessDotNet.Double; [assembly: InternalsVisibleTo("Hypar.Elements.Tests")] @@ -15,46 +16,42 @@ internal static class Tessellation /// Triangulate a collection of CSGs and pack the triangulated data into /// a supplied buffers object. /// - internal static void Tessellate(IEnumerable providers, - IGraphicsBuffers buffers, - bool mergeVertices = false, + internal static GraphicsBuffers Tessellate(IEnumerable providers, Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) { - var allVertices = new List<(Vector3 position, Vector3 normal, UV uv, Color color)>(); + + // Gather all the tessellations + var tesses = new List(); foreach (var provider in providers) { foreach (var target in provider.GetTessellationTargets()) { - PackTessellationIntoBuffers(target.GetTess(), buffers, allVertices, mergeVertices); + tesses.Add(target.GetTess()); } } - foreach (var v in allVertices) + // Pre-allocate a buffer big enough to hold all the tessellations + var buffers = new GraphicsBuffers(tesses.Sum(tess => tess.VertexCount), tesses.Sum(tess => tess.Elements.Length)); + ushort indexOffset = 0; + + foreach (var tess in tesses) { - if (modifyVertexAttributes != null) - { - var mod = modifyVertexAttributes(v); - buffers.AddVertex(mod.Item1, mod.Item2, mod.Item3, mod.Item4); - } - else - { - buffers.AddVertex(v.position, v.normal, v.uv); - } + PackTessellationIntoBuffers(tess, buffers, modifyVertexAttributes, ref indexOffset); } + + return buffers; } private static void PackTessellationIntoBuffers(Tess tess, IGraphicsBuffers buffers, - List<(Vector3 position, Vector3 normal, UV uv, Color color)> allVertices, - bool mergeVertices = false) + Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes, + ref ushort indexOffset) { if (tess.ElementCount == 0) { return; } - var vertexIndices = new ushort[tess.Vertices.Length]; - // We pick the first triangle from the tesselator, // instead of the first three vertices, which are not guaranteed to be // wound correctly. @@ -75,18 +72,28 @@ private static void PackTessellationIntoBuffers(Tess tess, var uu = U.Dot(v.Position.X, v.Position.Y, v.Position.Z); var vv = V.Dot(v.Position.X, v.Position.Y, v.Position.Z); - vertexIndices[i] = (ushort)GetOrCreateVertex(new Vector3(v.Position.X, v.Position.Y, v.Position.Z), - new Vector3(n.X, n.Y, n.Z), - new UV(uu, vv), - allVertices, - mergeVertices); + var v1 = new Vector3(v.Position.X, v.Position.Y, v.Position.Z); + var uv1 = new UV(uu, vv); + var c1 = default(Color); + + if (modifyVertexAttributes != null) + { + var mod = modifyVertexAttributes((v1, n, uv1, c1)); + buffers.AddVertex(mod.Item1, mod.Item2, mod.Item3, mod.Item4); + } + else + { + buffers.AddVertex(v1, n, uv1, c1); + } } for (var k = 0; k < tess.Elements.Length; k++) { - var index = vertexIndices[tess.Elements[k]]; + var index = (ushort)(tess.Elements[k] + indexOffset); buffers.AddIndex(index); } + + indexOffset += (ushort)tess.Vertices.Length; } private static Vector3 ToElementsVector(this ContourVertex v) diff --git a/Elements/src/Geometry/Transform.cs b/Elements/src/Geometry/Transform.cs index cab259f9c..9c240f9b1 100644 --- a/Elements/src/Geometry/Transform.cs +++ b/Elements/src/Geometry/Transform.cs @@ -385,7 +385,27 @@ public void Rotate(Vector3 axis, double angle) { var m = new Matrix(); m.SetupRotate(axis, angle * (Math.PI / 180.0)); - this.Matrix = this.Matrix * m; + this.Matrix *= m; + } + + /// + /// Apply a rotation around a to the transform around a point. + /// + /// The axis of rotation. + /// The angle of rotation in degrees. + /// The point around which to rotate. + public void Rotate(Vector3 axis, double angle, Vector3 origin) + { + var mT1 = new Matrix(); + mT1.SetupTranslation(origin.Negate()); + + var mT2 = new Matrix(); + mT2.SetupTranslation(origin); + + var mR = new Matrix(); + mR.SetupRotate(axis, angle * (Math.PI / 180.0)); + + this.Matrix = this.Matrix * mT1 * mR * mT2; } /// diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index 3e8e11cfd..e0347f272 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -86,7 +86,7 @@ public static void ToGlTF(this Model model, string path, out List err if (SaveGlb(model, path, out errors, drawEdges)) { return; - } + } // Else fall through to produce an empty GLTF. } else @@ -1425,16 +1425,13 @@ private static int ProcessSolidsAsCSG(GeometricElement geometricElement, // There's a special flag on Representation that allows you to // skip CSG unions. In this case, we tessellate all solids // individually, and do no booleaning. Voids are also ignored. - buffers = new GraphicsBuffers(); - Tessellation.Tessellate(geometricElement.Representation.SolidOperations.Select(so => new SolidTesselationTargetProvider(so.Solid, so.LocalTransform)), - buffers, - mergeVertices, + buffers = Tessellation.Tessellate(geometricElement.Representation.SolidOperations.Select(so => new SolidTesselationTargetProvider(so.Solid, so.LocalTransform)), geometricElement.ModifyVertexAttributes); } else { var csg = geometricElement.GetFinalCsgFromSolids(); - buffers = csg.Tessellate(mergeVertices, geometricElement.ModifyVertexAttributes); + buffers = csg.Tessellate(geometricElement.ModifyVertexAttributes); } if (buffers.Vertices.Count == 0) diff --git a/Elements/test/CsgTests.cs b/Elements/test/CsgTests.cs index 67fe218c8..e2f9343ed 100644 --- a/Elements/test/CsgTests.cs +++ b/Elements/test/CsgTests.cs @@ -174,35 +174,34 @@ public void UnionWithProblematicPolygons() var solid = element.GetFinalCsgFromSolids(); } - [Fact] - public void TessellatorProducesCorrectVertexNormals() - { - Name = nameof(TessellatorProducesCorrectVertexNormals); - var shape = new Polygon((4.96243, 50.58403), (5.78472, 50.58403), (5.78472, 65.83403), (-7.05727, 65.83403), (-7.05727, 50.57403), (4.96243, 50.57403)); - - var geoElem = new GeometricElement(representation: new Extrude(shape, 1, Vector3.ZAxis, false)); - Model.AddElement(geoElem); - var solid = geoElem.GetFinalCsgFromSolids(); - var mgb = new MockGraphicsBuffer(); - var arrows = new ModelArrows(); - Tessellation.Tessellate(new Csg.Solid[] { solid }.Select(s => new CsgTessellationTargetProvider(solid)), mgb); - for (int i = 0; i < mgb.Indices.Count; i += 3) - { - var a = mgb.Indices[i]; - var b = mgb.Indices[i + 1]; - var c = mgb.Indices[i + 2]; - var verts = new[] { mgb.Vertices[a], mgb.Vertices[b], mgb.Vertices[c] }; - verts.ToList().ForEach((v) => - { - arrows.Vectors.Add((v.position, v.normal, 0.2, Colors.Blue)); - }); - var triangle = new Polygon(verts.Select(v => v.position).ToList()); - var normal = verts[0].normal; - Assert.True(triangle.Normal().Dot(normal.Unitized()) > 0, "The vertex normals are pointing in the opposite direction as their triangles' winding should suggest"); - Model.AddElement(triangle.TransformedPolygon(new Transform(normal * 0.2))); - } - Model.AddElement(arrows); - } + // [Fact] + // public void TessellatorProducesCorrectVertexNormals() + // { + // Name = nameof(TessellatorProducesCorrectVertexNormals); + // var shape = new Polygon((4.96243, 50.58403), (5.78472, 50.58403), (5.78472, 65.83403), (-7.05727, 65.83403), (-7.05727, 50.57403), (4.96243, 50.57403)); + + // var geoElem = new GeometricElement(representation: new Extrude(shape, 1, Vector3.ZAxis, false)); + // Model.AddElement(geoElem); + // var solid = geoElem.GetFinalCsgFromSolids(); + // var arrows = new ModelArrows(); + // var mgb = Tessellation.Tessellate(new Csg.Solid[] { solid }.Select(s => new CsgTessellationTargetProvider(solid))); + // for (int i = 0; i < mgb.Indices.Count; i += 3) + // { + // var a = mgb.Indices[i]; + // var b = mgb.Indices[i + 1]; + // var c = mgb.Indices[i + 2]; + // var verts = new[] { mgb.Vertices[a], mgb.Vertices[b], mgb.Vertices[c] }; + // verts.ToList().ForEach((v) => + // { + // arrows.Vectors.Add((v.position, v.normal, 0.2, Colors.Blue)); + // }); + // var triangle = new Polygon(verts.Select(v => v.position).ToList()); + // var normal = verts[0].normal; + // Assert.True(triangle.Normal().Dot(normal.Unitized()) > 0, "The vertex normals are pointing in the opposite direction as their triangles' winding should suggest"); + // Model.AddElement(triangle.TransformedPolygon(new Transform(normal * 0.2))); + // } + // Model.AddElement(arrows); + // } private class MockGraphicsBuffer : IGraphicsBuffers { From 02712753926ae03efa2300d729fb2c863e68ea2f Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Fri, 27 May 2022 07:04:55 -0700 Subject: [PATCH 10/15] Do they speak english on what?! --- Elements/src/Geometry/Transform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Elements/src/Geometry/Transform.cs b/Elements/src/Geometry/Transform.cs index 9c240f9b1..6e6122436 100644 --- a/Elements/src/Geometry/Transform.cs +++ b/Elements/src/Geometry/Transform.cs @@ -389,7 +389,7 @@ public void Rotate(Vector3 axis, double angle) } /// - /// Apply a rotation around a to the transform around a point. + /// Apply a rotation to the transform around a point. /// /// The axis of rotation. /// The angle of rotation in degrees. From ebca233a2386a3a6f59c1c562d9f7fcd555e0c96 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Sat, 28 May 2022 12:09:11 -0700 Subject: [PATCH 11/15] Support generic creation of graphics buffers. --- Elements/src/Geometry/CsgExtensions.cs | 2 +- Elements/src/Geometry/GraphicsBuffers.cs | 134 ++++++------------ Elements/src/Geometry/IGraphicsBuffers.cs | 47 ++++++ .../src/Geometry/Tessellation/Tessellation.cs | 11 +- .../src/Serialization/glTF/GltfExtensions.cs | 2 +- Elements/test/CsgTests.cs | 69 ++++----- 6 files changed, 132 insertions(+), 133 deletions(-) create mode 100644 Elements/src/Geometry/IGraphicsBuffers.cs diff --git a/Elements/src/Geometry/CsgExtensions.cs b/Elements/src/Geometry/CsgExtensions.cs index 965b71855..ea1e22fb4 100644 --- a/Elements/src/Geometry/CsgExtensions.cs +++ b/Elements/src/Geometry/CsgExtensions.cs @@ -37,7 +37,7 @@ internal static GraphicsBuffers Tessellate(this Csg.Solid csg, internal static GraphicsBuffers Tessellate(this Csg.Solid[] csgs, Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) { - var buffers = Tessellation.Tessellation.Tessellate(csgs.Select(csg => new CsgTessellationTargetProvider(csg)), + var buffers = Tessellation.Tessellation.Tessellate(csgs.Select(csg => new CsgTessellationTargetProvider(csg)), modifyVertexAttributes); return buffers; } diff --git a/Elements/src/Geometry/GraphicsBuffers.cs b/Elements/src/Geometry/GraphicsBuffers.cs index 952d52c91..989b6d5a4 100644 --- a/Elements/src/Geometry/GraphicsBuffers.cs +++ b/Elements/src/Geometry/GraphicsBuffers.cs @@ -3,42 +3,6 @@ namespace Elements.Geometry { - /// - /// A generic container for graphics data. This is broken out primarily to facilitate - /// simpler testing of graphics buffers. - /// - internal interface IGraphicsBuffers - { - /// - /// Add a vertex to the graphics buffers. - /// - /// The position of the vertex. - /// The normal of the vertex. - /// The UV of the vertex. - /// The vertex color. - void AddVertex(Vector3 position, Vector3 normal, UV uv, Color? color = null); - - /// - /// Add a vertex to the graphics buffers. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - void AddVertex(double x, double y, double z, double nx, double ny, double nz, double u, double v, Color? color = null); - - /// - /// Add an index to the graphics buffers. - /// - /// The index to add. - void AddIndex(ushort index); - } - /// /// A container for graphics data. /// The buffers used in this class align with webgl requirements. @@ -48,57 +12,57 @@ public class GraphicsBuffers : IGraphicsBuffers /// /// A collection of vertex positions stored as sequential bytes. /// - public List Vertices { get; } + public List Vertices { get; private set; } /// /// A collection of indices stored as sequential bytes. /// - public List Indices { get; } + public List Indices { get; private set; } /// /// A collection of sequential normal values stored as sequential bytes. /// - public List Normals { get; } + public List Normals { get; private set; } /// /// A collection of sequential color values stored as sequential bytes. /// - public List Colors { get; } + public List Colors { get; private set; } /// /// A collection of UV values stored as sequential bytes. /// - public List UVs { get; } + public List UVs { get; private set; } /// /// The maximum of the axis-aligned bounding box of the data as [x,y,z]. /// - public double[] VMax { get; } + public double[] VMax { get; private set; } /// /// The minimum of the axis-aligned bounding box of the data as [x,y,z]. /// - public double[] VMin { get; } + public double[] VMin { get; private set; } /// /// The minimum normal of the data as [x,y,z]. /// - public double[] NMin { get; } + public double[] NMin { get; private set; } /// /// The maximum normal of the data as [x,y,z]. /// - public double[] NMax { get; } + public double[] NMax { get; private set; } /// /// The minimum color value as [r,g,b]. /// - public double[] CMin { get; } + public double[] CMin { get; private set; } /// /// The maximum color value as [r,g,b]. /// - public double[] CMax { get; } + public double[] CMax { get; private set; } /// /// The maximum index value. @@ -113,63 +77,19 @@ public class GraphicsBuffers : IGraphicsBuffers /// /// The maximum UV value as [u,v]. /// - public double[] UVMin { get; } + public double[] UVMin { get; private set; } /// /// The maximum UV value as [u,v]. /// - public double[] UVMax { get; } + public double[] UVMax { get; private set; } /// /// Construct an empty graphics buffers object. /// public GraphicsBuffers() { - // Initialize everything - this.Vertices = new List(); - this.Normals = new List(); - this.Indices = new List(); - this.UVs = new List(); - this.Colors = new List(); - - this.CMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - this.CMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - - this.VMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - this.VMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - - this.NMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - this.NMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - - this.UVMin = new double[2] { double.MaxValue, double.MaxValue }; - this.UVMax = new double[2] { double.MinValue, double.MinValue }; - } - - /// - /// Construct an empty graphics buffers object with pre-allocated collections. - /// - /// - /// - public GraphicsBuffers(int vertexCount, int indexCount) - { - // Initialize everything - this.Vertices = new List(sizeof(float) * 3 * vertexCount); - this.Normals = new List(sizeof(float) * 3 * vertexCount); - this.Indices = new List(sizeof(ushort) * indexCount); - this.UVs = new List(sizeof(float) * 2 * vertexCount); - this.Colors = new List(sizeof(float) * 3 * vertexCount); - - this.CMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - this.CMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - - this.VMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - this.VMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - - this.NMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; - this.NMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; - - this.UVMin = new double[2] { double.MaxValue, double.MaxValue }; - this.UVMax = new double[2] { double.MinValue, double.MinValue }; + Initialize(); } /// @@ -254,5 +174,31 @@ public void AddIndex(ushort index) this.IMin = Math.Min(this.IMin, index); } + /// + /// Initialize the graphics buffer to a known size. + /// + /// The number of vertices. + /// The number of indices. + public void Initialize(int vertexCount = 0, int indexCount = 0) + { + // Initialize everything + this.Vertices = new List(sizeof(float) * 3 * vertexCount); + this.Normals = new List(sizeof(float) * 3 * vertexCount); + this.Indices = new List(sizeof(ushort) * indexCount); + this.UVs = new List(sizeof(float) * 2 * vertexCount); + this.Colors = new List(sizeof(float) * 3 * vertexCount); + + this.CMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + this.CMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + + this.VMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + this.VMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + + this.NMin = new double[3] { double.MaxValue, double.MaxValue, double.MaxValue }; + this.NMax = new double[3] { double.MinValue, double.MinValue, double.MinValue }; + + this.UVMin = new double[2] { double.MaxValue, double.MaxValue }; + this.UVMax = new double[2] { double.MinValue, double.MinValue }; + } } } \ No newline at end of file diff --git a/Elements/src/Geometry/IGraphicsBuffers.cs b/Elements/src/Geometry/IGraphicsBuffers.cs new file mode 100644 index 000000000..87285a8a5 --- /dev/null +++ b/Elements/src/Geometry/IGraphicsBuffers.cs @@ -0,0 +1,47 @@ +using Elements.Geometry; + +namespace Elements +{ + /// + /// A generic container for graphics data. This is broken out primarily to facilitate + /// simpler testing of graphics buffers. + /// + internal interface IGraphicsBuffers + { + /// + /// Initialize a graphics buffer to a sepcific vertex size. + /// + /// The number of vertices. + /// The number of indices. + void Initialize(int vertexCount, int indexCount); + + /// + /// Add a vertex to the graphics buffers. + /// + /// The position of the vertex. + /// The normal of the vertex. + /// The UV of the vertex. + /// The vertex color. + void AddVertex(Vector3 position, Vector3 normal, UV uv, Color? color = null); + + /// + /// Add a vertex to the graphics buffers. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + void AddVertex(double x, double y, double z, double nx, double ny, double nz, double u, double v, Color? color = null); + + /// + /// Add an index to the graphics buffers. + /// + /// The index to add. + void AddIndex(ushort index); + } +} \ No newline at end of file diff --git a/Elements/src/Geometry/Tessellation/Tessellation.cs b/Elements/src/Geometry/Tessellation/Tessellation.cs index 36aa6f95c..295ea79d8 100644 --- a/Elements/src/Geometry/Tessellation/Tessellation.cs +++ b/Elements/src/Geometry/Tessellation/Tessellation.cs @@ -16,8 +16,8 @@ internal static class Tessellation /// Triangulate a collection of CSGs and pack the triangulated data into /// a supplied buffers object. /// - internal static GraphicsBuffers Tessellate(IEnumerable providers, - Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) + internal static T Tessellate(IEnumerable providers, + Func<(Vector3, Vector3, UV, Color), (Vector3, Vector3, UV, Color)> modifyVertexAttributes = null) where T : IGraphicsBuffers { // Gather all the tessellations @@ -31,15 +31,16 @@ internal static GraphicsBuffers Tessellate(IEnumerable tess.VertexCount), tesses.Sum(tess => tess.Elements.Length)); - ushort indexOffset = 0; + var buffers = (IGraphicsBuffers)Activator.CreateInstance(typeof(T)); + buffers.Initialize(tesses.Sum(tess => tess.VertexCount), tesses.Sum(tess => tess.Elements.Length)); + ushort indexOffset = 0; foreach (var tess in tesses) { PackTessellationIntoBuffers(tess, buffers, modifyVertexAttributes, ref indexOffset); } - return buffers; + return (T)buffers; } private static void PackTessellationIntoBuffers(Tess tess, diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index e0347f272..efefc2257 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -1425,7 +1425,7 @@ private static int ProcessSolidsAsCSG(GeometricElement geometricElement, // There's a special flag on Representation that allows you to // skip CSG unions. In this case, we tessellate all solids // individually, and do no booleaning. Voids are also ignored. - buffers = Tessellation.Tessellate(geometricElement.Representation.SolidOperations.Select(so => new SolidTesselationTargetProvider(so.Solid, so.LocalTransform)), + buffers = Tessellation.Tessellate(geometricElement.Representation.SolidOperations.Select(so => new SolidTesselationTargetProvider(so.Solid, so.LocalTransform)), geometricElement.ModifyVertexAttributes); } else diff --git a/Elements/test/CsgTests.cs b/Elements/test/CsgTests.cs index e2f9343ed..2c058df07 100644 --- a/Elements/test/CsgTests.cs +++ b/Elements/test/CsgTests.cs @@ -5,8 +5,6 @@ using System; using Xunit; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Newtonsoft.Json; using Elements.Geometry.Tessellation; @@ -174,40 +172,41 @@ public void UnionWithProblematicPolygons() var solid = element.GetFinalCsgFromSolids(); } - // [Fact] - // public void TessellatorProducesCorrectVertexNormals() - // { - // Name = nameof(TessellatorProducesCorrectVertexNormals); - // var shape = new Polygon((4.96243, 50.58403), (5.78472, 50.58403), (5.78472, 65.83403), (-7.05727, 65.83403), (-7.05727, 50.57403), (4.96243, 50.57403)); - - // var geoElem = new GeometricElement(representation: new Extrude(shape, 1, Vector3.ZAxis, false)); - // Model.AddElement(geoElem); - // var solid = geoElem.GetFinalCsgFromSolids(); - // var arrows = new ModelArrows(); - // var mgb = Tessellation.Tessellate(new Csg.Solid[] { solid }.Select(s => new CsgTessellationTargetProvider(solid))); - // for (int i = 0; i < mgb.Indices.Count; i += 3) - // { - // var a = mgb.Indices[i]; - // var b = mgb.Indices[i + 1]; - // var c = mgb.Indices[i + 2]; - // var verts = new[] { mgb.Vertices[a], mgb.Vertices[b], mgb.Vertices[c] }; - // verts.ToList().ForEach((v) => - // { - // arrows.Vectors.Add((v.position, v.normal, 0.2, Colors.Blue)); - // }); - // var triangle = new Polygon(verts.Select(v => v.position).ToList()); - // var normal = verts[0].normal; - // Assert.True(triangle.Normal().Dot(normal.Unitized()) > 0, "The vertex normals are pointing in the opposite direction as their triangles' winding should suggest"); - // Model.AddElement(triangle.TransformedPolygon(new Transform(normal * 0.2))); - // } - // Model.AddElement(arrows); - // } + [Fact] + public void TessellatorProducesCorrectVertexNormals() + { + Name = nameof(TessellatorProducesCorrectVertexNormals); + var shape = new Polygon((4.96243, 50.58403), (5.78472, 50.58403), (5.78472, 65.83403), (-7.05727, 65.83403), (-7.05727, 50.57403), (4.96243, 50.57403)); + + var geoElem = new GeometricElement(representation: new Extrude(shape, 1, Vector3.ZAxis, false)); + Model.AddElement(geoElem); + var solid = geoElem.GetFinalCsgFromSolids(); + var arrows = new ModelArrows(); + var mgb = Tessellation.Tessellate(new Csg.Solid[] { solid }.Select(s => new CsgTessellationTargetProvider(solid))); + for (int i = 0; i < mgb.Indices.Count; i += 3) + { + var a = mgb.Indices[i]; + var b = mgb.Indices[i + 1]; + var c = mgb.Indices[i + 2]; + var verts = new[] { mgb.Vertices[a], mgb.Vertices[b], mgb.Vertices[c] }; + verts.ToList().ForEach((v) => + { + arrows.Vectors.Add((v.position, v.normal, 0.2, Colors.Blue)); + }); + var triangle = new Polygon(verts.Select(v => v.position).ToList()); + var normal = verts[0].normal; + Assert.True(triangle.Normal().Dot(normal.Unitized()) > 0, "The vertex normals are pointing in the opposite direction as their triangles' winding should suggest"); + Model.AddElement(triangle.TransformedPolygon(new Transform(normal * 0.2))); + } + Model.AddElement(arrows); + } private class MockGraphicsBuffer : IGraphicsBuffers { - public List Indices { get; set; } = new List(); + public List Indices { get; set; } + + public List<(Vector3 position, Vector3 normal)> Vertices { get; set; } - public List<(Vector3 position, Vector3 normal)> Vertices { get; set; } = new List<(Vector3 position, Vector3 normal)>(); public void AddIndex(ushort index) { Indices.Add(index); @@ -222,6 +221,12 @@ public void AddVertex(double x, double y, double z, double nx, double ny, double { Vertices.Add((new Vector3(x, y, z), new Vector3(nx, ny, nz))); } + + public void Initialize(int vertexCount = 0, int indexCount = 0) + { + this.Vertices = new List<(Vector3 position, Vector3 normal)>(); + this.Indices = new List(); + } } } } \ No newline at end of file From acd906555410dbc3d1f1f5ccc5bc0ed315a084f1 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Sat, 28 May 2022 12:49:25 -0700 Subject: [PATCH 12/15] Remove merge vertices for now. --- Elements/src/Geometry/GraphicsBuffers.cs | 16 ++++++++++ .../src/Geometry/Tessellation/Tessellation.cs | 30 +++---------------- .../src/Serialization/glTF/GltfExtensions.cs | 15 ++++------ Elements/test/SolidTests.cs | 11 +++++++ 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Elements/src/Geometry/GraphicsBuffers.cs b/Elements/src/Geometry/GraphicsBuffers.cs index 989b6d5a4..a8e9f6448 100644 --- a/Elements/src/Geometry/GraphicsBuffers.cs +++ b/Elements/src/Geometry/GraphicsBuffers.cs @@ -9,6 +9,22 @@ namespace Elements.Geometry /// public class GraphicsBuffers : IGraphicsBuffers { + /// + /// The number of vertices represented by the buffer. + /// + public int VertexCount + { + get { return this.Vertices.Count / sizeof(float) / 3; } + } + + /// + /// The number of facets represeted by the buffer. + /// + public int FacetCount + { + get { return this.Indices.Count / sizeof(ushort) / 3; } + } + /// /// A collection of vertex positions stored as sequential bytes. /// diff --git a/Elements/src/Geometry/Tessellation/Tessellation.cs b/Elements/src/Geometry/Tessellation/Tessellation.cs index 295ea79d8..aa7c3e3dc 100644 --- a/Elements/src/Geometry/Tessellation/Tessellation.cs +++ b/Elements/src/Geometry/Tessellation/Tessellation.cs @@ -31,16 +31,16 @@ internal static T Tessellate(IEnumerable provide } // Pre-allocate a buffer big enough to hold all the tessellations - var buffers = (IGraphicsBuffers)Activator.CreateInstance(typeof(T)); - buffers.Initialize(tesses.Sum(tess => tess.VertexCount), tesses.Sum(tess => tess.Elements.Length)); + var buffer = (IGraphicsBuffers)Activator.CreateInstance(typeof(T)); + buffer.Initialize(tesses.Sum(tess => tess.VertexCount), tesses.Sum(tess => tess.Elements.Length)); ushort indexOffset = 0; foreach (var tess in tesses) { - PackTessellationIntoBuffers(tess, buffers, modifyVertexAttributes, ref indexOffset); + PackTessellationIntoBuffers(tess, buffer, modifyVertexAttributes, ref indexOffset); } - return (T)buffers; + return (T)buffer; } private static void PackTessellationIntoBuffers(Tess tess, @@ -102,28 +102,6 @@ private static Vector3 ToElementsVector(this ContourVertex v) return new Vector3(v.Position.X, v.Position.Y, v.Position.Z); } - private static int GetOrCreateVertex(Vector3 position, - Vector3 normal, - UV uv, - List<(Vector3 position, Vector3 normal, UV uv, Color color)> pts, - bool mergeVertices) - { - if (mergeVertices) - { - var index = pts.FindIndex(p => - { - return p.position.IsAlmostEqualTo(position) && p.normal.AngleTo(normal) < 45.0; - }); - if (index != -1) - { - return index; - } - } - - pts.Add((position, normal, uv, default(Color))); - return pts.Count - 1; - } - internal static (Vector3 U, Vector3 V) ComputeBasisAndNormalForTriangle(Vector3 a, Vector3 b, Vector3 c, out Vector3 n) { var tmp = (b - a).Unitized(); diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index efefc2257..f2bea2009 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -1131,8 +1131,7 @@ private static void GetRenderDataForElement(Element e, nodes, materialId, ref meshId, - content, - mergeVertices); + content); if (!meshElementMap.ContainsKey(e.Id)) { meshElementMap.Add(e.Id, new List { meshId }); @@ -1154,8 +1153,7 @@ private static void GetRenderDataForElement(Element e, nodes, materialId, ref meshId, - geometricElement, - mergeVertices); + geometricElement); if (meshId > -1 && !meshElementMap.ContainsKey(e.Id)) { meshElementMap.Add(e.Id, new List { meshId }); @@ -1365,8 +1363,7 @@ private static int ProcessGeometricRepresentation(Element e, List nodes, string materialId, ref int meshId, - GeometricElement geometricElement, - bool mergeVertices = false) + GeometricElement geometricElement) { geometricElement.UpdateRepresentations(); @@ -1389,8 +1386,7 @@ private static int ProcessGeometricRepresentation(Element e, ref buffers, bufferViews, accessors, - meshes, - mergeVertices); + meshes); // If the id == -1, the mesh is malformed. // It may have no geometry. @@ -1416,8 +1412,7 @@ private static int ProcessSolidsAsCSG(GeometricElement geometricElement, ref List buffer, List bufferViews, List accessors, - List meshes, - bool mergeVertices = false) + List meshes) { GraphicsBuffers buffers = null; if (geometricElement.Representation.SkipCSGUnion) diff --git a/Elements/test/SolidTests.cs b/Elements/test/SolidTests.cs index dbbcdbe63..1197e4cbe 100644 --- a/Elements/test/SolidTests.cs +++ b/Elements/test/SolidTests.cs @@ -10,6 +10,7 @@ using Elements.Serialization.glTF; using Elements.Serialization.JSON; using System.Linq; +using Elements.Geometry.Tessellation; namespace Elements.Tests { @@ -729,6 +730,16 @@ public void TwoHoles() Assert.Equal(14, result.Faces.Count); } + [Fact] + public void TessellationHasCorrectNumberOfVertices() + { + var panel = new Panel(Polygon.L(5, 5, 2)); + panel.UpdateRepresentations(); + var buffer = Tessellation.Tessellate(panel.Representation.SolidOperations.Select(so => new SolidTesselationTargetProvider(so.Solid, so.LocalTransform))); + Assert.Equal(12, buffer.VertexCount); // Two faces of 6 vertices each + Assert.Equal(8, buffer.FacetCount); // Two faces of 4 facets each. + } + private class DebugInfo { public List Solid { get; set; } From ef4b8d2507d5a2f81798e23aadd91b10a88595af Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Mon, 8 Aug 2022 21:00:57 -0700 Subject: [PATCH 13/15] Use RotateAboutPoint. --- CHANGELOG.md | 1 - Elements/src/Geometry/Bezier.cs | 2 +- Elements/src/Geometry/Curve.cs | 2 +- Elements/src/Geometry/Polygon.cs | 2 +- Elements/src/Geometry/Polyline.cs | 8 ++++---- Elements/src/Geometry/Transform.cs | 20 -------------------- 6 files changed, 7 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1291de111..0d69be669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ - `Polygon(bool disableValidation, params Vector3[] vertices)` - `Polyline(IList @vertices, bool disableValidation = false)` - `Polyline(bool disableValidation, params Vector3[] vertices)` -- `Transform.Rotate(Vector3 axis, double angle, Vector3 origin)` ### Changed - MeshElement constructor signature modified to be compatible with code generation. diff --git a/Elements/src/Geometry/Bezier.cs b/Elements/src/Geometry/Bezier.cs index 86a8b7f42..1d40eaaaf 100644 --- a/Elements/src/Geometry/Bezier.cs +++ b/Elements/src/Geometry/Bezier.cs @@ -85,7 +85,7 @@ public override Transform[] Frames(double startSetback = 0, transforms[i] = TransformAt(i * 1.0 / _samples); if (additionalRotation != 0.0) { - transforms[i].Rotate(transforms[i].ZAxis, additionalRotation, transforms[i].Origin); + transforms[i].RotateAboutPoint(transforms[i].Origin, transforms[i].ZAxis, additionalRotation); } } return transforms; diff --git a/Elements/src/Geometry/Curve.cs b/Elements/src/Geometry/Curve.cs index 6597793e9..3081063df 100644 --- a/Elements/src/Geometry/Curve.cs +++ b/Elements/src/Geometry/Curve.cs @@ -40,7 +40,7 @@ public virtual Transform[] Frames(double startSetback = 0.0, transforms[i] = TransformAt(parameters[i]); if (additionalRotation != 0.0) { - transforms[i].Rotate(transforms[i].ZAxis, additionalRotation, transforms[i].Origin); + transforms[i].RotateAboutPoint(transforms[i].Origin, transforms[i].ZAxis, additionalRotation); } } return transforms; diff --git a/Elements/src/Geometry/Polygon.cs b/Elements/src/Geometry/Polygon.cs index 0f7513186..ccc84e33e 100644 --- a/Elements/src/Geometry/Polygon.cs +++ b/Elements/src/Geometry/Polygon.cs @@ -2052,7 +2052,7 @@ public override Transform[] Frames(double startSetback = 0.0, result[i] = CreateMiterTransform(i, a, up); if (additionalRotation != 0.0) { - result[i].Rotate(result[i].ZAxis, additionalRotation, result[i].Origin); + result[i].RotateAboutPoint(result[i].Origin, result[i].ZAxis, additionalRotation); } } return result; diff --git a/Elements/src/Geometry/Polyline.cs b/Elements/src/Geometry/Polyline.cs index ab7660b28..ac7dc74b3 100644 --- a/Elements/src/Geometry/Polyline.cs +++ b/Elements/src/Geometry/Polyline.cs @@ -396,7 +396,7 @@ public override Transform[] Frames(double startSetback = 0.0, result[i] = CreateOrthogonalTransform(i, a, normals[i]); if (additionalRotation != 0.0) { - result[i].Rotate(result[i].ZAxis, additionalRotation, result[i].Origin); + result[i].RotateAboutPoint(result[i].Origin, result[i].ZAxis, additionalRotation); } } return result; @@ -1114,7 +1114,7 @@ public bool Intersects(Polygon polygon, out List sharedSegments) /// New polyline or null if any of points is not on polyline public Polyline GetSubsegment(Vector3 start, Vector3 end) { - if(start.IsAlmostEqualTo(end)) + if (start.IsAlmostEqualTo(end)) { return null; } @@ -1122,7 +1122,7 @@ public Polyline GetSubsegment(Vector3 start, Vector3 end) var startParameter = GetParameterAt(start); var endParameter = GetParameterAt(end); - if(startParameter < 0 || endParameter < 0) + if (startParameter < 0 || endParameter < 0) { return null; } @@ -1132,7 +1132,7 @@ public Polyline GetSubsegment(Vector3 start, Vector3 end) var vertices = new List(); var lastVertex = Vector3.Origin; - if(startParameter < endParameter) + if (startParameter < endParameter) { firstParameter = startParameter; lastParameter = endParameter; diff --git a/Elements/src/Geometry/Transform.cs b/Elements/src/Geometry/Transform.cs index 3720fa473..aa05b758b 100644 --- a/Elements/src/Geometry/Transform.cs +++ b/Elements/src/Geometry/Transform.cs @@ -388,26 +388,6 @@ public void Rotate(Vector3 axis, double angle) this.Matrix *= m; } - /// - /// Apply a rotation to the transform around a point. - /// - /// The axis of rotation. - /// The angle of rotation in degrees. - /// The point around which to rotate. - public void Rotate(Vector3 axis, double angle, Vector3 origin) - { - var mT1 = new Matrix(); - mT1.SetupTranslation(origin.Negate()); - - var mT2 = new Matrix(); - mT2.SetupTranslation(origin); - - var mR = new Matrix(); - mR.SetupRotate(axis, angle * (Math.PI / 180.0)); - - this.Matrix = this.Matrix * mT1 * mR * mT2; - } - /// /// Apply a rotation to the transform around the Z axis. /// From 59e4f4e1c62ef0b96da488fb732e2bd7850ea467 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Mon, 8 Aug 2022 21:07:24 -0700 Subject: [PATCH 14/15] Pickup PR comment. --- Elements/src/Geometry/Polygons.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Elements/src/Geometry/Polygons.cs b/Elements/src/Geometry/Polygons.cs index d2e4f368c..df2920bfd 100644 --- a/Elements/src/Geometry/Polygons.cs +++ b/Elements/src/Geometry/Polygons.cs @@ -37,7 +37,7 @@ public static Polygon Rectangle(Vector3 min, Vector3 max) var c = max; var d = new Vector3(min.X, max.Y); - return new Polygon(true, new[] { a, b, c, d }); + return new Polygon(true, a, b, c, d); } /// From 1ef7dce2097bfded5e7e27ce98a7c18c12d03e5d Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Mon, 8 Aug 2022 21:08:51 -0700 Subject: [PATCH 15/15] Use params version everywhere. --- Elements/src/Geometry/Polygons.cs | 4 ++-- Elements/src/Geometry/Profiles/WideFlangeProfile.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Elements/src/Geometry/Polygons.cs b/Elements/src/Geometry/Polygons.cs index df2920bfd..f54f08d08 100644 --- a/Elements/src/Geometry/Polygons.cs +++ b/Elements/src/Geometry/Polygons.cs @@ -21,7 +21,7 @@ public static Polygon Rectangle(double width, double height) var c = new Vector3(width / 2, height / 2); var d = new Vector3(-width / 2, height / 2); - return new Polygon(true, new[] { a, b, c, d }); + return new Polygon(true, a, b, c, d); } /// @@ -111,7 +111,7 @@ public static Polygon L(double width, double length, double thickness) var d = new Vector3(thickness, thickness, 0); var e = new Vector3(thickness, length, 0); var f = new Vector3(0, length, 0); - return new Polygon(true, new[] { a, b, c, d, e, f }); + return new Polygon(true, a, b, c, d, e, f); } /// diff --git a/Elements/src/Geometry/Profiles/WideFlangeProfile.cs b/Elements/src/Geometry/Profiles/WideFlangeProfile.cs index 49b7bffcb..08c125ce3 100644 --- a/Elements/src/Geometry/Profiles/WideFlangeProfile.cs +++ b/Elements/src/Geometry/Profiles/WideFlangeProfile.cs @@ -227,7 +227,7 @@ private static Polygon CreateProfile(double bf, var l = new Vector3(o.X + width / 2 + horizontalOffset, o.Y + height / 2 - thicknessFlange + verticalOffset); var m = new Vector3(o.X + width / 2 + horizontalOffset, o.Y + height / 2 + verticalOffset); - return new Polygon(false, new[] { a, b, c, e, f, g, h, i, j, k, l, m }); + return new Polygon(false, a, b, c, e, f, g, h, i, j, k, l, m); } } } \ No newline at end of file