// Compute the OBB for a set of points relative to a transform matrix and see if it is smaller than the current best value. If // so, return it. static void computeOBB(TheaArray<Vector3> const & points, CoordinateFrame3 const & cframe, OBB & current_best_result, bool overwrite) { if (points.empty()) return; Vector3 lo, hi; lo = hi = cframe.pointToObjectSpace(points[0]); Vector3 p; for (array_size_t i = 1; i < points.size(); ++i) { p = cframe.pointToObjectSpace(points[i]); lo = lo.min(p); hi = hi.max(p); } Vector3 e = hi - lo; double volume = e.x() * e.y() * e.z(); if (overwrite || volume < current_best_result.volume) { Vector3 c = 0.5f * (lo + hi); Vector3 he = 0.5f * e; current_best_result = OBB(c - he, c + he, cframe, volume); } }
// A rather hacky way of forming a joint descriptor, suitable only for exact matches like we want here void accumGroupFeatures(MeshGroup const & mg, TheaArray<double> & features) { for (MeshGroup::MeshConstIterator mi = mg.meshesBegin(); mi != mg.meshesEnd(); ++mi) { TheaArray<double> const & mf = (*mi)->getFeatures(); if (features.empty()) features.resize(mf.size()); alwaysAssertM(mf.size() == features.size(), "Feature vectors have different sizes"); for (array_size_t j = 0; j < features.size(); ++j) features[j] += mf[j]; } for (MeshGroup::GroupConstIterator ci = mg.childrenBegin(); ci != mg.childrenEnd(); ++ci) accumGroupFeatures(**ci, features); }
bool sampleMesh(string const & mesh_path, TheaArray<DVector3> & samples) { typedef DisplayMesh Mesh; typedef MeshGroup<Mesh> MG; try { MG mg("MeshGroup"); mg.load(mesh_path); MeshSampler<Mesh> sampler(mg); TheaArray<Vector3> pts; sampler.sampleEvenlyByArea(num_mesh_samples, pts); samples.resize(pts.size()); for (array_size_t i = 0; i < samples.size(); ++i) samples[i] = DVector3(pts[i]); } THEA_STANDARD_CATCH_BLOCKS(return false;, ERROR, "%s", "An error occurred")
static long findEdgeConnected(MeshT & mesh, TheaArray< TheaArray<FaceHandleT> > & components, typename boost::enable_if< Graphics::IsCGALMesh<MeshT> >::type * dummy = NULL) { // Begin with all faces as separate components TheaArray<typename MeshT::Facet *> facets; for (typename MeshT::Facet_iterator fi = mesh.facets_begin(); fi != mesh.facets_end(); fi++) facets.push_back(&(*fi)); typedef UnionFind<typename MeshT::Facet *> FacetUnionFind; FacetUnionFind uf(facets.begin(), facets.end()); // Now go over all edges, connecting the faces on either side for (typename MeshT::Edge_iterator ei = mesh.edges_begin(); ei != mesh.edges_end(); ei++) { if (!ei->is_border_edge()) { long handle1 = uf.getObjectID(&(*ei->facet())); long handle2 = uf.getObjectID(&(*ei->opposite()->facet())); uf.merge(handle1, handle2); } } // Reserve space for the result components.resize((array_size_t)uf.numSets()); typedef TheaUnorderedMap<long, array_size_t> ComponentIndexMap; ComponentIndexMap component_indices; // Loop over facets, adding each to the appropriate result subarray array_size_t component_index; for (typename MeshT::Facet_iterator fi = mesh.facets_begin(); fi != mesh.facets_end(); fi++) { long rep = uf.find(uf.getObjectID(&(*fi))); typename ComponentIndexMap::const_iterator existing = component_indices.find(rep); if (existing == component_indices.end()) { component_index = component_indices.size(); component_indices[rep] = component_index; components[component_index].clear(); components[component_index].reserve(uf.sizeOfSet(rep)); } else component_index = existing->second; components[component_index].push_back(fi); } return (long)components.size(); }
static void computeBestFitOBB(TheaArray<Vector3> const & points, Box3 & result, bool has_up, Vector3 const & up) { if (points.empty()) { result = Box3(); return; } else if (points.size() == 1) { result = Box3(AxisAlignedBox3(points[0])); return; } Vector3 centroid; CoordinateFrame3 cframe; if (has_up) { centroid = CentroidN<Vector3, 3>::compute(points.begin(), points.end()); Vector3 u, v; up.createOrthonormalBasis(u, v); cframe = CoordinateFrame3::_fromAffine(AffineTransform3(basisMatrix(u, v, up), centroid)); } else { Plane3 plane; LinearLeastSquares3<Vector3>::fitPlane(points.begin(), points.end(), plane, ¢roid); planeToCFrame(plane, centroid, cframe); } OBB best_obb; computeOBB(points, cframe, best_obb, true); Matrix3 rot = Matrix3::rotationAxisAngle((has_up ? up : Vector3::unitY()), Math::degreesToRadians(10)); for (int a = 10; a < 180; a += 10) { cframe._setRotation(cframe.getRotation() * rot); computeOBB(points, cframe, best_obb, false); } result = Box3(AxisAlignedBox3(best_obb.lo, best_obb.hi), best_obb.cframe); }
bool areSimilarFeatureVectors(TheaArray<double> const & f0, long nv0, TheaArray<double> const & f1, long nv1) { if (nv0 != nv1) return false; alwaysAssertM(f0.size() == f1.size(), "Feature vectors have different sizes"); // Compare histograms double diff = 0; for (array_size_t i = 0; i + 1 < f0.size(); ++i) diff += std::fabs(f0[i] - f1[i]); static double const HIST_THRESHOLD = 1e-2; if (diff > HIST_THRESHOLD * nv0) return false; // Compare scales static double const SCALE_THRESHOLD = 1e-2; if (!Math::fuzzyEq(f0.back(), f1.back(), SCALE_THRESHOLD * (std::fabs(f0.back()) + 1))) return false; return true; }
// Original comment: // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two // most signficant axes. Here we find the two longest axes and use them to triangulate. Inverse transforming them would // introduce more doubling point error and isn't worth it. // // SC says: // This doesn't work: the vertices can be collinear when projected onto the plane of the two longest axes of the bounding box. // Example (from real data): // // v[0] = (-13.7199, 4.45725, -8.00059) // v[1] = (-0.115787, 12.3116, -4.96109) // v[2] = (0.88992, 12.8922, -3.80342) // v[3] = (-0.115787, 12.3116, -2.64576) // v[4] = (-13.7199, 4.45725, 0.393742) // v[5] = (-13.7199, 4.45725, -0.856258) // v[6] = (-12.5335, 5.14221, -3.80342) // v[7] = (-13.7199, 4.45725, -6.75059) // // Instead, we will project onto the plane of the polygon. long Polygon3::triangulate(TheaArray<long> & tri_indices, Real epsilon) const { if (epsilon < 0) epsilon = Math::eps<Real>(); if (vertices.size() < 3) { tri_indices.clear(); } else if (vertices.size() == 3) { tri_indices.resize(3); tri_indices[0] = vertices[0].index; tri_indices[1] = vertices[1].index; tri_indices[2] = vertices[2].index; } else if (vertices.size() > 3) { tri_indices.clear(); array_size_t n = vertices.size(); proj_vertices.resize(n); Vector3 normal = computeNormal(); Vector3 axis0, axis1; normal.createOrthonormalBasis(axis0, axis1); Vector3 v0 = vertices[0].position; // a reference point for the plane of the polygon for (array_size_t i = 0; i < n; ++i) { Vector3 v = vertices[i].position - v0; proj_vertices[i] = Vector2(v.dot(axis0), v.dot(axis1)); } TheaArray<array_size_t> indices(n); bool flipped = false; if (projArea() > 0) { for (array_size_t v = 0; v < n; ++v) indices[v] = v; } else { for (array_size_t v = 0; v < n; ++v) indices[v] = (n - 1) - v; flipped = true; } array_size_t nv = n; array_size_t count = 2 * nv; for (array_size_t v = nv - 1; nv > 2; ) { if ((count--) <= 0) break; array_size_t u = v; if (nv <= u) u = 0; v = u + 1; if (nv <= v) v = 0; array_size_t w = v + 1; if (nv <= w) w = 0; if (snip(u, v, w, nv, indices, epsilon)) { array_size_t a = indices[u]; array_size_t b = indices[v]; array_size_t c = indices[w]; if (flipped) { tri_indices.push_back(vertices[c].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[a].index); } else { tri_indices.push_back(vertices[a].index); tri_indices.push_back(vertices[b].index); tri_indices.push_back(vertices[c].index); } array_size_t s = v, t = v + 1; for ( ; t < nv; ++s, ++t) indices[s] = indices[t]; nv--; count = 2 * nv; } } } return (long)tri_indices.size() / 3; }
static bool makeManifold(TheaArray<FaceT> & faces, long num_vertices, TheaArray<long> & vertex_map) { if (num_vertices <= 0 || faces.size() <= 0) return false; array_size_t nf = (array_size_t)faces.size(); array_size_t nv = (array_size_t)num_vertices; // Initialize every vertex to map to itself vertex_map.resize(nv); for (array_size_t i = 0; i < nv; ++i) vertex_map[i] = (long)i; // Associate each vertex with its incident faces TheaArray< TheaArray<array_size_t> > v2f(nv); for (array_size_t i = 0; i < nf; ++i) for (typename FaceT::const_iterator vi = faces[i].begin(); vi != faces[i].end(); ++vi) { debugAssertM(*vi >= 0 && (long)*vi < num_vertices, format("Manifold: Vertex index %ld out of range [0, %ld)", (long)*vi, num_vertices)); v2f[(array_size_t)*vi].push_back(i); } // Queue the set of vertices TheaStack<array_size_t> vertex_stack; for (long i = (long)nv - 1; i >= 0; --i) vertex_stack.push((array_size_t)i); // Split the faces at each vertex into manifold groups while (!vertex_stack.empty()) { array_size_t i = vertex_stack.top(); vertex_stack.pop(); // Set of edges (represented by their further vertices) incident at this vertex that have already been observed to be // shared by two faces TheaSet<array_size_t> shared_edges; // Group the faces into maximal edge-connected components typedef UnionFind<array_size_t> UnionFind; UnionFind uf(v2f[i].begin(), v2f[i].end()); for (array_size_t j = 0; j < v2f[i].size(); ++j) for (array_size_t k = j + 1; k < v2f[i].size(); ++k) if (shareEdgeAtVertex(faces[v2f[i][j]], faces[v2f[i][k]], i, shared_edges)) uf.merge((long)j, (long)k); // Retain only the faces edge-connected to the first one, assigning the rest to a copy of the vertex bool created_new_vertex = false; for (array_size_t j = 1; j < v2f[i].size(); ++j) { if (!uf.sameSet(0, j)) { array_size_t face = v2f[i][j]; array_size_t copy_index = v2f.size() - 1; if (!created_new_vertex) // create a copy of the vertex { copy_index++; v2f.push_back(TheaArray<array_size_t>()); vertex_map.push_back(vertex_map[i]); vertex_stack.push(copy_index); created_new_vertex = true; } v2f[copy_index].push_back(face); // Update the vertex index of this face to point to the copy for (typename FaceT::iterator vi = faces[face].begin(); vi != faces[face].end(); ++vi) if (*vi == (typename FaceT::value_type)i) { *vi = (typename FaceT::value_type)copy_index; break; } } } // If we made a copy of this vertex, we can get rid of faces reassigned to the copy if (created_new_vertex) { TheaArray<array_size_t> nbd; for (array_size_t j = 0; j < v2f[i].size(); ++j) if (uf.sameSet(0, j)) nbd.push_back(v2f[i][j]); v2f[i] = nbd; } } if ((long)vertex_map.size() > num_vertices) { THEA_DEBUG << "Manifold: Non-manifold vertices found and fixed"; return true; } else return false; }