/// <summary> /// Finds normal vector of a hyper-plane given by vertices. /// Stores the results to normalData. /// </summary> /// <param name="vertices"></param> /// <param name="normalData"></param> private void FindNormalVector(VertexWrap[] vertices, double[] normalData) { switch (Dimension) { case 2: FindNormalVector2D(vertices, normalData); break; case 3: FindNormalVector3D(vertices, normalData); break; case 4: FindNormalVector4D(vertices, normalData); break; default: { for (var i = 0; i < Dimension; i++) { nDNormalSolveVector[i] = 1.0; } for (var i = 0; i < Dimension; i++) { var row = jaggedNDMatrix[i]; var pos = vertices[i].Vertex.Position; for (int j = 0; j < Dimension; j++) { row[j] = pos[j]; } } StarMath.gaussElimination(Dimension, jaggedNDMatrix, nDNormalSolveVector, normalData); StarMath.normalizeInPlace(normalData, Dimension); break; } } }
/// <summary> /// Computes the volume of the (n=initialPoints.Count)D simplex defined by the /// pivot and initialPoints. /// This is computed as the determinant of the matrix | initialPoints[i] - pivot | /// </summary> /// <param name="pivot"></param> /// <param name="initialPoints"></param> /// <returns></returns> double GetSimplexVolume(VertexWrap pivot, List <VertexWrap> initialPoints) { var dim = initialPoints.Count; var m = nDMatrix; for (int i = 0; i < dim; i++) { var pts = initialPoints[i]; for (int j = 0; j < dim; j++) { m[i, j] = pts.PositionData[j] - pivot.PositionData[j]; } } return(Math.Abs(StarMath.determinantDestructive(m, dim))); }
/// <summary> /// Creates the Delaunay triangulation of the input data. /// Be careful with concurrency, because during the computation, the vertex position arrays get resized. /// </summary> /// <param name="data"></param> /// <returns></returns> public static DelaunayTriangulation <TVertex, TCell> Create(IEnumerable <TVertex> data) { if (data == null) { throw new ArgumentException("data can't be null."); } if (!(data is IList <TVertex>)) { data = data.ToArray(); } if (data.Count() == 0) { return new DelaunayTriangulation <TVertex, TCell> { Cells = Enumerable.Empty <TCell>() } } ; int dimension = data.First().Position.Length; // Resize the arrays and lift the data. foreach (var p in data) { double lenSq = StarMath.norm2(p.Position, dimension, true); var v = p.Position; Array.Resize(ref v, dimension + 1); p.Position = v; p.Position[dimension] = lenSq; } // Find the convex hull var delaunayFaces = ConvexHullInternal.GetConvexFacesInternal <TVertex, TCell>(data); // Resize the data back foreach (var p in data) { var v = p.Position; Array.Resize(ref v, dimension); p.Position = v; } // Remove the "upper" faces for (var i = delaunayFaces.Count - 1; i >= 0; i--) { var candidate = delaunayFaces[i]; if (candidate.Normal[dimension] >= 0) { for (int fi = 0; fi < candidate.AdjacentFaces.Length; fi++) { var f = candidate.AdjacentFaces[fi]; if (f != null) { for (int j = 0; j < f.AdjacentFaces.Length; j++) { if (object.ReferenceEquals(f.AdjacentFaces[j], candidate)) { f.AdjacentFaces[j] = null; } } } } var li = delaunayFaces.Count - 1; delaunayFaces[i] = delaunayFaces[li]; delaunayFaces.RemoveAt(li); } } // Create the "TCell" representation. int cellCount = delaunayFaces.Count; var cells = new TCell[cellCount]; for (int i = 0; i < cellCount; i++) { var face = delaunayFaces[i]; var vertices = new TVertex[dimension + 1]; for (int j = 0; j <= dimension; j++) { vertices[j] = (TVertex)face.Vertices[j].Vertex; } cells[i] = new TCell { Vertices = vertices, Adjacency = new TCell[dimension + 1] }; face.Tag = i; } for (int i = 0; i < cellCount; i++) { var face = delaunayFaces[i]; var cell = cells[i]; for (int j = 0; j <= dimension; j++) { if (face.AdjacentFaces[j] == null) { continue; } cell.Adjacency[j] = cells[face.AdjacentFaces[j].Tag]; } } return(new DelaunayTriangulation <TVertex, TCell> { Cells = cells }); }
/// <summary> /// Finds (dimension + 1) initial points. /// </summary> /// <param name="extremes"></param> /// <returns></returns> private List <VertexWrap> FindInitialPoints(List <VertexWrap> extremes) { List <VertexWrap> initialPoints = new List <VertexWrap>();// { extremes[0], extremes[1] }; VertexWrap first = null, second = null; double maxDist = 0; for (int i = 0; i < extremes.Count - 1; i++) { var a = extremes[i]; for (int j = i + 1; j < extremes.Count; j++) { var b = extremes[j]; var dist = StarMath.norm2(StarMath.subtract(a.PositionData, b.PositionData, Dimension), Dimension, true); if (dist > maxDist) { first = a; second = b; maxDist = dist; } } } initialPoints.Add(first); initialPoints.Add(second); for (int i = 2; i <= Dimension; i++) { double maximum = 0.0001; VertexWrap maxPoint = null; for (int j = 0; j < extremes.Count; j++) { var extreme = extremes[j]; if (initialPoints.Contains(extreme)) { continue; } var val = GetSimplexVolume(extreme, initialPoints); if (val > maximum) { maximum = val; maxPoint = extreme; } } if (maxPoint != null) { initialPoints.Add(maxPoint); } else { int vCount = InputVertices.Count; for (int j = 0; j < vCount; j++) { var point = InputVertices[j]; if (initialPoints.Contains(point)) { continue; } var val = GetSimplexVolume(point, initialPoints); if (val > maximum) { maximum = val; maxPoint = point; } } if (maxPoint != null) { initialPoints.Add(maxPoint); } else { ThrowSingular(); } } } return(initialPoints); }