/// <summary> /// Инициализировать корпус, если Vertices.Length == Dimension. /// </summary> void InitSingle() { var vertices = new int[Dimension]; for (int i = 0; i < Vertices.Length; i++) { vertices[i] = i; } var newFace = FacePool[ObjectManager.GetFace()]; newFace.Vertices = vertices; Array.Sort(vertices); MathHelper.CalculateFacePlane(newFace, Center); // Убедиться, что нормальная точка внизу в случае, если это используется для триангуляции if (newFace.Normal[Dimension - 1] >= 0.0) { for (int i = 0; i < Dimension; i++) { newFace.Normal[i] *= -1.0; } newFace.Offset = -newFace.Offset; newFace.IsNormalFlipped = !newFace.IsNormalFlipped; } ConvexFaces.Add(newFace.Index); }
/// <summary> /// Init the hull if Vertices.Length == Dimension. /// </summary> void InitSingle() { var vertices = new int[Dimension]; for (int i = 0; i < Vertices.Length; i++) { vertices[i] = i; } var newFace = FacePool[ObjectManager.GetFace()]; newFace.Vertices = vertices; Array.Sort(vertices); MathHelper.CalculateFacePlane(newFace, Center); // Make sure the normal point downwards in case this is used for triangulation if (newFace.Normal[Dimension - 1] >= 0.0) { for (int i = 0; i < Dimension; i++) { newFace.Normal[i] *= -1.0; } newFace.Offset = -newFace.Offset; newFace.IsNormalFlipped = !newFace.IsNormalFlipped; } ConvexFaces.Add(newFace.Index); }
/// <summary> /// Find the (dimension+1) initial points and create the simplexes. /// Creates the initial simplex of n+1 vertices by using points from the bounding box. /// Special care is taken to ensure that the vertices chosen do not result in a degenerate shape /// where vertices are collinear (co-planar, etc). This would technically be resolved when additional /// vertices are checked in the main loop, but: 1) a degenerate simplex would not eliminate any other /// vertices (thus no savings there), 2) the creation of the face normal is prone to error. /// </summary> private void CreateInitialSimplex() { var initialPoints = FindInitialPoints(); #region Create the first faces from (dimension + 1) vertices. var faces = new int[NumOfDimensions + 1]; for (var i = 0; i < NumOfDimensions + 1; i++) { var vertices = new int[NumOfDimensions]; for (int j = 0, k = 0; j <= NumOfDimensions; j++) { if (i != j) { vertices[k++] = initialPoints[j]; } } var newFace = FacePool[ObjectManager.GetFace()]; newFace.Vertices = vertices; Array.Sort(vertices); mathHelper.CalculateFacePlane(newFace, Center); faces[i] = newFace.Index; } // update the adjacency (check all pairs of faces) for (var i = 0; i < NumOfDimensions; i++) { for (var j = i + 1; j < NumOfDimensions + 1; j++) { UpdateAdjacency(FacePool[faces[i]], FacePool[faces[j]]); } } #endregion #region Init the vertex beyond buffers. foreach (var faceIndex in faces) { var face = FacePool[faceIndex]; FindBeyondVertices(face); if (face.VerticesBeyond.Count == 0) { ConvexFaces.Add(face.Index); // The face is on the hull } else { UnprocessedFaces.Add(face); } } #endregion // Set all vertices to false (unvisited). foreach (var vertex in initialPoints) { VertexVisited[vertex] = false; } }
/// <summary> /// Commits a cone and adds a vertex to the convex hull. /// </summary> private void CommitCone() { // Fill the adjacency. for (var i = 0; i < ConeFaceBuffer.Count; i++) { var face = ConeFaceBuffer[i]; var newFace = face.Face; var adjacentFace = face.Pivot; var oldFace = face.OldFace; var orderedPivotIndex = face.FaceIndex; newFace.AdjacentFaces[orderedPivotIndex] = adjacentFace.Index; adjacentFace.AdjacentFaces[face.PivotIndex] = newFace.Index; // let there be a connection. for (var j = 0; j < NumOfDimensions; j++) { if (j == orderedPivotIndex) continue; var connector = ObjectManager.GetConnector(); connector.Update(newFace, j, NumOfDimensions); ConnectFace(connector); } // the id adjacent face on the hull? If so, we can use simple method to find beyond vertices. if (adjacentFace.VerticesBeyond.Count == 0) FindBeyondVertices(newFace, oldFace.VerticesBeyond); // it is slightly more effective if the face with the lower number of beyond vertices comes first. else if (adjacentFace.VerticesBeyond.Count < oldFace.VerticesBeyond.Count) FindBeyondVertices(newFace, adjacentFace.VerticesBeyond, oldFace.VerticesBeyond); else FindBeyondVertices(newFace, oldFace.VerticesBeyond, adjacentFace.VerticesBeyond); // This face will definitely lie on the hull if (newFace.VerticesBeyond.Count == 0) { ConvexFaces.Add(newFace.Index); UnprocessedFaces.Remove(newFace); ObjectManager.DepositVertexBuffer(newFace.VerticesBeyond); newFace.VerticesBeyond = EmptyBuffer; } else // Add the face to the list { UnprocessedFaces.Add(newFace); } // recycle the object. ObjectManager.DepositDeferredFace(face); } // Recycle the affected faces. for (var fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++) { var face = AffectedFaceBuffer[fIndex]; UnprocessedFaces.Remove(FacePool[face]); ObjectManager.DepositFace(face); } }
/// <summary> /// Find the (dimension+1) initial points and create the simplexes. /// </summary> void InitConvexHull() { if (Vertices.Length < Dimension) { // In this case, there cannot be a single convex face, so we return an empty result. return; } else if (Vertices.Length == Dimension) { // The vertices are all on the hull and form a single simplex. InitSingle(); return; } var extremes = FindExtremes(); var initialPoints = FindInitialPoints(extremes); // Add the initial points to the convex hull. foreach (var vertex in initialPoints) { CurrentVertex = vertex; // update center must be called before adding the vertex. UpdateCenter(); AddConvexVertex(vertex); // Mark the vertex so that it's not included in any beyond set. VertexMarks[vertex] = true; } // Create the initial simplexes. var faces = CreateInitialHull(); // Init the vertex beyond buffers. foreach (var faceIndex in faces) { var face = FacePool[faceIndex]; FindBeyondVertices(face); if (face.VerticesBeyond.Count == 0) { ConvexFaces.Add(face.Index); // The face is on the hull } else { UnprocessedFaces.Add(face); } } // Unmark the vertices foreach (var vertex in initialPoints) { VertexMarks[vertex] = false; } }
/// <summary> /// Найти (размер + 1) начальных точек и создать симплексы /// </summary> void InitConvexHull() { if (Vertices.Length < Dimension) { // В этом случае не может быть одной выпуклой поверхности, так что мы возвращаем пустой результат return; } else if (Vertices.Length == Dimension) { // Все вершины на корпусе и образуют единый симплекс InitSingle(); return; } var extremes = FindExtremes(); var initialPoints = FindInitialPoints(extremes); // Добавление начальных точек в выпуклую оболочку foreach (var vertex in initialPoints) { CurrentVertex = vertex; // Центр обновления должен быть вызван, прежде чем добавить вершину UpdateCenter(); // Отметьте вершину так, чтобы она не была включена за пределы VertexMarks[vertex] = true; } // Создать начальный симплекс var faces = CreateInitialHull(initialPoints); // Инициализировать вершины за пределами буфера foreach (var faceIndex in faces) { var face = FacePool[faceIndex]; FindBeyondVertices(face); if (face.VerticesBeyond.Count == 0) { ConvexFaces.Add(face.Index); // The face is on the hull } else { UnprocessedFaces.Add(face); } } // Снять выделение с вершин foreach (var vertex in initialPoints) { VertexMarks[vertex] = false; } }
/// <summary> /// Handles singular vertex. /// </summary> private void HandleSingular() { SingularVertices.Add(CurrentVertex); // This means that all the affected faces must be on the hull and that all their "vertices beyond" are singular. for (var fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++) { var face = FacePool[AffectedFaceBuffer[fIndex]]; var vb = face.VerticesBeyond; for (var i = 0; i < vb.Count; i++) { SingularVertices.Add(vb[i]); } ConvexFaces.Add(face.Index); UnprocessedFaces.Remove(face); ObjectManager.DepositVertexBuffer(face.VerticesBeyond); face.VerticesBeyond = EmptyBuffer; } }
/// <summary> /// Рукоятки исключительных вершин /// </summary> void HandleSingular() { RollbackCenter(); SingularVertices.Add(CurrentVertex); // Это означает, что все затронутые грани должны находиться на корпусе и что все "вершины за пределами" единичны for (int fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++) { var face = FacePool[AffectedFaceBuffer[fIndex]]; var vb = face.VerticesBeyond; for (int i = 0; i < vb.Count; i++) { SingularVertices.Add(vb[i]); } ConvexFaces.Add(face.Index); UnprocessedFaces.Remove(face); ObjectManager.DepositVertexBuffer(face.VerticesBeyond); face.VerticesBeyond = EmptyBuffer; } }
/// <summary> /// Фиксирует конус и добавляет вершину к выпуклой оболочки /// </summary> void CommitCone() { // Заполнение смежностей for (int i = 0; i < ConeFaceBuffer.Count; i++) { var face = ConeFaceBuffer[i]; var newFace = face.Face; var adjacentFace = face.Pivot; var oldFace = face.OldFace; var orderedPivotIndex = face.FaceIndex; newFace.AdjacentFaces[orderedPivotIndex] = adjacentFace.Index; adjacentFace.AdjacentFaces[face.PivotIndex] = newFace.Index; // Пусть здесь будет соединение for (int j = 0; j < Dimension; j++) { if (j == orderedPivotIndex) { continue; } var connector = ObjectManager.GetConnector(); connector.Update(newFace, j, Dimension); ConnectFace(connector); } // Идентификатор смежной грани на корпусе? Если да, то мы можем использовать простой метод, чтобы найти вершины за пределами if (adjacentFace.VerticesBeyond.Count == 0) { FindBeyondVertices(newFace, oldFace.VerticesBeyond); } // Это более эффективно, если грань с меньшим числом вершин не приходит первой else if (adjacentFace.VerticesBeyond.Count < oldFace.VerticesBeyond.Count) { FindBeyondVertices(newFace, adjacentFace.VerticesBeyond, oldFace.VerticesBeyond); } else { FindBeyondVertices(newFace, oldFace.VerticesBeyond, adjacentFace.VerticesBeyond); } // Это лицо, грань, лежит на холме if (newFace.VerticesBeyond.Count == 0) { ConvexFaces.Add(newFace.Index); UnprocessedFaces.Remove(newFace); ObjectManager.DepositVertexBuffer(newFace.VerticesBeyond); newFace.VerticesBeyond = EmptyBuffer; } else // Добавить грань в список { UnprocessedFaces.Add(newFace); } // Утилизировать объект ObjectManager.DepositDeferredFace(face); } // Утилизировать поврежденные грани for (int fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++) { var face = AffectedFaceBuffer[fIndex]; UnprocessedFaces.Remove(FacePool[face]); ObjectManager.DepositFace(face); } }