diff --git a/.travis.yml b/.travis.yml index 7412884..3c0d4c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ dist: trusty sudo: required language: csharp mono: none +# Use net core 2.0.0 for compatibility reasons dotnet: 2.0.0 script: - dotnet restore - dotnet build -c Release + - dotnet test AR_Lib.Tests diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b72917a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/AR_Lib.Tests/bin/Debug/netcoreapp2.2/AR_Lib.Tests.dll", + "args": [], + "cwd": "${workspaceFolder}/AR_Lib.Tests", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c744f1b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/AR_Lib.Tests/AR_Lib.Tests.csproj" + ], + "problemMatcher": "$tsc" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/AR_Lib.Tests/AR_Lib.Tests.csproj" + ], + "problemMatcher": "$tsc" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/AR_Lib.Tests/AR_Lib.Tests.csproj" + ], + "problemMatcher": "$tsc" + } + ] +} \ No newline at end of file diff --git a/AR_GeometryLibrary.sln b/AR_GeometryLibrary.sln new file mode 100644 index 0000000..fffcc0e --- /dev/null +++ b/AR_GeometryLibrary.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AR_Lib", "AR_Lib\AR_Lib.csproj", "{FD765C61-21BE-47EF-A9D0-3098534472F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AR_Lib.Tests", "AR_Lib.Tests\AR_Lib.Tests.csproj", "{C2382EC6-F096-4E84-8057-A1507E62310F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|x64.ActiveCfg = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|x64.Build.0 = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Debug|x86.Build.0 = Debug|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|Any CPU.Build.0 = Release|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|x64.ActiveCfg = Release|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|x64.Build.0 = Release|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|x86.ActiveCfg = Release|Any CPU + {FD765C61-21BE-47EF-A9D0-3098534472F2}.Release|x86.Build.0 = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|x64.ActiveCfg = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|x64.Build.0 = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|x86.ActiveCfg = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Debug|x86.Build.0 = Debug|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|Any CPU.Build.0 = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|x64.ActiveCfg = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|x64.Build.0 = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|x86.ActiveCfg = Release|Any CPU + {C2382EC6-F096-4E84-8057-A1507E62310F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AR_Lib.Tests/AR_Lib.Tests.csproj b/AR_Lib.Tests/AR_Lib.Tests.csproj new file mode 100644 index 0000000..7f9c74f --- /dev/null +++ b/AR_Lib.Tests/AR_Lib.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + diff --git a/AR_Lib.Tests/Geometry/Point3dData.cs b/AR_Lib.Tests/Geometry/Point3dData.cs new file mode 100644 index 0000000..dcce367 --- /dev/null +++ b/AR_Lib.Tests/Geometry/Point3dData.cs @@ -0,0 +1,16 @@ +using AR_Lib.Geometry; +using System.Collections; +using System.Collections.Generic; + +namespace AR_Lib.Tests.Geometry +{ + public class Point3dEqualDataset : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { new Point3d(1, 1, 1), new Point3d(1, 1, 1) }; + yield return new object[] { new Point3d(2, 2, -1), new Point3d(2, 2, -1) }; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/AR_Lib.Tests/Geometry/Point3dTests.cs b/AR_Lib.Tests/Geometry/Point3dTests.cs new file mode 100644 index 0000000..b746f44 --- /dev/null +++ b/AR_Lib.Tests/Geometry/Point3dTests.cs @@ -0,0 +1,18 @@ +using AR_Lib.Geometry; +using Xunit; + +namespace AR_Lib.Tests.Geometry +{ + public class Point3dTests + { + [Theory] + [ClassData(typeof(Point3dEqualDataset))] + public void EqualsAndHashCode_HaveConsistentResults(Point3d pt, Point3d pt2) + { + bool b1 = (pt == pt2); + bool b2 = (pt.GetHashCode() == pt2.GetHashCode()); + + Assert.True(b1 && b1 == b2); + } + } +} diff --git a/AR_Lib.Tests/Geometry/Vector3dTests.cs b/AR_Lib.Tests/Geometry/Vector3dTests.cs new file mode 100644 index 0000000..7fa6f67 --- /dev/null +++ b/AR_Lib.Tests/Geometry/Vector3dTests.cs @@ -0,0 +1,7 @@ +namespace AR_Lib.Tests.Geometry +{ + public class Vector3dTests + { + + } +} \ No newline at end of file diff --git a/AR_Lib.Tests/Utilities/JsonFileDataAttribute.cs b/AR_Lib.Tests/Utilities/JsonFileDataAttribute.cs new file mode 100644 index 0000000..1777f5b --- /dev/null +++ b/AR_Lib.Tests/Utilities/JsonFileDataAttribute.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit.Sdk; + +namespace AR_Lib.Tests +{ + /// + /// Data Attribute to extract text data out of JSON files + /// + /// From: https://andrewlock.net/creating-a-custom-xunit-theory-test-dataattribute-to-load-data-from-json-files/ + /// + public class JsonFileDataAttribute : DataAttribute + { + private readonly string _filePath; + private readonly string _propertyName; + + /// + /// Load data from a JSON file as the data source for a theory + /// + /// The absolute or relative path to the JSON file to load + public JsonFileDataAttribute(string filePath) + : this(filePath, null) { } + + /// + /// Load data from a JSON file as the data source for a theory + /// + /// The absolute or relative path to the JSON file to load + /// The name of the property on the JSON file that contains the data for the test + public JsonFileDataAttribute(string filePath, string propertyName) + { + _filePath = filePath; + _propertyName = propertyName; + } + + /// + public override IEnumerable GetData(MethodInfo testMethod) + { + if (testMethod == null) { throw new ArgumentNullException(nameof(testMethod)); } + + // Get the absolute path to the JSON file + var path = Path.IsPathRooted(_filePath) + ? _filePath + : Path.GetRelativePath(Directory.GetCurrentDirectory(), _filePath); + + if (!File.Exists(path)) + { + throw new ArgumentException($"Could not find file at path: {path}"); + } + + // Load the file + var fileData = File.ReadAllText(_filePath); + + if (string.IsNullOrEmpty(_propertyName)) + { + //whole file is the data + return JsonConvert.DeserializeObject>(fileData); + } + + // Only use the specified property as the data + var allData = JObject.Parse(fileData); + var data = allData[_propertyName]; + return data.ToObject>(); + } + } +} \ No newline at end of file diff --git a/AR_Lib.csproj b/AR_Lib/AR_Lib.csproj similarity index 100% rename from AR_Lib.csproj rename to AR_Lib/AR_Lib.csproj diff --git a/AR_Lib/Collections/Lists.cs b/AR_Lib/Collections/Lists.cs new file mode 100644 index 0000000..7bf9bb5 --- /dev/null +++ b/AR_Lib/Collections/Lists.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; + +/// +/// Static class holding some utility methods regarding object collections +/// +public static class Lists +{ + /// + /// Initializes a new list full of objects initialized with their default constructor. + /// + /// Number of objects in the list. + /// Type of object in the list. + /// + public static List RepeatedDefault(int count) + { + return Repeated(default(T), count); + } + + /// + /// Initializes a new list full of objects initialized with their default constructor. + /// + /// Object to insert on every index of the list. + /// Number of objects in the list. + /// Type of object in the list. + /// + public static List Repeated(T value, int count) + { + List repeated = new List(count); + repeated.AddRange(Enumerable.Repeat(value, count)); + return repeated; + } +} diff --git a/Collections/Matrix.cs b/AR_Lib/Collections/Matrix.cs similarity index 89% rename from Collections/Matrix.cs rename to AR_Lib/Collections/Matrix.cs index 2b43d9f..e96088e 100644 --- a/Collections/Matrix.cs +++ b/AR_Lib/Collections/Matrix.cs @@ -1,8 +1,12 @@ using System; using System.Collections.Generic; -namespace AR_Lib +namespace AR_Lib.Collections { + /// + /// 2-Dimensional generic matrix + /// + /// Type of the objects in the matrix public class Matrix { /// Matrix Class @@ -42,7 +46,7 @@ public class Matrix #region Constructors /// - /// Generates a new empty square Matrix + /// Generates a new empty square Matrix /// /// Size of the square Matrix public Matrix(int n) @@ -51,7 +55,7 @@ public Matrix(int n) } /// - /// Generates an new empty Matrix class of the specified size + /// Generates an new empty Matrix class of the specified size /// /// Column size /// Row size @@ -61,7 +65,7 @@ public Matrix(int n, int m) } /// - /// Generates a new Matrix class out of a 2D array + /// Generates a new Matrix class out of a 2D array /// /// 2D array of data public Matrix(T[,] data) @@ -82,6 +86,8 @@ public Matrix(T[,] data) public void FlipMatrix() { //TODO: Implement FlipMatrix() + + throw new NotImplementedException(); } @@ -138,11 +144,10 @@ public void IncrementMatrixSize(int columnIncrement, int rowIncrement) /// List of all neighbour entities public List GetAllNeighboursAt(int column, int row) { - ///TODO: This is a hacked up implementation - ///It provides the neighbours out of order (first contiguous, then corners) + //HACK: This is a hacked up implementation. It provides the neighbours out of order (first contiguous, then corners) - List neighbours = GetContiguousNeighboursAt(column,row); - neighbours.AddRange(GetCornerNeighboursAt(column,row)); + List neighbours = GetContiguousNeighboursAt(column, row); + neighbours.AddRange(GetCornerNeighboursAt(column, row)); return neighbours; } @@ -156,7 +161,8 @@ public List GetAllNeighboursAt(int column, int row) public List GetCornerNeighboursAt(int column, int row) { //TODO: Implement GetCornerNeighboursOfEntityAt() - return null; + + throw new NotImplementedException(); } /// @@ -168,7 +174,8 @@ public List GetCornerNeighboursAt(int column, int row) public List GetContiguousNeighboursAt(int column, int row) { //TODO: Implement GetContiguousNeighboursOfEntityAt() - return null; + + throw new NotImplementedException(); } @@ -182,11 +189,9 @@ public List GetContiguousNeighboursAt(int column, int row) /// It accepts smaller and bigger array outputs /// Obtained from: https://stackoverflow.com/questions/6539571/how-to-resize-multidimensional-2d-array-in-c /// - /// Class of the object in the array /// 2D Array to resize /// Number of resulting columns in the array /// Number of resulting rows in the array - /// private void ResizeMatrix(ref T[,] original, int newCoNum, int newRoNum) { var newArray = new T[newCoNum, newRoNum]; diff --git a/Curves/Geodesics.cs b/AR_Lib/Curves/Geodesics.cs similarity index 81% rename from Curves/Geodesics.cs rename to AR_Lib/Curves/Geodesics.cs index 02074df..8f4a487 100644 --- a/Curves/Geodesics.cs +++ b/AR_Lib/Curves/Geodesics.cs @@ -7,6 +7,9 @@ namespace AR_Lib.Curve { + /// + /// Static class to compute geodeesics on triangular meshes. + /// public static class Geodesics { /// @@ -20,16 +23,18 @@ public static bool StartDir(HE_MeshPoint meshPoint, Vector3d vector, HE_Mesh mes // Start iteration // Create variables for current iteration step - HE_Face thisFace = new HE_Face(); + HE_Face thisFace = initialFace; Point3d thisPoint = new Point3d(); - Vector3d thisDirection = new Vector3d(); + Vector3d thisDirection = vector; int iter = 0; List geodPoints = new List(); - do + do { + Ray ray = new Ray(thisPoint, thisDirection); + // Find intersection between ray and boundary - AR_Lib.Intersect3D.RayFacePerimeter(thisPoint,thisDirection,thisFace, out Point3d nextPoint, out HE_HalfEdge halfEdge); + AR_Lib.Intersect3D.RayFacePerimeter(ray, thisFace, out Point3d nextPoint, out HE_HalfEdge halfEdge); // Intersection method should check for correct direction using sign of dot product @@ -41,7 +46,7 @@ public static bool StartDir(HE_MeshPoint meshPoint, Vector3d vector, HE_Mesh mes // Flip vector to next face Vector3d perpVector = Vector3d.CrossProduct(thisDirection, HE_MeshGeometry.FaceNormal(thisFace)); - Vector3d nextVector = Vector3d.CrossProduct(HE_MeshGeometry.FaceNormal(nextFace),perpVector); + Vector3d nextVector = Vector3d.CrossProduct(HE_MeshGeometry.FaceNormal(nextFace), perpVector); // Assign iteration variables to current thisPoint = nextPoint; diff --git a/AR_Lib/Curves/LevelSets.cs b/AR_Lib/Curves/LevelSets.cs new file mode 100644 index 0000000..8306406 --- /dev/null +++ b/AR_Lib/Curves/LevelSets.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using AR_Lib.HalfEdgeMesh; +using AR_Lib.Curve; +using AR_Lib.Geometry; + +namespace AR_Lib.Curve +{ + /// + /// Compute the level sets of a given function. + /// + public static class LevelSets + { + /// + /// Compute the level-sets for a mesh given a specified valueKey for the mesh vertex dictionary. + /// + /// Key of the value to be computed per vertex. + /// List of level values to be computed. + /// The mesh to compute the level-sets in. + /// Resulting level sets. + public static void ComputeLevels(string valueKey, List levels, HE_Mesh mesh, out List> levelSets) + { + List> resultLines = new List>(); + + for (int i = 0; i < levels.Count; i++) + { + resultLines.Add(new List()); + } + int iter = 0; + foreach (HE_Face face in mesh.Faces) + { + int count = 0; + foreach (double level in levels) + { + Line l = new Line(); + if (GetFaceLevel(valueKey, level, face, out l)) + { + resultLines[count].Add(l); + } + + count++; + } + iter++; + } + + levelSets = resultLines; + } + + /// + /// Compute the level on a specified face. + /// + /// Key of the value to be computed per vertex. + /// Level value to be computed. + /// Face to computee the level in. + /// Resulting level line on the face + /// True if successful, false if not. + public static bool GetFaceLevel(string valueKey, double level, HE_Face face, out Line line) + { + List adj = face.adjacentVertices(); + List vertexValues = new List { adj[0].UserValues[valueKey], adj[1].UserValues[valueKey], adj[2].UserValues[valueKey] }; + + List above = new List(); + List below = new List(); + + for (int i = 0; i < vertexValues.Count; i++) + { + if (vertexValues[i] < level) below.Add(i); + else above.Add(i); + } + + if (above.Count == 3 || below.Count == 3) + { + // Triangle is above or below level + line = new Line(new Point3d(), new Point3d()); + return false; + } + else + { + // Triangle intersects level + List intersectionPoints = new List(); + + foreach (int i in above) + { + foreach (int j in below) + { + double diff = vertexValues[i] - vertexValues[j]; + double desiredDiff = level - vertexValues[j]; + double unitizedDistance = desiredDiff / diff; + Vector3d edgeV = adj[i] - adj[j]; + Point3d levelPoint = (Point3d)adj[j] + (edgeV * unitizedDistance); + intersectionPoints.Add(levelPoint); + } + } + line = new Line(intersectionPoints[0], intersectionPoints[1]); + return true; + } + + + } + + /// + /// Compute the gradient on a given mesh given some per-vertex values + /// + /// Key of the values in the vertex.UserData dictionary + /// Mesh to compute the gradient. + /// A list containing all the gradient vectors per-face. + public static List ComputeGradientField(string valueKey, HE_Mesh mesh) + { + List gradientField = new List(); + + mesh.Faces.ForEach(face => gradientField.Add(ComputeFaceGradient(valueKey, face))); + + return gradientField; + } + + /// + /// Compute the gradient on a given mesh face given some per-vertex values + /// + /// Key of the values in the vertex.UserData dictionary + /// Face to compute thee gradient. + /// A vector representing the gradient on that mesh face + public static Vector3d ComputeFaceGradient(string valueKey, HE_Face face) + { + List adjacentVertices = face.adjacentVertices(); + Point3d i = adjacentVertices[0]; + Point3d j = adjacentVertices[1]; + Point3d k = adjacentVertices[2]; + + double gi = adjacentVertices[0].UserValues[valueKey]; + double gj = adjacentVertices[1].UserValues[valueKey]; + double gk = adjacentVertices[2].UserValues[valueKey]; + + Vector3d faceNormal = face.Normal / (2 * face.Area); + Vector3d rotatedGradient = (gi * (k - j) + gj * (i - k) + gk * (j - i)) / (2 * face.Area); + Vector3d gradient = rotatedGradient.Cross(faceNormal); + + return gradient; + } + + } +} \ No newline at end of file diff --git a/AR_Lib/Data/Settings.json b/AR_Lib/Data/Settings.json new file mode 100644 index 0000000..d4258cf --- /dev/null +++ b/AR_Lib/Data/Settings.json @@ -0,0 +1,4 @@ +{ + "DefaultTesselation": 10, + "Tolerance": 0.00001 +} \ No newline at end of file diff --git a/AR_Lib/Geometry/BaseCurve.cs b/AR_Lib/Geometry/BaseCurve.cs new file mode 100644 index 0000000..ef6f476 --- /dev/null +++ b/AR_Lib/Geometry/BaseCurve.cs @@ -0,0 +1,64 @@ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +using AR_Lib.Geometry; + +namespace AR_Lib.Geometry +{ + + // Excemptions + public class InvalidCurveException : Exception + { + public InvalidCurveException() + { + } + + public InvalidCurveException(string message) : base(message) + { + } + + public InvalidCurveException(string message, Exception innerException) : base(message, innerException) + { + } + + } + + public abstract class BaseCurve + { + // Public properties + public virtual Point3d StartPoint { get => _startPoint; set => _startPoint = value; } + public virtual Point3d EndPoint { get => _endPoint; set => _endPoint = value; } + public virtual double T0 { get => _t0; set => _t0 = value; } + public virtual double T1 { get => _t1; set => _t1 = value; } + + public virtual bool IsValid => _isValid; + + protected Point3d _startPoint; + protected Point3d _endPoint; + protected double _t0; + protected double _t1; + public double Length => ComputeLength(); + + // Private fields + protected bool _isValid; + + protected BaseCurve() + { + _isValid = false; + } + + + // Abstract methods + public abstract Point3d PointAt(double t); + public abstract Vector3d TangentAt(double t); + public abstract Vector3d NormalAt(double t); + public abstract Vector3d BinormalAt(double t); + public abstract Plane FrameAt(double t); + public abstract void CheckValidity(); + protected abstract double ComputeLength(); + + } + +} \ No newline at end of file diff --git a/LinAlg/BasePoint.cs b/AR_Lib/Geometry/BasePoint.cs similarity index 83% rename from LinAlg/BasePoint.cs rename to AR_Lib/Geometry/BasePoint.cs index 48428b9..feab3f5 100644 --- a/LinAlg/BasePoint.cs +++ b/AR_Lib/Geometry/BasePoint.cs @@ -5,9 +5,13 @@ namespace AR_Lib.Geometry public abstract class BasePoint { // Public properties - public double X { get { return x; } set { if (isUnset) isUnset = false; x = value; } } - public double Y { get { return y; } set { if (isUnset) isUnset = false; y = value; } } - public double Z { get { return z; } set { if (isUnset) isUnset = false; z = value; } } + public double X + { + get { return x; } + set { if (isUnset) isUnset = false; x = Math.Round(value, Settings.MaxDecimals); } + } + public double Y { get { return y; } set { if (isUnset) isUnset = false; y = Math.Round(value, Settings.MaxDecimals); } } + public double Z { get { return z; } set { if (isUnset) isUnset = false; z = Math.Round(value, Settings.MaxDecimals); } } public bool IsUnset { get => isUnset; } // Private parameters @@ -20,15 +24,15 @@ public abstract class BasePoint protected BasePoint() : this(0, 0, 0) { isUnset = true; } - protected BasePoint(BasePoint point) : this(point.x, point.y, point.z) + protected BasePoint(BasePoint point) : this(point.X, point.Y, point.Z) { } protected BasePoint(double xCoord, double yCoord, double zCoord) { - x = xCoord; - y = yCoord; - z = zCoord; + X = xCoord; + Y = yCoord; + Z = zCoord; isUnset = false; } @@ -78,9 +82,9 @@ public void Negate() // Override Methods public override bool Equals(object obj) { - if (obj is Point3d) + if (obj is BasePoint) { - Point3d pt = (Point3d)obj; + BasePoint pt = (BasePoint)obj; if (Math.Abs(this.X - pt.X) < 0.000001 && Math.Abs(this.Y - pt.Y) < 0.000001 && Math.Abs(this.Z - pt.Z) < 0.000001) { return true; } else { return false; } } @@ -107,4 +111,5 @@ public override int GetHashCode() } } + } diff --git a/AR_Lib/Geometry/Line.cs b/AR_Lib/Geometry/Line.cs new file mode 100644 index 0000000..98e7096 --- /dev/null +++ b/AR_Lib/Geometry/Line.cs @@ -0,0 +1,44 @@ +namespace AR_Lib.Geometry +{ + + public class Line : BaseCurve + { + + public Line() : this(new Point3d(), new Point3d()) { } + public Line(Point3d startPoint, Point3d endPoint) + { + this._startPoint = startPoint; + this._endPoint = endPoint; + } + + public override void CheckValidity() + { + // Check validity should change IsValid state depending on conditions + // i.e.: a line with the same point at start and end. + throw new System.NotImplementedException(); + } + + public override Point3d PointAt(double t) => _startPoint + t * (_endPoint - _startPoint); + public override Vector3d TangentAt(double t) + { + Vector3d tangent = _endPoint - _startPoint; + tangent.Normalize(); + return tangent; + } + public override Vector3d NormalAt(double t) + { + Vector3d tangent = TangentAt(t); + Vector3d v = new Vector3d(); + + if (tangent.Dot(Vector3d.WorldZ) == 1) v = Vector3d.WorldX; + else v = Vector3d.WorldZ; + + return tangent.Cross(v); + } + public override Vector3d BinormalAt(double t) => Vector3d.CrossProduct(TangentAt(t), NormalAt(t)); + public override Plane FrameAt(double t) => new Plane(PointAt(t), TangentAt(t), NormalAt(t), BinormalAt(t)); + protected override double ComputeLength() => _startPoint.DistanceTo(_endPoint); + + } + +} \ No newline at end of file diff --git a/AR_Lib/Geometry/Plane.cs b/AR_Lib/Geometry/Plane.cs new file mode 100644 index 0000000..68e7b29 --- /dev/null +++ b/AR_Lib/Geometry/Plane.cs @@ -0,0 +1,37 @@ +namespace AR_Lib.Geometry +{ + /// + /// Represents a 3-Dimensional plane + /// + public class Plane + { + public Point3d Origin { get => _origin; set => _origin = value; } + public Vector3d XAxis { get => _xAxis; set => _xAxis = value; } + public Vector3d YAxis { get => _yAxis; set => _yAxis = value; } + public Vector3d ZAxis { get => _zAxis; set => _zAxis = value; } + + private Point3d _origin; + private Vector3d _xAxis; + private Vector3d _yAxis; + private Vector3d _zAxis; + + public Plane(Point3d origin, Vector3d xAxis, Vector3d yAxis) + { + _origin = origin; + _xAxis = xAxis; + _yAxis = yAxis; + _zAxis = xAxis.Cross(yAxis); + } + public Plane(Point3d origin, Vector3d xAxis, Vector3d yAxis, Vector3d zAxis) + { + _origin = origin; + _xAxis = xAxis; + _yAxis = yAxis; + _zAxis = zAxis; + } + + public static Plane WorldXYZ => new Plane(Point3d.WorldOrigin, Vector3d.WorldX, Vector3d.WorldY, Vector3d.WorldZ); + + //TODO: Add utility methods to Plane class (flip Axis, relative coordinates...) + } +} diff --git a/LinAlg/Point2d.cs b/AR_Lib/Geometry/Point2d.cs similarity index 57% rename from LinAlg/Point2d.cs rename to AR_Lib/Geometry/Point2d.cs index da9c547..1169e62 100644 --- a/LinAlg/Point2d.cs +++ b/AR_Lib/Geometry/Point2d.cs @@ -1,5 +1,8 @@ namespace AR_Lib.Geometry { + /// + /// 2-Dimensional point + /// public class Point2d : BasePoint { /// @@ -18,37 +21,43 @@ public class Point2d : BasePoint /// /// X coordinate of the point /// Y coordinate of the point - public Point2d(double x, double y) : base(x, y, 0) - { - } + public Point2d(double x, double y) : base(x, y, 0) { } /// /// Constructs a new 2D point out of an existing point /// /// A 2D point - public Point2d(Point2d pt) - { - this.x = pt.X; - this.y = pt.Y; - this.z = 0; - } + public Point2d(Point2d pt) : base(pt.X, pt.Y, pt.Z) { } // Overrided methods + /// + /// String representation of a 2-dimensional point instance + /// + /// Returns string representation of this Point2d instance. public override string ToString() { return "Point2d{ " + this.X + "; " + this.Y + "}"; } + /// + /// Compares a Point2d instance to the given objects. + /// + /// Object to compare to. + /// Returns true if object is equals, false if not. public override bool Equals(object obj) { return base.Equals(obj); } - + /// + /// Gets the hash code for the corresponding Point2d instance. + /// + /// Returns an int representing the Point2d hash code. public override int GetHashCode() { return base.GetHashCode(); } } + } diff --git a/LinAlg/Point3d.cs b/AR_Lib/Geometry/Point3d.cs similarity index 100% rename from LinAlg/Point3d.cs rename to AR_Lib/Geometry/Point3d.cs diff --git a/AR_Lib/Geometry/Point4d.cs b/AR_Lib/Geometry/Point4d.cs new file mode 100644 index 0000000..30eaa32 --- /dev/null +++ b/AR_Lib/Geometry/Point4d.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using AR_Lib.Geometry; +using AR_Lib.LinearAlgebra; + + +namespace AR_Lib.Geometry +{ + public class Point4d : Point3d + { + public double Weight { get => weight; set { weight = value; if (isUnset) isUnset = false; } } + + private double weight; + + #region Constructors + + public Point4d() : base() + { + weight = 0; + } + + public Point4d(double x, double y, double z, double w) : base(x, y, z) + { + weight = w; + } + + public Point4d(Point3d pt, double w) : base(pt) + { + weight = w; + } + + #endregion + + #region Operators + + public static Point4d operator +(Point4d point, Point4d point2) => new Point4d(point.X + point2.X, point.Y + point2.Y, point.Z + point2.Z, point.Weight + point2.Weight); + public static Point4d operator -(Point4d point, Point4d point2) => new Point4d(point.X - point2.X, point.Y - point2.Y, point.Z - point2.Z, point.Weight - point2.Weight); + + public static Point4d operator -(Point4d point) => new Point4d(-point.X, -point.Y, -point.Z, point.Weight); + + public static Point4d operator *(Point4d point, double scalar) => new Point4d(point.X * scalar, point.Y * scalar, point.Z * scalar, point.Weight * scalar); + public static Point4d operator *(double scalar, Point4d point) => new Point4d(point.X * scalar, point.Y * scalar, point.Z * scalar, point.Weight * scalar); + + public static Point4d operator /(Point4d point, double scalar) => new Point4d(point.X / scalar, point.Y / scalar, point.Z / scalar, point.Weight / scalar); + public static Point4d operator /(double scalar, Point4d point) => new Point4d(point.X / scalar, point.Y / scalar, point.Z / scalar, point.Weight / scalar); + + public static bool operator ==(Point4d point, Point4d point2) => point.Equals(point2); + public static bool operator !=(Point4d point, Point4d point2) => !point.Equals(point2); + + public static Point4d operator +(Point4d point, Vector3d v) => new Point4d(point.X + v.X, point.Y + v.Y, point.Z + v.Z, point.Weight); + + #endregion + + #region Overriden methods + + public override bool Equals(object obj) + { + if (obj is Point4d) + { + Point4d pt = (Point4d)obj; + return base.Equals(obj) && this.Weight == pt.Weight; + + } + else return false; + + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion + + //TODO: Add hasWeightedCoordinates boolean and implement a weightCoordinates() method + + } + +} diff --git a/AR_Lib/Geometry/Polyline.cs b/AR_Lib/Geometry/Polyline.cs new file mode 100644 index 0000000..d2abd1d --- /dev/null +++ b/AR_Lib/Geometry/Polyline.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace AR_Lib.Geometry +{ + + public class Polyline : BaseCurve + { + private List _knots; + private List _segments; + + public bool IsClosed => _knots[0] == _knots[_knots.Count - 1]; + + #region Constructors + + public Polyline() + { + _knots = new List(); + _segments = new List(); + } + public Polyline(List knots) + { + _knots = knots; + RebuildSegments(); + } + + #endregion + + #region Polyline specific methods + + public void AddKnot(Point3d knot) + { + _knots.Add(knot); // Add knot to list + _segments.Add(new Line(_knots[_knots.Count - 1], knot)); //Add the corresponding segment + } + public void AddKnot(Point3d knot, int index) + { + _knots.Insert(index, knot); // Add knot to list + RebuildSegments(); + + } + public void RemoveKnot(Point3d knot) + { + if (_knots.Contains(knot)) + { + _knots.Remove(knot); + RebuildSegments(); + } + + } + private void RebuildSegments() + { + _segments = new List(_knots.Count - 1); + double t = 0; + for (int i = 1; i < _knots.Count; i++) + { + Line l = new Line(_knots[i - 1], _knots[i]); + // Assign parameter values + l.T0 = t; + t += l.Length; + l.T1 = t; + // Add segment to list. + _segments.Add(l); + } + } + + #endregion + + #region Overriden Methods + public override Vector3d BinormalAt(double t) => throw new NotImplementedException(); + public override Vector3d NormalAt(double t) => throw new NotImplementedException(); + public override Point3d PointAt(double t) => throw new NotImplementedException(); + public override Vector3d TangentAt(double t) => throw new NotImplementedException(); + public override Plane FrameAt(double t) => throw new NotImplementedException(); + protected override double ComputeLength() + { + double length = 0; + _segments.ForEach(segment => length += segment.Length); + return length; + } + + public override void CheckValidity() => throw new NotImplementedException(); + #endregion + + } + +} \ No newline at end of file diff --git a/AR_Lib/Geometry/Ray.cs b/AR_Lib/Geometry/Ray.cs new file mode 100644 index 0000000..900fcb0 --- /dev/null +++ b/AR_Lib/Geometry/Ray.cs @@ -0,0 +1,52 @@ +namespace AR_Lib.Geometry +{ + /// + /// Infinite 3d ray starting at a point. + /// + public class Ray + { + private Point3d _origin; + private Vector3d _direction; + + /// + /// Gets/sets the origin point of the ray. + /// + public Point3d Origin { get => _origin; set => _origin = value; } + + /// + /// Gets/sets the direction vector of the ray. + /// + /// + public Vector3d Direction { get => _direction; set => _direction = value; } + + #region Constructors + + /// + /// Initialize an Ray instance with origin and direction. + /// + /// Point representing the origin of the ray. + /// Vector representing the direction of the ray. + public Ray(Point3d origin, Vector3d direction) + { + _origin = origin; + _direction = direction; + } + + #endregion + + #region Public methods + + /// + /// Computes a point in the ray at the given parameter. + /// + /// Parameter to obtain point. + /// Returns a point at the specified parameter of the Ray. + public Point3d PointAt(double t) + { + return _origin + t * _direction; + } + + #endregion + } + +} \ No newline at end of file diff --git a/AR_Lib/Geometry/Sphere.cs b/AR_Lib/Geometry/Sphere.cs new file mode 100644 index 0000000..c16faec --- /dev/null +++ b/AR_Lib/Geometry/Sphere.cs @@ -0,0 +1,21 @@ +namespace AR_Lib.Geometry +{ + public class Sphere + { + private Point3d _origin; + private double _radius; + + public Point3d Origin { get => _origin; set => _origin = value; } + public double Radius { get => _radius; set => _radius = value; } + + public double DistanceTo(Sphere sphere) + { + return this.DistanceTo(sphere.Origin) - sphere.Radius; + } + public double DistanceTo(Point3d point) + { + return _origin.DistanceTo(point) - _radius; + } + } + +} \ No newline at end of file diff --git a/AR_Lib/Geometry/Vector3d.cs b/AR_Lib/Geometry/Vector3d.cs new file mode 100644 index 0000000..d9fd49a --- /dev/null +++ b/AR_Lib/Geometry/Vector3d.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; + +namespace AR_Lib.Geometry +{ + public class Vector3d : BasePoint + { + #region Constructors + public Vector3d() : base() { } + public Vector3d(Vector3d vector) : base(vector) { } + public Vector3d(Point3d point) : base(point) { } + public Vector3d(double xCoord, double yCoord, double zCoord) : base(xCoord, yCoord, zCoord) { } + + #endregion + + + #region Operators + + public static Vector3d operator +(Vector3d v, Vector3d point2) => new Vector3d(v.X + point2.X, v.Y + point2.Y, v.Z + point2.Z); + public static Vector3d operator -(Vector3d v, Vector3d point2) => new Vector3d(v.X - point2.X, v.Y - point2.Y, v.Z - point2.Z); + public static Vector3d operator *(Vector3d v, double scalar) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar); + public static Vector3d operator *(double scalar, Vector3d v) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar); + public static Vector3d operator -(Vector3d v) => new Vector3d(-v.X, -v.Y, -v.Z); + public static Vector3d operator /(Vector3d v, double scalar) => new Vector3d(v.X / scalar, v.Y / scalar, v.Z / scalar); + public static bool operator ==(Vector3d v, Vector3d w) => v.Equals(w); + public static bool operator !=(Vector3d v, Vector3d w) => !v.Equals(w); + + #endregion + + + #region Utility properties/methods + + // Computes the Euclidiean length squared of this vector + public double Length2 => DotProduct(this, this); + + // Computes the Euclidean length of this vector + public double Length => Math.Sqrt(Length2); + + // Divides this vector by it's euclidean length + public void Normalize() + { + double length = Length; + X /= length; + Y /= length; + Z /= length; + } + + // Returns a normalized copy of this vector + public Vector3d Unit() + { + double length = Length; + double x = X / length; + double y = Y / length; + double z = Z / length; + return new Vector3d(x, y, z); + } + + // Returns the dot product of this vector and v + public double Dot(Vector3d v) => Vector3d.DotProduct(this, v); + // Returns the cross product of this vector and v + public Vector3d Cross(Vector3d v) => Vector3d.CrossProduct(this, v); + + #endregion + + + #region Static Methods + public static double DotProduct(Vector3d u, Vector3d v) + { + // Dot product = u1*v1 + u2*v2 + u3*v3 + return u.X * v.X + u.Y * v.Y + u.Z * v.Z; + } + public static Vector3d CrossProduct(Vector3d u, Vector3d v) + { + double x = u.Y * v.Z - u.Z * v.Y; + double y = u.Z * v.X - u.X * v.Z; + double z = u.X * v.Y - u.Y * v.X; + + return new Vector3d(x, y, z); + } + public static double Angle(Vector3d u, Vector3d v) + { + // Angle = Arcosine of the CrossProduct of U & V divided with their multiplied lengths. + return Math.Acos(Vector3d.DotProduct(u, v) / (u.Length * v.Length)); + } + + // Global unit vectors + public static Vector3d WorldX => new Vector3d(1, 0, 0); + public static Vector3d WorldY => new Vector3d(0, 1, 0); + public static Vector3d WorldZ => new Vector3d(0, 0, 1); + + #endregion + + + #region Overridden methods + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override string ToString() + { + return "Vector3d" + base.ToString(); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion + + + } + +} diff --git a/HalfEdgeMesh/HE_Corner.cs b/AR_Lib/HalfEdgeMesh/HE_Corner.cs similarity index 73% rename from HalfEdgeMesh/HE_Corner.cs rename to AR_Lib/HalfEdgeMesh/HE_Corner.cs index f1ce1ce..1244ee0 100644 --- a/HalfEdgeMesh/HE_Corner.cs +++ b/AR_Lib/HalfEdgeMesh/HE_Corner.cs @@ -2,8 +2,14 @@ namespace AR_Lib.HalfEdgeMesh { + /// + /// Represents a corner of a given mesh face. + /// public class HE_Corner { + /// + /// The corner's + /// public HE_HalfEdge HalfEdge; public int Index; diff --git a/HalfEdgeMesh/HE_Edge.cs b/AR_Lib/HalfEdgeMesh/HE_Edge.cs similarity index 100% rename from HalfEdgeMesh/HE_Edge.cs rename to AR_Lib/HalfEdgeMesh/HE_Edge.cs diff --git a/HalfEdgeMesh/HE_Face.cs b/AR_Lib/HalfEdgeMesh/HE_Face.cs similarity index 53% rename from HalfEdgeMesh/HE_Face.cs rename to AR_Lib/HalfEdgeMesh/HE_Face.cs index 0e89377..67c8f7b 100644 --- a/HalfEdgeMesh/HE_Face.cs +++ b/AR_Lib/HalfEdgeMesh/HE_Face.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using AR_Lib.Geometry; namespace AR_Lib { @@ -9,17 +10,51 @@ namespace HalfEdgeMesh /// public class HE_Face { - public HE_HalfEdge HalfEdge; //One of the half-edges surrounding the face + #region Properties & Fields + + /// + /// One of the half-edges surrounding the face + /// + public HE_HalfEdge HalfEdge; + + /// + /// The face index on the mesh face list. + /// public int Index; - // Constructor + /// + /// Computes the area of the face. + /// + /// Returns the value of the face area. + public double Area => HE_MeshGeometry.Area(this); + + /// + /// Compute the normal vector of the face. + /// + /// Returns the perpendicular vector to the face + public Vector3d Normal => HE_MeshGeometry.FaceNormal(this); + + #endregion + + #region Constructors + /// + /// Initialize an empty half-edge mesh face. + /// public HE_Face() { HalfEdge = null; Index = -1; } + #endregion + + #region Topology related methods + + /// + /// Get all adjacent edges to this face. + /// + /// Returns a list of all adjacent edges in order. public List adjacentEdges() { HE_HalfEdge _edge = this.HalfEdge; @@ -34,6 +69,10 @@ public List adjacentEdges() return _edges; } + /// + /// Get all adjacent half-edges to this face. + /// + /// Returns a list of all adjacent half-edges in order. public List adjacentHalfEdges() { HE_HalfEdge _edge = this.HalfEdge; @@ -48,6 +87,10 @@ public List adjacentHalfEdges() return _halfEdges; } + /// + /// Get all adjacent vertices to this face. + /// + /// Returns a list of all adjacent vertices in order. public List adjacentVertices() { List _vertices = new List(); @@ -62,6 +105,10 @@ public List adjacentVertices() } + /// + /// Get all adjacent faces to this face. + /// + /// Returns a list of all adjacent faces in order. public List adjacentFaces() { List _faces = new List(); @@ -74,6 +121,10 @@ public List adjacentFaces() return _faces; } + /// + /// Get all adjacent corners to this face. + /// + /// Returns a list of all adjacent corners in order. public List adjacentCorners() { List _corners = new List(); @@ -86,8 +137,20 @@ public List adjacentCorners() return _corners; } + /// + /// Checks if the current face is a boundary face. + /// + /// Returns true if the face is a boundary face, false if not. public bool isBoundaryLoop() => this.HalfEdge.onBoundary; + #endregion + + #region Overriden Methods + + /// + /// Convert the mesh face to string. + /// + /// Returns the string representation of the mesh face. public override string ToString() { List faceVertices = this.adjacentVertices(); @@ -101,6 +164,8 @@ public override string ToString() return text; } + #endregion + } } } diff --git a/HalfEdgeMesh/HE_Generators.cs b/AR_Lib/HalfEdgeMesh/HE_Generators.cs similarity index 100% rename from HalfEdgeMesh/HE_Generators.cs rename to AR_Lib/HalfEdgeMesh/HE_Generators.cs diff --git a/HalfEdgeMesh/HE_HalfEdge.cs b/AR_Lib/HalfEdgeMesh/HE_HalfEdge.cs similarity index 100% rename from HalfEdgeMesh/HE_HalfEdge.cs rename to AR_Lib/HalfEdgeMesh/HE_HalfEdge.cs diff --git a/HalfEdgeMesh/HE_Mesh.cs b/AR_Lib/HalfEdgeMesh/HE_Mesh.cs similarity index 92% rename from HalfEdgeMesh/HE_Mesh.cs rename to AR_Lib/HalfEdgeMesh/HE_Mesh.cs index 8ab0c1b..5be2a7d 100644 --- a/HalfEdgeMesh/HE_Mesh.cs +++ b/AR_Lib/HalfEdgeMesh/HE_Mesh.cs @@ -10,7 +10,7 @@ namespace AR_Lib.HalfEdgeMesh /// public class HE_Mesh { - /// Public variables + #region Public properties public List Vertices; public List Edges; public List Faces; @@ -19,9 +19,15 @@ public class HE_Mesh public List Boundaries; public List Generators; - /// Computed properties + #endregion + + + #region Computed properties + public int EulerCharacteristic => Vertices.Count - Edges.Count + Faces.Count; + #endregion + #region Constructors @@ -71,6 +77,8 @@ public HE_Mesh(HE_Mesh halfEdgeMesh) #endregion + #region Error checking methods + /// /// Check if the mesh has isolated vertices /// @@ -112,10 +120,16 @@ public bool HasIsolatedFaces() /// True if there are non-manifold edges, false if not public bool HasNonManifoldEdges() { - // TODO: IMPLEMENT THIS METHOD! + //HACK: Implement HasNonManifoldEdges(). Currently it always returns FALSE + return false; } + #endregion + + + #region Indexing methods + /// /// Assign an index number to each mesh member /// @@ -148,7 +162,7 @@ public void indexElements() /// /// Assign an index to each vertex of the mesh /// - /// Dictionary containing Vertex<->Index assignments + /// Dictionary containing Vertex-Index assignments public Dictionary indexVertices() { int i = -1; @@ -161,7 +175,7 @@ public void indexElements() /// /// Assign an index to each face of the mesh /// - /// Dictionary containing Face<->Index assignments + /// Dictionary containing Face-Index assignments public Dictionary indexFaces() { int i = -1; @@ -174,7 +188,7 @@ public void indexElements() /// /// Assign an index to each edge of the mesh /// - /// Dictionary containing Edge<->Index assignments + /// Dictionary containing Edge-Index assignments public Dictionary indexEdges() { int i = -1; @@ -187,7 +201,7 @@ public void indexElements() /// /// Assign an index to each Half-Edge of the mesh /// - /// Dictionary containing Half-Edge<->Index assignments + /// Dictionary containing HalfEdge-Index assignments public Dictionary indexHalfEdes() { int i = -1; @@ -200,7 +214,7 @@ public void indexElements() /// /// Assign an index to each corner of the mesh /// - /// Dictionary containing Corner<->Index assignments + /// Dictionary containing Corner-Index assignments public Dictionary indexCorners() { int i = -1; @@ -210,17 +224,34 @@ public void indexElements() return index; } - #region Topology related methods + #endregion + + + #region Topology methods + + /// + /// Check if a mesh is triangular. + /// + /// Returns true if all faces are triangular. public bool isTriangularMesh() { if (isMesh() == isMeshResult.Triangular) return true; else return false; } + + /// + /// Check if a mesh is quad. + /// + /// Returns true if all faces are quads. public bool isQuadMesh() { if (isMesh() == isMeshResult.Quad) return true; else return false; } + /// + /// Check if a mesh is n-gonal. + /// + /// Returns true if the mesh contains ANY ngons. public bool isNgonMesh() { if (isMesh() == isMeshResult.Ngon) return true; @@ -252,7 +283,8 @@ private enum isMeshResult #endregion - /// Utility methods + + #region Utility methods public string GetMeshInfo() { string head = "--- Mesh Info ---\n"; @@ -283,6 +315,9 @@ public override string ToString() return "HE_Mesh{" + VEFH + "}"; } + #endregion + + #region Private methods private void createVertices(List points) { diff --git a/HalfEdgeMesh/HE_MeshGeometry.cs b/AR_Lib/HalfEdgeMesh/HE_MeshGeometry.cs similarity index 75% rename from HalfEdgeMesh/HE_MeshGeometry.cs rename to AR_Lib/HalfEdgeMesh/HE_MeshGeometry.cs index 081db45..7388c59 100644 --- a/HalfEdgeMesh/HE_MeshGeometry.cs +++ b/AR_Lib/HalfEdgeMesh/HE_MeshGeometry.cs @@ -22,7 +22,7 @@ public static class HE_MeshGeometry /// /// The length. /// Edge. - public static double Length(HE_Edge edge) => Vector(edge.HalfEdge).Norm; + public static double Length(HE_Edge edge) => Vector(edge.HalfEdge).Length; /// /// Calculates the midpoint of the specifiec edge. @@ -59,7 +59,7 @@ public static double Area(HE_Face face) Vector3d u = Vector(face.HalfEdge); Vector3d v = -Vector(face.HalfEdge.Prev); - return 0.5 * u.Cross(v).Norm; + return 0.5 * u.Cross(v).Length; } /// @@ -123,10 +123,10 @@ public static Point3d Circumcenter(HE_Face face) Vector3d ab = b - a; Vector3d w = ab.Cross(ac); - Vector3d u = (w.Cross(ab)) * ac.Norm2; - Vector3d v = (ac.Cross(w)) * ab.Norm2; + Vector3d u = (w.Cross(ab)) * ac.Length2; + Vector3d v = (ac.Cross(w)) * ab.Length2; - Point3d x = (u + v) / (2 * w.Norm2); + Point3d x = (u + v) / (2 * w.Length2); return x + a; } @@ -142,7 +142,7 @@ public static Vector3d[] OrthonormalBases(HE_Face face) Vector3d normal = FaceNormal(face); Vector3d e2 = normal.Cross(e1); - return new Vector3d[]{ e1 , e2 }; + return new Vector3d[] { e1, e2 }; } /// @@ -170,7 +170,7 @@ public static double Cotan(HE_HalfEdge hE) Vector3d u = Vector(hE.Prev); Vector3d v = -Vector(hE.Next); - return u.Dot(v) / u.Cross(v).Norm; + return u.Dot(v) / u.Cross(v).Length; } @@ -215,8 +215,8 @@ public static double CircumcentricDualarea(HE_Vertex vertex) double area = 0.0; foreach (HE_HalfEdge hE in vertex.adjacentHalfEdges()) { - double u2 = Vector(hE.Prev).Norm2; - double v2 = Vector(hE).Norm2; + double u2 = Vector(hE.Prev).Length2; + double v2 = Vector(hE).Length2; double cotAlpha = Cotan(hE.Prev); double cotBeta = Cotan(hE); @@ -228,7 +228,7 @@ public static double CircumcentricDualarea(HE_Vertex vertex) /// /// Computes the equally weighted normal arround the specified vertex /// - /// The normal vector. + /// The normal vector at that vertex. /// Vertex. public static Vector3d VertexNormalEquallyWeighted(HE_Vertex vertex) { @@ -238,10 +238,15 @@ public static Vector3d VertexNormalEquallyWeighted(HE_Vertex vertex) return n.Unit(); } + /// + /// Computes the area weighted normal arround the specified vertex + /// + /// The normal vector at that vertex. + /// Vertex. public static Vector3d VertexNormalAreaWeighted(HE_Vertex vertex) { Vector3d n = new Vector3d(); - foreach(HE_Face f in vertex.adjacentFaces()) + foreach (HE_Face f in vertex.adjacentFaces()) { Vector3d normal = FaceNormal(f); double area = Area(f); @@ -251,6 +256,11 @@ public static Vector3d VertexNormalAreaWeighted(HE_Vertex vertex) return n.Unit(); } + /// + /// Computes the angle weighted normal arround the specified vertex + /// + /// The normal vector at that vertex. + /// Vertex. public static Vector3d VertexNormalAngleWeighted(HE_Vertex vertex) { Vector3d n = new Vector3d(); @@ -264,6 +274,11 @@ public static Vector3d VertexNormalAngleWeighted(HE_Vertex vertex) return n.Unit(); } + /// + /// Computes the gauss curvature weighted normal arround the specified vertex + /// + /// The normal vector at that vertex. + /// Vertex. public static Vector3d VertexNormalGaussCurvature(HE_Vertex vertex) { Vector3d n = new Vector3d(); @@ -275,6 +290,11 @@ public static Vector3d VertexNormalGaussCurvature(HE_Vertex vertex) return n.Unit(); } + /// + /// Computes the mean curvature weighted normal arround the specified vertex + /// + /// The normal vector at that vertex. + /// Vertex. public static Vector3d VertexNormalMeanCurvature(HE_Vertex vertex) { Vector3d n = new Vector3d(); @@ -286,6 +306,11 @@ public static Vector3d VertexNormalMeanCurvature(HE_Vertex vertex) return n.Unit(); } + /// + /// Computes the sphere inscribed normal arround the specified vertex + /// + /// The normal vector at that vertex. + /// Vertex. public static Vector3d VertexNormalSphereInscribed(HE_Vertex vertex) { Vector3d n = new Vector3d(); @@ -294,25 +319,40 @@ public static Vector3d VertexNormalSphereInscribed(HE_Vertex vertex) Vector3d u = Vector(c.HalfEdge.Prev); Vector3d v = -Vector(c.HalfEdge.Next); - n += ((u.Cross(v) / (u.Norm2 * v.Norm2))); + n += ((u.Cross(v) / (u.Length2 * v.Length2))); } return n.Unit(); } + /// + /// Computes the angle defect at the given vertex + /// + /// Vertex to compute angle defect. + /// Number representing the deviation of the current vertex from $2\PI$ public static double AngleDefect(HE_Vertex vertex) { double angleSum = 0.0; foreach (HE_Corner c in vertex.adjacentCorners()) angleSum += Angle(c); - if (vertex.OnBoundary()) angleSum = Math.PI - angleSum; + //if (vertex.OnBoundary()) angleSum = Math.PI - angleSum; - return vertex.OnBoundary() ? Math.PI - angleSum: 2 * Math.PI - angleSum; + return vertex.OnBoundary() ? Math.PI - angleSum : 2 * Math.PI - angleSum; } + /// + /// Compute the Gaussian curvature at the given vertex + /// + /// Vertex to compute Gaussian curvature + /// Number representing the gaussian curvature at that vertex. public static double scalarGaussCurvature(HE_Vertex vertex) { - return AngleDefect(vertex); + return AngleDefect(vertex) / HE_MeshGeometry.CircumcentricDualarea(vertex); } + /// + /// Compute the Mean curvature at the given vertex + /// + /// Vertex to compute Mean curvature + /// Number representing the Mean curvature at that vertex. public static double scalarMeanCurvature(HE_Vertex vertex) { double sum = 0.0; @@ -321,6 +361,11 @@ public static double scalarMeanCurvature(HE_Vertex vertex) return sum; } + /// + /// Compute the total angle defect of the mesh. + /// + /// Mesh to compute angle defect. + /// Returns the total angle defect as a scalar value. public static double TotalAngleDefect(HE_Mesh Mesh) { double totalDefect = 0.0; @@ -329,6 +374,11 @@ public static double TotalAngleDefect(HE_Mesh Mesh) return totalDefect; } + /// + /// Compute the principal curvature scalar values at a given vertes. + /// + /// Vertex to compute the curvature. + /// Returns an array of 2 values {k1, k2}. public static double[] PrincipalCurvatures(HE_Vertex vertex) { double A = CircumcentricDualarea(vertex); @@ -342,7 +392,7 @@ public static double[] PrincipalCurvatures(HE_Vertex vertex) double k1 = H - discriminant; double k2 = H + discriminant; - return new double[]{k1,k2}; + return new double[] { k1, k2 }; } } diff --git a/HalfEdgeMesh/HE_MeshPoint.cs b/AR_Lib/HalfEdgeMesh/HE_MeshPoint.cs similarity index 74% rename from HalfEdgeMesh/HE_MeshPoint.cs rename to AR_Lib/HalfEdgeMesh/HE_MeshPoint.cs index d942851..5d68e63 100644 --- a/HalfEdgeMesh/HE_MeshPoint.cs +++ b/AR_Lib/HalfEdgeMesh/HE_MeshPoint.cs @@ -4,6 +4,9 @@ namespace AR_Lib.HalfEdgeMesh { + /// + /// Represents a point on a mesh as it's face index and barycentric coordinatees. + /// public class HE_MeshPoint { public int FaceIndex; @@ -18,7 +21,7 @@ public HE_MeshPoint(int faceIndex, double u, double v, double w) V = v; W = w; } - + public HE_MeshPoint(Point3d point, HE_Face face) { List adj = face.adjacentVertices(); @@ -27,7 +30,11 @@ public HE_MeshPoint(Point3d point, HE_Face face) V = bary[1]; W = bary[2]; } - + + /// + /// Converts a mesh point into a string. + /// + /// String representation of the mesh point. public override string ToString() { return "MeshPoint{ " + FaceIndex + "; " + U + ", " + V + ", " + W + " }"; diff --git a/HalfEdgeMesh/HE_MeshTopology.cs b/AR_Lib/HalfEdgeMesh/HE_MeshTopology.cs similarity index 100% rename from HalfEdgeMesh/HE_MeshTopology.cs rename to AR_Lib/HalfEdgeMesh/HE_MeshTopology.cs diff --git a/HalfEdgeMesh/HE_Vertex.cs b/AR_Lib/HalfEdgeMesh/HE_Vertex.cs similarity index 100% rename from HalfEdgeMesh/HE_Vertex.cs rename to AR_Lib/HalfEdgeMesh/HE_Vertex.cs diff --git a/IO/IO.cs b/AR_Lib/IO/IO.cs similarity index 100% rename from IO/IO.cs rename to AR_Lib/IO/IO.cs diff --git a/LinAlg/Complex.cs b/AR_Lib/LinearAlgebra/Complex.cs similarity index 100% rename from LinAlg/Complex.cs rename to AR_Lib/LinearAlgebra/Complex.cs diff --git a/AR_Lib/LinearAlgebra/LeastSquares.cs b/AR_Lib/LinearAlgebra/LeastSquares.cs new file mode 100644 index 0000000..0c8234d --- /dev/null +++ b/AR_Lib/LinearAlgebra/LeastSquares.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using AR_Lib.Geometry; + +namespace AR_Lib.LinearAlgebra +{ + /// + /// Fit a line through a set of 2-dimensional points. + /// + public static class LineFit2d + { + + // Find the least squares linear fit. + // Return the total error. + // Found at: http://csharphelper.com/blog/2014/10/find-a-linear-least-squares-fit-for-a-set-of-points-in-c/ + + /// + /// Find the least squares best fitting line to the given points. + /// + /// The points to fit the line through. + /// + /// + /// + public static double FindLinearLeastSquaresFit( + List points, out double m, out double b) + { + // Perform the calculation. + // Find the values S1, Sx, Sy, Sxx, and Sxy. + double S1 = points.Count; + double Sx = 0, Sy = 0, Sxx = 0, Sxy = 0; + + foreach (Point2d pt in points) + { + Sx += pt.X; + Sy += pt.Y; + Sxx += pt.X * pt.X; + Sxy += pt.X * pt.Y; + } + + // Solve for m and b. + m = (Sxy * S1 - Sx * Sy) / (Sxx * S1 - Sx * Sx); + b = (Sxy * Sx - Sy * Sxx) / (Sx * Sx - S1 * Sxx); + + return Math.Sqrt(ErrorSquared(points, m, b)); + } + // Return the error squared. + private static double ErrorSquared(List points, double m, double b) + { + double total = 0; + foreach (Point2d pt in points) + { + double dy = pt.Y - (m * pt.X + b); + total += dy * dy; + } + return total; + } + } +} \ No newline at end of file diff --git a/LinAlg/Triplet.cs b/AR_Lib/LinearAlgebra/Triplet.cs similarity index 100% rename from LinAlg/Triplet.cs rename to AR_Lib/LinearAlgebra/Triplet.cs diff --git a/AR_Lib/Nurbs/Surface.cs b/AR_Lib/Nurbs/Surface.cs new file mode 100644 index 0000000..72c3784 --- /dev/null +++ b/AR_Lib/Nurbs/Surface.cs @@ -0,0 +1,464 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using AR_Lib.Collections; + +namespace AR_Lib.Geometry +{ + //HACK: This implementation needs to be cleaned up and fixed: + + //FIXME: Coordinates should be divided by weight + + //FIXME: Degree 3 works but others do strange stuff (deg = 1 or 2 creates a surface with a subset of the control points) + + /// + /// Class representing an arbitrary N.U.R.B.S surface. + /// + public class Surface + { + // This class is implemented based on the explanation on NURBS found at: + // https://www.gamasutra.com/view/feature/131808/using_nurbs_surfaces_in_realtime_.php?page=4 + + #region Public Fields + + /// + /// Degree in the U direction + /// + /// The UDegree property gets/sets the degree value of the surface in the U direction + public int UDegree { get => uDegree; set => uDegree = value; } + + /// + /// Degree in the V direction + /// + /// The UDegree property gets/sets the degree value of the surface in the V direction + public int VDegree { get => vDegree; set => vDegree = value; } + + /// + /// The ControlPoints property represents the surface's control points as a list + /// + /// The ControlPoints property gets/sets the control points of the field _controlPoints + public List ControlPoints { get => _controlPoints; set => _controlPoints = value; } + + public List Vertices { get => _vertices; set => _vertices = value; } + public List UKnots { get => _uKnots; set => _uKnots = value; } + public List VKnots { get => _vKnots; set => _vKnots = value; } + + #endregion + + + #region Private Properties + + // Integer properties + + int uDegree, vDegree; // Surface degrees + int uOrder, vOrder; // Surface order + int uKnotCount, vKnotCount; // Knot count in each direction + int uControlPointCount, vControlPointCount; // Control point count in each direction + int uBasisSpanCount, vBasisSpanCount; // Basis span count in each direction + int uTessellationCount, vTessellationCount; // Tesselation count in each direction (for rendering) + + // Collection properties + List _controlPoints; //TODO: This should actually be a Matrix + List _uBasisCoefficients, _vBasisCoefficients; // Computed basis coefficients + List _uKnots, _vKnots; // Knot values in each direction + List _uBasis, _duBasis, _vBasis, _dvBasis; + List _uTemp, _duTemp; + List _uTessKnotSpan, _vTessKnotSpan; + List _vertices; + + // Other properties + bool _isValid; // TODO: This does nothing for now + + #endregion + + + #region Constructors + + // TODO: Implement more constructors + + /// + /// Initializes a new surface class + /// + /// Degree in U direction + /// Degree in V direction + /// Control point count in U direction + /// Control point count in V direction + /// List of control points + /// Knot values in the U direction + /// Knot value in the V direction + /// Tesselation count in the U direction + /// Tesselation count in the V direction + public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cntrlPts, List uKnotsValues, List vKnotsValues, int uTessellations, int vTessellations) + { + //Assign incoming values + + uDegree = uDeg; + vDegree = vDeg; + uControlPointCount = uCntrPts; + vControlPointCount = vCntrlPts; + _controlPoints = cntrlPts; + _uKnots = uKnotsValues; + _vKnots = vKnotsValues; + + //Compute some useful property values + + uOrder = uDegree + 1; + vOrder = vDegree + 1; + + uKnotCount = uOrder + uControlPointCount; + vKnotCount = vOrder + vControlPointCount; + + uBasisSpanCount = uOrder - 2 + uDegree; + vBasisSpanCount = vOrder - 2 + uDegree; + + // Initialize empty objects + + _uBasisCoefficients = new List(); + _vBasisCoefficients = new List(); + _uBasis = new List(); + _duBasis = new List(); + _vBasis = new List(); + _duBasis = new List(); + _uTemp = new List(); + _duTemp = new List(); + _uTessKnotSpan = new List(); + _vTessKnotSpan = new List(); + _vertices = new List(); + + // Run initialization routine + + ComputeBasisCoefficients(); + + SetTesselations(uTessellations, vTessellations); + + } + + #endregion + + + #region Public Methods + + /// + /// Tesselates the given surface for rendering + /// HACK: Currently it only computes the vertices, not the mesh. + /// + public void TessellateSurface() + { + //FIXME: Rename this variables to fit the convention. + + List pControlPoints = _controlPoints; + int u, v, k, l; + int uKnot, vKnot; + List UTemp = _uTemp, dUTemp = _duTemp; + Point4d Pw = new Point4d(); + int vertexCount; + int iCPOffset; + double VBasis, dVBasis; + int idx, uidx; + + if ((uTessellationCount == 0) || (vTessellationCount == 0)) + return; + + vertexCount = 2 * (vTessellationCount + 1); + + // Step over the U and V coordinates and generate triangle strips to render + // + for (u = 0; u <= uTessellationCount; u++) + { + // What's the current knot span in the U direction? + uKnot = _uTessKnotSpan[u]; + + // Calculate the offset into the pre-calculated basis functions array + uidx = u * uOrder; + vKnot = -1; + + // Create one row of vertices + for (v = 0; v <= vTessellationCount; v++) + { + idx = u * uTessellationCount + v; + if (vKnot != _vTessKnotSpan[v]) + { + vKnot = _vTessKnotSpan[v]; + // + // If our knot span in the V direction has changed, then calculate some + // temporary variables. These are the sum of the U-basis functions times + // the control points (times the weights because the control points have + // the weights factored in). + // + for (k = 0; k <= vDegree; k++) + { + iCPOffset = (uKnot - uDegree) * vControlPointCount + (vKnot - vDegree); + + UTemp.Add(new Point4d()); + UTemp[k] = _uBasis[uidx] * pControlPoints[iCPOffset + k]; + + dUTemp.Add(new Point4d()); + _duTemp[k] = _duBasis[uidx] * pControlPoints[iCPOffset + k]; + + for (l = 1; l <= uDegree; l++) + { + iCPOffset += vControlPointCount; + UTemp[k] += _uBasis[uidx + l] * pControlPoints[iCPOffset + k]; + _duTemp[k] += _duBasis[uidx + l] * pControlPoints[iCPOffset + k]; + } + } + } + + // Compute the point in the U and V directions + VBasis = _vBasis[(v * vOrder)]; + dVBasis = _dvBasis[(v * vOrder)]; + Pw = VBasis * UTemp[0]; + + for (k = 1; k <= vDegree; k++) + { + VBasis = _vBasis[(v * vOrder + k)]; + dVBasis = _dvBasis[(v * vOrder + k)]; + Pw += VBasis * UTemp[k]; + } + + // Bring the 4-D points back into 3-D + Pw *= 1.0 / Pw.Weight; + + // Store the vertex position. + _vertices.Add(new Point3d(Pw)); + + } + } + + + } + public void UpdateControlPoints() + { + //TODO: Implement UpdateControlPoints + // This method should contain the logic AFTER control points have been changed + // i.e.: recomputing some of the basis functions...etc + } + + #endregion + + + #region Private Methods + + // Any private methods should go here + + /// + /// Computes the coefficient given i,p,k values and the current interval + /// + /// Knot list + /// Current interval + /// i + /// p + /// k + /// The computed coefficient + public double ComputeCoefficient(List knots, int interval, int i, int p, int k) + { + //TODO: Check ComputeCoefficient + double result = 0.0; + + if (p == 0) + { + if (i == interval) + result = 1.0; + } + else if (k == 0) + { + if (knots[i + p] != knots[i]) + result -= knots[i] * ComputeCoefficient(knots, interval, i, p - 1, 0) / (knots[i + p] - knots[i]); + if (knots[i + p + 1] != knots[i + 1]) + result += knots[i + p + 1] * ComputeCoefficient(knots, interval, i + 1, p - 1, 0) / (knots[i + p + 1] - knots[i + 1]); + } + else if (k == p) + { + if (knots[i + p] != knots[i]) + result += ComputeCoefficient(knots, interval, i, p - 1, p - 1) / (knots[i + p] - knots[i]); + if (knots[i + p + 1] != knots[i + 1]) + result -= ComputeCoefficient(knots, interval, i + 1, p - 1, p - 1) / (knots[i + p + 1] - knots[i + 1]); + } + else if (k > p) + { + result = 0.0; + } + else + { + double C1, C2; + if (knots[i + p] != knots[i]) + { + C1 = ComputeCoefficient(knots, interval, i, p - 1, k - 1); + C2 = ComputeCoefficient(knots, interval, i, p - 1, k); + result += (C1 - knots[i] * C2) / (knots[i + p] - knots[i]); + } + if (knots[i + p + 1] != knots[i + 1]) + { + C1 = ComputeCoefficient(knots, interval, i + 1, p - 1, k - 1); + C2 = ComputeCoefficient(knots, interval, i + 1, p - 1, k); + result -= (C1 - knots[i + p + 1] * C2) / (knots[i + p + 1] - knots[i + 1]); + } + } + return result; + } + + /// + /// Compute all basis coefficients of the surface + /// + public void ComputeBasisCoefficients() + { + //TODO: Check ComputeBasisCoefficients + int i, j, k; + + // + // Start with U. For each Basis span calculate coefficients + // for uOrder polynomials each having uOrder coefficients + // + + for (i = 0; i < uBasisSpanCount; i++) + { + for (j = 0; j < uOrder; j++) + { + for (k = 0; k < uOrder; k++) + { + _uBasisCoefficients.Add( + ComputeCoefficient(_uKnots, i + uDegree, i + j, uDegree, k)); + } + } + } + + for (i = 0; i < vBasisSpanCount; i++) + { + for (j = 0; j < vOrder; j++) + { + for (k = 0; k < vOrder; k++) + { + _vBasisCoefficients.Add( + ComputeCoefficient(_vKnots, i + vDegree, i + j, vDegree, k)); + } + } + } + + } + + /// + /// Evaluate the basis functions of the surface + /// + public void EvaluateBasisFunctions() + { + //TODO: Check EvaluateBasisFunctions, rename properties to something more meaningfull + int i, j, k, idx; + double u, uinc; + double v, vinc; + + // + // First evaluate the U basis functions and derivitives at uniformly spaced u values + // + idx = 0; + u = _uKnots[idx + uDegree]; + uinc = (_uKnots[uKnotCount - uOrder] - _uKnots[uDegree]) / (uTessellationCount); + + for (i = 0; i <= uTessellationCount; i++) + { + while ((idx < uKnotCount - uDegree * 2 - 2) && (u >= _uKnots[idx + uDegree + 1])) + idx++; + + _uTessKnotSpan.Add(idx + uDegree); + + // + // Evaluate using Horner's method + // + for (j = 0; j < uOrder; j++) + { + _uBasis.Add(_uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]); + _duBasis.Add(_uBasis[(i * uOrder + j)] * uDegree); + for (k = uDegree - 1; k >= 0; k--) + { + _uBasis[(i * uOrder + j)] = _uBasis[(i * uOrder + j)] * u + + _uBasisCoefficients[(idx * uOrder + j) * uOrder + k]; + if (k > 0) + { + _duBasis[(i * uOrder + j)] = _duBasis[(i * uOrder + j)] * u + + _uBasisCoefficients[(idx * uOrder + j) * uOrder + k] * k; + } + } + } + + u += uinc; + } + + // + // Finally evaluate the V basis functions at uniformly spaced v values + // + idx = 0; + v = _vKnots[idx + vDegree]; + vinc = (_vKnots[vKnotCount - vOrder] - _vKnots[vDegree]) / (vTessellationCount); + + for (i = 0; i <= vTessellationCount; i++) + { + while ((idx < vKnotCount - vDegree * 2 - 2) && (v >= _vKnots[idx + vDegree + 1])) + idx++; + + //vTessKnotSpan[i] = idx + vDegree; + _vTessKnotSpan.Add(idx + vDegree); + + // + // Evaluate using Horner's method + // + for (j = 0; j < vOrder; j++) + { + // vBasis[(i * vOrder + j) * SIMD_SIZE] = vBasisCoefficients[(idx * vOrder + j) * vOrder + vDegree]; + // dvBasis[(i * vOrder + j) * SIMD_SIZE] = vBasis[(i * vOrder + j) * SIMD_SIZE] * vDegree; + _vBasis.Add(_vBasisCoefficients[(idx * vOrder + j) * vOrder + vDegree]); + _dvBasis.Add(_vBasis[(i * vOrder + j)] * vDegree); + for (k = vDegree - 1; k >= 0; k--) + { + _vBasis[(i * vOrder + j)] = _vBasis[(i * vOrder + j)] * v + + _vBasisCoefficients[(idx * vOrder + j) * vOrder + k]; + if (k > 0) + { + _dvBasis[(i * vOrder + j)] = _dvBasis[(i * vOrder + j)] * v + + _vBasisCoefficients[(idx * vOrder + j) * vOrder + k] * k; + } + } + } + v += vinc; + } + } + + /// + /// Set the ammount of tesselations for surface rendering + /// + /// Tesselations in the U direction + /// Tesselations in the V direction + private void SetTesselations(int uTessellations, int vTessellations) + { + //TODO: Implement SetTesselations + + if ((uTessellations != uTessellationCount) || (vTessellations != vTessellationCount)) + { + uTessellationCount = uTessellations; + vTessellationCount = vTessellations; + + // + // Overwrite all entities with emepty values + // + _uBasis = new List(uOrder * (uTessellationCount + 1)); + _vBasis = new List(vOrder * (vTessellationCount + 1)); + _duBasis = new List(uOrder * (uTessellationCount + 1)); + _dvBasis = new List(vOrder * (vTessellationCount + 1)); + + _uTessKnotSpan = new List(uTessellationCount + 1); + _vTessKnotSpan = new List(vTessellationCount + 1); + + int iVertices = ((uTessellations + 1) * (vTessellations + 1)); //2 * (vTessellations + 1); + _vertices = new List(iVertices); + + // + // Re-evaluate the basis functions + // + EvaluateBasisFunctions(); + } + } + + #endregion + + } + +} diff --git a/AR_Lib/SpatialStructures/Octree.cs b/AR_Lib/SpatialStructures/Octree.cs new file mode 100644 index 0000000..c7aafa1 --- /dev/null +++ b/AR_Lib/SpatialStructures/Octree.cs @@ -0,0 +1,10 @@ +namespace AR_Lib.SpatialSearch +{ + /// + /// Class for computing Octree spatial searches. + /// + public class Octree + { + //TODO: This class is empty! + } +} \ No newline at end of file diff --git a/AR_Lib/SpatialStructures/PointCloud.cs b/AR_Lib/SpatialStructures/PointCloud.cs new file mode 100644 index 0000000..dc80aad --- /dev/null +++ b/AR_Lib/SpatialStructures/PointCloud.cs @@ -0,0 +1,35 @@ +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using AR_Lib.Geometry; + +namespace AR_Lib.SpatialSearch +{ + /// + /// Represents a collection of points with a color assigned to them. + /// + public class PointCloud + { + List _points; + + /// + /// Collection of points in the point cloud. + /// + public List Points { get => _points; private set => _points = value; } + + } + + /// + /// Class representing a point contained in a point cloud + /// + public class PointCloudMember : BasePoint + { + private Color _color; + + /// + /// Color at this point + /// + /// The current color if set, defaults to white. + public Color Color { get => _color; set => _color = value; } + } +} \ No newline at end of file diff --git a/AR_Lib/SpatialStructures/Quadtree.cs b/AR_Lib/SpatialStructures/Quadtree.cs new file mode 100644 index 0000000..24c90a7 --- /dev/null +++ b/AR_Lib/SpatialStructures/Quadtree.cs @@ -0,0 +1,10 @@ +namespace AR_Lib.SpatialSearch +{ + /// + /// Class to compute Quadtree spatial searches + /// + public class Quadtree + { + // TODO: This class is empty! + } +} \ No newline at end of file diff --git a/Utility/Convert.cs b/AR_Lib/Utility/Convert.cs similarity index 82% rename from Utility/Convert.cs rename to AR_Lib/Utility/Convert.cs index 67953e5..037fb34 100644 --- a/Utility/Convert.cs +++ b/AR_Lib/Utility/Convert.cs @@ -10,6 +10,9 @@ namespace AR_Lib { + /// + /// Static class to handle unit and type conversions. + /// public static class Convert { @@ -47,13 +50,13 @@ public static Point3d RelativeCoordinatesFromPlane(Point3d point, Plane plane) { Vector3d relativeVector = point - plane.Origin; - double x = Vector3d.DotProduct(relativeVector,plane.XAxis); - double y = Vector3d.DotProduct(relativeVector,plane.YAxis); - double z = Vector3d.DotProduct(relativeVector,plane.ZAxis); + double x = Vector3d.DotProduct(relativeVector, plane.XAxis); + double y = Vector3d.DotProduct(relativeVector, plane.YAxis); + double z = Vector3d.DotProduct(relativeVector, plane.ZAxis); - return new Point3d(x,y,z); + return new Point3d(x, y, z); } - + } } \ No newline at end of file diff --git a/AR_Lib/Utility/Intersect.cs b/AR_Lib/Utility/Intersect.cs new file mode 100644 index 0000000..33800f4 --- /dev/null +++ b/AR_Lib/Utility/Intersect.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using AR_Lib.Curve; +using AR_Lib.Geometry; +using AR_Lib.HalfEdgeMesh; +using AR_Lib.LinearAlgebra; + +namespace AR_Lib +{ + //INFO: Error enums for all classes are in file IntersectErrors.cs + + /// + /// Static class containing all 3-dimensional intersection methods. + /// + public static partial class Intersect3D + { + /// + /// Intersect a 3d line with a plane. + /// + /// The 3d line to intersect. + /// The 3d plane to intersect. + /// The resulting intersection point, if it exists. + /// + public static ISLinePlane LinePlane(Line line, Plane plane, out Point3d intersectionPoint) + { + Vector3d u = line.EndPoint - line.StartPoint; + Vector3d w = line.StartPoint - plane.Origin; + + double D = Vector3d.DotProduct(plane.ZAxis, u); + double N = -Vector3d.DotProduct(plane.ZAxis, w); + + if (D <= 0.000001) // Segment is parallel to plane + { + if (N == 0) // Segment lies in plane + { + intersectionPoint = null; + return ISLinePlane.OnPlane; + } + else + { + intersectionPoint = null; + return ISLinePlane.NoIntersection; + } + } + + // They are not parallel + // Compute intersect param + double sI = N / D; + if (sI < 0 || sI > 1) + { + intersectionPoint = null; + return ISLinePlane.NoIntersection; + } + + intersectionPoint = line.StartPoint + u * sI; // Compute segment intersection point + return ISLinePlane.Point; + } + + /// + /// Compute the intersection between a mesh face perimeter and a ray tangent to the face. + /// + /// The tangent ray. + /// The mesh face. + /// The resulting intersection point. + /// The half-edge on where the intersection lies. + /// + public static ISRayFacePerimeter RayFacePerimeter(Ray ray, HE_Face Face, out Point3d result, out HE_HalfEdge halfEdge) + { + Vector3d faceNormal = HE_MeshGeometry.FaceNormal(Face); + Vector3d biNormal = Vector3d.CrossProduct(ray.Direction, faceNormal); + + Plane perpPlane = new Plane(ray.Origin, ray.Direction, faceNormal, biNormal); + + List vertices = Face.adjacentVertices(); + + Point3d temp = new Point3d(); + + Line line = new Line(vertices[0], vertices[1]); + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.Point; } // No intersection found + if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // Intersection found + + line = new Line(vertices[1], vertices[2]); + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.NoIntersection; } // No intersection found + if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // Intersection found + + line = new Line(vertices[2], vertices[0]); + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.NoIntersection; } + if (temp != ray.Origin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } else { result = null; halfEdge = null; return ISRayFacePerimeter.Error; } + } + + /// + /// Compute the intersection between two 3-dimensional lines. + /// + /// First line to intersect. + /// Second line to intersect. + /// Struct containing the intersection result. + /// Returns an enum containing the intersection status. + public static ISLineLine LineLine(Line lineA, Line lineB, out IRLineLine result) + { + + Vector3d u = lineA.EndPoint - lineA.StartPoint; + Vector3d v = lineB.EndPoint - lineB.StartPoint; + Vector3d w = lineA.StartPoint - lineB.StartPoint; + double a = u.Dot(u); // always >= 0 + double b = u.Dot(v); + double c = v.Dot(v); // always >= 0 + double d = u.Dot(w); + double e = v.Dot(w); + double D = a * c - b * b; // always >= 0 + double sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 + double tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 + + // compute the line parameters of the two closest points + if (D < Settings.Tolerance) + { // the lines are almost parallel + sN = 0.0; // force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } + else + { // get the closest points on the infinite lines + sN = (b * e - c * d); + tN = (a * e - b * d); + if (sN < 0.0) + { // sc < 0 => the s=0 edge is visible + sN = 0.0; + tN = e; + tD = c; + } + else if (sN > sD) + { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) + { // tc < 0 => the t=0 edge is visible + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + sN = 0.0; + else if (-d > a) + sN = sD; + else + { + sN = -d; + sD = a; + } + } + else if (tN > tD) + { // tc > 1 => the t=1 edge is visible + tN = tD; + // recompute sc for this edge + if ((-d + b) < 0.0) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else + { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (Math.Abs(sN) < Settings.Tolerance ? 0.0 : sN / sD); + tc = (Math.Abs(tN) < Settings.Tolerance ? 0.0 : tN / tD); + + // get the difference of the two closest points + Vector3d dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) + + result.Distance = dP.Length; // return the closest distance + result.tA = sc; + result.tB = tc; + result.pointA = lineA.PointAt(sc); + result.pointB = lineB.PointAt(tc); + + if (result.Distance <= Settings.Tolerance) return ISLineLine.Point; + else if (result.Distance > Settings.Tolerance) return ISLineLine.NoIntersection; + else return ISLineLine.Error; + } + } +} \ No newline at end of file diff --git a/AR_Lib/Utility/IntersectErrors.cs b/AR_Lib/Utility/IntersectErrors.cs new file mode 100644 index 0000000..ec5bac1 --- /dev/null +++ b/AR_Lib/Utility/IntersectErrors.cs @@ -0,0 +1,41 @@ +using AR_Lib.Geometry; + +namespace AR_Lib +{ + public static partial class Intersect3D + { + //INFO: IS prefix stands for Intersection Status + + //INFO: IR prefix stands for Intersection Result + public enum ISLinePlane + { + NoIntersection, + Point, + OnPlane + } + + public enum ISRayFacePerimeter + { + NoIntersection, + Point, + Error + } + + public enum ISLineLine + { + NoIntersection, + Point, + Error + } + + public struct IRLineLine + { + public double Distance; + public double tA; + public double tB; + public Point3d pointA; + public Point3d pointB; + } + + } +} \ No newline at end of file diff --git a/AR_Lib/Utility/Settings.cs b/AR_Lib/Utility/Settings.cs new file mode 100644 index 0000000..ef27619 --- /dev/null +++ b/AR_Lib/Utility/Settings.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; + +namespace AR_Lib +{ + /// + /// Multi-layered struct that holds the library settings + /// + public class Settings + { + private static int _maxDecimals = 8; + private static double _tolerance = 0.0000001; + + /// + /// Determines the minimum value allowed when using this library. + /// + public static double Tolerance => _tolerance; + + /// + /// Determines how many decimals are allowed when using the library. + /// + public static int MaxDecimals => _maxDecimals; + + /// + /// Modifies the tolerance and computes the maxDecimals value accordingly. + /// + /// Desired tolerance + public static void ModifyTolerance(double tolerance) + { + _tolerance = tolerance; + string t = tolerance.ToString("N14"); + _maxDecimals = t.Substring(t.IndexOf(".") + 1).IndexOf("1"); + } + + } +} \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..f4c11b9 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Global owners +* @AlanRynne \ No newline at end of file diff --git a/Curves/Curves.cs b/Curves/Curves.cs deleted file mode 100644 index ecb59bc..0000000 --- a/Curves/Curves.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -using AR_Lib.Geometry; - -namespace AR_Lib.Curve -{ - - // Excemptions - public class InvalidCurveException : Exception - { - public InvalidCurveException() - { - } - - public InvalidCurveException(string message) : base(message) - { - } - - public InvalidCurveException(string message, Exception innerException) : base(message, innerException) - { - } - } - - public abstract class BaseCurve - { - // Public properties - public Point3d startPoint; - public Point3d endPoint; - public double T0; - public double T1; - public bool IsValid { get => isValid; } - - - // Private fields - private bool isValid; - - protected BaseCurve() - { - isValid = false; - } - - - // Abstract methods - public abstract double Length { get; set; } - public abstract Point3d PointAt(double t); - public abstract Vector3d TangentAt(double t); - public abstract Vector3d NormalAt(double t); - public abstract Vector3d BinormalAt(double t); - public abstract Plane TNBFrameAt(double t); - - public abstract void CheckValidity(); - - } - - public class Line : BaseCurve - { - - public Line(): this(new Point3d(), new Point3d()){} - public Line(Point3d startPoint, Point3d endPoint) - { - this.startPoint = startPoint; - this.endPoint = endPoint; - } - - public override void CheckValidity() - { - // Check validity should change IsValid state depending on conditions - // i.e.: a line with the same point at start and end. - throw new System.NotImplementedException(); - } - - public override Point3d PointAt(double t) => endPoint + t * (endPoint - startPoint); - - public override Vector3d TangentAt(double t) => throw new NotImplementedException(); - - public override Vector3d NormalAt(double t) => throw new NotImplementedException(); - - public override Vector3d BinormalAt(double t) => Vector3d.CrossProduct(TangentAt(t), NormalAt(t)); - - public override Plane TNBFrameAt(double t) => new Plane(PointAt(t),TangentAt(t), NormalAt(t),BinormalAt(t)); - - public override double Length { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - } - - public class Polyline : BaseCurve - { - public List knots; - public List segments; - - public override Vector3d BinormalAt(double t) => throw new NotImplementedException(); - - public override void CheckValidity() => throw new NotImplementedException(); - - public override Vector3d NormalAt(double t) => throw new NotImplementedException(); - - public override Point3d PointAt(double t) => throw new NotImplementedException(); - - public override Vector3d TangentAt(double t) => throw new NotImplementedException(); - - public override Plane TNBFrameAt(double t) => throw new NotImplementedException(); - - public override double Length { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - } - -} \ No newline at end of file diff --git a/Curves/LevelSets.cs b/Curves/LevelSets.cs deleted file mode 100644 index b943dd4..0000000 --- a/Curves/LevelSets.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using AR_Lib.HalfEdgeMesh; -using AR_Lib.Curve; -using AR_Lib.Geometry; - -namespace AR_Lib.Curve -{ - public static class LevelSets - { - public static void Compute(string valueKey, List levels, HE_Mesh mesh, out List> levelSets) - { - List> resultLines = new List>(); - - for(int i = 0;i < levels.Count; i++) - { - resultLines.Add(new List()); - } - int iter = 0; - foreach(HE_Face face in mesh.Faces) - { - int count = 0; - foreach(double level in levels) - { - Line l = new Line(); - if(getFaceLevel(valueKey,level,face,out l)) - { - resultLines[count].Add(l); - } - - count++; - } - iter++; - } - - levelSets = resultLines; - } - - public static bool getFaceLevel(string valueKey, double level, HE_Face face, out Line line) - { - List adj = face.adjacentVertices(); - List vertexValues = new List{ adj[0].UserValues[valueKey], adj[1].UserValues[valueKey], adj[2].UserValues[valueKey] }; - - List above = new List(); - List below = new List(); - - for (int i = 0; i < vertexValues.Count; i++) - { - if (vertexValues[i] < level) below.Add(i); - else above.Add(i); - } - - if (above.Count == 3 || below.Count == 3) - { - // Triangle is above or below level - line = new Line(new Point3d(),new Point3d()); - return false; - } - else - { - // Triangle intersects level - List intersectionPoints = new List(); - - foreach (int i in above) - { - foreach (int j in below) - { - double diff = vertexValues[i] - vertexValues[j]; - double desiredDiff = level - vertexValues[j]; - double unitizedDistance = desiredDiff / diff; - Vector3d edgeV = adj[i] - adj[j]; - Point3d levelPoint = (Point3d)adj[j] + (edgeV * unitizedDistance); - intersectionPoints.Add(levelPoint); - } - } - line = new Line(intersectionPoints[0], intersectionPoints[1]); - return true; - } - - - } - } -} - - // bool CheckLevelSetInFace(double level, MeshFace face, out Line line) - // { - // List vertexValues = new List { VertexValues[face.A], VertexValues[face.B], VertexValues[face.C] }; - // List faceVertices = new List { _mesh.Vertices[face.A], _mesh.Vertices[face.B], _mesh.Vertices[face.C] }; - - // List above = new List(); - // List below = new List(); - - // for (int i = 0; i < vertexValues.Count; i++) - // { - // if (vertexValues[i] < level) below.Add(i); - // else above.Add(i); - // } - - // if (above.Count == 3 || below.Count == 3) - // { - // // Triangle is above or below level - // line = new Line(); - // return false; - // } - // else - // { - // // Triangle intersects level - // List intersectionPoints = new List(); - - // foreach (int i in above) - // { - // foreach (int j in below) - // { - // double diff = vertexValues[i] - vertexValues[j]; - // double desiredDiff = level - vertexValues[j]; - // double unitizedDistance = desiredDiff / diff; - // Vector3d edgeV = faceVertices[i] - faceVertices[j]; - // Point3d levelPoint = faceVertices[j] + edgeV * unitizedDistance; - // intersectionPoints.Add(levelPoint); - // } - // } - // line = new Line(intersectionPoints[0], intersectionPoints[1]); - // return true; - // } - // } diff --git a/Data/Settings.json b/Data/Settings.json deleted file mode 100644 index a65a42d..0000000 --- a/Data/Settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Constants": - { - "DefaultTesselation": 10, - "Tolerance": 0.00001 - - }, -} diff --git a/IO/Conversion.cs b/IO/Conversion.cs deleted file mode 100644 index b6480de..0000000 --- a/IO/Conversion.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -namespace AR_Lib -{ - // - namespace Conversion - { - // Unit/Class conversion class - public static class Convert - { - - } - } -} diff --git a/LinAlg/Plane.cs b/LinAlg/Plane.cs deleted file mode 100644 index e6abb3e..0000000 --- a/LinAlg/Plane.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace AR_Lib -{ - namespace Geometry - { - public class Plane - { - public Point3d Origin { get => _origin; set => _origin = value; } - public Vector3d XAxis { get => _xAxis; set => _xAxis = value; } - public Vector3d YAxis { get => _yAxis; set => _yAxis = value; } - public Vector3d ZAxis { get => _zAxis; set => _zAxis = value; } - - private Point3d _origin; - private Vector3d _xAxis; - private Vector3d _yAxis; - private Vector3d _zAxis; - - public Plane(Point3d origin, Vector3d xAxis, Vector3d yAxis, Vector3d zAxis) - { - _origin = origin; - _xAxis = xAxis; - _yAxis = yAxis; - _zAxis = zAxis; - } - - public static Plane WorldXYZ => new Plane(Point3d.WorldOrigin, Vector3d.WorldX, Vector3d.WorldY, Vector3d.WorldZ); - } - - } -} \ No newline at end of file diff --git a/LinAlg/Vector3d.cs b/LinAlg/Vector3d.cs deleted file mode 100644 index cbcb6e2..0000000 --- a/LinAlg/Vector3d.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace AR_Lib -{ - namespace Geometry - { - public class Vector3d : BasePoint - { - // Constructors - public Vector3d() : base() { } - public Vector3d(Vector3d vector) : base(vector) { } - public Vector3d(Point3d point) : base(point) { } - public Vector3d(double xCoord, double yCoord, double zCoord) : base(xCoord, yCoord, zCoord) { } - - // Operators - public static Vector3d operator +(Vector3d v, Vector3d point2) => new Vector3d(v.X + point2.X, v.Y + point2.Y, v.Z + point2.Z); - public static Vector3d operator -(Vector3d v, Vector3d point2) => new Vector3d(v.X - point2.X, v.Y - point2.Y, v.Z - point2.Z); - public static Vector3d operator *(Vector3d v, double scalar) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar); - public static Vector3d operator *(double scalar, Vector3d v) => new Vector3d(v.X * scalar, v.Y * scalar, v.Z * scalar); - public static Vector3d operator -(Vector3d v) => new Vector3d(-v.X, -v.Y, -v.Z); - public static Vector3d operator /(Vector3d v, double scalar) => new Vector3d(v.X / scalar, v.Y / scalar, v.Z / scalar); - public static bool operator ==(Vector3d v, Vector3d w) => v.Equals(w); - public static bool operator !=(Vector3d v, Vector3d w) => !v.Equals(w); - - // Computes the Euclidiean length squared of this vector - public double Norm2 => DotProduct(this, this); - // Computes the Euclidean length of this vector - public double Norm => Math.Sqrt(Norm2); - - // Divides this vector by it's euclidean length - public void Normalize() - { - double length = Norm; - X /= length; - Y /= length; - Z /= length; - } - - // Returns a normalized copy of this vector - public Vector3d Unit() - { - double length = Norm; - double x = X / length; - double y = Y / length; - double z = Z / length; - return new Vector3d(x, y, z); - } - - // Returns the dot product of this vector and v - public double Dot(Vector3d v) => Vector3d.DotProduct(this, v); - // Returns the cross product of this vector and v - public Vector3d Cross(Vector3d v) => Vector3d.CrossProduct(this, v); - - - // Global Static Methods - public static double DotProduct(Vector3d u, Vector3d v) - { - // Dot product = u1*v1 + u2*v2 + u3*v3 - return u.X * v.X + u.Y * v.Y + u.Z * v.Z; - } - - - public static Vector3d CrossProduct(Vector3d u, Vector3d v) - { - double x = u.Y * v.Z - u.Z * v.Y; - double y = u.Z * v.X - u.X * v.Z; - double z = u.X * v.Y - u.Y * v.X; - - return new Vector3d(x, y, z); - } - public static double Angle(Vector3d u, Vector3d v) - { - // Angle = Arcosine of the CrossProduct of U & V divided with their multiplied lengths. - return Math.Acos(Vector3d.DotProduct(u, v) / (u.Norm * v.Norm)); - } - - // Global unit vectors - public static Vector3d WorldX => new Vector3d(1, 0, 0); - public static Vector3d WorldY => new Vector3d(0, 1, 0); - public static Vector3d WorldZ => new Vector3d(0, 0, 1); - - // Overrided methods - public override bool Equals(object obj) - { - return base.Equals(obj); - } - - public override string ToString() - { - return "Vector3d" + base.ToString(); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - - } - - } -} diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs deleted file mode 100644 index 7018a48..0000000 --- a/Nurbs/Surface.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using AR_Lib.LinearAlgebra; - -namespace AR_Lib.Geometry.Nurbs -{ - class Point4d : Point3d - { - public double Weight { get => weight; set { weight = value; if (isUnset) isUnset = false; } } - - private double weight; - - // Constructors - public Point4d() : base() - { - weight = 0; - } - public Point4d(double x, double y, double z, double w) : base(x, y, z) - { - weight = w; - } - - public Point4d(Point3d pt, double w) : base(pt) - { - weight = w; - } - - - } - - class Surface - { - // TODO: Check for license? Make sure of this part - // This class is implemented based on the explanation on NURBS found at: - // https://www.gamasutra.com/view/feature/131808/using_nurbs_surfaces_in_realtime_.php?page=4 - #region Public Fields - - #endregion - - #region Computed Properties - - int U_Order => uDegree + 1; - int V_Order => uDegree + 1; - int U_Knots => U_Order + uControlPoints; - int V_Knots => U_Order + vControlPoints; - int U_BasisSpans => U_Knots - 2 * uDegree; - int V_BasisSpans => V_Knots - 2 * uDegree; - - #endregion - - #region Private properties - - int uDegree, vDegree; - int uControlPoints, vControlPoints; - Matrix controlPoints; - List uKnotValues, vKnotValues; - int defaultTesselations; - bool isValid; - List uBasisCoefficients, vBasisCoefficients; - int uTesselations, vTesselations; - - #endregion - - #region Constructors - - // TODO: Implement constructors - public Surface( - int uDeg, int vDeg, - int uCntrPts, int vCntrlPts, - Matrix cntrlPts, - List uKnots, List vKnots) - { - uDegree = uDeg; - vDegree = vDeg; - uControlPoints = uCntrPts; - vControlPoints = vCntrlPts; - controlPoints = cntrlPts; - uKnotValues = uKnots; - vKnotValues = vKnots; - } - - #endregion - - #region Public Methods - - public void ComputeBasisCoefficients() - { - - } - public void ComputeCoefficient() - { - - } - public void EvaluateBasisFunctions() - { - - } - public void UpdateControlPoints() - { - // This method should contain the logic AFTER control points have been changed - // i.e.: recomputing some of the basis functions...etc - } - - #endregion - - #region Private Methods - - // Any private methods should go here - - #endregion - } -} diff --git a/Utility/Intersect.cs b/Utility/Intersect.cs deleted file mode 100644 index c70fcf2..0000000 --- a/Utility/Intersect.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -using AR_Lib.HalfEdgeMesh; -using AR_Lib.Geometry; -using AR_Lib.LinearAlgebra; -using AR_Lib.Curve; - - -namespace AR_Lib -{ - public static class Intersect3D - { - - // Line Plane 3D intersection - // Input: S = a segment, and Pn = a plane = {Point V0; Vector n;} - // Output: *I0 = the intersect point (when it exists) - // Return: 0 = disjoint (no intersection) - // 1 = intersection in the unique point *I0 - // 2 = the segment lies in the plane - public static int LinePlane(Line S, Plane Pn, out Point3d I) - { - Vector3d u = S.endPoint - S.startPoint; - Vector3d w = S.startPoint - Pn.Origin; - - double D = Vector3d.DotProduct(Pn.ZAxis,u); - double N = -Vector3d.DotProduct(Pn.ZAxis,w); - - if (D <= 0.000001) // Segment is parallel to plane - { - if(N == 0) // Segment lies in plane - { - I = null; - return 2; - } - else - { - I = null; - return 0; // No intersection - } - } - - // They are not parallel - // Compute intersect param - double sI = N / D; - if (sI < 0 || sI > 1) - { - I = null; - return 0; // No intersection - } - - I = S.startPoint + u * sI; // Compute segment intersection point - return 1; - } - - public static int RayFacePerimeter(Point3d RayOrigin, Vector3d RayDir, HE_Face Face, out Point3d result, out HE_HalfEdge halfEdge) - { - Vector3d faceNormal = HE_MeshGeometry.FaceNormal(Face); - Vector3d biNormal = Vector3d.CrossProduct(RayDir,faceNormal); - - Plane perpPlane = new Plane(RayOrigin, RayDir, faceNormal, biNormal); - - List vertices = Face.adjacentVertices(); - - Point3d temp = new Point3d(); - - Line line = new Line(vertices[0], vertices[1]); - if(LinePlane(line, perpPlane, out temp) != 1) { result = null; halfEdge = null; return 0; } // No intersection found - if(temp != RayOrigin && temp != null){ result = temp; halfEdge = null; return 1; } // Intersection found - - line = new Line(vertices[1], vertices[2]); - if(LinePlane(line, perpPlane, out temp) != 1) { result = null; halfEdge = null; return 0; } // No intersection found - if(temp != RayOrigin && temp != null){ result = temp; halfEdge = null; return 1; } // Intersection found - - line = new Line(vertices[2], vertices[0]); - if(LinePlane(line, perpPlane, out temp) != 1) { result = null; halfEdge = null; return 0; } // No intersection found - if(temp != RayOrigin && temp != null){ result = temp; halfEdge = null; return 1; } // Intersection found - - else{ result = null; halfEdge = null; return 4; } // Error 4 means something weird happened! - } - } -} \ No newline at end of file diff --git a/Utility/Settings.cs b/Utility/Settings.cs deleted file mode 100644 index 1fd021a..0000000 --- a/Utility/Settings.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; - -using Newtonsoft.Json; - -namespace AR_Lib -{ - /// - /// Multi-layered struct that holds the library settings - /// - public struct Settings - { - /// - /// Constants of the library - /// - public ConstStruct Constants; - - - /// - /// Data structure that save the library constants - /// - public struct ConstStruct - { - public double Tolerance; - public double DefaultTesselation; - } - - } - - public static class SettingsReader - { - - public static Settings ReadSettings() - { - var fileName = "Settings.json"; - var assembly = Assembly.GetExecutingAssembly(); - - string resourceName = assembly.GetManifestResourceNames().Single(str => str.EndsWith(fileName)); - string settingsString; - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - using (StreamReader reader = new StreamReader(stream)) - - { - settingsString = reader.ReadToEnd(); - - } - Settings deserializedSettings = JsonConvert.DeserializeObject(settingsString); - - return deserializedSettings; - } - } -}