From 9b293b39b8dd1e47baec37d93d051e0e8eaa8184 Mon Sep 17 00:00:00 2001 From: Guido Maciocci Date: Sat, 17 Jul 2021 11:58:44 +1000 Subject: [PATCH] Refactor Plane. Add validity check. --- .../Core/TrigonometryTests.cs | 4 +- src/GShark.Test.XUnit/Geometry/PlaneTests.cs | 17 ++++- .../Operation/FittingTests.cs | 2 +- src/GShark/Core/Trigonometry.cs | 2 +- src/GShark/Geometry/Plane.cs | 64 +++++++++++++++---- src/GShark/Operation/Tessellation.cs | 2 +- 6 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/GShark.Test.XUnit/Core/TrigonometryTests.cs b/src/GShark.Test.XUnit/Core/TrigonometryTests.cs index 7caea608..0d38d41f 100644 --- a/src/GShark.Test.XUnit/Core/TrigonometryTests.cs +++ b/src/GShark.Test.XUnit/Core/TrigonometryTests.cs @@ -32,7 +32,7 @@ public void It_Returns_True_If_Three_Points_Are_Collinear() Point3 pt3 = new Point3( 51.299, 37.950, 0.0); // Assert - Trigonometry.AreThreePointsCollinear(pt1, pt2, pt3, GeoSharkMath.MinTolerance).Should().BeTrue(); + Trigonometry.ArePointsCollinear(pt1, pt2, pt3, GeoSharkMath.MinTolerance).Should().BeTrue(); } [Fact] @@ -44,7 +44,7 @@ public void It_Returns_False_If_Three_Points_Are_Not_Collinear() Point3 pt3 = new Point3( 51.299, 37.950, 0.0); // Assert - Trigonometry.AreThreePointsCollinear(pt1, pt2, pt3, GeoSharkMath.MinTolerance).Should().BeFalse(); + Trigonometry.ArePointsCollinear(pt1, pt2, pt3, GeoSharkMath.MinTolerance).Should().BeFalse(); } [Theory] diff --git a/src/GShark.Test.XUnit/Geometry/PlaneTests.cs b/src/GShark.Test.XUnit/Geometry/PlaneTests.cs index 28a3eb11..893c14d7 100644 --- a/src/GShark.Test.XUnit/Geometry/PlaneTests.cs +++ b/src/GShark.Test.XUnit/Geometry/PlaneTests.cs @@ -37,6 +37,19 @@ public void It_Initializes_A_Plane() plane.Origin.Equals(origin).Should().BeTrue(); } + [Fact] + public void It_Checks_If_Plane_Is_Valid() + { + //Arrange + var validPlane = new Plane(new Point3(5, 5, 5), new Vector3(10, 0, 0), new Vector3(0, 5, 0)); + var invalidPlane = new Plane(validPlane); + invalidPlane.XAxis = new Vector3(5,5,5); + + //Assert + validPlane.IsValid.Should().BeTrue(); + invalidPlane.IsValid.Should().BeFalse(); + } + [Fact] public void It_Trows_An_Exception_If_The_Three_Point_Are_Collinear() { @@ -54,7 +67,7 @@ public void It_Trows_An_Exception_If_The_Three_Point_Are_Collinear() } [Fact] - public void It_Creates_A_Plane_By_Tree_Points() + public void It_Creates_A_Plane_By_Three_Points() { // Arrange var pt1 = new Point3(20, 20, 0); @@ -92,7 +105,7 @@ public void It_Returns_A_Flipped_Plane() { // Arrange var plane = Plane.PlaneXY; - var expectedPlane = new Plane(Plane.PlaneXY.Origin, -plane.XAxis, plane.YAxis); + var expectedPlane = new Plane(Plane.PlaneXY.Origin, plane.YAxis, plane.XAxis); // Act Plane flippedPlane = plane.Flip(); diff --git a/src/GShark.Test.XUnit/Operation/FittingTests.cs b/src/GShark.Test.XUnit/Operation/FittingTests.cs index 555c0364..88bfcb9d 100644 --- a/src/GShark.Test.XUnit/Operation/FittingTests.cs +++ b/src/GShark.Test.XUnit/Operation/FittingTests.cs @@ -85,7 +85,7 @@ public void Returns_A_Sets_Of_Interpolated_Beziers_From_A_Collection_Of_Points() crvs.Count.Should().Be(4); for (int i = 0; i < crvs.Count - 1; i++) { - bool areCollinear = Trigonometry.AreThreePointsCollinear(crvs[i].LocationPoints[2], crvs[i].LocationPoints[3], + bool areCollinear = Trigonometry.ArePointsCollinear(crvs[i].LocationPoints[2], crvs[i].LocationPoints[3], crvs[i + 1].LocationPoints[1]); areCollinear.Should().BeTrue(); } diff --git a/src/GShark/Core/Trigonometry.cs b/src/GShark/Core/Trigonometry.cs index f2589c0a..40725308 100644 --- a/src/GShark/Core/Trigonometry.cs +++ b/src/GShark/Core/Trigonometry.cs @@ -46,7 +46,7 @@ public static bool ArePointsCoplanar(IList points) /// Third point. /// Tolerance ser per default as 1e-6 /// True if the three points are collinear. - public static bool AreThreePointsCollinear(Point3 pt1, Point3 pt2, Point3 pt3, double tol = 1e-6) + public static bool ArePointsCollinear(Point3 pt1, Point3 pt2, Point3 pt3, double tol = GeoSharkMath.MaxTolerance) { // Find the area of the triangle without using square root and multiply it for 0.5 // http://www.stumblingrobot.com/2016/05/01/use-cross-product-compute-area-triangles-given-vertices/ diff --git a/src/GShark/Geometry/Plane.cs b/src/GShark/Geometry/Plane.cs index 905b5dc8..505e5fa1 100644 --- a/src/GShark/Geometry/Plane.cs +++ b/src/GShark/Geometry/Plane.cs @@ -19,9 +19,9 @@ public class Plane : IEquatable, ITransformable /// The vector representing the normal of the plane. public Plane(Point3 origin, Vector3 direction) { - ZAxis = direction.Unitize(); - XAxis = Vector3.XAxis.PerpendicularTo(ZAxis).Unitize(); - YAxis = Vector3.CrossProduct(ZAxis, XAxis).Unitize(); + var normal = direction.Unitize(); + XAxis = Vector3.XAxis.PerpendicularTo(normal).Unitize(); + YAxis = Vector3.CrossProduct(normal, XAxis).Unitize(); Origin = origin; } @@ -33,7 +33,7 @@ public Plane(Point3 origin, Vector3 direction) /// Third point representing the y direction. public Plane(Point3 pt1, Point3 pt2, Point3 pt3) { - if(LinearAlgebra.Orientation(pt1, pt2, pt3) == 0) + if(Trigonometry.ArePointsCollinear(pt1, pt2, pt3)) { throw new Exception("Plane cannot be created, the tree points must not be collinear"); } @@ -45,7 +45,6 @@ public Plane(Point3 pt1, Point3 pt2, Point3 pt3) Origin = pt1; XAxis = dir1.Unitize(); YAxis = Vector3.CrossProduct(normal, dir1).Unitize(); - ZAxis = normal.Unitize(); } /// @@ -59,7 +58,19 @@ public Plane(Point3 origin, Vector3 xDirection, Vector3 yDirection) Origin = origin; XAxis = xDirection.IsUnitVector ? xDirection : xDirection.Unitize(); YAxis = yDirection.IsUnitVector ? yDirection : yDirection.Unitize(); - ZAxis = Vector3.CrossProduct(XAxis, YAxis); + + } + + /// + /// Constructs a plane from another plane. + /// + /// Input plane. + public Plane(Plane plane) + { + Origin = plane.Origin; + XAxis = plane.XAxis; + YAxis = plane.YAxis; + } /// @@ -78,29 +89,53 @@ public Plane(Point3 origin, Vector3 xDirection, Vector3 yDirection) public static Plane PlaneZX => new Plane(Point3.Origin, Vector3.ZAxis, Vector3.XAxis); /// - /// Gets the normal of the plan. + /// Gets a plane whose components are Unset. + /// + public static Plane Unset => new Plane(Point3.Unset, Vector3.Unset, Vector3.Unset); + + /// + /// Gets the normal of the plane. /// public Vector3 Normal => ZAxis; /// /// Gets the origin of the plane. /// - public Point3 Origin { get; } + public Point3 Origin { get; set; } /// /// Gets the XAxis of the plane. /// - public Vector3 XAxis { get; } + public Vector3 XAxis { get; set; } /// /// Gets the YAxis of the plane. /// - public Vector3 YAxis { get; } + public Vector3 YAxis { get; set; } /// /// Gets the ZAxis of the plane. /// - public Vector3 ZAxis { get; } + public Vector3 ZAxis => Vector3.CrossProduct(XAxis, YAxis).Unitize(); + + /// + /// Returns true if origin and axis vectors are valid and orthogonal. + /// + public bool IsValid + { + get + { + //check all axes and origin are valid + var areAxesValid = Origin.IsValid && XAxis.IsValid && YAxis.IsValid && ZAxis.IsValid; + + //check that vectors are orthogonal + var areAxesOrthogonal = Vector3.DotProduct(XAxis, YAxis) == 0 && Vector3.DotProduct(YAxis, ZAxis) == 0; + + if (areAxesValid && areAxesOrthogonal) return true; + + return false; + } + } /// /// Finds the closest point on a plane. @@ -160,13 +195,14 @@ public Point3 PointAt(double u, double v) } /// - /// Swapping out the X and Y axes and inverting the Z axis. + /// Flip plane by swapping X and Y axes. /// /// The flipped plane. public Plane Flip() { - //ToDo flip by reversing X, Y, or swapping X and Y. - return new Plane(Origin, -XAxis, YAxis); + var xAxis = YAxis; + var yAxis = XAxis; + return new Plane(Origin, xAxis, yAxis); } /// diff --git a/src/GShark/Operation/Tessellation.cs b/src/GShark/Operation/Tessellation.cs index c5e2626f..f904c9f1 100644 --- a/src/GShark/Operation/Tessellation.cs +++ b/src/GShark/Operation/Tessellation.cs @@ -92,7 +92,7 @@ public static (List tValues, List pts) CurveAdaptiveSampleRange( Vector3 diff2 = pt1 - pt2; if ((Vector3.DotProduct(diff, diff) < setTolerance && Vector3.DotProduct(diff2, diff2) > setTolerance) - || !Trigonometry.AreThreePointsCollinear(pt1, pt2, pt3, setTolerance)) + || !Trigonometry.ArePointsCollinear(pt1, pt2, pt3, setTolerance)) { // Get the exact middle value or a random value start + (end - start) * (0.45 + 0.1 * random.NextDouble()); double tMiddle = start + (end - start) * 0.5;