diff --git a/contrib/MeshOptimizer/MeshOptimizer.cpp b/contrib/MeshOptimizer/MeshOptimizer.cpp index 61053b12c0ffd1a31a582de470ed7b6bf1e9edd2..4cf44f2d1acb7a1db9e317c82b7f8558d5e51a77 100644 --- a/contrib/MeshOptimizer/MeshOptimizer.cpp +++ b/contrib/MeshOptimizer/MeshOptimizer.cpp @@ -71,36 +71,96 @@ static std::set<MVertex *> getAllBndVertices } +// Test intersection between sphere and segment +static bool testSegSphereIntersect(SPoint3 A, SPoint3 B, const SPoint3& P, const double rr) +{ + // Test if separating plane between sphere and segment vertices + // For each vertex, separation if vertex is outside sphere and P on opposite side + // to other seg. vertex w.r.t plane of normal (vertex-P) through vertex + const SVector3 PA(P, A), PB(P, B); + const double aa = dot(PA, PA), ab = dot(PA, PB); + if ((aa > rr) & (ab > aa)) return false; + const double bb = dot(PB, PB); + if ((bb > rr) & (ab > bb)) return false; + + // Test if separating plane between sphere and line + // For A, separation if projection Q of P on (AB) lies outside the sphere + const SVector3 AB(A, B); + const double d = ab - aa, e = dot(AB, AB); + const SVector3 PQ = PA * e - d * AB; + if (dot(PQ, PQ) > rr * e * e) return false; + + // Return true (intersection) if no separation at all + return true; +} + + +// Test intersection between sphere and triangle +// Inspired by Christer Ericson, http://realtimecollisiondetection.net/blog/?p=103 +static bool testTriSphereIntersect(SPoint3 A, SPoint3 B, SPoint3 C, const SPoint3& P, const double rr) +{ + // Test if separating plane between sphere and triangle plane + const SVector3 PA(P, A), AB(A, B), AC(A, C); + const SVector3 V = crossprod(AB, AC); // Normal to triangle plane + const double d = dot(PA, V); // Dist. from P to triangle plane times norm of V + const double e = dot(V, V); // Norm of V + if (d * d > rr * e) return false; // Test if separating plane between sphere and triangle plane + + // Test if separating plane between sphere and triangle vertices + const SVector3 PB(P, B), PC(P, B); + const double aa = dot(PA, PA), ab = dot(PA, PB), ac = dot(PA, PC); + const double bb = dot(PB, PB), bc = dot(PB, PC), cc = dot(PC, PC); + if ((aa > rr) & (ab > aa) & (ac > aa)) return false; // For each triangle vertex, separation if vertex is outside sphere + if ((bb > rr) & (ab > bb) & (bc > bb)) return false; // and P on opposite side to other two triangle vertices w.r.t + if ((cc > rr) & (ac > cc) & (bc > cc)) return false; // plane of normal (vertex-P) through vertex + + // Test if separating plane between sphere and triangle edges + const SVector3 BC(B, C); + const double d1 = ab - aa, d2 = bc - bb, d3 = ac - cc; + const double e1 = dot(AB, AB), e2 = dot(BC, BC), e3 = dot(AC, AC); + const SVector3 PQ1 = PA * e1 - d1 * AB; // Q1 projection of P on line (AB) + const SVector3 PQ2 = PB * e2 - d2 * BC; // Q2 projection of P on line (BC) + const SVector3 PQ3 = PC * e3 + d3 * AC; // Q3 projection of P on line (AC) + const SVector3 PQC = PC * e1 - PQ1; + const SVector3 PQA = PA * e2 - PQ2; + const SVector3 PQB = PB * e3 - PQ3; + if ((dot(PQ1, PQ1) > rr * e1 * e1) & (dot(PQ1, PQC) > 0)) return false; // For A, separation if Q lies outside the sphere and if P and C + if ((dot(PQ2, PQ2) > rr * e2 * e2) & (dot(PQ2, PQA) > 0)) return false; // are on opposite sides of plane through AB with normal PQ + if ((dot(PQ3, PQ3) > rr * e3 * e3) & (dot(PQ3, PQB) > 0)) return false; // Same for other two vertices + + // Return true (intersection) if no separation at all + return true; +} + + // Approximate test of intersection element with circle/sphere by sampling -static bool testElInDist(const SPoint3 p, double limDist, MElement *el) +static bool testElInDist(const SPoint3 P, double limDist, MElement *el) { const double sampleLen = 0.5*limDist; // Distance between sample points + const double limDistSq = limDist*limDist; if (el->getDim() == 2) { // 2D? for (int iEd = 0; iEd < el->getNumEdges(); iEd++) { // Loop over edges of element MEdge ed = el->getEdge(iEd); - const int nPts = int(ed.length()/sampleLen)+2; // Nb of sample points based on edge length - for (int iPt = 0; iPt < nPts; iPt++) { // Loop over sample points - const SPoint3 pt = ed.interpolate(iPt/float(nPts-1)); - if (p.distance(pt) < limDist) return true; + const SPoint3 A = ed.getVertex(0)->point(); + const SPoint3 B = ed.getVertex(1)->point(); + if (testSegSphereIntersect(A, B, P, limDistSq)) { + return true; } } } else { // 3D for (int iFace = 0; iFace < el->getNumFaces(); iFace++) { // Loop over faces of element MFace face = el->getFace(iFace); - double lMax = 0.; // Max. edge length in face - const int nVert = face.getNumVertices(); - for (int iEd = 0; iEd < nVert; iEd++) - lMax = std::max(lMax, face.getEdge(iEd).length()); - const int nPts = int(lMax/sampleLen)+2; // Nb of sample points based on max. edge length in face - for (int iPt0 = 0; iPt0 < nPts; iPt0++) { - const double u = iPt0/float(nPts-1); - for (int iPt1 = 0; iPt1 < nPts; iPt1++) { // Loop over sample points - const double vMax = (nVert == 3) ? 1.-u : 1.; - const SPoint3 pt = face.interpolate(u, vMax*iPt1/float(nPts-1)); - if (p.distance(pt) < limDist) return true; - } + const SPoint3 A = face.getVertex(0)->point(); + const SPoint3 B = face.getVertex(1)->point(); + const SPoint3 C = face.getVertex(2)->point(); + if (face.getNumVertices() == 3) + return testTriSphereIntersect(A, B, C, P, limDistSq); + else { + const SPoint3 D = face.getVertex(3)->point(); + return (testTriSphereIntersect(A, B, C, P, limDistSq) || + testTriSphereIntersect(A, C, D, P, limDistSq)); } } } @@ -129,9 +189,10 @@ static std::set<MElement*> getSurroundingPatch(MElement *el, int minLayers, const std::vector<MElement*> &neighbours = vertex2elements.find ((*it)->getVertex(i))->second; for (std::vector<MElement*>::const_iterator itN = neighbours.begin(); - itN != neighbours.end(); ++itN){ - if ((d < minLayers) || testElInDist(pnt, limDist, *itN)) - if (patch.insert(*itN).second) currentLayer.push_back(*itN); // Assume that if an el is too far, its neighbours are too far as well + itN != neighbours.end(); ++itN) { + if ((patch.find(*itN) == patch.end()) && + ((d < minLayers) || testElInDist(pnt, limDist, *itN))) + if (patch.insert(*itN).second) currentLayer.push_back(*itN); // Assume that if an el is too far, its neighbours are too far as well } } }