C# PointByCoordinates
In this section were going to finish the PointByCoordinates class and create Unit Tests for it.
Let’ start of with some changes to the Abstract Classes, Base Classes, and Interfaces.
Within the IPointBase3D Interface, I added a TranslatePointAlongVector method, to aid in calculating the points location relative to an AxisSystem.
//IPointBase3D.cs using GeometryCoreProject.AbstractClasses; namespace GeometryCoreProject.WireframeBaseInterfaces { public interface IPointBase3D : IGeometryBase { double X { get; } double Y { get; } double Z { get; } IPointBase3D TranslatePointAlongVector(IPointBase3D iRefPoint, IVectorBase3D iRefVector, double iOffset); void SetCoordinates(double[] iCoordinates); double[] GetCoordinates(); } }
Within the PointBase3D Class, I created the method for the TranslatePointAlongVector, and also changed the SetCoordinates method to be a Virtual method so it can be overridden higher up the code stack. Also I added two operator methods, an Add and Subtract to make adding and subtracting points from each other easier. Finally the Equal method was fully defined so we can validate if two points are equal or not.
//PointBase3D.cs using GeometryCoreProject.AbstractClasses; using GeometryCoreProject.WireframeBaseInterfaces; namespace GeometryCoreProject.WireframeBaseClasses { public class PointBase3D : GeometryBase , IPointBase3D { public double X { get; set; } = 0; public double Y { get; set; } = 0; public double Z { get; set; } = 0; public PointBase3D() { } public PointBase3D(double iX, double iY, double iZ) { this.X = iX; this.Y = iY;this.Z = iZ; } public IPointBase3D TranslatePointAlongVector(IPointBase3D iRefPoint, IVectorBase3D iRefVector, double iOffset) { double X = (iRefVector.UnitVector.I * iOffset) + iRefPoint.X; double Y = (iRefVector.UnitVector.J * iOffset) + iRefPoint.Y; double Z = (iRefVector.UnitVector.K * iOffset) + iRefPoint.Z; IPointBase3D ReturnPoint = new PointBase3D(); ReturnPoint.SetCoordinates(new double[]{ X,Y,Z}); return ReturnPoint; } public virtual void SetCoordinates(double[] iCoordinates) { this.X = iCoordinates[0]; this.Y = iCoordinates[1]; this.Z = iCoordinates[2]; } public double[] GetCoordinates() { return new double[] { this.X, this.Y, this.Z }; } public static IPointBase3D operator +(PointBase3D iPoint1, PointBase3D iPoint2) { return new PointBase3D(iPoint1.X+iPoint2.X, iPoint1.Y + iPoint2.Y, iPoint1.Z + iPoint2.Z); } public static IPointBase3D operator -(PointBase3D iPoint1, PointBase3D iPoint2) { return new PointBase3D(iPoint1.X - iPoint2.X, iPoint1.Y - iPoint2.Y, iPoint1.Z - iPoint2.Z); } public override bool Equals(object obj) { bool ReturnBoolean = false; if((IPointBase3D)obj is IPointBase3D) { IPointBase3D ValidationPoint = (IPointBase3D)obj; if (this.X.Equals(ValidationPoint.X) && this.Y.Equals(ValidationPoint.Y) && this.Z.Equals(ValidationPoint.Z)) { ReturnBoolean = true; } } return ReturnBoolean; } public override int GetHashCode() { return 1; } } }
Within the IVectorBase3D Interface and additional property was added for the Inverse Vector.
//IVectorBase3D.cs namespace GeometryCoreProject.WireframeBaseInterfaces { public interface IVectorBase3D { double I { get; set; } double J { get; set; } double K { get; set; } double Magnitude { get; } IVectorBase3D UnitVector { get; } IVectorBase3D InverseVector { get; } bool Equals(object obj); int GetHashCode(); } }
And in the Implementation VectorBase3D Class, the Inverse Vector Property was defined and two constructors created, one default constructor and one which take I,J,K parameters.
//VectorBase3D.cs using GeometryCoreProject.AbstractClasses; using GeometryCoreProject.WireframeBaseInterfaces; using System; namespace GeometryCoreProject.WireframeBaseClasses { public class VectorBase3D : GeometryBase, IVectorBase3D { public double I { get; set; } public double J { get; set; } public double K { get; set; } public double Magnitude => Math.Sqrt(Math.Pow(this.I,2)+ Math.Pow(this.J,2) + Math.Pow(this.K,2)); public IVectorBase3D UnitVector { get { IVectorBase3D UnitVector = new VectorBase3D(); UnitVector.I = (this.I / this.Magnitude); UnitVector.J = (this.J / this.Magnitude); UnitVector.K = (this.K / this.Magnitude); return UnitVector; } } public IVectorBase3D InverseVector { get { IVectorBase3D InverseVector = new VectorBase3D(); InverseVector.I = (-this.I); InverseVector.J = (-this.J); InverseVector.K = (-this.K); return InverseVector; } } public VectorBase3D() { } public VectorBase3D(double iI, double iJ, double iK) { this.I = iI; this.J = iJ; this.K = iK; } public override bool Equals(object obj) { return false; } public override int GetHashCode() { return 1; } } }
A lot has changed within the PointByCoordinates Class, all of the methods and property’s have been defined to allow the correct creation of a Point By Coordinates, with either a Reference Axis System or a Reference Point (but not both).
//PointByCoordinates.cs using GeometryCoreProject.WireframeBaseClasses; using GeometryCoreProject.WireframeBaseInterfaces; using GeometryTypesProject.GeometryTypesInterfaces; using System; namespace GeometryTypesProject.GeometryTypesClasses { public class PointByCoordinates : PointBase3D, IPointByCoordinates { IPointBase3D _BasePoint { get; set; } = new PointBase3D(); public IPointBase3D RefPoint { get; set; } = new PointBase3D(); public IAxisSystemBase3D RefAxisSystem { get; set; } = null; public void SetX(double iX){this.X = iX;} public void SetY(double iY){this.X = iY;} public void SetZ(double iZ){this.X = iZ;} public void SetXYZ(double iX, double iY, double iZ) { this.X = iX; this.Y = iY; this.Z = iZ; } public override void SetCoordinates(double[] iCoordinates) { this._BasePoint.SetCoordinates(iCoordinates); if (this.RefAxisSystem == null) { base.SetCoordinates(((PointBase3D)this._BasePoint + (PointBase3D)this.RefPoint).GetCoordinates()); } else { IPointBase3D TranslatedPoint = this.PointRelativeToAxisSystem(this.RefAxisSystem); base.SetCoordinates(((PointBase3D)TranslatedPoint + (PointBase3D)this.RefPoint).GetCoordinates()); } } public PointByCoordinates() { } public PointByCoordinates(double iX, double iY,double iZ) { this.X = iX; this.Y = iY; this.Z = iZ; } public PointByCoordinates(double iX, double iY, double iZ, IPointBase3D iRefPoint) { this.X = iRefPoint.X + iX; this.Y = iRefPoint.Y + iY; this.Z = iRefPoint.Z + iZ; } public PointByCoordinates(double iX, double iY, double iZ, IAxisSystemBase3D iRefAxisSystem) { this.RefAxisSystem = iRefAxisSystem; IPointBase3D NewPoint = this.TranslatePointAlongVector(iRefAxisSystem.Origin, iRefAxisSystem.XAxis,iX); NewPoint = this.TranslatePointAlongVector(NewPoint, iRefAxisSystem.YAxis, iY); NewPoint = this.TranslatePointAlongVector(NewPoint, iRefAxisSystem.ZAxis, iZ); this.X = NewPoint.X; this.Y = NewPoint.Y; this.Z = NewPoint.Z; } public PointByCoordinates(double iX, double iY, double iZ, IPointBase3D iRefPoint, IAxisSystemBase3D iRefAxisSystem ) { this.RefAxisSystem = iRefAxisSystem; this.RefPoint = iRefPoint; } public void RemoveRefPoint() { this.RefPoint = new PointBase3D(); this.SetCoordinates(this._BasePoint.GetCoordinates()); } public void RemoveRefAxisSystem() { this.RefPoint = new PointBase3D(); this.RefAxisSystem = null; this.SetCoordinates(this._BasePoint.GetCoordinates()); } public void AddRefPoint(IPointBase3D iRefPoint) { if (this.RefAxisSystem != null) { throw new Exception("The Point Already Uses a Reference AxisSystem."); } // Store the Original Coordinates this._BasePoint.SetCoordinates(this.GetCoordinates()); // Set the Reference Point this.RefPoint = iRefPoint; // Update the Coordinates this.SetCoordinates(this._BasePoint.GetCoordinates()); } public void AddRefAxisSystem(IAxisSystemBase3D iRefAxisSystem) { if (!this.RefPoint.Equals(new PointBase3D())) { throw new Exception("The Point Already Uses a Reference Point."); } // Store the Original Coordinates this._BasePoint.SetCoordinates(this.GetCoordinates()); // Set the Reference Point this.RefPoint = iRefAxisSystem.Origin; // Set the Reference AxisSystem this.RefAxisSystem = iRefAxisSystem; // Update the Coordinates this.SetCoordinates(this._BasePoint.GetCoordinates()); } private IPointBase3D PointRelativeToAxisSystem(IAxisSystemBase3D iRefAxisSystem) { IPointBase3D ReturnPoint = new PointBase3D(); ReturnPoint = this.TranslatePointAlongVector(ReturnPoint, iRefAxisSystem.XAxis, this._BasePoint.X); ReturnPoint = this.TranslatePointAlongVector(ReturnPoint, iRefAxisSystem.YAxis, this._BasePoint.Y); ReturnPoint = this.TranslatePointAlongVector(ReturnPoint, iRefAxisSystem.ZAxis, this._BasePoint.Z); return ReturnPoint; } } }
Unit Testing
No Good Class is without a Unit Test to aid with Development and Production testing, there are Ten unit tests, which tests all of the end user interactions. Form the creation of a simple PointByCoordinate, to updating the initial XYZ values, Adding Reference Points and Reference AxisSystem’s and removing them.
This will help ensure we don’t break anything while making changes later on.
To do this first we need to add additional Solution Folders, Projects and Classes, not forgetting a couple of NuGet Packages.
To build the Unit tests we need to add addition NuGet Packages, like FluentAssertions which will make building the unit testing easier.
Finally we can add a new Class and build our Unit Test for PointByCoordinates.
Note :- on some of the cases I had to use System.Math.Round to simplify the resulting value so that the Should().Be(Value) could be kept simple, and ensure an exact match each time.
//PointByCoordinatesUnitTest.cs using GeometryFactoriesProject.FactoryBaseClasses; using GeometryTypesProject.GeometryTypesInterfaces; using GeometryCoreProject.WireframeBaseInterfaces; using GeometryCoreProject.WireframeBaseClasses; using System; using Xunit; using FluentAssertions; namespace GeometryUnitTestsProject.PointByCoordinates { public class PointByCoordinatesUnitTest { // Create an Instance of the Factory WireframeFactory _WireframeFactory = new WireframeFactory(); [Fact] // Create Point at 10,10,10 public void PointByCoordinatesTest1() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(10); PointByCoordinates.Y.Should().Be(10); PointByCoordinates.Z.Should().Be(10); } [Fact] // Create Point at 10,10,10 then Update the Values to 12,23,34 public void PointByCoordinatesTest2() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Update Point Locations PointByCoordinates.SetCoordinates(new double[] { 12,23,34}); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(12); PointByCoordinates.Y.Should().Be(23); PointByCoordinates.Z.Should().Be(34); } [Fact] // Create a Point at 10,10,10 then Add a Reference Point at 20,20,20 public void PointByCoordinatesTest3() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferencePoint IPointByCoordinates RefPointByCoordinates = this._WireframeFactory.AddNewPointCoord(20, 20, 20); //Set the ReferencePoint PointByCoordinates.AddRefPoint(RefPointByCoordinates); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(30); PointByCoordinates.Y.Should().Be(30); PointByCoordinates.Z.Should().Be(30); } [Fact] /* Create a Point at 10,10,10 then Add a Reference Point at 20,20,20, then Update the Values to 12,23,34*/ public void PointByCoordinatesTest4() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferencePoint IPointByCoordinates RefPointByCoordinates = this._WireframeFactory.AddNewPointCoord(20, 20, 20); //Set the ReferencePoint PointByCoordinates.AddRefPoint(RefPointByCoordinates); // Update Point Locations PointByCoordinates.SetCoordinates(new double[] { 12, 23, 34 }); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(32); PointByCoordinates.Y.Should().Be(43); PointByCoordinates.Z.Should().Be(54); } [Fact] /* Create a Point at 10,10,10 then Add a Reference Point at 20,20,20, then Update the Values to 12,23,34, then Remove the Reference Point*/ public void PointByCoordinatesTest5() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferencePoint IPointByCoordinates RefPointByCoordinates = this._WireframeFactory.AddNewPointCoord(20, 20, 20); // Set the ReferencePoint PointByCoordinates.AddRefPoint(RefPointByCoordinates); // Update Point Locations PointByCoordinates.SetCoordinates(new double[] { 12, 23, 34 }); // Remove Reference Point PointByCoordinates.RemoveRefPoint(); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(12); PointByCoordinates.Y.Should().Be(23); PointByCoordinates.Z.Should().Be(34); } [Fact] /* Create a Point at 10,10,10 then Add a Reference AxisSystem*/ public void PointByCoordinatesTest6() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferenceAxisSystem IAxisSystemBase3D RefAxisSystem = new AxisSystemBase3D(); RefAxisSystem.Origin = this._WireframeFactory.AddNewPointCoord(20, 20, 20); RefAxisSystem.XAxis = new VectorBase3D(0.707106, 0.707106,0); RefAxisSystem.YAxis = new VectorBase3D(-0.707106, 0.707106, 0); RefAxisSystem.ZAxis = new VectorBase3D(0, 0, 1); // Set the ReferenceAxisSystem PointByCoordinates.AddRefAxisSystem(RefAxisSystem); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(20); Math.Round(PointByCoordinates.Y,3).Should().Be(34.142); PointByCoordinates.Z.Should().Be(30); } [Fact] /* Create a Point at 10,10,10 then Add a Reference AxisSystem then then Update the Values to 12,23,34*/ public void PointByCoordinatesTest7() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferenceAxisSystem IAxisSystemBase3D RefAxisSystem = new AxisSystemBase3D(); RefAxisSystem.Origin = this._WireframeFactory.AddNewPointCoord(20, 20, 20); RefAxisSystem.XAxis = new VectorBase3D(0.707106, 0.707106, 0); RefAxisSystem.YAxis = new VectorBase3D(-0.707106, 0.707106, 0); RefAxisSystem.ZAxis = new VectorBase3D(0, 0, 1); // Set the ReferenceAxisSystem PointByCoordinates.AddRefAxisSystem(RefAxisSystem); // Update Point Locations PointByCoordinates.SetCoordinates(new double[] { 12, 23, 34 }); // Test the Point X,Y,Z Values Math.Round(PointByCoordinates.X,3).Should().Be(12.222); Math.Round(PointByCoordinates.Y, 3).Should().Be(44.749); PointByCoordinates.Z.Should().Be(54); } [Fact] /* Create a Point at 10,10,10 then Add a Reference AxisSystem then then Update the Values to 12,23,34 then Remove the Reference AxisSystem*/ public void PointByCoordinatesTest8() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferenceAxisSystem IAxisSystemBase3D RefAxisSystem = new AxisSystemBase3D(); RefAxisSystem.Origin = this._WireframeFactory.AddNewPointCoord(20, 20, 20); RefAxisSystem.XAxis = new VectorBase3D(0.707106, 0.707106, 0); RefAxisSystem.YAxis = new VectorBase3D(-0.707106, 0.707106, 0); RefAxisSystem.ZAxis = new VectorBase3D(0, 0, 1); // Set the ReferenceAxisSystem PointByCoordinates.AddRefAxisSystem(RefAxisSystem); // Update Point Locations PointByCoordinates.SetCoordinates(new double[] { 12, 23, 34 }); // Remove the Referene AxisSystem PointByCoordinates.RemoveRefAxisSystem(); // Test the Point X,Y,Z Values PointByCoordinates.X.Should().Be(12); PointByCoordinates.Y.Should().Be(23); PointByCoordinates.Z.Should().Be(34); } [Fact] /* Create a Point at 10,10,10 then Add a Reference Point at 20,20,20 then Add a Reference AxisSystem, to Throw Exception */ public void PointByCoordinatesTest9() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferencePoint IPointByCoordinates RefPointByCoordinates = this._WireframeFactory.AddNewPointCoord(20, 20, 20); //Set the ReferencePoint PointByCoordinates.AddRefPoint(RefPointByCoordinates); // Create ReferenceAxisSystem IAxisSystemBase3D RefAxisSystem = new AxisSystemBase3D(); RefAxisSystem.Origin = this._WireframeFactory.AddNewPointCoord(20, 20, 20); RefAxisSystem.XAxis = new VectorBase3D(0.707106, 0.707106, 0); RefAxisSystem.YAxis = new VectorBase3D(-0.707106, 0.707106, 0); RefAxisSystem.ZAxis = new VectorBase3D(0, 0, 1); // Set the ReferenceAxisSystem Action comparison = () => { PointByCoordinates.AddRefAxisSystem(RefAxisSystem); }; comparison.Should().Throw<Exception>(); } [Fact] /* Create a Point at 10,10,10 then Add a Reference AxisSystem then Add a Reference Point at 20,20,20, to Throw Exception */ public void PointByCoordinatesTest10() { // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = this._WireframeFactory.AddNewPointCoord(10, 10, 10); // Create ReferenceAxisSystem IAxisSystemBase3D RefAxisSystem = new AxisSystemBase3D(); RefAxisSystem.Origin = this._WireframeFactory.AddNewPointCoord(20, 20, 20); RefAxisSystem.XAxis = new VectorBase3D(0.707106, 0.707106, 0); RefAxisSystem.YAxis = new VectorBase3D(-0.707106, 0.707106, 0); RefAxisSystem.ZAxis = new VectorBase3D(0, 0, 1); // Set the ReferenceAxisSystem PointByCoordinates.AddRefAxisSystem(RefAxisSystem); // Create ReferencePoint IPointByCoordinates RefPointByCoordinates = this._WireframeFactory.AddNewPointCoord(20, 20, 20); //Set the ReferencePoint Action comparison = () => { PointByCoordinates.AddRefPoint(RefPointByCoordinates); }; comparison.Should().Throw<Exception>(); } } }