-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Testing Various Cases, and Potential fix #24
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,6 +91,31 @@ namespace quickhull { | |
return ConvexHull<T>(m_mesh,m_vertexData, CCW, useOriginalIndices); | ||
} | ||
|
||
// Check if the mesh remains convex after adding new faces | ||
template<typename T> | ||
bool QuickHull<T>::isMeshConvex(const std::vector<size_t>& newFaces) { | ||
for (const size_t& faceIndex : newFaces) { | ||
const auto& face = m_mesh.m_faces[faceIndex]; | ||
bool positive = false; | ||
bool negative = false; | ||
for (auto i : tempPoints) { | ||
Vector3 vertex = m_vertexData[i]; | ||
double distance = face.m_P.m_N.dotProduct(vertex) + face.m_P.m_D; | ||
if (distance > m_epsilon) { | ||
positive = true; | ||
} else if (distance < -m_epsilon) { | ||
negative = true; | ||
} | ||
|
||
// If we find both positive and negative distances, the polyhedron is not convex | ||
if (positive && negative) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should there be any positive distances for a valid convex hulll? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, so the way I'm checking it is if all the other points that are a part of the convex hull, don't lie on the same side of the face it's not a convex hull. I wrote this to account for incorrect orientation of the normal, since I am testing it after each iteration, and I wasn't sure whether the normals were oriented correctly after each step or just at the last step. Although, now that you've pointed it out, I think I should have tried checking only for the negatives first in case the normals are oriented correctly, it will be a more sound check. I will check the code to verify the normals aren't rearranged later and if it works I'll make the changes. Thanks for pointing it out |
||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
template<typename T> | ||
void QuickHull<T>::createConvexHalfEdgeMesh() { | ||
m_visibleFaces.clear(); | ||
|
@@ -113,6 +138,7 @@ namespace quickhull { | |
|
||
// Process faces until the face list is empty. | ||
size_t iter = 0; | ||
int incorrect_count=0; | ||
while (!m_faceList.empty()) { | ||
iter++; | ||
if (iter == std::numeric_limits<size_t>::max()) { | ||
|
@@ -135,8 +161,16 @@ namespace quickhull { | |
} | ||
|
||
// Pick the most distant point to this triangle plane as the point to which we extrude | ||
// int breakFlag=0; | ||
const vec3& activePoint = m_vertexData[tf.m_mostDistantPoint]; | ||
// std::cout << "Active Point" << activePoint.x << " " << activePoint.y << " " << activePoint.z << std::endl; | ||
const size_t activePointIndex = tf.m_mostDistantPoint; | ||
// std::cout << activePointIndex << std::endl; | ||
// if (activePointIndex==0) | ||
// { | ||
// breakFlag=1; | ||
// } | ||
tempPoints.push_back(activePointIndex); | ||
|
||
// 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 | ||
|
@@ -161,6 +195,9 @@ namespace quickhull { | |
pvf.m_visibilityCheckedOnIteration = iter; | ||
const T d = P.m_N.dotProduct(activePoint)+P.m_D; | ||
if (d>0) { | ||
// if (d>m_epsilon && d*d > m_epsilonSquared*P.m_sqrNLength) { | ||
// if (d>0 && d*d > m_epsilonSquared*P.m_sqrNLength) { | ||
// if (d>m_epsilon) { | ||
pvf.m_isVisibleFaceOnCurrentIteration = 1; | ||
pvf.m_horizonEdgesOnCurrentIteration = 0; | ||
m_visibleFaces.push_back(faceData.m_faceIndex); | ||
|
@@ -250,7 +287,9 @@ namespace quickhull { | |
A = horizonEdgeVertexIndices[0]; | ||
B = horizonEdgeVertexIndices[1]; | ||
C = activePointIndex; | ||
|
||
// std::cout << "PtA" << m_vertexData[A].x << " " << m_vertexData[A].y << " " << m_vertexData[A].z << std::endl; | ||
// std::cout << "PtB" << m_vertexData[B].x << " " << m_vertexData[B].y << " " << m_vertexData[B].z << std::endl; | ||
// std::cout << "PtC" << m_vertexData[C].x << " " << m_vertexData[C].y << " " << m_vertexData[C].z << std::endl; | ||
const size_t newFaceIndex = m_mesh.addFace(); | ||
m_newFaceIndices.push_back(newFaceIndex); | ||
|
||
|
@@ -272,6 +311,7 @@ namespace quickhull { | |
|
||
const Vector3<T> planeNormal = mathutils::getTriangleNormal(m_vertexData[A],m_vertexData[B],activePoint); | ||
newFace.m_P = Plane<T>(planeNormal,activePoint); | ||
// std::cout << "N" << planeNormal.x << " " << planeNormal.y << " " << planeNormal.z << std::endl; | ||
newFace.m_he = AB; | ||
|
||
m_mesh.m_halfEdges[CA].m_opp = m_newHalfEdgeIndices[i>0 ? i*2-1 : 2*horizonEdgeCount-1]; | ||
|
@@ -292,6 +332,9 @@ namespace quickhull { | |
} | ||
} | ||
// The points are no longer needed: we can move them to the vector pool for reuse. | ||
// for (auto disabledPt : *disabledPoints) { | ||
// std::cout << "Disabled Point" << m_vertexData[disabledPt].x << " " << m_vertexData[disabledPt].y << " " << m_vertexData[disabledPt].z << std::endl; | ||
// } | ||
reclaimToIndexVectorPool(disabledPoints); | ||
} | ||
|
||
|
@@ -306,8 +349,27 @@ namespace quickhull { | |
} | ||
} | ||
} | ||
// if (isMeshConvex(m_newFaceIndices)) | ||
// { | ||
// // if (breakFlag==1) | ||
// // { | ||
// // m_indexVectorPool.clear(); | ||
// // break; | ||
// // } | ||
// // std::cout << "YAY\n"; | ||
// } | ||
// else | ||
// { | ||
// incorrect_count+=1; | ||
// // std::cout << "OOP\n"; | ||
// // m_indexVectorPool.clear(); | ||
// // break; | ||
// } | ||
} | ||
|
||
if (!isMeshConvex(m_newFaceIndices)) | ||
{ | ||
std::cout << "Invalid Output" << "\n"; | ||
} | ||
// Cleanup | ||
m_indexVectorPool.clear(); | ||
} | ||
|
@@ -400,6 +462,10 @@ namespace quickhull { | |
if (trianglePlane.isPointOnPositiveSide(m_vertexData[v[3]])) { | ||
std::swap(v[0],v[1]); | ||
} | ||
tempPoints.push_back(v[0]); | ||
tempPoints.push_back(v[1]); | ||
tempPoints.push_back(v[2]); | ||
tempPoints.push_back(v[3]); | ||
return m_mesh.setup(v[0],v[1],v[2],v[3]); | ||
} | ||
|
||
|
@@ -431,6 +497,7 @@ namespace quickhull { | |
if (distToRay > maxD) { | ||
maxD=distToRay; | ||
maxI=i; | ||
// Maybe here | ||
} | ||
} | ||
if (maxD == m_epsilonSquared) { | ||
|
@@ -483,7 +550,15 @@ namespace quickhull { | |
} | ||
|
||
// Create a tetrahedron half edge mesh and compute planes defined by each triangle | ||
m_mesh.setup(baseTriangle[0],baseTriangle[1],baseTriangle[2],maxI); | ||
// std::cout << "Active Point Base" << m_vertexData[baseTriangle[0]].x << " " << m_vertexData[baseTriangle[0]].y << " " << m_vertexData[baseTriangle[0]].z << std::endl; | ||
// std::cout << "Active Point Base" << m_vertexData[baseTriangle[1]].x << " " << m_vertexData[baseTriangle[1]].y << " " << m_vertexData[baseTriangle[1]].z << std::endl; | ||
// std::cout << "Active Point Base" << m_vertexData[baseTriangle[2]].x << " " << m_vertexData[baseTriangle[2]].y << " " << m_vertexData[baseTriangle[2]].z << std::endl; | ||
// std::cout << "Active Point Base" << m_vertexData[maxI].x << " " << m_vertexData[maxI].y << " " << m_vertexData[maxI].z << std::endl; | ||
tempPoints.push_back(baseTriangle[0]); | ||
tempPoints.push_back(baseTriangle[1]); | ||
tempPoints.push_back(baseTriangle[2]); | ||
tempPoints.push_back(maxI); | ||
m_mesh.setup(baseTriangle[0],baseTriangle[1],baseTriangle[2],maxI); | ||
for (auto& f : m_mesh.m_faces) { | ||
auto v = m_mesh.getVertexIndicesOfFace(f); | ||
const Vector3<T>& va = m_vertexData[v[0]]; | ||
|
@@ -492,6 +567,10 @@ namespace quickhull { | |
const Vector3<T> N1 = mathutils::getTriangleNormal(va, vb, vc); | ||
const Plane<T> plane(N1,va); | ||
f.m_P = plane; | ||
// std::cout << "Pt1" << va.x << " " << va.y << " " << va.z << std::endl; | ||
// std::cout << "Pt2" << vb.x << " " << vb.y << " " << vb.z << std::endl; | ||
// std::cout << "Pt3" << vc.x << " " << vc.y << " " << vc.z << std::endl; | ||
// std::cout << "N" << N1.x << " " << N1.y << " " << N1.z << std::endl; | ||
} | ||
|
||
// Finally we assign a face for each vertex outside the tetrahedron (vertices inside the tetrahedron have no role anymore) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is your proposed fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, so basically normalizing the output of getTriangleNormal function, since we were comparing the distance (unnormalized) directly to epsilon in
quickhull/QuickHull.cpp
Line 462 in 4ef66c6
and based on my understanding of the code that wasn't intended.
Also, the above line and
quickhull/QuickHull.hpp
Lines 196 to 206 in 4ef66c6
Both compare distances to find the maximum distance, in such a situation when the distance is not the Euclidean distance the comparison could select an "incorrect point" (Now I know that we had discussed that choosing the absolute maximum point doesn't make a difference in floating point arithmetic, but it's possible that due to this non-Euclidean distance we pick a point which is very close, which could lead to the errors [this is just speculation on my end] leading to those errors)
PS: I'm sorry if I wasn't able to explain this properly in the original comment. I hope it's clearer now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good find! Since normalization is somewhat expensive, you might want to check all the places this function is called. If they all need to be normalized, then this is perfect, but if not, it would be ideal to only add normalization where needed. Have you noticed any difference in speed from this change?