C# Geometry Engine Base Classes
Just a quick side note, I’m not a classically trained developer, so there maybe some mistakes in here, so be kind, and hopefully I can learn from you as much as you can learn from me.
So, we’re going to start of with a geometry base class for our geometry engine. So let’s start out in visual studio with a Solution, Projects(.Net Core Class Library’s), Classes and a .Net Core Console Project.
Many of these are just going to be placeholders, for this post and in future posts well concentrate on other areas.
Let’s start of with the Solution, Projects and Folders.
Next, the Geometry Core Abstract Class and it’s Interface.
The Interface for now wont have anything but later on we will add some additional content.
//IGeometryBase.cs namespace GeometryCoreProject.AbstractClasses { public interface IGeometryBase{} }
The Abstract object will inherit the System Object class and implement the Geometry Base Interface. All of our geometry objects will have a UUID, Name and Color. Also notice we will ensure that each derived class will override Equals and GetHashCode, these are really useful.
//GeometryBase.cs using System; using System.Drawing; namespace GeometryCoreProject.AbstractClasses { public class GeometryBase : Object, IGeometryBase { public Guid UUID { get; private set; } public string Name { get; set; } public Color Color { get; set; } protected internal GeometryBase() { this.UUID = Guid.NewGuid(); } public abstract override bool Equals(object obj); public abstract override int GetHashCode(); } }
Next well setup the Wireframe Base Interfaces
Again these will be stubbed out and later on we’ll add more content. Since we are using interfaces, we want to include the two methods for Equals and GetHashCode also.
//IAxisSystemBase3D.cs namespace GeometryCoreProject.WireframeBaseInterfaces { public interface IAxisSystemBase3D { IPointBase3D Origin { get; set; } IVectorBase3D XAxis { get; set; } IVectorBase3D YAxis { get; set; } IVectorBase3D ZAxis { get; set; } bool Equals(object obj); int GetHashCode(); } }
//ILineBase3D.cs namespace GeometryCoreProject.WireframeBaseInterfaces { public interface ILineBase3D { bool Equals(object obj); int GetHashCode(); } }
//IPlaneBase3D.cs namespace GeometryCoreProject.WireframeBaseInterfaces { public interface IPlaneBase3D { bool Equals(object obj); int GetHashCode(); } }
//IPointBase3D.cs using GeometryCoreProject.AbstractClasses; namespace GeometryCoreProject.WireframeBaseInterfaces { public interface IPointBase3D : IGeometryBase { double X { get; } double Y { get; } double Z { get; } void SetCoordinates(double[] iCoordinates); double[] GetCoordinates(); bool Equals(object obj); int GetHashCode(); } }
//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; } bool Equals(object obj); int GetHashCode(); } }
Now we will start to define the guts of the Wireframe Base Classes.
These base classes will support all of the wireframe types later on such as point by coordinates or point on plane will use point base class. To allow the project to build we will just stub out the two methods for Equals and GetHashCode, with some simple code.
//AxisSystemBase3D.cs using GeometryCoreProject.AbstractClasses; using GeometryCoreProject.WireframeBaseInterfaces; namespace GeometryCoreProject.WireframeBaseClasses { public class AxisSystemBase3D : GeometryBase, IAxisSystemBase3D { public IPointBase3D Origin { get; set; } public IVectorBase3D XAxis { get; set; } public IVectorBase3D YAxis { get; set; } public IVectorBase3D ZAxis { get; set; } public override bool Equals(object obj) { return false; } public override int GetHashCode() { return 1; } } }
//PointBase3D.cs using GeometryCoreProject.AbstractClasses; using GeometryCoreProject.WireframeBaseInterfaces; namespace GeometryCoreProject.WireframeBaseClasses { public class PointBase3D : GeometryBase , IPointBase3D { public double X { get; set; } public double Y { get; set; } public double Z { get; set; } public double[] GetCoordinates() { return new double[] { this.X,this.Y,this.Z}; } public void SetCoordinates(double[] iCoordinates) { this.X = iCoordinates[0]; this.Y = iCoordinates[1]; this.Z = iCoordinates[2]; } public override bool Equals(object obj) { return false; } public override int GetHashCode() { return 1; } } }
//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 override bool Equals(object obj) { return false; } public override int GetHashCode() { return 1; } } }
Finally we can create the PointByCoordinates Interface and Class.
The interface for PointByCoordinates will have any additional property’s and methods required for this specific point type.
//IPointByCoordinates.cs using GeometryCoreProject.WireframeBaseInterfaces; namespace GeometryTypesProject.GeometryTypesInterfaces { public interface IPointByCoordinates : IPointBase3D { IPointBase3D RefPoint { get; set; } IAxisSystemBase3D RefAxisSystem { get; set; } void SetX(double iX); void SetY(double iY); void SetZ(double iZ); void SetXYZ(double iX,double iY, double iZ); void RemoveRefPoint(); void RemoveRefAxisSystem(); } }
In the PointByCoordinates class well create the logic for this class.
//PointByCoordinates.cs using GeometryCoreProject.WireframeBaseClasses; using GeometryCoreProject.WireframeBaseInterfaces; using GeometryTypesProject.GeometryTypesInterfaces; namespace GeometryTypesProject.GeometryTypesClasses { public class PointByCoordinates : PointBase3D, IPointByCoordinates { public IPointBase3D RefPoint { get; set; } = null; 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 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; } private IPointBase3D TranslatePointAlongVector(IPointBase3D iRefPoint, IVectorBase3D iRefVector, double iOffset) { IPointBase3D ReturnPoint = new PointBase3D(); //ReturnPoint.X = (iRefVector.UnitVector.I * iOffset) + iRefPoint.X; //ReturnPoint.Y = (iRefVector.UnitVector.J * iOffset) + iRefPoint.Y; //ReturnPoint.Z = (iRefVector.UnitVector.K * iOffset) + iRefPoint.Z; return ReturnPoint; } public void RemoveRefPoint() { this.RefPoint = null; } public void RemoveRefAxisSystem() { this.RefAxisSystem = null; } } }
Finally, we want to create the factory’s that the end user will see.
This Interface and Abstract Class are empty for now, but we will come back to them later on.
//IFactoryBase.cs namespace GeometryFactoriesProject.AbstractClasses { public interface IFactoryBase { } }
//FactoryBase.cs namespace GeometryFactoriesProject.AbstractClasses { public abstract class FactoryBase : IFactoryBase { } }
The specific WireframeFactory will contain all the constructors for all of our different geometry types.
Ok, we should be at the top of the code stack now, at the factory level that will be exposed for our end users. Everything up till now is stubbed out with no error handling, I just wanted to show you the rough layout and how it will work from the end users perspective, with two simple functions for creating points.
So first the interface for the factory, with our two method descriptions.
//IWireframeFactory.cs using GeometryCoreProject.WireframeBaseInterfaces; using GeometryTypesProject.GeometryTypesInterfaces; namespace GeometryFactoriesProject.FactoryBaseInterfaces { public interface IWireframeFactory { IPointByCoordinates AddNewPointCoord(double iX, double iY, double iZ); IPointByCoordinates AddNewPointCoordWithReference(double iX, double iY, double iZ, IPointBase3D iRefPoint = null, IAxisSystemBase3D iRefAxisSystem = null); } }
And the class implementation with our two methods.
//WireframeFactory.cs using GeometryCoreProject.WireframeBaseInterfaces; using GeometryFactoriesProject.FactoryBaseInterfaces; using GeometryTypesProject.GeometryTypesClasses; using GeometryTypesProject.GeometryTypesInterfaces; namespace GeometryFactoriesProject.FactoryBaseClasses { public class WireframeFactory : IWireframeFactory { public WireframeFactory() { } public IPointByCoordinates AddNewPointCoord(double iX, double iY, double iZ) { return new PointByCoordinates(iX,iY,iZ); } public IPointByCoordinates AddNewPointCoordWithReference(double iX, double iY, double iZ, IPointBase3D iRefPoint = null, IAxisSystemBase3D iRefAxisSystem = null) { if(iRefPoint == null && iRefAxisSystem ==null) { return new PointByCoordinates(iX, iY, iZ); } else { if (iRefPoint == null) { return new PointByCoordinates(iX, iY, iZ, iRefAxisSystem); } else if (iRefAxisSystem == null) { return new PointByCoordinates(iX, iY, iZ, iRefPoint); } else { return new PointByCoordinates(iX, iY, iZ, iRefPoint, iRefAxisSystem); } } } } }
So now we can get to the use case.
//Program.cs using GeometryFactoriesProject.FactoryBaseClasses; using GeometryTypesProject.GeometryTypesInterfaces; using System; namespace TestConsole { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); // Create an Instance of the Factory WireframeFactory WireframeFactory = new WireframeFactory(); // Create an instance of the IPointByCoordinates IPointByCoordinates PointByCoordinates = WireframeFactory.AddNewPointCoord(10,10,10); // Update the Coordinates PointByCoordinates.SetCoordinates(new double[] { 12, 24, 36 }); // Get the Coordinates double[] Coords = PointByCoordinates.GetCoordinates(); } } }
We can now see all of the Functions, Methods, and Property’s for our PointByCoordinates object.
At run time we can also see some of the additional property’s from the abstract classes.