/** * @brief Refine the mesh by quadrisection. * * Note that this function will delete all stored data on edges and faces. But * this can be easily fixed. * * @param mesh The mesh */ std::unique_ptr<SurfaceMesh> refineMesh(const SurfaceMesh &mesh){ std::unique_ptr<SurfaceMesh> refinedMesh(new SurfaceMesh); // Copy over vertices to refinedMesh for (auto vertex : mesh.get_level_id<1>()){ auto key = mesh.get_name(vertex); refinedMesh->insert(key, *vertex); } // Split edges and generate a map of names before to after std::map<std::array<int,2>, int> edgeMap; for(auto edge : mesh.get_level_id<2>()){ auto edgeName = mesh.get_name(edge); auto v1 = *mesh.get_simplex_up({edgeName[0]}); auto v2 = *mesh.get_simplex_up({edgeName[1]}); auto newVertex = refinedMesh->add_vertex(Vertex(0.5*(v1+v2))); edgeMap.emplace(std::make_pair(edgeName, newVertex)); } // Connect edges and face and copy data for(auto face : mesh.get_level_id<3>()){ auto name = mesh.get_name(face); int a, b, c; // Skip checking if found auto it = edgeMap.find({name[0],name[1]}); a = it->second; it = edgeMap.find({name[1],name[2]}); b = it->second; it = edgeMap.find({name[0],name[2]}); c = it->second; refinedMesh->insert({name[0], a}); refinedMesh->insert({a, name[1]}); refinedMesh->insert({name[1], b}); refinedMesh->insert({b, name[2]}); refinedMesh->insert({name[0], c}); refinedMesh->insert({c, name[2]}); refinedMesh->insert({a,b,c}); refinedMesh->insert({name[0],a,c}, *face); refinedMesh->insert({name[1],a,b}, *face); refinedMesh->insert({name[2],b,c}, *face); } return refinedMesh; }
tensor<double, 3, 2> getTangent(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { auto cover = mesh.get_name(faceID); auto vertexID = mesh.get_simplex_up({cover[0]}); std::set<SurfaceMesh::KeyType> next(std::begin(cover)+1, std::end(cover)); return surfacemesh_detail::getTangentF(mesh, (*vertexID).position, vertexID, next); }
Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { Vector norm; auto name = mesh.get_name(faceID); // Get the three vertices auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); if ((*faceID).orientation == 1) { norm = cross(c-b, a-b); } else if ((*faceID).orientation == -1) { norm = cross(a-b, c-b); } else { // std::cerr << "ERROR(getNormal): Orientation undefined, cannot compute " // << "normal. Did you call compute_orientation()?" << std::endl; throw std::runtime_error("ERROR(getNormal): Orientation undefined, cannot compute normal. Did you call compute_orientation()?"); } return norm; }
double getArea(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { auto name = mesh.get_name(faceID); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); return getArea(a, b, c); }
void print(const SurfaceMesh &mesh) { std::cout << "Level: 1" << std::endl; for (auto node : mesh.get_level_id<1>()) { std::cout << " " << *node << std::endl; } std::cout << "Level: 2" << std::endl; for (auto node : mesh.get_level_id<2>()) { auto name = mesh.get_name(node); std::cout << " " << casc::to_string(name) << std::endl; } std::cout << "Level: 3" << std::endl; for (auto node : mesh.get_level_id<3>()) { auto name = mesh.get_name(node); std::cout << " " << casc::to_string(name) << std::endl; } }
void coarse(SurfaceMesh &mesh, double coarseRate, double flatRate, double denseWeight){ // TODO: Check if all polygons are closed (0) std::cout << "Before coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; // Compute the average edge length double avgLen = 0; if (denseWeight > 0){ avgLen = surfacemesh_detail::getMeanEdgeLength(mesh); } double sparsenessRatio = 1; double flatnessRatio = 1; // Construct list of vertices to decimate std::vector<SurfaceMesh::SimplexID<1> > toRemove; for(auto vertexID : mesh.get_level_id<1>()){ // Sparseness as coarsening criteria if(denseWeight > 0){ // Get max length of edges. auto edges = mesh.up(vertexID); double maxLen = 0; for(auto edgeID : edges){ auto name = mesh.get_name(edgeID); auto v = *mesh.get_simplex_down(edgeID, name[0]) - *mesh.get_simplex_down(edgeID, name[1]); double tmpLen = std::sqrt(v|v); if(tmpLen > maxLen) maxLen = tmpLen; } sparsenessRatio = std::pow(maxLen/avgLen, denseWeight); } // Curvature as coarsening criteria if(flatRate > 0){ auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, RINGS); auto eigenvalues = surfacemesh_detail::getEigenvalues(lst).eigenvalues(); // The closer this ratio is to 0 the flatter the local region. flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], flatRate); } // Add vertex to delete list if(sparsenessRatio * flatnessRatio < coarseRate){ toRemove.push_back(vertexID); } } std::cout << toRemove.size() << " vertices are marked to be removed." << std::endl; for(auto vertexID : toRemove){ surfacemesh_detail::decimateVertex(mesh, vertexID); } std::cout << "After coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; }
/** * http://research.microsoft.com/en-us/um/people/chazhang/publications/icip01_ChaZhang.pdf * http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf */ double getVolume(const SurfaceMesh &mesh) { bool orientError = false; double volume = 0; for (auto faceID : mesh.get_level_id<3>()) { auto name = mesh.get_name(faceID); auto a = (*mesh.get_simplex_up({name[0]})).position; auto b = (*mesh.get_simplex_up({name[1]})).position; auto c = (*mesh.get_simplex_up({name[2]})).position; Vector norm; double tmp; if ((*faceID).orientation == 1) { // a->b->c norm = cross(b, c); tmp = dot(a, norm); } else if ((*faceID).orientation == -1) { // c->b->a norm = cross(b, a); tmp = dot(c, norm); } else { orientError = true; } // * Probably less efficient way #1 // tmp = dot(a, getNormal(mesh, faceID)); // * Less efficient way #2 // norm = getNormalFromTangent(getTangent(mesh, faceID)); // auto wedge = a^b^c; // tmp = std::sqrt(wedge|wedge); // auto sgn = dot((a+b+c)/3, norm); // if(sgn <= 0) { // tmp = -1*tmp; // } volume += tmp; } if(orientError){ std::cerr << "ERROR getVolume(): Orientation undefined for one or more " << "simplices. Did you call compute_orientation()?" << std::endl; } return volume/6; }
void coarseIT(SurfaceMesh &mesh, double coarseRate, double flatRate, double denseWeight){ std::cout << "Before coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; // Compute the average edge length double avgLen = 0; if (denseWeight > 0){ avgLen = surfacemesh_detail::getMeanEdgeLength(mesh); } double sparsenessRatio = 1; double flatnessRatio = 1; // Backup vertices so we can modify in place auto range = mesh.get_level_id<1>(); const std::vector<SurfaceMesh::SimplexID<1> > Vertices(range.begin(), range.end()); for(auto vertexID : Vertices) { // Sparseness as coarsening criteria if(denseWeight > 0){ // Get max length of edges. auto edges = mesh.up(vertexID); double maxLen = 0; for(auto edgeID : edges){ auto name = mesh.get_name(edgeID); auto v = *mesh.get_simplex_down(edgeID, name[0]) - *mesh.get_simplex_down(edgeID, name[1]); double tmpLen = std::sqrt(v|v); if(tmpLen > maxLen) maxLen = tmpLen; } sparsenessRatio = std::pow(maxLen/avgLen, denseWeight); } // Curvature as coarsening criteria if(flatRate > 0){ auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, RINGS); auto eigenvalues = surfacemesh_detail::getEigenvalues(lst).eigenvalues(); // The closer this ratio is to 0 the flatter the local region. flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], flatRate); } // Check if we should delete if(sparsenessRatio * flatnessRatio < coarseRate){ surfacemesh_detail::decimateVertex(mesh, vertexID); } } std::cout << "After coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; }
std::tuple<double, double, int, int> getMinMaxAngles( const SurfaceMesh &mesh, int maxMinAngle, int minMaxAngle) { double minAngle = 360; double maxAngle = 0; int small = 0; int large = 0; // for each triangle for(auto nid : mesh.get_level_id<3>()){ auto name = mesh.get_name(nid); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); std::array<double, 3> angles; try{ angles[0] = angle(a,b,c); angles[1] = angle(b,a,c); angles[2] = angle(a,c,b); } catch (std::runtime_error& e){ throw std::runtime_error("ERROR(getMinMaxAngles): Cannot compute angles of face with zero area."); } for(double angle : angles){ if (angle < minAngle){ minAngle = angle; } if (angle > maxAngle){ maxAngle = angle; } if (angle < maxMinAngle){ ++small; } if (angle > minMaxAngle){ ++large; } } } return std::make_tuple(minAngle, maxAngle, small, large); }
void printQualityInfo(const std::string &filename, const SurfaceMesh &mesh){ std::stringstream anglefilename; anglefilename << filename << ".angle"; std::ofstream angleOut(anglefilename.str()); if(!angleOut.is_open()) { std::stringstream ss; ss << "Couldn't open file " << filename << ".angle"; throw std::runtime_error(ss.str()); } std::stringstream areafilename; areafilename << filename << ".area"; std::ofstream areaOut(areafilename.str()); if(!areaOut.is_open()) { std::stringstream ss; ss << "Couldn't open file " << filename << ".area"; throw std::runtime_error(ss.str()); } for(auto faceID : mesh.get_level_id<3>()){ auto name = mesh.get_name(faceID); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); areaOut << getArea(a,b,c) << "\n"; // Print angles in degrees angleOut << angle(a,b,c) << "\n" << angle(b,a,c) << "\n" << angle(a,c,b) << "\n"; } areaOut.close(); angleOut.close(); }
void generateHistogram(const SurfaceMesh &mesh) { // compute angle distribution std::array<double, 18> histogram; histogram.fill(0); for (auto face : mesh.get_level_id<3>()) { auto vertexIDs = mesh.get_name(face); // Unpack the ID's for convenience Vertex a = *mesh.get_simplex_up<1>({vertexIDs[0]}); Vertex b = *mesh.get_simplex_up<1>({vertexIDs[1]}); Vertex c = *mesh.get_simplex_up<1>({vertexIDs[2]}); auto binAngle = [&](double angle) -> int{ return std::floor(angle/10); }; histogram[binAngle(angle(a, b, c))]++; histogram[binAngle(angle(b, a, c))]++; histogram[binAngle(angle(c, a, b))]++; } int factor = mesh.size<3>()*3; std::for_each(histogram.begin(), histogram.end(), [&factor](double &n){ n = 100.0*n/factor; }); std::cout << "Angle Distribution:" << std::endl; for (int x = 0; x < 18; x++) std::cout << x*10 << "-" << (x+1)*10 << ": " << std::setprecision(2) << std::fixed << histogram[x] << std::endl; std::cout << std::endl << std::endl; // compute the edge length distribution std::cout << "Edge Length Distribution:" << std::endl; std::vector<double> lengths; for (auto edge : mesh.get_level_id<2>()) { auto vertexIDs = mesh.down(edge); auto t1 = *vertexIDs.cbegin(); auto t2 = *(++vertexIDs.cbegin()); auto v1 = *t1; auto v2 = *t2; double len = magnitude(v2-v1); lengths.push_back(len); } std::sort(lengths.begin(), lengths.end()); std::array<double, 20> histogramLength; histogramLength.fill(0); double interval = (lengths.back() - lengths.front())/20; double low = lengths.front(); if (interval <= 0.0000001) // floating point roundoff prevention { std::cout << lengths.front() << ": " << 100 << std::endl << std::endl; } else { for (auto length : lengths) { histogramLength[std::floor((length-low)/interval)]++; } factor = mesh.size<2>(); std::for_each(histogramLength.begin(), histogramLength.end(), [&factor](double &n){ n = 100.0*n/factor; }); for (int x = 0; x < 20; x++) std::cout << x*interval << "-" << (x+1)*interval << ": " << std::setprecision(2) << std::fixed << histogramLength[x] << std::endl; std::cout << std::endl << std::endl; } // Compute the valence distribution std::array<double, 20> histogramValence; histogramValence.fill(0); for (auto vertexID : mesh.get_level_id<1>()) { // TODO bounds checking here... histogramValence[getValence(mesh, vertexID)]++; } factor = mesh.size<1>(); // std::for_each(histogramValence.begin(), histogramValence.end(), // [&factor](double& n){ // n = 100.0*n/factor;}); std::cout << "Valence distribution:" << std::endl; for (int x = 0; x < 20; x++) std::cout << x << ": " << histogramValence[x] << std::endl; std::cout << std::endl << std::endl; }