#include "smoothing.h" #include "OpenMesh/Core/Mesh/SmartHandles.hh" #include #include "util.h" #include #include /* vα/--\ / ------\ vi / ---X / /---- / \ / /--- / -\ / /---- / \ vj --- | \ \-- / -\ \- / /--\ \-- / /------- \-+--- vβ */ /* Poids pour le laplacien uniforme, constant à 1. */ double uniform_weight(MyMesh &mesh, HalfedgeHandle vi_vj) { (void) mesh; (void) vi_vj; return 1; } /* Masse pour le laplacien uniforme, 1 / taille(N1(vi)). */ double uniform_mass(MyMesh &mesh, VertexHandle vi) { double count = 0; for (VertexHandle v : mesh.vv_range(vi)) { (void) v; count++; } return 1. / count; } /* Calcule le poids cotangent pour l'arête reliant vi à vj. */ double cotangent_weight(MyMesh &mesh, HalfedgeHandle vi_vj) { // Voir le graphique en haut de ce fichier. Point pj = mesh.point(mesh.to_vertex_handle(vi_vj)); HalfedgeHandle vj_vb = mesh.next_halfedge_handle(vi_vj); Point pb = mesh.point(mesh.to_vertex_handle(vj_vb)); HalfedgeHandle vj_vi = mesh.opposite_halfedge_handle(vi_vj); Point pi = mesh.point(mesh.to_vertex_handle(vj_vi)); HalfedgeHandle vi_va = mesh.next_halfedge_handle(vj_vi); Point pa = mesh.point(mesh.to_vertex_handle(vi_va)); double a = angle_between(pi, pa, pj); double b = angle_between(pj, pb, pi); return cotan(a) + cotan(b); } /* Calcule l'aire de chaque face et la stoque dans une propriété * "face_area" du maillage. */ void compute_face_areas(MyMesh &mesh) { auto area = OpenMesh::getOrMakeProperty (mesh, "face_area"); for (FaceHandle fh : mesh.faces()) { MyMesh::FaceVertexIter fv_it = mesh.fv_iter(fh); Point pi = mesh.point(*fv_it); Point pj = mesh.point(*++fv_it); Point pk = mesh.point(*++fv_it); area[fh] = triangle_area(pi, pj, pk); } } /* Calcule l'aire barycentrique incidente à vert. */ double barycentric_vertex_area(MyMesh &mesh, VertexHandle vert) { auto area = OpenMesh::getProperty(mesh, "face_area"); double sum = 0; for (FaceHandle fh : mesh.vf_range(vert)) { sum += area[fh]; } return sum / 3; } /* Fonction de masse pour le laplacien cotangent. */ double cotangent_mass(MyMesh &mesh, VertexHandle vi) { double area = barycentric_vertex_area(mesh, vi); return 1. / (2 * area); } /* Calcule la matrice laplacienne avec les fonctions de poids d'arête * et de masse de sommet spécifiées par l'utilisatrice. */ Eigen::SparseMatrix laplacian_matrix(MyMesh &mesh, double (*edge_weight)(MyMesh &, HalfedgeHandle), double (*vertex_mass)(MyMesh &, VertexHandle)) { compute_face_areas(mesh); size_t n_verts = mesh.n_vertices(); Eigen::SparseMatrix weight(n_verts, n_verts); Eigen::SparseMatrix mass(n_verts, n_verts); for (VertexHandle vi : mesh.vertices()) { if (mesh.is_boundary(vi)) continue; double sum = 0; for (HalfedgeHandle vi_vj : mesh.voh_range(vi)) { // For each outgoing halfedge VertexHandle vj = mesh.to_vertex_handle(vi_vj); double halfedge_weight = edge_weight(mesh, vi_vj); weight.insert(vi.idx(), vj.idx()) = halfedge_weight; sum -= halfedge_weight; } // weight(i, i) = -Σ(j in N1(i)) weight(i, j) weight.insert(vi.idx(), vi.idx()) = sum; mass.insert(vi.idx(), vi.idx()) = vertex_mass(mesh, vi); } return mass * weight; } /* Adoucissement du maillage. */ void smooth(MyMesh &mesh, SmoothingMethod method, double cotan_factor) { // Calcul de la matrice laplacienne en fonction de la méthode choisie. double factor; Eigen::SparseMatrix laplacian; if (method == SmoothingMethod::COTANGENT) { factor = cotan_factor; laplacian = laplacian_matrix(mesh, cotangent_weight, cotangent_mass); } else { factor = 1; laplacian = laplacian_matrix(mesh, uniform_weight, uniform_mass); } // laplacian = laplacian * laplacian; // Mise au carré. // Transfert des coordonées de chaque point dans une matrice. size_t n_verts = mesh.n_vertices(); Eigen::VectorX X(n_verts), Y(n_verts), Z(n_verts); for (VertexHandle vert : mesh.vertices()) { if (mesh.is_boundary(vert)) continue; size_t id = vert.idx(); Point p = mesh.point(vert); X(id) = p[0]; Y(id) = p[1]; Z(id) = p[2]; } // Application du laplacien pour calculer les décalages. X = laplacian * X; Y = laplacian * Y; Z = laplacian * Z; // Application des décalages. for (VertexHandle vert : mesh.vertices()) { if (mesh.is_boundary(vert)) continue; size_t id = vert.idx(); Point offset {X(id), Y(id), Z(id)}; mesh.set_point(vert, mesh.point(vert) + offset * factor); } }