From c09969c2e1e8dd9469c1cf80e19ee03a30a36628 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 7 Apr 2019 02:45:53 +0200 Subject: [PATCH 01/23] Added first implementation UNTESTED --- Nurbs/Surface.cs | 384 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 354 insertions(+), 30 deletions(-) diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs index 7018a48..760dd0e 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -5,7 +5,7 @@ namespace AR_Lib.Geometry.Nurbs { - class Point4d : Point3d + public class Point4d : Point3d { public double Weight { get => weight; set { weight = value; if (isUnset) isUnset = false; } } @@ -29,75 +29,399 @@ public Point4d(Point3d pt, double w) : base(pt) } - class Surface + public 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 + public int UDegree { get => uDegree; set => uDegree = value; } + public int VDegree { get => vDegree; set => vDegree = value; } + public List ControlPoints { get => controlPoints; set => controlPoints = value; } + public List PVertices { get => pVertices; set => pVertices = value; } + public List UKnots { get => uKnots; set => uKnots = value; } + public List VKnots { get => vKnots; set => vKnots = value; } #endregion - #region Computed Properties + #region Private 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 + // Integer properties + int uDegree, vDegree; + int uOrder, vOrder; + int uKnotCount, vKnotCount; + int uControlPointCount, vControlPointCount; + int uBasisSpanCount, vBasisSpanCount; + int uTessellationCount, vTessellationCount; - #region Private properties + // Collection properties + List controlPoints; + List uBasisCoefficients, vBasisCoefficients; + List uKnots, vKnots; + List uBasis, duBasis, vBasis, dvBasis; + List uTemp, duTemp; + List uTessKnotSpan, vTessKnotSpan; + List pVertices; - int uDegree, vDegree; - int uControlPoints, vControlPoints; - Matrix controlPoints; - List uKnotValues, vKnotValues; - int defaultTesselations; + // Other properties 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) + public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cntrlPts, List uKnotsValues, List vKnotsValues, int uTesselations, int vTesselations) { + //Assign incoming values uDegree = uDeg; vDegree = vDeg; - uControlPoints = uCntrPts; - vControlPoints = vCntrlPts; + uControlPointCount = uCntrPts; + vControlPointCount = vCntrlPts; controlPoints = cntrlPts; - uKnotValues = uKnots; - vKnotValues = vKnots; + uKnots = uKnotsValues; + vKnots = vKnotsValues; + uTessellationCount = uTesselations; + vTessellationCount = vTesselations; + + //Compute some useful property values + uOrder = uDegree + 1; + vOrder = vDegree + 1; + uBasisSpanCount = uOrder + uControlPointCount; + vBasisSpanCount = vOrder + vControlPointCount; + uKnotCount = uOrder + uControlPointCount; + vKnotCount = vOrder + vControlPointCount; + + // 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(); + pVertices = new List(); + + // Run initialization routine + + ComputeBasisCoefficients(); + + SetTesselations(uTessellationCount, vTessellationCount); + } #endregion #region Public Methods - public void ComputeBasisCoefficients() + 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; } - public void ComputeCoefficient() + 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[(i * uOrder + j) * uOrder + k] = + 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[(i * vOrder + j) * vOrder + k] = + ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k); + } + } + } } public void EvaluateBasisFunctions() { + //TODO: Check EvaluateBasisFunctions + int i, j, k, idx; + double u, uinc; + double v, vinc; + int SIMD_SIZE = 1; + + // + // 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[i] = idx + uDegree; + + // + // Evaluate using Horner's method + // + for (j = 0; j < uOrder; j++) + { + uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]; + duBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree; + for (k = uDegree - 1; k >= 0; k--) + { + uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * u + + uBasisCoefficients[(idx * uOrder + j) * uOrder + k]; + if (k > 0) + { + duBasis[(i * uOrder + j) * SIMD_SIZE] = duBasis[(i * uOrder + j) * SIMD_SIZE] * u + + uBasisCoefficients[(idx * uOrder + j) * uOrder + k] * k; + } + } + // + // Make three copies. This isn't necessary if we're using straight C + // code but for the Pentium III optimizations, it is. + // + } + + 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; + + // + // 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; + for (k = vDegree - 1; k >= 0; k--) + { + vBasis[(i * vOrder + j) * SIMD_SIZE] = vBasis[(i * vOrder + j) * SIMD_SIZE] * v + + vBasisCoefficients[(idx * vOrder + j) * vOrder + k]; + if (k > 0) + { + dvBasis[(i * vOrder + j) * SIMD_SIZE] = dvBasis[(i * vOrder + j) * SIMD_SIZE] * v + + vBasisCoefficients[(idx * vOrder + j) * vOrder + k] * k; + } + } + } + v += vinc; + } + } + public void SetTesselations(int uTessellations, int vTessellations) + { + //TODO: Implement SetTesselations + int SIMD_SIZE = 1; + + if ((uTessellations != uTessellationCount) || (vTessellations != vTessellationCount)) + { + uTessellationCount = uTessellations; + vTessellationCount = vTessellations; + + // + // Overwrite all entities with emepty values + // + uBasis = new List(uOrder * SIMD_SIZE * (uTessellationCount + 1)); + vBasis = new List(vOrder * SIMD_SIZE * (vTessellationCount + 1)); + duBasis = new List(uOrder * SIMD_SIZE * (uTessellationCount + 1)); + dvBasis = new List(vOrder * SIMD_SIZE * (vTessellationCount + 1)); + + uTessKnotSpan = new List(uTessellationCount + 1); + vTessKnotSpan = new List(vTessellationCount + 1); + + int iVertices = ((uTessellations + 1) * (vTessellations + 1)); //2 * (vTessellations + 1); + pVertices = new List(iVertices); + + // + // Re-evaluate the basis functions + // + EvaluateBasisFunctions(); + } + } + public void TesselateSurface() + { + List pControlPoints = controlPoints; + int u, v, k, l; + int uKnot, vKnot; + List UTemp = uTemp, dUTemp = duTemp; + Point4d Pw = new Point4d(); + double rhw; + int iVertices; + int iCPOffset; + double VBasis, dVBasis; + int idx, uidx; + + if ((uTessellationCount == 0) || (vTessellationCount == 0)) + return; + + iVertices = 2 * (vTessellationCount + 1); + + // Step over the U and V coordinates and generate triangle strips to render + // + for (u = 0; u <= uTessellationCount; u++) + { + int SIMD_SIZE = 1; + + // 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 * SIMD_SIZE; + 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[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; + UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; + UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; + UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; + dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; + dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; + dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + + for (l = 1; l <= uDegree; l++) + { + iCPOffset += vControlPointCount; + UTemp[k].X += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; + UTemp[k].Y += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; + UTemp[k].Z += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; + UTemp[k].Weight += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Weight; + dUTemp[k].X += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; + dUTemp[k].Y += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; + dUTemp[k].Z += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; + dUTemp[k].Weight += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Weight; + } + } + } + + // Compute the point in the U and V directions + VBasis = vBasis[(v * vOrder) * SIMD_SIZE]; + dVBasis = dvBasis[(v * vOrder) * SIMD_SIZE]; + + Pw.X = VBasis * UTemp[0].X; + Pw.Y = VBasis * UTemp[0].Y; + Pw.Z = VBasis * UTemp[0].Z; + Pw.Weight = VBasis * UTemp[0].Weight; + + for (k = 1; k <= vDegree; k++) + { + VBasis = vBasis[(v * vOrder + k) * SIMD_SIZE]; + dVBasis = dvBasis[(v * vOrder + k) * SIMD_SIZE]; + Pw.X += VBasis * UTemp[k].X; + Pw.Y += VBasis * UTemp[k].Y; + Pw.Z += VBasis * UTemp[k].Z; + Pw.Weight += VBasis * UTemp[k].Weight; + } + + // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D + rhw = 1.0f / Pw.Weight; + Pw.X = Pw.X * rhw; + Pw.Y = Pw.Y * rhw; + Pw.Z = Pw.Z * rhw; + + // Store the vertex position. + pVertices[idx].X = Pw.X; + pVertices[idx].Y = Pw.Y; + pVertices[idx].Z = Pw.Z; + } + } + } 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 } From babae72ae9fdd8e99bae27014a92bbeb6edf62f0 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 7 Apr 2019 22:20:51 +0200 Subject: [PATCH 02/23] Working Surface class without weights Still some bugs to be looked at and the addition of more functionality Reorganized namespaces and some of the project structure --- Collections/Lists.cs | 17 +++++++ Collections/Matrix.cs | 2 +- LinAlg/BasePoint.cs | 4 +- LinAlg/Point4d.cs | 78 ++++++++++++++++++++++++++++ Nurbs/Surface.cs | 115 ++++++++++++++++++++---------------------- 5 files changed, 154 insertions(+), 62 deletions(-) create mode 100644 Collections/Lists.cs create mode 100644 LinAlg/Point4d.cs diff --git a/Collections/Lists.cs b/Collections/Lists.cs new file mode 100644 index 0000000..599a6f2 --- /dev/null +++ b/Collections/Lists.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +public static class Lists +{ + public static List RepeatedDefault(int count) + { + return Repeated(default(T), count); + } + + 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/Collections/Matrix.cs index 2b43d9f..a887c24 100644 --- a/Collections/Matrix.cs +++ b/Collections/Matrix.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace AR_Lib +namespace AR_Lib.Collections { public class Matrix { diff --git a/LinAlg/BasePoint.cs b/LinAlg/BasePoint.cs index 48428b9..09d0750 100644 --- a/LinAlg/BasePoint.cs +++ b/LinAlg/BasePoint.cs @@ -78,9 +78,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; } } diff --git a/LinAlg/Point4d.cs b/LinAlg/Point4d.cs new file mode 100644 index 0000000..fb408e6 --- /dev/null +++ b/LinAlg/Point4d.cs @@ -0,0 +1,78 @@ +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 + + // 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(); + } + + } + +} diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs index 760dd0e..5035b40 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -5,36 +5,13 @@ namespace AR_Lib.Geometry.Nurbs { - public 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; - } - - - } - public 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 + public int UDegree { get => uDegree; set => uDegree = value; } public int VDegree { get => vDegree; set => vDegree = value; } public List ControlPoints { get => controlPoints; set => controlPoints = value; } @@ -47,18 +24,18 @@ public class Surface #region Private Properties // Integer properties - int uDegree, vDegree; - int uOrder, vOrder; - int uKnotCount, vKnotCount; - int uControlPointCount, vControlPointCount; - int uBasisSpanCount, vBasisSpanCount; - int uTessellationCount, vTessellationCount; + 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; - List uBasisCoefficients, vBasisCoefficients; - List uKnots, vKnots; - List uBasis, duBasis, vBasis, dvBasis; + 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 pVertices; @@ -73,7 +50,7 @@ public class Surface #region Constructors // TODO: Implement constructors - public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cntrlPts, List uKnotsValues, List vKnotsValues, int uTesselations, int vTesselations) + 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; @@ -83,17 +60,17 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn controlPoints = cntrlPts; uKnots = uKnotsValues; vKnots = vKnotsValues; - uTessellationCount = uTesselations; - vTessellationCount = vTesselations; //Compute some useful property values uOrder = uDegree + 1; vOrder = vDegree + 1; - uBasisSpanCount = uOrder + uControlPointCount; - vBasisSpanCount = vOrder + vControlPointCount; + uKnotCount = uOrder + uControlPointCount; vKnotCount = vOrder + vControlPointCount; + uBasisSpanCount = uOrder - 2 + uControlPointCount; + vBasisSpanCount = vOrder - 2 + vControlPointCount; + // Initialize empty objects uBasisCoefficients = new List(); vBasisCoefficients = new List(); @@ -111,7 +88,7 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn ComputeBasisCoefficients(); - SetTesselations(uTessellationCount, vTessellationCount); + SetTesselations(uTessellations, vTessellations); } @@ -182,8 +159,9 @@ public void ComputeBasisCoefficients() { for (k = 0; k < uOrder; k++) { - uBasisCoefficients[(i * uOrder + j) * uOrder + k] = - ComputeCoefficient(uKnots, i + uDegree, i + j, uDegree, k); + //uBasisCoefficients[(i * uOrder + j) * uOrder + k] = + uBasisCoefficients.Add( + ComputeCoefficient(uKnots, i + uDegree, i + j, uDegree, k)); } } } @@ -194,8 +172,10 @@ public void ComputeBasisCoefficients() { for (k = 0; k < vOrder; k++) { - vBasisCoefficients[(i * vOrder + j) * vOrder + k] = - ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k); + // vBasisCoefficients[(i * vOrder + j) * vOrder + k] = + // ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k); + vBasisCoefficients.Add( + ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k)); } } } @@ -221,15 +201,17 @@ public void EvaluateBasisFunctions() while ((idx < uKnotCount - uDegree * 2 - 2) && (u >= uKnots[idx + uDegree + 1])) idx++; - uTessKnotSpan[i] = idx + uDegree; + uTessKnotSpan.Add(idx + uDegree); // // Evaluate using Horner's method // for (j = 0; j < uOrder; j++) { - uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]; - duBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree; + //uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]; + //duBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree; + uBasis.Add(uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]); + duBasis.Add(uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree); for (k = uDegree - 1; k >= 0; k--) { uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * u + @@ -261,15 +243,18 @@ public void EvaluateBasisFunctions() while ((idx < vKnotCount - vDegree * 2 - 2) && (v >= vKnots[idx + vDegree + 1])) idx++; - vTessKnotSpan[i] = idx + vDegree; + //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[(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) * SIMD_SIZE] * vDegree); for (k = vDegree - 1; k >= 0; k--) { vBasis[(i * vOrder + j) * SIMD_SIZE] = vBasis[(i * vOrder + j) * SIMD_SIZE] * v + @@ -297,10 +282,10 @@ public void SetTesselations(int uTessellations, int vTessellations) // // Overwrite all entities with emepty values // - uBasis = new List(uOrder * SIMD_SIZE * (uTessellationCount + 1)); - vBasis = new List(vOrder * SIMD_SIZE * (vTessellationCount + 1)); - duBasis = new List(uOrder * SIMD_SIZE * (uTessellationCount + 1)); - dvBasis = new List(vOrder * SIMD_SIZE * (vTessellationCount + 1)); + 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); @@ -314,7 +299,7 @@ public void SetTesselations(int uTessellations, int vTessellations) EvaluateBasisFunctions(); } } - public void TesselateSurface() + public void TessellateSurface() { List pControlPoints = controlPoints; int u, v, k, l; @@ -361,10 +346,21 @@ public void TesselateSurface() for (k = 0; k <= vDegree; k++) { iCPOffset = (uKnot - uDegree) * vControlPointCount + (vKnot - vDegree); + + // UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; + // UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; + // UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; + // UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + UTemp.Add(new Point4d()); UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + // dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; + // dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; + // dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; + // dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + dUTemp.Add(new Point4d()); dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; @@ -373,10 +369,12 @@ public void TesselateSurface() for (l = 1; l <= uDegree; l++) { iCPOffset += vControlPointCount; + UTemp[k].X += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; UTemp[k].Y += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; UTemp[k].Z += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; UTemp[k].Weight += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Weight; + dUTemp[k].X += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; dUTemp[k].Y += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; dUTemp[k].Z += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; @@ -405,15 +403,14 @@ public void TesselateSurface() } // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D - rhw = 1.0f / Pw.Weight; + rhw = 1.0 / Pw.Weight; Pw.X = Pw.X * rhw; Pw.Y = Pw.Y * rhw; Pw.Z = Pw.Z * rhw; // Store the vertex position. - pVertices[idx].X = Pw.X; - pVertices[idx].Y = Pw.Y; - pVertices[idx].Z = Pw.Z; + pVertices.Add(new Point3d(Pw)); + } } From ab68ace035dfd68043a2ff2e42c9c768c115f379 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 8 Apr 2019 01:49:57 +0200 Subject: [PATCH 03/23] Started project re-structuring --- Collections/Matrix.cs | 15 +- {LinAlg => Geometry}/BasePoint.cs | 0 Geometry/Plane.cs | 28 +++ {LinAlg => Geometry}/Point2d.cs | 0 {LinAlg => Geometry}/Point3d.cs | 0 {LinAlg => Geometry}/Point4d.cs | 8 +- Geometry/Vector3d.cs | 114 +++++++++++ HalfEdgeMesh/HE_Mesh.cs | 31 ++- LinAlg/Plane.cs | 29 --- LinAlg/Vector3d.cs | 103 ---------- {LinAlg => LinearAlgebra}/Complex.cs | 0 {LinAlg => LinearAlgebra}/Triplet.cs | 0 Nurbs/Surface.cs | 275 ++++++++++++++------------- 13 files changed, 325 insertions(+), 278 deletions(-) rename {LinAlg => Geometry}/BasePoint.cs (100%) create mode 100644 Geometry/Plane.cs rename {LinAlg => Geometry}/Point2d.cs (100%) rename {LinAlg => Geometry}/Point3d.cs (100%) rename {LinAlg => Geometry}/Point4d.cs (94%) create mode 100644 Geometry/Vector3d.cs delete mode 100644 LinAlg/Plane.cs delete mode 100644 LinAlg/Vector3d.cs rename {LinAlg => LinearAlgebra}/Complex.cs (100%) rename {LinAlg => LinearAlgebra}/Triplet.cs (100%) diff --git a/Collections/Matrix.cs b/Collections/Matrix.cs index a887c24..9dc41b0 100644 --- a/Collections/Matrix.cs +++ b/Collections/Matrix.cs @@ -82,6 +82,8 @@ public Matrix(T[,] data) public void FlipMatrix() { //TODO: Implement FlipMatrix() + + throw new NotImplementedException(); } @@ -138,11 +140,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 +157,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 +170,8 @@ public List GetCornerNeighboursAt(int column, int row) public List GetContiguousNeighboursAt(int column, int row) { //TODO: Implement GetContiguousNeighboursOfEntityAt() - return null; + + throw new NotImplementedException(); } diff --git a/LinAlg/BasePoint.cs b/Geometry/BasePoint.cs similarity index 100% rename from LinAlg/BasePoint.cs rename to Geometry/BasePoint.cs diff --git a/Geometry/Plane.cs b/Geometry/Plane.cs new file mode 100644 index 0000000..6fe0a31 --- /dev/null +++ b/Geometry/Plane.cs @@ -0,0 +1,28 @@ +namespace AR_Lib.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); + + //TODO: Add utility methods to Plane class (flip Axis, relative coordinates...) + } +} diff --git a/LinAlg/Point2d.cs b/Geometry/Point2d.cs similarity index 100% rename from LinAlg/Point2d.cs rename to Geometry/Point2d.cs diff --git a/LinAlg/Point3d.cs b/Geometry/Point3d.cs similarity index 100% rename from LinAlg/Point3d.cs rename to Geometry/Point3d.cs diff --git a/LinAlg/Point4d.cs b/Geometry/Point4d.cs similarity index 94% rename from LinAlg/Point4d.cs rename to Geometry/Point4d.cs index fb408e6..30eaa32 100644 --- a/LinAlg/Point4d.cs +++ b/Geometry/Point4d.cs @@ -9,7 +9,6 @@ namespace AR_Lib.Geometry { public class Point4d : Point3d { - public double Weight { get => weight; set { weight = value; if (isUnset) isUnset = false; } } private double weight; @@ -53,8 +52,7 @@ public Point4d(Point3d pt, double w) : base(pt) #endregion - // Overriden methods - + #region Overriden methods public override bool Equals(object obj) { @@ -73,6 +71,10 @@ public override int GetHashCode() return base.GetHashCode(); } + #endregion + + //TODO: Add hasWeightedCoordinates boolean and implement a weightCoordinates() method + } } diff --git a/Geometry/Vector3d.cs b/Geometry/Vector3d.cs new file mode 100644 index 0000000..028c5da --- /dev/null +++ b/Geometry/Vector3d.cs @@ -0,0 +1,114 @@ +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 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); + + #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.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); + + #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_Mesh.cs b/HalfEdgeMesh/HE_Mesh.cs index 8ab0c1b..14424fd 100644 --- a/HalfEdgeMesh/HE_Mesh.cs +++ b/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 /// @@ -210,7 +224,10 @@ public void indexElements() return index; } - #region Topology related methods + #endregion + + + #region Topology methods public bool isTriangularMesh() { if (isMesh() == isMeshResult.Triangular) return true; @@ -252,7 +269,8 @@ private enum isMeshResult #endregion - /// Utility methods + + #region Utility methods public string GetMeshInfo() { string head = "--- Mesh Info ---\n"; @@ -283,6 +301,9 @@ public override string ToString() return "HE_Mesh{" + VEFH + "}"; } + #endregion + + #region Private methods private void createVertices(List points) { 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/LinAlg/Complex.cs b/LinearAlgebra/Complex.cs similarity index 100% rename from LinAlg/Complex.cs rename to LinearAlgebra/Complex.cs diff --git a/LinAlg/Triplet.cs b/LinearAlgebra/Triplet.cs similarity index 100% rename from LinAlg/Triplet.cs rename to LinearAlgebra/Triplet.cs diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs index 5035b40..0d7365b 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -3,13 +3,21 @@ using System.Collections.Generic; using AR_Lib.LinearAlgebra; + namespace AR_Lib.Geometry.Nurbs { + //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 { - // 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 public int UDegree { get => uDegree; set => uDegree = value; } @@ -21,6 +29,7 @@ public class Surface #endregion + #region Private Properties // Integer properties @@ -35,7 +44,7 @@ public class Surface 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 uBasis, duBasis, vBasis, dvBasis; List uTemp, duTemp; List uTessKnotSpan, vTessKnotSpan; List pVertices; @@ -47,6 +56,7 @@ public class Surface #endregion + #region Constructors // TODO: Implement constructors @@ -94,8 +104,138 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn #endregion + #region Public Methods + public void TessellateSurface() + { + List pControlPoints = controlPoints; + int u, v, k, l; + int uKnot, vKnot; + List UTemp = uTemp, dUTemp = duTemp; + Point4d Pw = new Point4d(); + double rhw; + int iVertices; + int iCPOffset; + double VBasis, dVBasis; + int idx, uidx; + + if ((uTessellationCount == 0) || (vTessellationCount == 0)) + return; + + iVertices = 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[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; + // UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; + // UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; + // UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + UTemp.Add(new Point4d()); + UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; + UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; + UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; + UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + // dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; + // dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; + // dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; + // dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + dUTemp.Add(new Point4d()); + dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; + dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; + dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; + dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + + for (l = 1; l <= uDegree; l++) + { + iCPOffset += vControlPointCount; + + UTemp[k].X += uBasis[uidx + l] * pControlPoints[iCPOffset + k].X; + UTemp[k].Y += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Y; + UTemp[k].Z += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Z; + UTemp[k].Weight += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Weight; + + dUTemp[k].X += duBasis[uidx + l] * pControlPoints[iCPOffset + k].X; + dUTemp[k].Y += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Y; + dUTemp[k].Z += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Z; + dUTemp[k].Weight += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Weight; + } + } + } + + // Compute the point in the U and V directions + VBasis = vBasis[(v * vOrder)]; + dVBasis = dvBasis[(v * vOrder)]; + + Pw.X = VBasis * UTemp[0].X; + Pw.Y = VBasis * UTemp[0].Y; + Pw.Z = VBasis * UTemp[0].Z; + Pw.Weight = VBasis * UTemp[0].Weight; + + for (k = 1; k <= vDegree; k++) + { + VBasis = vBasis[(v * vOrder + k)]; + dVBasis = dvBasis[(v * vOrder + k)]; + Pw.X += VBasis * UTemp[k].X; + Pw.Y += VBasis * UTemp[k].Y; + Pw.Z += VBasis * UTemp[k].Z; + Pw.Weight += VBasis * UTemp[k].Weight; + } + + // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D + rhw = 1.0 / Pw.Weight; + Pw.X = Pw.X * rhw; + Pw.Y = Pw.Y * rhw; + Pw.Z = Pw.Z * rhw; + + // Store the vertex position. + pVertices.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 + public double ComputeCoefficient(List knots, int interval, int i, int p, int k) { //TODO: Check ComputeCoefficient @@ -269,10 +409,9 @@ public void EvaluateBasisFunctions() v += vinc; } } - public void SetTesselations(int uTessellations, int vTessellations) + private void SetTesselations(int uTessellations, int vTessellations) { //TODO: Implement SetTesselations - int SIMD_SIZE = 1; if ((uTessellations != uTessellationCount) || (vTessellations != vTessellationCount)) { @@ -299,136 +438,8 @@ public void SetTesselations(int uTessellations, int vTessellations) EvaluateBasisFunctions(); } } - public void TessellateSurface() - { - List pControlPoints = controlPoints; - int u, v, k, l; - int uKnot, vKnot; - List UTemp = uTemp, dUTemp = duTemp; - Point4d Pw = new Point4d(); - double rhw; - int iVertices; - int iCPOffset; - double VBasis, dVBasis; - int idx, uidx; - - if ((uTessellationCount == 0) || (vTessellationCount == 0)) - return; - - iVertices = 2 * (vTessellationCount + 1); - - // Step over the U and V coordinates and generate triangle strips to render - // - for (u = 0; u <= uTessellationCount; u++) - { - int SIMD_SIZE = 1; - - // 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 * SIMD_SIZE; - 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[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; - // UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; - // UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; - // UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; - UTemp.Add(new Point4d()); - UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; - UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; - UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; - UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; - // dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; - // dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; - // dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; - // dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; - dUTemp.Add(new Point4d()); - dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; - dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; - dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; - dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; - - for (l = 1; l <= uDegree; l++) - { - iCPOffset += vControlPointCount; - - UTemp[k].X += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; - UTemp[k].Y += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; - UTemp[k].Z += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; - UTemp[k].Weight += uBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Weight; - - dUTemp[k].X += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].X; - dUTemp[k].Y += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Y; - dUTemp[k].Z += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Z; - dUTemp[k].Weight += duBasis[uidx + l * SIMD_SIZE] * pControlPoints[iCPOffset + k].Weight; - } - } - } - - // Compute the point in the U and V directions - VBasis = vBasis[(v * vOrder) * SIMD_SIZE]; - dVBasis = dvBasis[(v * vOrder) * SIMD_SIZE]; - - Pw.X = VBasis * UTemp[0].X; - Pw.Y = VBasis * UTemp[0].Y; - Pw.Z = VBasis * UTemp[0].Z; - Pw.Weight = VBasis * UTemp[0].Weight; - - for (k = 1; k <= vDegree; k++) - { - VBasis = vBasis[(v * vOrder + k) * SIMD_SIZE]; - dVBasis = dvBasis[(v * vOrder + k) * SIMD_SIZE]; - Pw.X += VBasis * UTemp[k].X; - Pw.Y += VBasis * UTemp[k].Y; - Pw.Z += VBasis * UTemp[k].Z; - Pw.Weight += VBasis * UTemp[k].Weight; - } - - // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D - rhw = 1.0 / Pw.Weight; - Pw.X = Pw.X * rhw; - Pw.Y = Pw.Y * rhw; - Pw.Z = Pw.Z * rhw; - - // Store the vertex position. - pVertices.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 - - #endregion } } From 0d99e17c864503ff76b581c7cafcff50fb71aabf Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Tue, 9 Apr 2019 12:42:36 +0200 Subject: [PATCH 04/23] Autoformat changes --- Geometry/BasePoint.cs | 1 + Geometry/Point2d.cs | 1 + Geometry/Vector3d.cs | 1 + Nurbs/Surface.cs | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Geometry/BasePoint.cs b/Geometry/BasePoint.cs index 09d0750..2142e5d 100644 --- a/Geometry/BasePoint.cs +++ b/Geometry/BasePoint.cs @@ -107,4 +107,5 @@ public override int GetHashCode() } } + } diff --git a/Geometry/Point2d.cs b/Geometry/Point2d.cs index da9c547..319ab94 100644 --- a/Geometry/Point2d.cs +++ b/Geometry/Point2d.cs @@ -51,4 +51,5 @@ public override int GetHashCode() } } + } diff --git a/Geometry/Vector3d.cs b/Geometry/Vector3d.cs index 028c5da..a6fc887 100644 --- a/Geometry/Vector3d.cs +++ b/Geometry/Vector3d.cs @@ -111,4 +111,5 @@ public override int GetHashCode() } + } diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs index 0d7365b..f5b2a67 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -7,7 +7,7 @@ namespace AR_Lib.Geometry.Nurbs { //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) /// @@ -442,4 +442,5 @@ private void SetTesselations(int uTessellations, int vTessellations) #endregion } + } From 72f1a14302c48765eb6e69c53d15e2c4368999c7 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Tue, 9 Apr 2019 14:16:46 +0200 Subject: [PATCH 05/23] Cleaned up Surface class --- Nurbs/Surface.cs | 89 ++++++++++++++---------------------------------- 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/Nurbs/Surface.cs b/Nurbs/Surface.cs index f5b2a67..f76b4f8 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -1,13 +1,13 @@ using System; -using System.Collections; using System.Collections.Generic; -using AR_Lib.LinearAlgebra; +using System.Text; - -namespace AR_Lib.Geometry.Nurbs +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) /// @@ -63,6 +63,7 @@ public class Surface 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; @@ -72,6 +73,7 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn vKnots = vKnotsValues; //Compute some useful property values + uOrder = uDegree + 1; vOrder = vDegree + 1; @@ -82,6 +84,7 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn vBasisSpanCount = vOrder - 2 + vControlPointCount; // Initialize empty objects + uBasisCoefficients = new List(); vBasisCoefficients = new List(); uBasis = new List(); @@ -109,6 +112,8 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn public void TessellateSurface() { + //FIXME: Rename this variables to fit the convention. + List pControlPoints = controlPoints; int u, v, k, l; int uKnot, vKnot; @@ -153,38 +158,17 @@ public void TessellateSurface() { iCPOffset = (uKnot - uDegree) * vControlPointCount + (vKnot - vDegree); - // UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; - // UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; - // UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; - // UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; UTemp.Add(new Point4d()); - UTemp[k].X = uBasis[uidx] * pControlPoints[iCPOffset + k].X; - UTemp[k].Y = uBasis[uidx] * pControlPoints[iCPOffset + k].Y; - UTemp[k].Z = uBasis[uidx] * pControlPoints[iCPOffset + k].Z; - UTemp[k].Weight = uBasis[uidx] * pControlPoints[iCPOffset + k].Weight; - // dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; - // dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; - // dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; - // dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + UTemp[k] = uBasis[uidx] * pControlPoints[iCPOffset + k]; + dUTemp.Add(new Point4d()); - dUTemp[k].X = duBasis[uidx] * pControlPoints[iCPOffset + k].X; - dUTemp[k].Y = duBasis[uidx] * pControlPoints[iCPOffset + k].Y; - dUTemp[k].Z = duBasis[uidx] * pControlPoints[iCPOffset + k].Z; - dUTemp[k].Weight = duBasis[uidx] * pControlPoints[iCPOffset + k].Weight; + duTemp[k] = duBasis[uidx] * pControlPoints[iCPOffset + k]; for (l = 1; l <= uDegree; l++) { iCPOffset += vControlPointCount; - - UTemp[k].X += uBasis[uidx + l] * pControlPoints[iCPOffset + k].X; - UTemp[k].Y += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Y; - UTemp[k].Z += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Z; - UTemp[k].Weight += uBasis[uidx + l] * pControlPoints[iCPOffset + k].Weight; - - dUTemp[k].X += duBasis[uidx + l] * pControlPoints[iCPOffset + k].X; - dUTemp[k].Y += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Y; - dUTemp[k].Z += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Z; - dUTemp[k].Weight += duBasis[uidx + l] * pControlPoints[iCPOffset + k].Weight; + UTemp[k] += uBasis[uidx + l] * pControlPoints[iCPOffset + k]; + duTemp[k] += duBasis[uidx + l] * pControlPoints[iCPOffset + k]; } } } @@ -192,27 +176,17 @@ public void TessellateSurface() // Compute the point in the U and V directions VBasis = vBasis[(v * vOrder)]; dVBasis = dvBasis[(v * vOrder)]; - - Pw.X = VBasis * UTemp[0].X; - Pw.Y = VBasis * UTemp[0].Y; - Pw.Z = VBasis * UTemp[0].Z; - Pw.Weight = VBasis * UTemp[0].Weight; + Pw = VBasis * UTemp[0]; for (k = 1; k <= vDegree; k++) { VBasis = vBasis[(v * vOrder + k)]; dVBasis = dvBasis[(v * vOrder + k)]; - Pw.X += VBasis * UTemp[k].X; - Pw.Y += VBasis * UTemp[k].Y; - Pw.Z += VBasis * UTemp[k].Z; - Pw.Weight += VBasis * UTemp[k].Weight; + Pw += VBasis * UTemp[k]; } - // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D - rhw = 1.0 / Pw.Weight; - Pw.X = Pw.X * rhw; - Pw.Y = Pw.Y * rhw; - Pw.Z = Pw.Z * rhw; + // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D + Pw *= 1.0 / Pw.Weight; // Store the vertex position. pVertices.Add(new Point3d(Pw)); @@ -279,7 +253,6 @@ public double ComputeCoefficient(List knots, int interval, int i, int p, 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; } @@ -299,7 +272,6 @@ public void ComputeBasisCoefficients() { for (k = 0; k < uOrder; k++) { - //uBasisCoefficients[(i * uOrder + j) * uOrder + k] = uBasisCoefficients.Add( ComputeCoefficient(uKnots, i + uDegree, i + j, uDegree, k)); } @@ -312,8 +284,6 @@ public void ComputeBasisCoefficients() { for (k = 0; k < vOrder; k++) { - // vBasisCoefficients[(i * vOrder + j) * vOrder + k] = - // ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k); vBasisCoefficients.Add( ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, k)); } @@ -323,11 +293,10 @@ public void ComputeBasisCoefficients() } public void EvaluateBasisFunctions() { - //TODO: Check EvaluateBasisFunctions + //TODO: Check EvaluateBasisFunctions, rename properties to something more meaningfull int i, j, k, idx; double u, uinc; double v, vinc; - int SIMD_SIZE = 1; // // First evaluate the U basis functions and derivitives at uniformly spaced u values @@ -348,24 +317,18 @@ public void EvaluateBasisFunctions() // for (j = 0; j < uOrder; j++) { - //uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]; - //duBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree; uBasis.Add(uBasisCoefficients[(idx * uOrder + j) * uOrder + uDegree]); - duBasis.Add(uBasis[(i * uOrder + j) * SIMD_SIZE] * uDegree); + duBasis.Add(uBasis[(i * uOrder + j)] * uDegree); for (k = uDegree - 1; k >= 0; k--) { - uBasis[(i * uOrder + j) * SIMD_SIZE] = uBasis[(i * uOrder + j) * SIMD_SIZE] * u + + uBasis[(i * uOrder + j)] = uBasis[(i * uOrder + j)] * u + uBasisCoefficients[(idx * uOrder + j) * uOrder + k]; if (k > 0) { - duBasis[(i * uOrder + j) * SIMD_SIZE] = duBasis[(i * uOrder + j) * SIMD_SIZE] * u + + duBasis[(i * uOrder + j)] = duBasis[(i * uOrder + j)] * u + uBasisCoefficients[(idx * uOrder + j) * uOrder + k] * k; } } - // - // Make three copies. This isn't necessary if we're using straight C - // code but for the Pentium III optimizations, it is. - // } u += uinc; @@ -394,14 +357,14 @@ public void EvaluateBasisFunctions() // 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) * SIMD_SIZE] * vDegree); + dvBasis.Add(vBasis[(i * vOrder + j)] * vDegree); for (k = vDegree - 1; k >= 0; k--) { - vBasis[(i * vOrder + j) * SIMD_SIZE] = vBasis[(i * vOrder + j) * SIMD_SIZE] * v + + vBasis[(i * vOrder + j)] = vBasis[(i * vOrder + j)] * v + vBasisCoefficients[(idx * vOrder + j) * vOrder + k]; if (k > 0) { - dvBasis[(i * vOrder + j) * SIMD_SIZE] = dvBasis[(i * vOrder + j) * SIMD_SIZE] * v + + dvBasis[(i * vOrder + j)] = dvBasis[(i * vOrder + j)] * v + vBasisCoefficients[(idx * vOrder + j) * vOrder + k] * k; } } From adb7ec0f6ba6879ce3e5e5ac04b2403faad9f38f Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 10 Apr 2019 10:11:35 +0200 Subject: [PATCH 06/23] Namespace reaming Created class files for: * Line * Polyline * BaseCurve --- Curves/Curves.cs | 109 ----------------- Geometry/BaseCurve.cs | 63 ++++++++++ Geometry/Line.cs | 44 +++++++ Geometry/Plane.cs | 7 ++ Geometry/Polyline.cs | 27 +++++ LinearAlgebra/LeastSquares.cs | 47 ++++++++ Nurbs/Surface.cs | 215 +++++++++++++++++++++------------- Utility/Intersect.cs | 30 ++--- 8 files changed, 338 insertions(+), 204 deletions(-) delete mode 100644 Curves/Curves.cs create mode 100644 Geometry/BaseCurve.cs create mode 100644 Geometry/Line.cs create mode 100644 Geometry/Polyline.cs create mode 100644 LinearAlgebra/LeastSquares.cs 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/Geometry/BaseCurve.cs b/Geometry/BaseCurve.cs new file mode 100644 index 0000000..b7b489f --- /dev/null +++ b/Geometry/BaseCurve.cs @@ -0,0 +1,63 @@ + +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 Point3d StartPoint; + public Point3d EndPoint; + public Point3d T0; + public Point3d T1; + + protected Point3d _startPoint; + protected Point3d _endPoint; + protected double _t0; + protected double _t1; + public bool IsValid => _isValid; + 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/Geometry/Line.cs b/Geometry/Line.cs new file mode 100644 index 0000000..9e1091b --- /dev/null +++ b/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) => _endPoint + 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/Geometry/Plane.cs b/Geometry/Plane.cs index 6fe0a31..f49ada8 100644 --- a/Geometry/Plane.cs +++ b/Geometry/Plane.cs @@ -13,6 +13,13 @@ public class Plane 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; diff --git a/Geometry/Polyline.cs b/Geometry/Polyline.cs new file mode 100644 index 0000000..c5b5dcd --- /dev/null +++ b/Geometry/Polyline.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace AR_Lib.Geometry +{ + + public class Polyline : BaseCurve + { + private List _knots; + private List _segments; + + 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(); + + } + +} \ No newline at end of file diff --git a/LinearAlgebra/LeastSquares.cs b/LinearAlgebra/LeastSquares.cs new file mode 100644 index 0000000..ecceb53 --- /dev/null +++ b/LinearAlgebra/LeastSquares.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using AR_Lib.Geometry; + +namespace AR_Lib.LinearAlgebra +{ + 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/ + 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/Nurbs/Surface.cs b/Nurbs/Surface.cs index f76b4f8..72c3784 100644 --- a/Nurbs/Surface.cs +++ b/Nurbs/Surface.cs @@ -2,6 +2,8 @@ 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: @@ -20,12 +22,27 @@ public class Surface #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; } - public List ControlPoints { get => controlPoints; set => controlPoints = value; } - public List PVertices { get => pVertices; set => pVertices = value; } - public List UKnots { get => uKnots; set => uKnots = value; } - public List VKnots { get => vKnots; set => vKnots = 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 @@ -33,6 +50,7 @@ public class Surface #region Private Properties // Integer properties + int uDegree, vDegree; // Surface degrees int uOrder, vOrder; // Surface order int uKnotCount, vKnotCount; // Knot count in each direction @@ -41,25 +59,36 @@ public class Surface 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 pVertices; + 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; - - + bool _isValid; // TODO: This does nothing for now #endregion #region Constructors - // TODO: Implement 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 @@ -68,9 +97,9 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn vDegree = vDeg; uControlPointCount = uCntrPts; vControlPointCount = vCntrlPts; - controlPoints = cntrlPts; - uKnots = uKnotsValues; - vKnots = vKnotsValues; + _controlPoints = cntrlPts; + _uKnots = uKnotsValues; + _vKnots = vKnotsValues; //Compute some useful property values @@ -80,22 +109,22 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn uKnotCount = uOrder + uControlPointCount; vKnotCount = vOrder + vControlPointCount; - uBasisSpanCount = uOrder - 2 + uControlPointCount; - vBasisSpanCount = vOrder - 2 + 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(); - pVertices = new List(); + _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 @@ -110,17 +139,20 @@ public Surface(int uDeg, int vDeg, int uCntrPts, int vCntrlPts, List cn #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; + List pControlPoints = _controlPoints; int u, v, k, l; int uKnot, vKnot; - List UTemp = uTemp, dUTemp = duTemp; + List UTemp = _uTemp, dUTemp = _duTemp; Point4d Pw = new Point4d(); - double rhw; - int iVertices; + int vertexCount; int iCPOffset; double VBasis, dVBasis; int idx, uidx; @@ -128,14 +160,14 @@ public void TessellateSurface() if ((uTessellationCount == 0) || (vTessellationCount == 0)) return; - iVertices = 2 * (vTessellationCount + 1); + 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]; + uKnot = _uTessKnotSpan[u]; // Calculate the offset into the pre-calculated basis functions array uidx = u * uOrder; @@ -145,9 +177,9 @@ public void TessellateSurface() for (v = 0; v <= vTessellationCount; v++) { idx = u * uTessellationCount + v; - if (vKnot != vTessKnotSpan[v]) + if (vKnot != _vTessKnotSpan[v]) { - 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 @@ -159,37 +191,37 @@ public void TessellateSurface() iCPOffset = (uKnot - uDegree) * vControlPointCount + (vKnot - vDegree); UTemp.Add(new Point4d()); - UTemp[k] = uBasis[uidx] * pControlPoints[iCPOffset + k]; + UTemp[k] = _uBasis[uidx] * pControlPoints[iCPOffset + k]; dUTemp.Add(new Point4d()); - duTemp[k] = duBasis[uidx] * pControlPoints[iCPOffset + k]; + _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]; + 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)]; + 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)]; + VBasis = _vBasis[(v * vOrder + k)]; + dVBasis = _dvBasis[(v * vOrder + k)]; Pw += VBasis * UTemp[k]; } - // rhw is the factor to multiply by inorder to bring the 4-D points back into 3-D + // Bring the 4-D points back into 3-D Pw *= 1.0 / Pw.Weight; // Store the vertex position. - pVertices.Add(new Point3d(Pw)); + _vertices.Add(new Point3d(Pw)); } } @@ -210,6 +242,15 @@ public void UpdateControlPoints() // 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 @@ -256,6 +297,10 @@ public double ComputeCoefficient(List knots, int interval, int i, int p, } return result; } + + /// + /// Compute all basis coefficients of the surface + /// public void ComputeBasisCoefficients() { //TODO: Check ComputeBasisCoefficients @@ -272,8 +317,8 @@ public void ComputeBasisCoefficients() { for (k = 0; k < uOrder; k++) { - uBasisCoefficients.Add( - ComputeCoefficient(uKnots, i + uDegree, i + j, uDegree, k)); + _uBasisCoefficients.Add( + ComputeCoefficient(_uKnots, i + uDegree, i + j, uDegree, k)); } } } @@ -284,13 +329,17 @@ public void ComputeBasisCoefficients() { for (k = 0; k < vOrder; k++) { - vBasisCoefficients.Add( - ComputeCoefficient(vKnots, i + vDegree, i + j, vDegree, 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 @@ -302,31 +351,31 @@ public void EvaluateBasisFunctions() // 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); + 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])) + while ((idx < uKnotCount - uDegree * 2 - 2) && (u >= _uKnots[idx + uDegree + 1])) idx++; - uTessKnotSpan.Add(idx + uDegree); + _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); + _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]; + _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; + _duBasis[(i * uOrder + j)] = _duBasis[(i * uOrder + j)] * u + + _uBasisCoefficients[(idx * uOrder + j) * uOrder + k] * k; } } } @@ -338,16 +387,16 @@ public void EvaluateBasisFunctions() // Finally evaluate the V basis functions at uniformly spaced v values // idx = 0; - v = vKnots[idx + vDegree]; - vinc = (vKnots[vKnotCount - vOrder] - vKnots[vDegree]) / (vTessellationCount); + 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])) + while ((idx < vKnotCount - vDegree * 2 - 2) && (v >= _vKnots[idx + vDegree + 1])) idx++; //vTessKnotSpan[i] = idx + vDegree; - vTessKnotSpan.Add(idx + vDegree); + _vTessKnotSpan.Add(idx + vDegree); // // Evaluate using Horner's method @@ -356,22 +405,28 @@ public void EvaluateBasisFunctions() { // 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); + _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]; + _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; + _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 @@ -384,16 +439,16 @@ private void SetTesselations(int uTessellations, int 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)); + _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); + _uTessKnotSpan = new List(uTessellationCount + 1); + _vTessKnotSpan = new List(vTessellationCount + 1); int iVertices = ((uTessellations + 1) * (vTessellations + 1)); //2 * (vTessellations + 1); - pVertices = new List(iVertices); + _vertices = new List(iVertices); // // Re-evaluate the basis functions diff --git a/Utility/Intersect.cs b/Utility/Intersect.cs index c70fcf2..8418136 100644 --- a/Utility/Intersect.cs +++ b/Utility/Intersect.cs @@ -21,15 +21,15 @@ public static class Intersect3D // 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; + 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); + 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 + if (N == 0) // Segment lies in plane { I = null; return 2; @@ -50,14 +50,14 @@ public static int LinePlane(Line S, Plane Pn, out Point3d I) return 0; // No intersection } - I = S.startPoint + u * sI; // Compute segment intersection point + 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); + Vector3d biNormal = Vector3d.CrossProduct(RayDir, faceNormal); Plane perpPlane = new Plane(RayOrigin, RayDir, faceNormal, biNormal); @@ -66,18 +66,18 @@ public static int RayFacePerimeter(Point3d RayOrigin, Vector3d RayDir, HE_Face F 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 + 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 + 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 + 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! + else { result = null; halfEdge = null; return 4; } // Error 4 means something weird happened! } } } \ No newline at end of file From 10489b689b173399ab3a650944f6e2eeb4da64ad Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sat, 13 Apr 2019 13:13:28 +0200 Subject: [PATCH 07/23] Added gradient computation to the LevelSets class --- Curves/LevelSets.cs | 119 ++++++++++++++++++++++------------------ HalfEdgeMesh/HE_Face.cs | 3 + 2 files changed, 69 insertions(+), 53 deletions(-) diff --git a/Curves/LevelSets.cs b/Curves/LevelSets.cs index b943dd4..e2f3540 100644 --- a/Curves/LevelSets.cs +++ b/Curves/LevelSets.cs @@ -8,22 +8,29 @@ namespace AR_Lib.Curve { public static class LevelSets { - public static void Compute(string valueKey, List levels, HE_Mesh mesh, out List> 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++) + for (int i = 0; i < levels.Count; i++) { resultLines.Add(new List()); } int iter = 0; - foreach(HE_Face face in mesh.Faces) + foreach (HE_Face face in mesh.Faces) { int count = 0; - foreach(double level in levels) + foreach (double level in levels) { Line l = new Line(); - if(getFaceLevel(valueKey,level,face,out l)) + if (GetFaceLevel(valueKey, level, face, out l)) { resultLines[count].Add(l); } @@ -34,16 +41,24 @@ public static void Compute(string valueKey, List levels, HE_Mesh mesh, o } levelSets = resultLines; - } + } - public static bool getFaceLevel(string valueKey, double level, HE_Face face, out Line line) + /// + /// 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 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); @@ -53,7 +68,7 @@ public static bool getFaceLevel(string valueKey, double level, HE_Face face, out if (above.Count == 3 || below.Count == 3) { // Triangle is above or below level - line = new Line(new Point3d(),new Point3d()); + line = new Line(new Point3d(), new Point3d()); return false; } else @@ -79,47 +94,45 @@ public static bool getFaceLevel(string valueKey, double level, HE_Face face, out } + + /// + /// 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; + } + } -} - - // 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; - // } - // } +} \ No newline at end of file diff --git a/HalfEdgeMesh/HE_Face.cs b/HalfEdgeMesh/HE_Face.cs index 0e89377..83ea0fa 100644 --- a/HalfEdgeMesh/HE_Face.cs +++ b/HalfEdgeMesh/HE_Face.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using AR_Lib.Geometry; namespace AR_Lib { @@ -12,6 +13,8 @@ public class HE_Face public HE_HalfEdge HalfEdge; //One of the half-edges surrounding the face public int Index; + public double Area => HE_MeshGeometry.Area(this); + public Vector3d Normal => HE_MeshGeometry.FaceNormal(this); // Constructor public HE_Face() From 2c7f9bb8e6f133a3b2c5670bb708907c86de009b Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sat, 13 Apr 2019 19:40:31 +0200 Subject: [PATCH 08/23] Added polyline basic implementation. Modified Basecurve with public fields --- Geometry/BaseCurve.cs | 11 ++++---- Geometry/Polyline.cs | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Geometry/BaseCurve.cs b/Geometry/BaseCurve.cs index b7b489f..f33481a 100644 --- a/Geometry/BaseCurve.cs +++ b/Geometry/BaseCurve.cs @@ -28,16 +28,17 @@ public InvalidCurveException(string message, Exception innerException) : base(me public abstract class BaseCurve { // Public properties - public Point3d StartPoint; - public Point3d EndPoint; - public Point3d T0; - public Point3d T1; + 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 bool IsValid => _isValid; public double Length => ComputeLength(); // Private fields diff --git a/Geometry/Polyline.cs b/Geometry/Polyline.cs index c5b5dcd..d2abd1d 100644 --- a/Geometry/Polyline.cs +++ b/Geometry/Polyline.cs @@ -9,6 +9,64 @@ 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(); @@ -20,7 +78,9 @@ protected override double ComputeLength() _segments.ForEach(segment => length += segment.Length); return length; } + public override void CheckValidity() => throw new NotImplementedException(); + #endregion } From 7cfd1c3d3d18391c4a9d77864a833efc23a88fe8 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sat, 13 Apr 2019 19:48:10 +0200 Subject: [PATCH 09/23] Added xml docs to HE_Face and HE_MeshGeometry --- HalfEdgeMesh/HE_Face.cs | 47 +++++++++++++++++++++++- HalfEdgeMesh/HE_MeshGeometry.cs | 64 +++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/HalfEdgeMesh/HE_Face.cs b/HalfEdgeMesh/HE_Face.cs index 83ea0fa..b1954ff 100644 --- a/HalfEdgeMesh/HE_Face.cs +++ b/HalfEdgeMesh/HE_Face.cs @@ -10,19 +10,34 @@ namespace HalfEdgeMesh /// public class HE_Face { + #region Properties & Fields public HE_HalfEdge HalfEdge; //One of the half-edges surrounding the face public int Index; public double Area => HE_MeshGeometry.Area(this); public Vector3d Normal => HE_MeshGeometry.FaceNormal(this); - // Constructor + #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; @@ -37,6 +52,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; @@ -51,6 +70,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(); @@ -65,6 +88,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(); @@ -77,6 +104,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(); @@ -89,8 +120,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(); @@ -104,6 +147,8 @@ public override string ToString() return text; } + #endregion + } } } diff --git a/HalfEdgeMesh/HE_MeshGeometry.cs b/HalfEdgeMesh/HE_MeshGeometry.cs index 081db45..df8fda0 100644 --- a/HalfEdgeMesh/HE_MeshGeometry.cs +++ b/HalfEdgeMesh/HE_MeshGeometry.cs @@ -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 }; } /// @@ -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(); @@ -299,20 +324,35 @@ public static Vector3d VertexNormalSphereInscribed(HE_Vertex vertex) 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 }; } } From 179a03fbd7050127d5207862b103543fb64ff787 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 14 Apr 2019 00:16:43 +0200 Subject: [PATCH 10/23] Changes in settings structure --- Data/Settings.json | 10 +++------- Utility/Settings.cs | 17 +++-------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/Data/Settings.json b/Data/Settings.json index a65a42d..d4258cf 100644 --- a/Data/Settings.json +++ b/Data/Settings.json @@ -1,8 +1,4 @@ { - "Constants": - { - "DefaultTesselation": 10, - "Tolerance": 0.00001 - - }, -} + "DefaultTesselation": 10, + "Tolerance": 0.00001 +} \ No newline at end of file diff --git a/Utility/Settings.cs b/Utility/Settings.cs index 1fd021a..3797f36 100644 --- a/Utility/Settings.cs +++ b/Utility/Settings.cs @@ -10,23 +10,12 @@ namespace AR_Lib /// /// Multi-layered struct that holds the library settings /// - public struct Settings + public class Settings { - /// - /// Constants of the library - /// - public ConstStruct Constants; + public static double Tolerance = 0.000001; + public static double DefaultTesselation = 10; - /// - /// Data structure that save the library constants - /// - public struct ConstStruct - { - public double Tolerance; - public double DefaultTesselation; - } - } public static class SettingsReader From 6bc1798838e4eb55dc33bb586fbfdde9f559e7b7 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 14 Apr 2019 00:17:31 +0200 Subject: [PATCH 11/23] Added LineLine intersection Changed outputs to enums for clarity --- Utility/Intersect.cs | 129 +++++++++++++++++++++++++++++-------- Utility/IntersectErrors.cs | 41 ++++++++++++ 2 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 Utility/IntersectErrors.cs diff --git a/Utility/Intersect.cs b/Utility/Intersect.cs index 8418136..8826695 100644 --- a/Utility/Intersect.cs +++ b/Utility/Intersect.cs @@ -1,25 +1,18 @@ using System; using System.Collections; using System.Collections.Generic; - -using AR_Lib.HalfEdgeMesh; +using AR_Lib.Curve; using AR_Lib.Geometry; +using AR_Lib.HalfEdgeMesh; using AR_Lib.LinearAlgebra; -using AR_Lib.Curve; - namespace AR_Lib { - public static class Intersect3D - { + //INFO: Error enums for all classes are in file IntersectErrors.cs - // 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) + public static partial class Intersect3D + { + public static ISLinePlane LinePlane(Line S, Plane Pn, out Point3d I) { Vector3d u = S.EndPoint - S.StartPoint; Vector3d w = S.StartPoint - Pn.Origin; @@ -32,12 +25,12 @@ public static int LinePlane(Line S, Plane Pn, out Point3d I) if (N == 0) // Segment lies in plane { I = null; - return 2; + return ISLinePlane.OnPlane; } else { I = null; - return 0; // No intersection + return ISLinePlane.NoIntersection; } } @@ -47,14 +40,13 @@ public static int LinePlane(Line S, Plane Pn, out Point3d I) if (sI < 0 || sI > 1) { I = null; - return 0; // No intersection + return ISLinePlane.NoIntersection; } I = S.StartPoint + u * sI; // Compute segment intersection point - return 1; + return ISLinePlane.Point; } - - public static int RayFacePerimeter(Point3d RayOrigin, Vector3d RayDir, HE_Face Face, out Point3d result, out HE_HalfEdge halfEdge) + public static ISRayFacePerimeter 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); @@ -66,18 +58,103 @@ public static int RayFacePerimeter(Point3d RayOrigin, Vector3d RayDir, HE_Face F 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 + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.Point; } // No intersection found + if (temp != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // 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 + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.NoIntersection; } // No intersection found + if (temp != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // 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 + if (LinePlane(line, perpPlane, out temp) != ISLinePlane.Point) { result = null; halfEdge = null; return ISRayFacePerimeter.NoIntersection; } + if (temp != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } else { result = null; halfEdge = null; return ISRayFacePerimeter.Error; } + } - else { result = null; halfEdge = null; return 4; } // Error 4 means something weird happened! + 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/Utility/IntersectErrors.cs b/Utility/IntersectErrors.cs new file mode 100644 index 0000000..ec5bac1 --- /dev/null +++ b/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 From ba3b5e798bd6c1de34c1b86d6a4e823698a2fb75 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 14 Apr 2019 00:17:38 +0200 Subject: [PATCH 12/23] Minor modfications --- Geometry/Line.cs | 2 +- Geometry/Ray.cs | 34 +++++++++++++++++++++++++++++++++ Geometry/Vector3d.cs | 11 ++++++----- HalfEdgeMesh/HE_MeshGeometry.cs | 18 ++++++++--------- 4 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 Geometry/Ray.cs diff --git a/Geometry/Line.cs b/Geometry/Line.cs index 9e1091b..98e7096 100644 --- a/Geometry/Line.cs +++ b/Geometry/Line.cs @@ -18,7 +18,7 @@ public override void CheckValidity() throw new System.NotImplementedException(); } - public override Point3d PointAt(double t) => _endPoint + t * (_endPoint - _startPoint); + public override Point3d PointAt(double t) => _startPoint + t * (_endPoint - _startPoint); public override Vector3d TangentAt(double t) { Vector3d tangent = _endPoint - _startPoint; diff --git a/Geometry/Ray.cs b/Geometry/Ray.cs new file mode 100644 index 0000000..d4a2b55 --- /dev/null +++ b/Geometry/Ray.cs @@ -0,0 +1,34 @@ +namespace AR_Lib.Geometry +{ + /// + /// Infinite 3d ray starting at a point. + /// + public class Ray + { + private Point3d _origin; + private Vector3d _direction; + + public Point3d Origin { get => _origin; set => _origin = value; } + public Vector3d Direction { get => _direction; set => _direction = value; } + + #region Constructors + + public Ray(Point3d origin, Vector3d direction) + { + _origin = origin; + _direction = direction; + } + + #endregion + + #region Public methods + + public Point3d PointAt(double t) + { + return _origin + t * _direction; + } + + #endregion + } + +} \ No newline at end of file diff --git a/Geometry/Vector3d.cs b/Geometry/Vector3d.cs index a6fc887..d9fd49a 100644 --- a/Geometry/Vector3d.cs +++ b/Geometry/Vector3d.cs @@ -31,14 +31,15 @@ public class Vector3d : BasePoint #region Utility properties/methods // Computes the Euclidiean length squared of this vector - public double Norm2 => DotProduct(this, this); + public double Length2 => DotProduct(this, this); + // Computes the Euclidean length of this vector - public double Norm => Math.Sqrt(Norm2); + public double Length => Math.Sqrt(Length2); // Divides this vector by it's euclidean length public void Normalize() { - double length = Norm; + double length = Length; X /= length; Y /= length; Z /= length; @@ -47,7 +48,7 @@ public void Normalize() // Returns a normalized copy of this vector public Vector3d Unit() { - double length = Norm; + double length = Length; double x = X / length; double y = Y / length; double z = Z / length; @@ -79,7 +80,7 @@ public static Vector3d CrossProduct(Vector3d u, Vector3d v) 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)); + return Math.Acos(Vector3d.DotProduct(u, v) / (u.Length * v.Length)); } // Global unit vectors diff --git a/HalfEdgeMesh/HE_MeshGeometry.cs b/HalfEdgeMesh/HE_MeshGeometry.cs index df8fda0..7388c59 100644 --- a/HalfEdgeMesh/HE_MeshGeometry.cs +++ b/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; } @@ -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); @@ -319,7 +319,7 @@ 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(); } From bb0aaffe6153a053547e86620f45d5fa647dabca Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 14 Apr 2019 20:35:46 +0200 Subject: [PATCH 13/23] Added max decimals to settings Added max decimals cap to BasePoint --- Geometry/BasePoint.cs | 18 +++++++++++------- Utility/Settings.cs | 14 +++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Geometry/BasePoint.cs b/Geometry/BasePoint.cs index 2142e5d..feab3f5 100644 --- a/Geometry/BasePoint.cs +++ b/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; } diff --git a/Utility/Settings.cs b/Utility/Settings.cs index 3797f36..9e1541a 100644 --- a/Utility/Settings.cs +++ b/Utility/Settings.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using System.Reflection; - using Newtonsoft.Json; namespace AR_Lib @@ -12,9 +11,18 @@ namespace AR_Lib /// public class Settings { - public static double Tolerance = 0.000001; + public static double Tolerance => _tolerance; public static double DefaultTesselation = 10; + public static int MaxDecimals => _maxDecimals; + private static int _maxDecimals = 8; + private static double _tolerance = 0.0000001; + public static void ModifyTolerance(double tolerance) + { + _tolerance = tolerance; + string t = tolerance.ToString("N14"); + _maxDecimals = t.Substring(t.IndexOf(".") + 1).IndexOf("1"); + } } @@ -40,4 +48,4 @@ public static Settings ReadSettings() return deserializedSettings; } } -} +} \ No newline at end of file From fc29b0dd2f16d9e986dd5db03bba4e59f5c04d7c Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 17 Apr 2019 11:11:11 +0200 Subject: [PATCH 14/23] Added spatial search and pointcloud empty classes --- SpatialStructures/Octree.cs | 7 +++++++ SpatialStructures/PointCloud.cs | 22 ++++++++++++++++++++++ SpatialStructures/Quadtree.cs | 7 +++++++ 3 files changed, 36 insertions(+) create mode 100644 SpatialStructures/Octree.cs create mode 100644 SpatialStructures/PointCloud.cs create mode 100644 SpatialStructures/Quadtree.cs diff --git a/SpatialStructures/Octree.cs b/SpatialStructures/Octree.cs new file mode 100644 index 0000000..05fe323 --- /dev/null +++ b/SpatialStructures/Octree.cs @@ -0,0 +1,7 @@ +namespace AR_Lib.SpatialSearch +{ + public class Octree + { + //TODO: This class is empty! + } +} \ No newline at end of file diff --git a/SpatialStructures/PointCloud.cs b/SpatialStructures/PointCloud.cs new file mode 100644 index 0000000..3db69f7 --- /dev/null +++ b/SpatialStructures/PointCloud.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using AR_Lib.Geometry; + +namespace AR_Lib.SpatialSearch +{ + public class PointCloud + { + List _points; + + public List Points { get => _points; set => _points = value; } + + } + + public class PointCloudMember : BasePoint + { + private Color _color; + + public Color Color { get => _color; set => _color = value; } + } +} \ No newline at end of file diff --git a/SpatialStructures/Quadtree.cs b/SpatialStructures/Quadtree.cs new file mode 100644 index 0000000..0b5a98f --- /dev/null +++ b/SpatialStructures/Quadtree.cs @@ -0,0 +1,7 @@ +namespace AR_Lib.SpatialSearch +{ + public class Quadtree + { + // TODO: This class is empty! + } +} \ No newline at end of file From 55a4f885425ee2dd50a1f2d6d32d4d68426928bb Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 17 Apr 2019 11:12:59 +0200 Subject: [PATCH 15/23] Added sphere class --- Geometry/Sphere.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Geometry/Sphere.cs diff --git a/Geometry/Sphere.cs b/Geometry/Sphere.cs new file mode 100644 index 0000000..c16faec --- /dev/null +++ b/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 From 6c0d88b60b365126d2bfd143b721029c0dcda381 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 19:56:36 +0200 Subject: [PATCH 16/23] Major restructuring Added Xunit tests project --- .vscode/launch.json | 27 +++++++++ .vscode/tasks.json | 36 ++++++++++++ AR_GeometryLibrary.sln | 48 ++++++++++++++++ AR_Lib.Tests/AR_Lib.Tests.csproj | 19 +++++++ AR_Lib.Tests/UnitTest1.cs | 17 ++++++ AR_Lib.csproj => AR_Lib/AR_Lib.csproj | 4 +- AR_Lib/Collections/Lists.cs | 33 +++++++++++ {Collections => AR_Lib/Collections}/Matrix.cs | 12 ++-- {Curves => AR_Lib/Curves}/Geodesics.cs | 15 +++-- {Curves => AR_Lib/Curves}/LevelSets.cs | 3 + {Data => AR_Lib/Data}/Settings.json | 0 {Geometry => AR_Lib/Geometry}/BaseCurve.cs | 2 +- {Geometry => AR_Lib/Geometry}/BasePoint.cs | 0 {Geometry => AR_Lib/Geometry}/Line.cs | 0 {Geometry => AR_Lib/Geometry}/Plane.cs | 4 +- {Geometry => AR_Lib/Geometry}/Point2d.cs | 28 ++++++---- {Geometry => AR_Lib/Geometry}/Point3d.cs | 0 {Geometry => AR_Lib/Geometry}/Point4d.cs | 0 {Geometry => AR_Lib/Geometry}/Polyline.cs | 0 AR_Lib/Geometry/Ray.cs | 52 +++++++++++++++++ {Geometry => AR_Lib/Geometry}/Sphere.cs | 0 {Geometry => AR_Lib/Geometry}/Vector3d.cs | 0 .../HalfEdgeMesh}/HE_Corner.cs | 6 ++ .../HalfEdgeMesh}/HE_Edge.cs | 0 .../HalfEdgeMesh}/HE_Face.cs | 19 ++++++- .../HalfEdgeMesh}/HE_Generators.cs | 0 .../HalfEdgeMesh}/HE_HalfEdge.cs | 0 .../HalfEdgeMesh}/HE_Mesh.cs | 24 ++++++-- .../HalfEdgeMesh}/HE_MeshGeometry.cs | 0 .../HalfEdgeMesh}/HE_MeshPoint.cs | 11 +++- .../HalfEdgeMesh}/HE_MeshTopology.cs | 0 .../HalfEdgeMesh}/HE_Vertex.cs | 0 {IO => AR_Lib/IO}/IO.cs | 0 .../LinearAlgebra}/Complex.cs | 0 .../LinearAlgebra}/LeastSquares.cs | 11 ++++ .../LinearAlgebra}/Triplet.cs | 0 {Nurbs => AR_Lib/Nurbs}/Surface.cs | 0 .../SpatialStructures}/Octree.cs | 3 + AR_Lib/SpatialStructures/PointCloud.cs | 35 ++++++++++++ .../SpatialStructures}/Quadtree.cs | 3 + {Utility => AR_Lib/Utility}/Convert.cs | 13 +++-- {Utility => AR_Lib/Utility}/Intersect.cs | 56 ++++++++++++++----- .../Utility}/IntersectErrors.cs | 0 AR_Lib/Utility/Settings.cs | 39 +++++++++++++ Collections/Lists.cs | 17 ------ Geometry/Ray.cs | 34 ----------- IO/Conversion.cs | 13 ----- SpatialStructures/PointCloud.cs | 22 -------- Utility/Settings.cs | 51 ----------------- 49 files changed, 468 insertions(+), 189 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 AR_GeometryLibrary.sln create mode 100644 AR_Lib.Tests/AR_Lib.Tests.csproj create mode 100644 AR_Lib.Tests/UnitTest1.cs rename AR_Lib.csproj => AR_Lib/AR_Lib.csproj (93%) create mode 100644 AR_Lib/Collections/Lists.cs rename {Collections => AR_Lib/Collections}/Matrix.cs (95%) rename {Curves => AR_Lib/Curves}/Geodesics.cs (81%) rename {Curves => AR_Lib/Curves}/LevelSets.cs (98%) rename {Data => AR_Lib/Data}/Settings.json (100%) rename {Geometry => AR_Lib/Geometry}/BaseCurve.cs (99%) rename {Geometry => AR_Lib/Geometry}/BasePoint.cs (100%) rename {Geometry => AR_Lib/Geometry}/Line.cs (100%) rename {Geometry => AR_Lib/Geometry}/Plane.cs (93%) rename {Geometry => AR_Lib/Geometry}/Point2d.cs (57%) rename {Geometry => AR_Lib/Geometry}/Point3d.cs (100%) rename {Geometry => AR_Lib/Geometry}/Point4d.cs (100%) rename {Geometry => AR_Lib/Geometry}/Polyline.cs (100%) create mode 100644 AR_Lib/Geometry/Ray.cs rename {Geometry => AR_Lib/Geometry}/Sphere.cs (100%) rename {Geometry => AR_Lib/Geometry}/Vector3d.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Corner.cs (73%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Edge.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Face.cs (88%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Generators.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_HalfEdge.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Mesh.cs (95%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_MeshGeometry.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_MeshPoint.cs (74%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_MeshTopology.cs (100%) rename {HalfEdgeMesh => AR_Lib/HalfEdgeMesh}/HE_Vertex.cs (100%) rename {IO => AR_Lib/IO}/IO.cs (100%) rename {LinearAlgebra => AR_Lib/LinearAlgebra}/Complex.cs (100%) rename {LinearAlgebra => AR_Lib/LinearAlgebra}/LeastSquares.cs (78%) rename {LinearAlgebra => AR_Lib/LinearAlgebra}/Triplet.cs (100%) rename {Nurbs => AR_Lib/Nurbs}/Surface.cs (100%) rename {SpatialStructures => AR_Lib/SpatialStructures}/Octree.cs (53%) create mode 100644 AR_Lib/SpatialStructures/PointCloud.cs rename {SpatialStructures => AR_Lib/SpatialStructures}/Quadtree.cs (55%) rename {Utility => AR_Lib/Utility}/Convert.cs (82%) rename {Utility => AR_Lib/Utility}/Intersect.cs (65%) rename {Utility => AR_Lib/Utility}/IntersectErrors.cs (100%) create mode 100644 AR_Lib/Utility/Settings.cs delete mode 100644 Collections/Lists.cs delete mode 100644 Geometry/Ray.cs delete mode 100644 IO/Conversion.cs delete mode 100644 SpatialStructures/PointCloud.cs delete mode 100644 Utility/Settings.cs 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..ff0868b --- /dev/null +++ b/AR_Lib.Tests/AR_Lib.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/AR_Lib.Tests/UnitTest1.cs b/AR_Lib.Tests/UnitTest1.cs new file mode 100644 index 0000000..911b61d --- /dev/null +++ b/AR_Lib.Tests/UnitTest1.cs @@ -0,0 +1,17 @@ +using System; +using Xunit; +using AR_Lib.Geometry; + +namespace AR_Lib.Tests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + Point3d pt = new Point3d(0, 0, 0); + Point3d pt2 = new Point3d(1, 1, 1); + Assert.Tru + } + } +} diff --git a/AR_Lib.csproj b/AR_Lib/AR_Lib.csproj similarity index 93% rename from AR_Lib.csproj rename to AR_Lib/AR_Lib.csproj index ec6ab62..592d254 100644 --- a/AR_Lib.csproj +++ b/AR_Lib/AR_Lib.csproj @@ -24,8 +24,8 @@ - + 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 95% rename from Collections/Matrix.cs rename to AR_Lib/Collections/Matrix.cs index 9dc41b0..e96088e 100644 --- a/Collections/Matrix.cs +++ b/AR_Lib/Collections/Matrix.cs @@ -3,6 +3,10 @@ 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) @@ -185,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/Curves/LevelSets.cs b/AR_Lib/Curves/LevelSets.cs similarity index 98% rename from Curves/LevelSets.cs rename to AR_Lib/Curves/LevelSets.cs index e2f3540..8306406 100644 --- a/Curves/LevelSets.cs +++ b/AR_Lib/Curves/LevelSets.cs @@ -6,6 +6,9 @@ namespace AR_Lib.Curve { + /// + /// Compute the level sets of a given function. + /// public static class LevelSets { /// diff --git a/Data/Settings.json b/AR_Lib/Data/Settings.json similarity index 100% rename from Data/Settings.json rename to AR_Lib/Data/Settings.json diff --git a/Geometry/BaseCurve.cs b/AR_Lib/Geometry/BaseCurve.cs similarity index 99% rename from Geometry/BaseCurve.cs rename to AR_Lib/Geometry/BaseCurve.cs index f33481a..ef6f476 100644 --- a/Geometry/BaseCurve.cs +++ b/AR_Lib/Geometry/BaseCurve.cs @@ -34,7 +34,7 @@ public abstract class BaseCurve public virtual double T1 { get => _t1; set => _t1 = value; } public virtual bool IsValid => _isValid; - + protected Point3d _startPoint; protected Point3d _endPoint; protected double _t0; diff --git a/Geometry/BasePoint.cs b/AR_Lib/Geometry/BasePoint.cs similarity index 100% rename from Geometry/BasePoint.cs rename to AR_Lib/Geometry/BasePoint.cs diff --git a/Geometry/Line.cs b/AR_Lib/Geometry/Line.cs similarity index 100% rename from Geometry/Line.cs rename to AR_Lib/Geometry/Line.cs diff --git a/Geometry/Plane.cs b/AR_Lib/Geometry/Plane.cs similarity index 93% rename from Geometry/Plane.cs rename to AR_Lib/Geometry/Plane.cs index f49ada8..68e7b29 100644 --- a/Geometry/Plane.cs +++ b/AR_Lib/Geometry/Plane.cs @@ -1,6 +1,8 @@ namespace AR_Lib.Geometry { - + /// + /// Represents a 3-Dimensional plane + /// public class Plane { public Point3d Origin { get => _origin; set => _origin = value; } diff --git a/Geometry/Point2d.cs b/AR_Lib/Geometry/Point2d.cs similarity index 57% rename from Geometry/Point2d.cs rename to AR_Lib/Geometry/Point2d.cs index 319ab94..1169e62 100644 --- a/Geometry/Point2d.cs +++ b/AR_Lib/Geometry/Point2d.cs @@ -1,5 +1,8 @@ namespace AR_Lib.Geometry { + /// + /// 2-Dimensional point + /// public class Point2d : BasePoint { /// @@ -18,33 +21,38 @@ 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/Geometry/Point3d.cs b/AR_Lib/Geometry/Point3d.cs similarity index 100% rename from Geometry/Point3d.cs rename to AR_Lib/Geometry/Point3d.cs diff --git a/Geometry/Point4d.cs b/AR_Lib/Geometry/Point4d.cs similarity index 100% rename from Geometry/Point4d.cs rename to AR_Lib/Geometry/Point4d.cs diff --git a/Geometry/Polyline.cs b/AR_Lib/Geometry/Polyline.cs similarity index 100% rename from Geometry/Polyline.cs rename to AR_Lib/Geometry/Polyline.cs 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/Geometry/Sphere.cs b/AR_Lib/Geometry/Sphere.cs similarity index 100% rename from Geometry/Sphere.cs rename to AR_Lib/Geometry/Sphere.cs diff --git a/Geometry/Vector3d.cs b/AR_Lib/Geometry/Vector3d.cs similarity index 100% rename from Geometry/Vector3d.cs rename to AR_Lib/Geometry/Vector3d.cs 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 88% rename from HalfEdgeMesh/HE_Face.cs rename to AR_Lib/HalfEdgeMesh/HE_Face.cs index b1954ff..67c8f7b 100644 --- a/HalfEdgeMesh/HE_Face.cs +++ b/AR_Lib/HalfEdgeMesh/HE_Face.cs @@ -11,10 +11,27 @@ namespace HalfEdgeMesh public class HE_Face { #region Properties & Fields - public HE_HalfEdge HalfEdge; //One of the half-edges surrounding the face + + /// + /// One of the half-edges surrounding the face + /// + public HE_HalfEdge HalfEdge; + + /// + /// The face index on the mesh face list. + /// public int Index; + /// + /// 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 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 95% rename from HalfEdgeMesh/HE_Mesh.cs rename to AR_Lib/HalfEdgeMesh/HE_Mesh.cs index 14424fd..5be2a7d 100644 --- a/HalfEdgeMesh/HE_Mesh.cs +++ b/AR_Lib/HalfEdgeMesh/HE_Mesh.cs @@ -162,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; @@ -175,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; @@ -188,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; @@ -201,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; @@ -214,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; @@ -228,16 +228,30 @@ public void indexElements() #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; diff --git a/HalfEdgeMesh/HE_MeshGeometry.cs b/AR_Lib/HalfEdgeMesh/HE_MeshGeometry.cs similarity index 100% rename from HalfEdgeMesh/HE_MeshGeometry.cs rename to AR_Lib/HalfEdgeMesh/HE_MeshGeometry.cs 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/LinearAlgebra/Complex.cs b/AR_Lib/LinearAlgebra/Complex.cs similarity index 100% rename from LinearAlgebra/Complex.cs rename to AR_Lib/LinearAlgebra/Complex.cs diff --git a/LinearAlgebra/LeastSquares.cs b/AR_Lib/LinearAlgebra/LeastSquares.cs similarity index 78% rename from LinearAlgebra/LeastSquares.cs rename to AR_Lib/LinearAlgebra/LeastSquares.cs index ecceb53..0c8234d 100644 --- a/LinearAlgebra/LeastSquares.cs +++ b/AR_Lib/LinearAlgebra/LeastSquares.cs @@ -4,12 +4,23 @@ 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) { diff --git a/LinearAlgebra/Triplet.cs b/AR_Lib/LinearAlgebra/Triplet.cs similarity index 100% rename from LinearAlgebra/Triplet.cs rename to AR_Lib/LinearAlgebra/Triplet.cs diff --git a/Nurbs/Surface.cs b/AR_Lib/Nurbs/Surface.cs similarity index 100% rename from Nurbs/Surface.cs rename to AR_Lib/Nurbs/Surface.cs diff --git a/SpatialStructures/Octree.cs b/AR_Lib/SpatialStructures/Octree.cs similarity index 53% rename from SpatialStructures/Octree.cs rename to AR_Lib/SpatialStructures/Octree.cs index 05fe323..c7aafa1 100644 --- a/SpatialStructures/Octree.cs +++ b/AR_Lib/SpatialStructures/Octree.cs @@ -1,5 +1,8 @@ namespace AR_Lib.SpatialSearch { + /// + /// Class for computing Octree spatial searches. + /// public class Octree { //TODO: This class is empty! 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/SpatialStructures/Quadtree.cs b/AR_Lib/SpatialStructures/Quadtree.cs similarity index 55% rename from SpatialStructures/Quadtree.cs rename to AR_Lib/SpatialStructures/Quadtree.cs index 0b5a98f..24c90a7 100644 --- a/SpatialStructures/Quadtree.cs +++ b/AR_Lib/SpatialStructures/Quadtree.cs @@ -1,5 +1,8 @@ namespace AR_Lib.SpatialSearch { + /// + /// Class to compute Quadtree spatial searches + /// public class Quadtree { // TODO: This class is empty! 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/Utility/Intersect.cs b/AR_Lib/Utility/Intersect.cs similarity index 65% rename from Utility/Intersect.cs rename to AR_Lib/Utility/Intersect.cs index 8826695..33800f4 100644 --- a/Utility/Intersect.cs +++ b/AR_Lib/Utility/Intersect.cs @@ -10,26 +10,36 @@ 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 { - public static ISLinePlane LinePlane(Line S, Plane Pn, out Point3d I) + /// + /// 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 = S.EndPoint - S.StartPoint; - Vector3d w = S.StartPoint - Pn.Origin; + Vector3d u = line.EndPoint - line.StartPoint; + Vector3d w = line.StartPoint - plane.Origin; - double D = Vector3d.DotProduct(Pn.ZAxis, u); - double N = -Vector3d.DotProduct(Pn.ZAxis, w); + 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 { - I = null; + intersectionPoint = null; return ISLinePlane.OnPlane; } else { - I = null; + intersectionPoint = null; return ISLinePlane.NoIntersection; } } @@ -39,19 +49,28 @@ public static ISLinePlane LinePlane(Line S, Plane Pn, out Point3d I) double sI = N / D; if (sI < 0 || sI > 1) { - I = null; + intersectionPoint = null; return ISLinePlane.NoIntersection; } - I = S.StartPoint + u * sI; // Compute segment intersection point + intersectionPoint = line.StartPoint + u * sI; // Compute segment intersection point return ISLinePlane.Point; } - public static ISRayFacePerimeter RayFacePerimeter(Point3d RayOrigin, Vector3d RayDir, HE_Face Face, out Point3d result, out HE_HalfEdge halfEdge) + + /// + /// 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(RayDir, faceNormal); + Vector3d biNormal = Vector3d.CrossProduct(ray.Direction, faceNormal); - Plane perpPlane = new Plane(RayOrigin, RayDir, faceNormal, biNormal); + Plane perpPlane = new Plane(ray.Origin, ray.Direction, faceNormal, biNormal); List vertices = Face.adjacentVertices(); @@ -59,17 +78,24 @@ public static ISRayFacePerimeter RayFacePerimeter(Point3d RayOrigin, Vector3d Ra 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 != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // 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 != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } // 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 != RayOrigin && temp != null) { result = temp; halfEdge = null; return ISRayFacePerimeter.Point; } else { result = null; halfEdge = null; return ISRayFacePerimeter.Error; } + 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) { diff --git a/Utility/IntersectErrors.cs b/AR_Lib/Utility/IntersectErrors.cs similarity index 100% rename from Utility/IntersectErrors.cs rename to AR_Lib/Utility/IntersectErrors.cs 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/Collections/Lists.cs b/Collections/Lists.cs deleted file mode 100644 index 599a6f2..0000000 --- a/Collections/Lists.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -public static class Lists -{ - public static List RepeatedDefault(int count) - { - return Repeated(default(T), count); - } - - public static List Repeated(T value, int count) - { - List repeated = new List(count); - repeated.AddRange(Enumerable.Repeat(value, count)); - return repeated; - } -} diff --git a/Geometry/Ray.cs b/Geometry/Ray.cs deleted file mode 100644 index d4a2b55..0000000 --- a/Geometry/Ray.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace AR_Lib.Geometry -{ - /// - /// Infinite 3d ray starting at a point. - /// - public class Ray - { - private Point3d _origin; - private Vector3d _direction; - - public Point3d Origin { get => _origin; set => _origin = value; } - public Vector3d Direction { get => _direction; set => _direction = value; } - - #region Constructors - - public Ray(Point3d origin, Vector3d direction) - { - _origin = origin; - _direction = direction; - } - - #endregion - - #region Public methods - - public Point3d PointAt(double t) - { - return _origin + t * _direction; - } - - #endregion - } - -} \ No newline at end of file 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/SpatialStructures/PointCloud.cs b/SpatialStructures/PointCloud.cs deleted file mode 100644 index 3db69f7..0000000 --- a/SpatialStructures/PointCloud.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using AR_Lib.Geometry; - -namespace AR_Lib.SpatialSearch -{ - public class PointCloud - { - List _points; - - public List Points { get => _points; set => _points = value; } - - } - - public class PointCloudMember : BasePoint - { - private Color _color; - - public Color Color { get => _color; set => _color = value; } - } -} \ No newline at end of file diff --git a/Utility/Settings.cs b/Utility/Settings.cs deleted file mode 100644 index 9e1541a..0000000 --- a/Utility/Settings.cs +++ /dev/null @@ -1,51 +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 class Settings - { - public static double Tolerance => _tolerance; - public static double DefaultTesselation = 10; - public static int MaxDecimals => _maxDecimals; - - private static int _maxDecimals = 8; - private static double _tolerance = 0.0000001; - public static void ModifyTolerance(double tolerance) - { - _tolerance = tolerance; - string t = tolerance.ToString("N14"); - _maxDecimals = t.Substring(t.IndexOf(".") + 1).IndexOf("1"); - } - - } - - 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; - } - } -} \ No newline at end of file From a862a56c6f8a77840eb0cddb1b98ab1e870ffcd4 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 21:37:58 +0200 Subject: [PATCH 17/23] Added first tests and datasets for theory Removed .xml documentation generation (because of warnings) for now. --- AR_Lib.Tests/Geometry/Point3dData.cs | 16 ++++++++++++++++ AR_Lib.Tests/Geometry/Point3dTests.cs | 18 ++++++++++++++++++ AR_Lib.Tests/Geometry/Vector3dTests.cs | 7 +++++++ AR_Lib.Tests/UnitTest1.cs | 17 ----------------- AR_Lib/AR_Lib.csproj | 4 ++-- 5 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 AR_Lib.Tests/Geometry/Point3dData.cs create mode 100644 AR_Lib.Tests/Geometry/Point3dTests.cs create mode 100644 AR_Lib.Tests/Geometry/Vector3dTests.cs delete mode 100644 AR_Lib.Tests/UnitTest1.cs 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/UnitTest1.cs b/AR_Lib.Tests/UnitTest1.cs deleted file mode 100644 index 911b61d..0000000 --- a/AR_Lib.Tests/UnitTest1.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Xunit; -using AR_Lib.Geometry; - -namespace AR_Lib.Tests -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - Point3d pt = new Point3d(0, 0, 0); - Point3d pt2 = new Point3d(1, 1, 1); - Assert.Tru - } - } -} diff --git a/AR_Lib/AR_Lib.csproj b/AR_Lib/AR_Lib.csproj index 592d254..ec6ab62 100644 --- a/AR_Lib/AR_Lib.csproj +++ b/AR_Lib/AR_Lib.csproj @@ -24,8 +24,8 @@ - + From 993f263022d7f335dbd2b1a6b908a7f486502fa3 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:12:56 +0200 Subject: [PATCH 18/23] Added Json file reader for tests --- .../Utilities/JsonFileDataAttribute.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 AR_Lib.Tests/Utilities/JsonFileDataAttribute.cs 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 From bffa8b493573c223c5117313aa72f18e722b7997 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:25:53 +0200 Subject: [PATCH 19/23] Modified travis-ci dotnet version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7412884..a5d7f15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: trusty sudo: required language: csharp mono: none -dotnet: 2.0.0 +dotnet: 2.2.0 script: - dotnet restore - dotnet build -c Release From c0c980ef24317743bec9b91aa61ca67d866eced8 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:27:23 +0200 Subject: [PATCH 20/23] Added test and modified version --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a5d7f15..92fba06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,8 @@ dist: trusty sudo: required language: csharp mono: none -dotnet: 2.2.0 +dotnet: 2.2 script: - dotnet restore - dotnet build -c Release + - dotnet test From 7920ca54243da8f079e089009a30045934ad5767 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:30:37 +0200 Subject: [PATCH 21/23] Modified tests to .net-core2.0 --- .travis.yml | 2 +- AR_Lib.Tests/AR_Lib.Tests.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 92fba06..4736225 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: trusty sudo: required language: csharp mono: none -dotnet: 2.2 +dotnet: 2.0.0 script: - dotnet restore - dotnet build -c Release diff --git a/AR_Lib.Tests/AR_Lib.Tests.csproj b/AR_Lib.Tests/AR_Lib.Tests.csproj index ff0868b..7f9c74f 100644 --- a/AR_Lib.Tests/AR_Lib.Tests.csproj +++ b/AR_Lib.Tests/AR_Lib.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2 + netcoreapp2.0 false @@ -12,8 +12,8 @@ - - + + From 1df14edd149772688dd0098c1f47fb671d975716 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:37:45 +0200 Subject: [PATCH 22/23] Specified test project --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4736225..3c0d4c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +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 + - dotnet test AR_Lib.Tests From 7c8cf5295fb1558559932638f7fceccab68f0077 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Mon, 1 Jul 2019 22:54:36 +0200 Subject: [PATCH 23/23] Added codeowners file --- CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CODEOWNERS 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