From a2e3445632fcc3ae69a9e54a72335e24a172d072 Mon Sep 17 00:00:00 2001 From: Garux Date: Sun, 24 Nov 2024 01:36:12 +0500 Subject: [PATCH] update quickhull https://github.com/akuukka/quickhull/commit/4ef66c68950cb4db11d3b75bfe4034d807485ad0 --- libs/quickhull/QuickHull.cpp | 55 +++++++++------- libs/quickhull/QuickHull.hpp | 85 +++++++++++-------------- libs/quickhull/Structs/Mesh.hpp | 20 +++--- libs/quickhull/Structs/Plane.hpp | 4 +- libs/quickhull/Structs/Vector3.hpp | 5 +- libs/quickhull/Tests/QuickHullTests.cpp | 71 +++++++++++++-------- 6 files changed, 132 insertions(+), 108 deletions(-) diff --git a/libs/quickhull/QuickHull.cpp b/libs/quickhull/QuickHull.cpp index fc02f3d0..44a59f72 100644 --- a/libs/quickhull/QuickHull.cpp +++ b/libs/quickhull/QuickHull.cpp @@ -44,17 +44,13 @@ namespace quickhull { template HalfEdgeMesh QuickHull::getConvexHullAsMesh(const FloatType* vertexData, size_t vertexCount, bool CCW, FloatType epsilon) { VertexDataSource vertexDataSource((const vec3*)vertexData,vertexCount); - buildMesh(vertexDataSource, CCW, false, epsilon); + buildMesh(vertexDataSource, epsilon); return HalfEdgeMesh(m_mesh, m_vertexData); } template - void QuickHull::buildMesh(const VertexDataSource& pointCloud, bool CCW, bool useOriginalIndices, T epsilon) { - // CCW is unused for now - (void)CCW; - // useOriginalIndices is unused for now - (void)useOriginalIndices; - + void QuickHull::buildMesh(const VertexDataSource& pointCloud, T epsilon) + { if (pointCloud.size()==0) { m_mesh = MeshBuilder(); return; @@ -71,11 +67,14 @@ namespace quickhull { // Reset diagnostics m_diagnostics = DiagnosticsData(); + + // The planar case happens when all the points appear to lie on a two dimensional + // subspace of R^3. + m_planar = false; - m_planar = false; // The planar case happens when all the points appear to lie on a two dimensional subspace of R^3. createConvexHalfEdgeMesh(); if (m_planar) { - const size_t extraPointIndex = m_planarPointCloudTemp.size()-1; + const size_t extraPointIndex = m_planarPointCloudTemp.size() - 1; for (auto& he : m_mesh.m_halfEdges) { if (he.m_endVertex == extraPointIndex) { he.m_endVertex = 0; @@ -88,7 +87,7 @@ namespace quickhull { template ConvexHull QuickHull::getConvexHull(const VertexDataSource& pointCloud, bool CCW, bool useOriginalIndices, T epsilon) { - buildMesh(pointCloud,CCW,useOriginalIndices,epsilon); + buildMesh(pointCloud, epsilon); return ConvexHull(m_mesh,m_vertexData, CCW, useOriginalIndices); } @@ -100,7 +99,7 @@ namespace quickhull { // Compute base tetrahedron setupInitialTetrahedron(); - assert(m_mesh.m_faces.size()==4); + assert(m_mesh.m_faces.size() == 4); // Init face stack with those faces that have points assigned to them m_faceList.clear(); @@ -117,8 +116,10 @@ namespace quickhull { while (!m_faceList.empty()) { iter++; if (iter == std::numeric_limits::max()) { - // Visible face traversal marks visited faces with iteration counter (to mark that the face has been visited on this iteration) and the max value represents unvisited faces. At this point we have to reset iteration counter. This shouldn't be an - // issue on 64 bit machines. + // Visible face traversal marks visited faces with iteration counter + // (to mark that the face has been visited on this iteration) and the max + // value represents unvisited faces. At this point we have to reset iteration + // counter. This shouldn't be an issue on 64 bit machines. iter = 0; } @@ -137,7 +138,9 @@ namespace quickhull { const vec3& activePoint = m_vertexData[tf.m_mostDistantPoint]; const size_t activePointIndex = tf.m_mostDistantPoint; - // Find out the faces that have our active point on their positive side (these are the "visible faces"). The face on top of the stack of course is one of them. At the same time, we create a list of horizon edges. + // Find out the faces that have our active point on their positive side + // (these are the "visible faces"). The face on top of the stack of course is + // one of them. At the same time, we create a list of horizon edges. m_horizonEdges.clear(); m_possiblyVisibleFaces.clear(); m_visibleFaces.clear(); @@ -171,21 +174,28 @@ namespace quickhull { assert(faceData.m_faceIndex != topFaceIndex); } - // The face is not visible. Therefore, the halfedge we came from is part of the horizon edge. + // The face is not visible. Therefore, the halfedge we entered from + // is part of the horizon edge. pvf.m_isVisibleFaceOnCurrentIteration = 0; m_horizonEdges.push_back(faceData.m_enteredFromHalfEdge); - // Store which half edge is the horizon edge. The other half edges of the face will not be part of the final mesh so their data slots can by recycled. + + // Store which half edge is the horizon edge. The other half edges of the face + // will not be part of the final mesh so their data slots can by recycled. const auto halfEdges = m_mesh.getHalfEdgeIndicesOfFace(m_mesh.m_faces[m_mesh.m_halfEdges[faceData.m_enteredFromHalfEdge].m_face]); const std::int8_t ind = (halfEdges[0]==faceData.m_enteredFromHalfEdge) ? 0 : (halfEdges[1]==faceData.m_enteredFromHalfEdge ? 1 : 2); m_mesh.m_faces[m_mesh.m_halfEdges[faceData.m_enteredFromHalfEdge].m_face].m_horizonEdgesOnCurrentIteration |= (1<begin(),tf.m_pointsOnPositiveSide->end(),activePointIndex); + auto it = std::find(tf.m_pointsOnPositiveSide->begin(), + tf.m_pointsOnPositiveSide->end(), + activePointIndex); tf.m_pointsOnPositiveSide->erase(it); if (tf.m_pointsOnPositiveSide->size()==0) { reclaimToIndexVectorPool(tf.m_pointsOnPositiveSide); @@ -307,11 +317,12 @@ namespace quickhull { */ template - std::array QuickHull::getExtremeValues() { - std::array outIndices{0,0,0,0,0,0}; - T extremeVals[6] = {m_vertexData[0].x,m_vertexData[0].x,m_vertexData[0].y,m_vertexData[0].y,m_vertexData[0].z,m_vertexData[0].z}; + std::array QuickHull::getExtremeValues() { + std::array outIndices{0, 0, 0, 0, 0, 0}; + T extremeVals[6] = { m_vertexData[0].x, m_vertexData[0].x, m_vertexData[0].y, + m_vertexData[0].y, m_vertexData[0].z, m_vertexData[0].z }; const size_t vCount = m_vertexData.size(); - for (size_t i=1;i& pos = m_vertexData[i]; if (pos.x>extremeVals[0]) { extremeVals[0]=pos.x; diff --git a/libs/quickhull/QuickHull.hpp b/libs/quickhull/QuickHull.hpp index 436c4d9f..09bbe93f 100644 --- a/libs/quickhull/QuickHull.hpp +++ b/libs/quickhull/QuickHull.hpp @@ -43,15 +43,13 @@ * - Each point that was assigned to visible faces is now assigned to at most one of the newly created faces. * - Those new faces that have points assigned to them are added to the top of Face Stack. * - M is now the convex hull. - * - * TO DO: - * - Implement a proper 2D QuickHull and use that to solve the degenerate 2D case (when all the points lie on the same plane in 3D space). * */ namespace quickhull { struct DiagnosticsData { - size_t m_failedHorizonEdges; // How many times QuickHull failed to solve the horizon edge. Failures lead to degenerated convex hulls. + size_t m_failedHorizonEdges; // How many times QuickHull failed to solve the horizon edge. + // Failures lead to degenerate convex hulls. DiagnosticsData() : m_failedHorizonEdges(0) { } }; @@ -79,51 +77,64 @@ namespace quickhull { std::vector m_horizonEdges; struct FaceData { size_t m_faceIndex; - size_t m_enteredFromHalfEdge; // If the face turns out not to be visible, this half edge will be marked as horizon edge + size_t m_enteredFromHalfEdge; // If the face turns out not to be visible, + // this half edge will be marked as horizon edge FaceData(size_t fi, size_t he) : m_faceIndex(fi),m_enteredFromHalfEdge(he) {} }; std::vector m_possiblyVisibleFaces; std::deque m_faceList; - // Create a half edge mesh representing the base tetrahedron from which the QuickHull iteration proceeds. m_extremeValues must be properly set up when this is called. + // Create a half edge mesh representing the base tetrahedron from which the QuickHull + // iteration proceeds. m_extremeValues must be properly set up when this is called. void setupInitialTetrahedron(); - // Given a list of half edges, try to rearrange them so that they form a loop. Return true on success. + // Given a list of half edges, try to rearrange them so that they form a loop. + // Return true on success. bool reorderHorizonEdges(std::vector& horizonEdges); - // Find indices of extreme values (max x, min x, max y, min y, max z, min z) for the given point cloud + // Find indices of extreme values (max x, min x, max y, min y, max z, min z) for the + // given point cloud std::array getExtremeValues(); // Compute scale of the vertex data. FloatType getScale(const std::array& extremeValues); - // Each face contains a unique pointer to a vector of indices. However, many - often most - faces do not have any points on the positive - // side of them especially at the the end of the iteration. When a face is removed from the mesh, its associated point vector, if such - // exists, is moved to the index vector pool, and when we need to add new faces with points on the positive side to the mesh, - // we reuse these vectors. This reduces the amount of std::vectors we have to deal with, and impact on performance is remarkable. + // Each face contains a unique pointer to a vector of indices. + // However, many - often most - faces do not have any points on the positive + // side of them especially at the the end of the iteration. When a face is removed + // from the mesh, its associated point vector, if such exists, is moved to the index + // vector pool, and when we need to add new faces with points on the positive side to the + // mesh, we reuse these vectors. This reduces the amount of std::vectors we have to deal + // with, and impact on performance is remarkable. Pool> m_indexVectorPool; inline std::unique_ptr> getIndexVectorFromPool(); inline void reclaimToIndexVectorPool(std::unique_ptr>& ptr); - // Associates a point with a face if the point resides on the positive side of the plane. Returns true if the points was on the positive side. + // Associates a point with a face if the point resides on the positive side of the plane. + // Returns true if the points was on the positive side. inline bool addPointToFace(typename MeshBuilder::Face& f, size_t pointIndex); - // This will update m_mesh from which we create the ConvexHull object that getConvexHull function returns + // This will update m_mesh from which we create the ConvexHull object that getConvexHull + // function returns void createConvexHalfEdgeMesh(); - // Constructs the convex hull into a MeshBuilder object which can be converted to a ConvexHull or Mesh object - void buildMesh(const VertexDataSource& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps); + // Constructs the convex hull into a MeshBuilder object which can be converted to a + // ConvexHull or Mesh object + void buildMesh(const VertexDataSource& pointCloud, FloatType eps); // The public getConvexHull functions will setup a VertexDataSource object and call this - ConvexHull getConvexHull(const VertexDataSource& pointCloud, bool CCW, bool useOriginalIndices, FloatType eps); + ConvexHull getConvexHull(const VertexDataSource& pointCloud, + bool CCW, bool useOriginalIndices, FloatType eps); public: // Computes convex hull for a given point cloud. // Params: // pointCloud: a vector of of 3D points // CCW: whether the output mesh triangles should have CCW orientation - // useOriginalIndices: should the output mesh use same vertex indices as the original point cloud. If this is false, - // then we generate a new vertex buffer which contains only the vertices that are part of the convex hull. - // eps: minimum distance to a plane to consider a point being on positive of it (for a point cloud with scale 1) + // useOriginalIndices: should the output mesh use same vertex indices as the original point + // cloud. If this is false, then we generate a new vertex buffer which contains only + // the vertices that are part of the convex hull. + // eps: minimum distance to a plane to consider a point being on positive of it + // (for a point cloud with scale 1) ConvexHull getConvexHull(const std::vector>& pointCloud, bool CCW, bool useOriginalIndices, @@ -133,40 +144,22 @@ namespace quickhull { // Params: // vertexData: pointer to the first 3D point of the point cloud // vertexCount: number of vertices in the point cloud - // CCW: whether the output mesh triangles should have CCW orientation - // useOriginalIndices: should the output mesh use same vertex indices as the original point cloud. If this is false, - // then we generate a new vertex buffer which contains only the vertices that are part of the convex hull. - // eps: minimum distance to a plane to consider a point being on positive side of it (for a point cloud with scale 1) ConvexHull getConvexHull(const Vector3* vertexData, size_t vertexCount, bool CCW, bool useOriginalIndices, FloatType eps = defaultEps()); - // Computes convex hull for a given point cloud. This function assumes that the vertex data resides in memory - // in the following format: x_0,y_0,z_0,x_1,y_1,z_1,... - // Params: - // vertexData: pointer to the X component of the first point of the point cloud. - // vertexCount: number of vertices in the point cloud - // CCW: whether the output mesh triangles should have CCW orientation - // useOriginalIndices: should the output mesh use same vertex indices as the original point cloud. If this is false, - // then we generate a new vertex buffer which contains only the vertices that are part of the convex hull. - // eps: minimum distance to a plane to consider a point being on positive side of it (for a point cloud with scale 1) + // Computes convex hull for a given point cloud. + // This function assumes that the vertex data resides in memory in the following format: + // x_0,y_0,z_0,x_1,y_1,z_1,... ConvexHull getConvexHull(const FloatType* vertexData, size_t vertexCount, bool CCW, bool useOriginalIndices, FloatType eps = defaultEps()); - // Computes convex hull for a given point cloud. This function assumes that the vertex data resides in memory - // in the following format: x_0,y_0,z_0,x_1,y_1,z_1,... - // Params: - // vertexData: pointer to the X component of the first point of the point cloud. - // vertexCount: number of vertices in the point cloud - // CCW: whether the output mesh triangles should have CCW orientation - // eps: minimum distance to a plane to consider a point being on positive side of it (for a point cloud with scale 1) - // Returns: - // Convex hull of the point cloud as a mesh object with half edge structure. + // Convex hull of the point cloud as a mesh object with half edge structure. HalfEdgeMesh getConvexHullAsMesh(const FloatType* vertexData, size_t vertexCount, bool CCW, @@ -178,10 +171,6 @@ namespace quickhull { } }; - /* - * Inline function definitions - */ - template std::unique_ptr> QuickHull::getIndexVectorFromPool() { auto r = m_indexVectorPool.get(); @@ -193,7 +182,9 @@ namespace quickhull { void QuickHull::reclaimToIndexVectorPool(std::unique_ptr>& ptr) { const size_t oldSize = ptr->size(); if ((oldSize+1)*128 < ptr->capacity()) { - // Reduce memory usage! Huge vectors are needed at the beginning of iteration when faces have many points on their positive side. Later on, smaller vectors will suffice. + // Reduce memory usage! Huge vectors are needed at the beginning of iteration when + // faces have many points on their positive side. Later on, smaller vectors will + // suffice. ptr.reset(nullptr); return; } diff --git a/libs/quickhull/Structs/Mesh.hpp b/libs/quickhull/Structs/Mesh.hpp index 2cffbd6c..f58c5afc 100644 --- a/libs/quickhull/Structs/Mesh.hpp +++ b/libs/quickhull/Structs/Mesh.hpp @@ -23,11 +23,11 @@ namespace quickhull { size_t m_opp; size_t m_face; size_t m_next; - + void disable() { m_endVertex = std::numeric_limits::max(); } - + bool isDisabled() const { return m_endVertex == std::numeric_limits::max(); } @@ -54,7 +54,7 @@ namespace quickhull { { } - + void disable() { m_he = std::numeric_limits::max(); } @@ -67,12 +67,12 @@ namespace quickhull { // Mesh data std::vector m_faces; std::vector m_halfEdges; - + // When the mesh is modified and faces and half edges are removed from it, we do not actually remove them from the container vectors. // Insted, they are marked as disabled which means that the indices can be reused when we need to add new faces and half edges to the mesh. // We store the free indices in the following vectors. std::vector m_disabledFaces,m_disabledHalfEdges; - + size_t addFace() { if (m_disabledFaces.size()) { size_t index = m_disabledFaces.back(); @@ -112,17 +112,17 @@ namespace quickhull { } MeshBuilder() = default; - + // Create a mesh with initial tetrahedron ABCD. Dot product of AB with the normal of triangle ABC should be negative. void setup(size_t a, size_t b, size_t c, size_t d) { m_faces.clear(); m_halfEdges.clear(); m_disabledFaces.clear(); m_disabledHalfEdges.clear(); - + m_faces.reserve(4); m_halfEdges.reserve(12); - + // Create halfedges HalfEdge AB; AB.m_endVertex = b; @@ -245,11 +245,11 @@ namespace quickhull { return {f.m_he,m_halfEdges[f.m_he].m_next,m_halfEdges[m_halfEdges[f.m_he].m_next].m_next}; } }; - + } -#endif +#endif diff --git a/libs/quickhull/Structs/Plane.hpp b/libs/quickhull/Structs/Plane.hpp index 903a7efe..35ef3520 100644 --- a/libs/quickhull/Structs/Plane.hpp +++ b/libs/quickhull/Structs/Plane.hpp @@ -25,7 +25,9 @@ namespace quickhull { Plane() = default; // Construct a plane using normal N and any point P on the plane - Plane(const Vector3& N, const Vector3& P) : m_N(N), m_D(-N.dotProduct(P)), m_sqrNLength(m_N.x*m_N.x+m_N.y*m_N.y+m_N.z*m_N.z) { + Plane(const Vector3& N, const Vector3& P) : + m_N(N), m_D(-N.dotProduct(P)), m_sqrNLength(m_N.x*m_N.x+m_N.y*m_N.y+m_N.z*m_N.z) + { } }; diff --git a/libs/quickhull/Structs/Vector3.hpp b/libs/quickhull/Structs/Vector3.hpp index 47a5f746..995f2a6f 100644 --- a/libs/quickhull/Structs/Vector3.hpp +++ b/libs/quickhull/Structs/Vector3.hpp @@ -119,7 +119,10 @@ namespace quickhull { const T dz = z-other.z; return dx*dx+dy*dy+dz*dz; } - + + bool operator==(const Vector3& other) const { + return x == other.x && y == other.y && z == other.z; + } }; // Overload also << operator for easy printing of debug data diff --git a/libs/quickhull/Tests/QuickHullTests.cpp b/libs/quickhull/Tests/QuickHullTests.cpp index dd833ff2..b62ddba3 100644 --- a/libs/quickhull/Tests/QuickHullTests.cpp +++ b/libs/quickhull/Tests/QuickHullTests.cpp @@ -5,6 +5,8 @@ #include #include +#define VERIFY(x) if (!(x)) { std::cout << "test failed: '" << #x << "' is not true." << std::endl; } + namespace quickhull { namespace tests { @@ -20,10 +22,12 @@ namespace quickhull { return from + (FloatType)dist(rng)*(to-from); }; - static void assertSameValue(FloatType a, FloatType b) { - assert(std::abs(a-b)<0.0001f); + static void CHECK_CLOSE(FloatType a, FloatType b, FloatType maxDiff = 0.0001f) { + if (std::abs(a-b) >= maxDiff) { + throw std::runtime_error("Difference too big"); + } } - + static void testVector3() { typedef Vector3 vec3; vec3 a(1,0,0); @@ -39,13 +43,15 @@ namespace quickhull { } template - static std::vector> createSphere(T radius, size_t M, Vector3 offset = Vector3(0,0,0)) { + static std::vector> createSphere(T radius, size_t M, + Vector3 offset = Vector3(0,0,0)) { std::vector> pc; const T pi = 3.14159f; for (int i=0;i<=M;i++) { FloatType y = std::sin(pi/2 + (FloatType)i/(M)*pi); FloatType r = std::cos(pi/2 + (FloatType)i/(M)*pi); - FloatType K = FloatType(1)-std::abs((FloatType)((FloatType)i-M/2.0f))/(FloatType)(M/2.0f); + FloatType K = + FloatType(1)-std::abs((FloatType)((FloatType)i-M/2.0f))/(FloatType)(M/2.0f); const size_t pcount = (size_t)(1 + K*M + FloatType(1)/2); for (size_t j=0;j1 ? r*std::cos((FloatType)j/pcount*pi*2) : 0; @@ -69,8 +75,9 @@ namespace quickhull { } } - // Test worst case scenario: more and more points on the unit sphere. All points should be part of the convex hull, as long as we can make epsilon smaller without - // running out of numerical accuracy. + // Test worst case scenario: more and more points on the unit sphere. + // All points should be part of the convex hull, as long as we can make epsilon + // smaller without running out of numerical accuracy. size_t i = 1; FloatType eps = 0.002f; for (;;) { @@ -120,33 +127,36 @@ namespace quickhull { pc.emplace_back(h&1?-2:2, h&2?-2:2, h&4?-2:2); } HalfEdgeMesh mesh = qh.getConvexHullAsMesh(&pc[0].x, pc.size(), true); - assert(mesh.m_faces.size() == 12); - assert(mesh.m_halfEdges.size() == 36); - assert(mesh.m_vertices.size() == 8); + VERIFY(mesh.m_faces.size() == 12); + VERIFY(mesh.m_halfEdges.size() == 36); + VERIFY(mesh.m_vertices.size() == 8); // Verify that for each face f, f.halfedgeIndex equals next(next(next(f.halfedgeIndex))). for (const auto& f : mesh.m_faces) { size_t next = mesh.m_halfEdges[f.m_halfEdgeIndex].m_next; next = mesh.m_halfEdges[next].m_next; next = mesh.m_halfEdges[next].m_next; - assert(next == f.m_halfEdgeIndex); + VERIFY(next == f.m_halfEdgeIndex); } } static void testPlanes() { - Vector3 N(1,0,0); - Vector3 p(2,0,0); - Plane P(N,p); - auto dist = mathutils::getSignedDistanceToPlane(Vector3(3,0,0), P); - assertSameValue(dist,1); + vec3 N(1, 0, 0); + vec3 p(2, 0, 0); + Plane P(N, p); + VERIFY(P.isPointOnPositiveSide(p)); + VERIFY(P.isPointOnPositiveSide(vec3(3, 0, 0))); + VERIFY(!P.isPointOnPositiveSide(vec3(1, 0, 0))); + FloatType dist = mathutils::getSignedDistanceToPlane(vec3(3,0,0), P); + CHECK_CLOSE(dist,1); dist = mathutils::getSignedDistanceToPlane(Vector3(1,0,0), P); - assertSameValue(dist,-1); + CHECK_CLOSE(dist,-1); dist = mathutils::getSignedDistanceToPlane(Vector3(1,0,0), P); - assertSameValue(dist,-1); + CHECK_CLOSE(dist,-1); N = Vector3(2,0,0); P = Plane(N,p); dist = mathutils::getSignedDistanceToPlane(Vector3(6,0,0), P); - assertSameValue(dist,8); + CHECK_CLOSE(dist,8); } static void testVertexBufferAddress() { @@ -180,11 +190,12 @@ namespace quickhull { const auto indices = hull.getIndexBuffer(); assert(vertices.size() == 3); assert(indices.size() >= 6); - const vec3 triangle[3] = { vertices[indices[0]], vertices[indices[1]], vertices[indices[2]] }; + const vec3 triangle[3] = { vertices[indices[0]], vertices[indices[1]], + vertices[indices[2]] }; normal[i] = mathutils::getTriangleNormal(triangle[0], triangle[1], triangle[2]); } const auto dot = normal[0].dotProduct(normal[1]); - assertSameValue(dot, -1); + CHECK_CLOSE(dot, -1); } int run() { @@ -196,10 +207,12 @@ namespace quickhull { // Seed RNG using Unix time std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - auto seed = std::chrono::duration_cast(now.time_since_epoch()).count(); + auto seed = + std::chrono::duration_cast(now.time_since_epoch()).count(); rng.seed((unsigned int)seed); - // Test 1 : Put N points inside unit cube. Result mesh must have exactly 8 vertices because the convex hull is the unit cube. + // Test 1 : Put N points inside unit cube. Result mesh must have exactly 8 vertices + // because the convex hull is the unit cube. pc.clear(); for (int i=0;i<8;i++) { pc.emplace_back(i&1 ? -1 : 1,i&2 ? -1 : 1,i&4 ? -1 : 1); @@ -210,7 +223,7 @@ namespace quickhull { } hull = qh.getConvexHull(pc,true,false); assert(hull.getVertexBuffer().size()==8); - assert(hull.getIndexBuffer().size()==3*2*6); // 6 cube faces, 2 triangles per face, 3 indices per triangle + assert(hull.getIndexBuffer().size()==3*2*6); // 6 faces, 2 triangles/face, 3 indices/tri assert(&(hull.getVertexBuffer()[0])!=&(pc[0])); auto hull2 = hull; assert(hull2.getVertexBuffer().size()==hull.getVertexBuffer().size()); @@ -225,7 +238,8 @@ namespace quickhull { assert(hull.getVertexBuffer().size()==pc.size()); assert(&(hull.getVertexBuffer()[0])==&(pc[0])); - // Test 2 : random N points from the boundary of unit sphere. Result mesh must have exactly N points. + // Test 2 : random N points from the boundary of unit sphere. + // Result mesh must have exactly N points. pc = createSphere(1, 50); hull = qh.getConvexHull(pc,true,false); assert(pc.size() == hull.getVertexBuffer().size()); @@ -239,7 +253,8 @@ namespace quickhull { hull = qh.getConvexHull(pc,true,false); assert(pc.size()/2 == hull.getVertexBuffer().size()); - // Test 2.1 : Multiply x components of the unit sphere vectors by a huge number => essentially we get a line + // Test 2.1 : Multiply x components of the unit sphere vectors by a huge + // number => essentially we get a line const FloatType mul = 2*2*2; while (true) { for (auto& p : pc) { @@ -256,7 +271,9 @@ namespace quickhull { vec3 centerPoint(2,2,2); pc.push_back(centerPoint); for (size_t i=0;i<100;i++) { - auto newp = centerPoint + vec3(rnd(-0.000001f,0.000001f),rnd(-0.000001f,0.000001f),rnd(-0.000001f,0.000001f)); + auto newp = centerPoint + vec3(rnd(-0.000001f,0.000001f), + rnd(-0.000001f,0.000001f), + rnd(-0.000001f,0.000001f)); pc.push_back(newp); } hull = qh.getConvexHull(pc,true,false);