bool Polyhedron::IsConvex() const { // This function is O(n^2). /** @todo Real-Time Collision Detection, p. 64: A faster O(n) approach is to compute for each face F of P the centroid C of F, and for all neighboring faces G of F test if C lies behind the supporting plane of G. If some C fails to lie behind the supporting plane of one or more neighboring faces, P is concave, and is otherwise assumed convex. However, note that just as the corresponding polygonal convexity test may fail for a pentagram this test may fail for, for example, a pentagram extruded out of its plane and capped at the ends. */ for(int f = 0; f < NumFaces(); ++f) { Plane p = FacePlane(f); for(int i = 0; i < NumVertices(); ++i) { float d = p.SignedDistance(Vertex(i)); if (d > 1e-3f) // Tolerate a small epsilon error. { printf("Distance of vertex %d from plane %d: %f", i, f, d); return false; } } } return true; }
bool Polygon::Intersects(const Plane &plane) const { // Project the points of this polygon onto the 1D axis of the plane normal. // If there are points on both sides of the plane, then the polygon intersects the plane. float minD = inf; float maxD = -inf; for(size_t i = 0; i < p.size(); ++i) { float d = plane.SignedDistance(p[i]); minD = Min(minD, d); maxD = Max(maxD, d); } // Allow a very small epsilon tolerance. return minD <= 1e-4f && maxD >= -1e-4f; }
void Polyhedron::MergeConvex(const float3 &point) { // LOGI("mergeconvex."); std::set<std::pair<int, int> > deletedEdges; std::map<std::pair<int, int>, int> remainingEdges; for(size_t i = 0; i < v.size(); ++i) if (point.DistanceSq(v[i]) < 1e-3f) return; // bool hadDisconnectedHorizon = false; for(int i = 0; i < (int)f.size(); ++i) { // Delete all faces that don't contain the given point. (they have point in their positive side) Plane p = FacePlane(i); Face &face = f[i]; if (p.SignedDistance(point) > 1e-5f) { bool isConnected = (deletedEdges.empty()); int v0 = face.v.back(); for(size_t j = 0; j < face.v.size() && !isConnected; ++j) { int v1 = face.v[j]; if (deletedEdges.find(std::make_pair(v1, v0)) != deletedEdges.end()) { isConnected = true; break; } v0 = v1; } if (isConnected) { v0 = face.v.back(); for(size_t j = 0; j < face.v.size(); ++j) { int v1 = face.v[j]; deletedEdges.insert(std::make_pair(v0, v1)); // LOGI("Edge %d,%d is to be deleted.", v0, v1); v0 = v1; } // LOGI("Deleting face %d: %s. Distance to vertex %f", i, face.ToString().c_str(), p.SignedDistance(point)); std::swap(f[i], f.back()); f.pop_back(); --i; continue; } // else // hadDisconnectedHorizon = true; } int v0 = face.v.back(); for(size_t j = 0; j < face.v.size(); ++j) { int v1 = face.v[j]; remainingEdges[std::make_pair(v0, v1)] = i; // LOGI("Edge %d,%d is to be deleted.", v0, v1); v0 = v1; } } // The polyhedron contained our point, nothing to merge. if (deletedEdges.empty()) return; // Add the new point to this polyhedron. // if (!v.back().Equals(point)) v.push_back(point); /* // Create a look-up index of all remaining uncapped edges of the polyhedron. std::map<std::pair<int,int>, int> edgesToFaces; for(size_t i = 0; i < f.size(); ++i) { Face &face = f[i]; int v0 = face.v.back(); for(size_t j = 0; j < face.v.size(); ++j) { int v1 = face.v[j]; edgesToFaces[std::make_pair(v1, v0)] = i; v0 = v1; } } */ // Now fix all edges by adding new triangular faces for the point. // for(size_t i = 0; i < deletedEdges.size(); ++i) for(std::set<std::pair<int, int> >::iterator iter = deletedEdges.begin(); iter != deletedEdges.end(); ++iter) { std::pair<int, int> opposite = std::make_pair(iter->second, iter->first); if (deletedEdges.find(opposite) != deletedEdges.end()) continue; // std::map<std::pair<int,int>, int>::iterator iter = edgesToFaces.find(deletedEdges[i]); // std::map<std::pair<int,int>, int>::iterator iter = edgesToFaces.find(deletedEdges[i]); // if (iter != edgesToFaces.end()) { // If the adjoining face is planar to the triangle we'd like to add, instead extend the face to enclose // this vertex. //float3 newTriangleNormal = (v[v.size()-1]-v[iter->second]).Cross(v[iter->first]-v[iter->second]).Normalized(); std::map<std::pair<int, int>, int>::iterator existing = remainingEdges.find(opposite); assert(existing != remainingEdges.end()); MARK_UNUSED(existing); #if 0 int adjoiningFace = existing->second; if (FaceNormal(adjoiningFace).Dot(newTriangleNormal) >= 0.99999f) ///\todo float3::IsCollinear { bool added = false; Face &adjoining = f[adjoiningFace]; for(size_t i = 0; i < adjoining.v.size(); ++i) if (adjoining.v[i] == iter->second) { adjoining.v.insert(adjoining.v.begin() + i + 1, v.size()-1); added = true; /* int prev2 = (i + adjoining.v.size() - 1) % adjoining.v.size(); int prev = i; int cur = i + 1; int next = (i + 2) % adjoining.v.size(); int next2 = (i + 3) % adjoining.v.size(); if (float3::AreCollinear(v[prev2], v[prev], v[cur])) adjoining.v.erase(adjoining.v.begin() + prev); else if (float3::AreCollinear(v[prev], v[cur], v[next])) adjoining.v.erase(adjoining.v.begin() + cur); else if (float3::AreCollinear(v[cur], v[next], v[next2])) adjoining.v.erase(adjoining.v.begin() + next2); */ break; } assert(added); assume(added); } else #endif // if (!v[deletedEdges[i].first].Equals(point) && !v[deletedEdges[i].second].Equals(point)) { Face tri; tri.v.push_back(iter->second); tri.v.push_back((int)v.size()-1); tri.v.push_back(iter->first); f.push_back(tri); // LOGI("Added face %d: %s.", (int)f.size()-1, tri.ToString().c_str()); } } } #define mathasserteq(lhs, op, rhs) do { if (!((lhs) op (rhs))) { LOGE("Condition %s %s %s (%g %s %g) failed!", #lhs, #op, #rhs, (double)(lhs), #op, (double)(rhs)); assert(false); } } while(0) // mathasserteq(NumVertices() + NumFaces(), ==, 2 + NumEdges()); assert(FaceIndicesValid()); // assert(EulerFormulaHolds()); // assert(IsClosed()); // assert(FacesAreNondegeneratePlanar()); // assert(IsConvex()); // if (hadDisconnectedHorizon) // MergeConvex(point); }