// Copyright (c) 2007 INRIA Sophia-Antipolis (France), INRIA Lorraine LORIA. // All rights reserved. // // This file is part of CGAL (www.cgal.org). // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // SPDX-License-Identifier: GPL-3.0+ // // Author(s) : Marc Pouget and Frédéric Cazals #ifndef CGAL_RIDGE_3_H_ #define CGAL_RIDGE_3_H_ #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { enum Ridge_interrogation_type {MAX_RIDGE, MIN_RIDGE, CREST_RIDGE}; enum Ridge_type {NO_RIDGE=0, MAX_ELLIPTIC_RIDGE, MAX_HYPERBOLIC_RIDGE, MAX_CREST_RIDGE, MIN_ELLIPTIC_RIDGE, MIN_HYPERBOLIC_RIDGE, MIN_CREST_RIDGE}; //are ridges tagged as elliptic or hyperbolic using 3rd or 4th order //differential quantitities? //with Ridge_order_3 P1 and P2 are not used and the sharpness is not defined. enum Ridge_order {Ridge_order_3 = 3, Ridge_order_4 = 4}; //--------------------------------------------------------------------------- //Ridge_line : a connected sequence of edges of a //TriangularPolyhedralSurface crossed by a //ridge (with a barycentric coordinate to compute the crossing point), //with a Ridge_type and weights : strength and sharpness. Note //sharpness is only available (more precisely only meaningful) //if the Ridge_approximation has //been computed with the Ridge_order Ridge_order_4. //(else, if it is computed with Ridge_order_3 it keeps its initial //value 0) //-------------------------------------------------------------------------- template < class TriangleMesh > class Ridge_line { typedef typename boost::property_map::type VPM; typedef typename boost::property_traits::value_type Point_3; typedef typename Kernel_traits::Kernel Kernel; public: typedef typename Kernel::FT FT; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef std::pair< halfedge_descriptor, FT> Ridge_halfedge; typedef Ridge_halfedge ridge_halfhedge; //kept for backward compatibility Ridge_type line_type() const {return m_line_type;} Ridge_type& line_type() {return m_line_type;} const FT strength() const {return m_strength;} FT& strength() {return m_strength;} const FT sharpness() const {return m_sharpness;} FT& sharpness() {return m_sharpness;} const std::list* line() const { return &m_line;} std::list* line() { return &m_line;} //constructor Ridge_line(const TriangleMesh& P); /* The output is : line_type, strength, sharpness, list of points of the polyline. An insert operator << is also available. */ template void dump_4ogl(std::ostream& out_stream, VertexPointMap vpm) const ; template void dump_verbose(std::ostream& out_stream, VertexPointMap vpm) const ; protected: const TriangleMesh& P; //one of MAX_ELLIPTIC_RIDGE, MAX_HYPERBOLIC_RIDGE, MAX_CREST_RIDGE, //MIN_ELLIPTIC_RIDGE, MIN_HYPERBOLIC_RIDGE or MIN_CREST_RIDGE Ridge_type m_line_type; std::list m_line; FT m_strength;// = integral of ppal curvature along the line FT m_sharpness;// = (integral of second derivative of curvature // along the line) multiplied by the squared of // the size of the model // (which is the radius of the smallest enclosing // ball) }; //-------------------------------------------------------------------------- // IMPLEMENTATION OF Ridge_line members //-------------------------------------------------------------------------- //constructor template < class TriangleMesh > Ridge_line:: Ridge_line(const TriangleMesh& P) : P(P), m_strength(0.), m_sharpness(0.) {} template < class TriangleMesh > template void Ridge_line:: dump_4ogl(std::ostream& out_stream, VertexPointMap vpm) const { out_stream << line_type() << " " << strength() << " " << sharpness() << " "; typedef typename boost::property_traits::value_type Point_3; typename std::list::const_iterator iter = line()->begin(), ite = line()->end(); for (;iter!=ite;iter++){ //he: p->q, r is the crossing point Point_3 p = get(vpm, target(opposite(iter->first,P),P)), q = get(vpm,target(iter->first,P)); Point_3 r = CGAL::barycenter(p, iter->second, q); out_stream << " " << r ; } out_stream << std::endl; } //verbose output template < class TriangleMesh > template void Ridge_line:: dump_verbose(std::ostream& out_stream, VertexPointMap vpm) const { typedef typename boost::property_traits::value_type Point_3; out_stream << "Line type is : " << line_type() << std::endl << "Strength is : " << strength() << std::endl << "Sharpness is : " << sharpness() << std::endl << "Polyline point coordinates are : " << std::endl; typename std::list::const_iterator iter = line()->begin(), ite = line()->end(); for (;iter!=ite;iter++){ //he: p->q, r is the crossing point Point_3 p = get(vpm, target(opposite(iter->first,P),P)), q = get(vpm,target(iter->first,P)); Point_3 r = CGAL::barycenter(p, iter->second, q); out_stream << r << std::endl; } } template std::ostream& operator<<(std::ostream& out_stream, const Ridge_line& ridge_line) { ridge_line.dump_verbose(out_stream); return out_stream; } //--------------------------------------------------------------------------- //Ridge_approximation //-------------------------------------------------------------------------- template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > class Ridge_approximation { typedef typename boost::property_map::const_type VPM; typedef typename boost::property_traits::value_type Point_3; typedef typename Kernel_traits::Kernel Kernel; public: typedef typename Kernel::FT FT; typedef typename Kernel::Vector_3 Vector_3; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::graph_traits::face_iterator face_iterator; //requirements for the templates TriangleMesh and VertexFTMap or VertexVectorMap CGAL_static_assertion((boost::is_same::value)); CGAL_static_assertion((boost::is_same::value)); CGAL_static_assertion((boost::is_same::value)); CGAL_static_assertion((boost::is_same::value)); typedef std::pair< halfedge_descriptor, FT> Ridge_halfedge; typedef Ridge_halfedge Ridge_halfhedge; // kept for backward compatibility typedef CGAL::Ridge_line Ridge_line; Ridge_approximation(const TriangleMesh &P, const VertexFTMap& vertex2k1_pm, const VertexFTMap& vertex2k2_pm, const VertexFTMap& vertex2b0_pm, const VertexFTMap& vertex2b3_pm, const VertexVectorMap& vertex2d1_pm, const VertexVectorMap& vertex2d2_pm, const VertexFTMap& vertex2P1_pm, const VertexFTMap& vertex2P2_pm); template OutputIterator compute_max_ridges(OutputIterator it, Ridge_order ord = Ridge_order_3); template OutputIterator compute_min_ridges(OutputIterator it, Ridge_order ord = Ridge_order_3); template OutputIterator compute_crest_ridges(OutputIterator it, Ridge_order ord = Ridge_order_3); //Find MAX_RIDGE, MIN_RIDGE or CREST_RIDGE ridges iterate on P facets, //find a non-visited, regular (i.e. if there is a coherent //orientation of ppal dir at the facet vertices), 2Xing triangle, //follow non-visited, regular, 2Xing triangles in both sens to //create a Ridge line. Each time an edge is added the strength and //sharpness(if Ridge_order_4) are updated. template OutputIterator compute_ridges(Ridge_interrogation_type r_type, OutputIterator ridge_lines_it, Ridge_order ord = Ridge_order_3); protected: const TriangleMesh& P; FT squared_model_size;//squared radius of the smallest enclosing sphere of the TriangleMesh //used to make the sharpness scale independant and iso indep Ridge_order tag_order; typedef std::map Facet2bool_map_type; Facet2bool_map_type is_visited_map; //Property maps const VertexFTMap &k1, &k2, &b0, &b3, &P1, &P2; const VertexVectorMap &d1, &d2; VPM vpm; //is a facet crossed by a BLUE, RED or CREST_RIDGE ridge? if so, return //the crossed edges and more precise type from MAX_ELLIPTIC_RIDGE, //MAX_HYPERBOLIC_RIDGE, MAX_CREST_RIDGE, MIN_ELLIPTIC_RIDGE, //MIN_HYPERBOLIC_RIDGE, MIN_CREST_RIDGE or NO_RIDGE Ridge_type facet_ridge_type(const face_descriptor f, halfedge_descriptor& he1, halfedge_descriptor& he2, Ridge_interrogation_type r_type); //is an edge crossed by a BLUE/RED ridge? (color is MAX_RIDGE or //MIN_RIDGE ). As we only test edges of regular triangles, the ppal //direction at endpoints d_p and d_q cannot be orthogonal. If both //extremalities vanish, we consider no crossing occurs. If only one //of them vanishes, we consider it as an positive infinitesimal and //apply the general rule. The general rule is that for both //non-vanishing extremalities, a crossing occurs if their sign //differ; Assuming the accute rule to orient the ppal directions, //there is a crossing iff d_p.d_q * b_p*b_q < 0 void xing_on_edge(const halfedge_descriptor he, bool& is_crossed, Ridge_interrogation_type color); //given a ridge segment of a given color, in a triangle crossing he1 //(v_p1 -> v_q1) and he2 (v_p2 -> v_q2) return true if it is //elliptic, false if it is hyperbolic. bool tag_as_elliptic_hyperbolic(const Ridge_interrogation_type color, const halfedge_descriptor he1, const halfedge_descriptor he2); //for the computation with tag_order == 3 only //for a ridge segment [r1,r2] in a triangle (v1,v2,v3), let r = r2 - //r1 and normalize, the projection of a point p on the line (r1,r2) //is pp=r1+tr, with t=(p-r1)*r then the vector v starting at p is //pointing to the ridge line (r1,r2) if (pp-p)*v >0. Return the sign //of b, for a ppal direction pointing to the ridge segment, //appearing at least at two vertices of the facet. // // for color = MAX_RIDGE, sign = 1 if MAX_ELLIPTIC_RIDGE, -1 if // MAX_HYPERBOLIC_RIDGE // // for color = MIN_RIDGE, sign = -1 if MIN_ELLIPTIC_RIDGE, 1 if // MIN_HYPERBOLIC_RIDGE int b_sign_pointing_to_ridge(const vertex_descriptor v1, const vertex_descriptor v2, const vertex_descriptor v3, const Vector_3 r1, const Vector_3 r2, const Ridge_interrogation_type color); //a ridge line begins with a segment in a triangle given by the 2 he //crossed void init_ridge_line(Ridge_line* ridge_line, const halfedge_descriptor h1, const halfedge_descriptor h2, const Ridge_type r_type); //When the line is extended with a he, the bary coord of the //crossing point is computed, the pair (he,coord) is added and the //weights are updated void addback(Ridge_line* ridge_line, const halfedge_descriptor he, const Ridge_type r_type); void addfront(Ridge_line* ridge_line, const halfedge_descriptor he, const Ridge_type r_type); //compute the barycentric coordinate of the xing point (blue or red) //for he: p->q (wrt the extremality values b0/3). coord is st //xing_point = coord*p + (1-coord)*q FT bary_coord(const halfedge_descriptor he, const Ridge_type r_type); }; // IMPLEMENTATION OF Ridge_approximation members ///////////////////////////////////////////////////////////////////////////// //constructor template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: Ridge_approximation(const TriangleMesh &p, const VertexFTMap& vertex2k1_pm, const VertexFTMap& vertex2k2_pm, const VertexFTMap& vertex2b0_pm, const VertexFTMap& vertex2b3_pm, const VertexVectorMap& vertex2d1_pm, const VertexVectorMap& vertex2d2_pm, const VertexFTMap& vertex2P1_pm, const VertexFTMap& vertex2P2_pm) : P(p), k1(vertex2k1_pm), k2(vertex2k2_pm), b0(vertex2b0_pm), b3(vertex2b3_pm), P1(vertex2P1_pm), P2(vertex2P2_pm), d1(vertex2d1_pm), d2(vertex2d2_pm), vpm(get(CGAL::vertex_point,p)) { //init the is_visited_map and check that the mesh is a triangular one. face_iterator itb,ite; boost::tie(itb,ite) = faces(P); for(;itb!=ite;itb++) { is_visited_map[*itb] = false; } CGAL_precondition( is_triangle_mesh(p) ); std::vector points; BOOST_FOREACH(vertex_descriptor v, vertices(p)){ points.push_back(get(vpm,v)); } Bbox_3 bb = bbox_3(points.begin(), points.end()); double width = bb.xmax() - bb.xmin(); double yw = bb.ymax() - bb.ymin(); width = (std::max)(width,yw); double zw = bb.zmax() - bb.zmin(); width = (std::max)(width,zw); squared_model_size = (width*width)/4.0 ; tag_order = Ridge_order_3; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > template OutputIterator Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: compute_max_ridges(OutputIterator it, Ridge_order ord) { compute_ridges(MAX_RIDGE, it, ord); return it; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > template OutputIterator Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: compute_min_ridges(OutputIterator it, Ridge_order ord) { compute_ridges(MIN_RIDGE, it, ord); return it; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > template OutputIterator Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: compute_crest_ridges(OutputIterator it, Ridge_order ord) { compute_ridges(CREST_RIDGE, it, ord); return it; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > template OutputIterator Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: compute_ridges(Ridge_interrogation_type r_type, OutputIterator ridge_lines_it, Ridge_order ord) { tag_order = ord; //reinit the is_visited_map face_iterator itb,ite; boost::tie(itb,ite) = faces(P); for(;itb!=ite;itb++) is_visited_map[*itb] = false; boost::tie(itb,ite) = faces(P); for(;itb!=ite;itb++) { face_descriptor f = *itb; if (is_visited_map.find(f)->second) continue; is_visited_map.find(f)->second = true; halfedge_descriptor h1, h2, curhe1, curhe2, curhe; //h1 h2 are the hedges crossed if any, r_type should be //MAX_RIDGE, MIN_RIDGE or CREST_RIDGE ; cur_ridge_type should be //MAX_ELLIPTIC_RIDGE, MAX_HYPERBOLIC_RIDGE, MAX_CREST_RIDGE, //MIN_ELLIPTIC_RIDGE, MIN_HYPERBOLIC_RIDGE, MIN_CREST_RIDGE or NO_RIDGE Ridge_type cur_ridge_type = facet_ridge_type(f,h1,h2,r_type); if ( cur_ridge_type == NO_RIDGE ) continue; //a ridge_line is begining and stored Ridge_line* cur_ridge_line = new Ridge_line(P); init_ridge_line(cur_ridge_line, h1, h2, cur_ridge_type); *ridge_lines_it++ = cur_ridge_line; //next triangle adjacent to h1 (push_front) if ( ! is_border_edge(h1,P) ) { f = face(opposite(h1,P),P); curhe = h1; while (cur_ridge_type == facet_ridge_type(f,curhe1,curhe2,r_type)) { //follow the ridge from curhe if (is_visited_map.find(f)->second) break; is_visited_map.find(f)->second = true; if (opposite(curhe,P) == curhe1) curhe = curhe2; else curhe = curhe1;//curhe stays at the ridge extremity addfront(cur_ridge_line, curhe, cur_ridge_type); if ( ! is_border_edge(curhe,P) ) f = face(opposite(curhe,P),P); else break; } //exit from the while if //1. border or already visited (this is a ridge loop) //2. not same type, then do not set visisted cause a MAX_ELLIPTIC_RIDGE // follows a MAX_HYPERBOLIC_RIDGE } //next triangle adjacent to h2 (push_back) if ( ! is_border_edge(h2,P) ) { f = face(opposite(h2,P),P); curhe = h2; while (cur_ridge_type == facet_ridge_type(f,curhe1,curhe2,r_type)) { //follow the ridge from curhe if (is_visited_map.find(f)->second) break; is_visited_map.find(f)->second = true; if (opposite(curhe,P) == curhe1) curhe = curhe2; else curhe = curhe1; addback(cur_ridge_line, curhe, cur_ridge_type); if ( ! is_border_edge(curhe,P) ) f = face(opposite(curhe,P),P); else break; } } } return ridge_lines_it; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > Ridge_type Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: facet_ridge_type(const face_descriptor f, halfedge_descriptor& he1, halfedge_descriptor& he2, Ridge_interrogation_type r_type) { //polyhedral data //we have v1--h1-->v2--h2-->v3--h3-->v1 const halfedge_descriptor h1 = halfedge(f,P); const vertex_descriptor v2 = target(h1,P); const halfedge_descriptor h2 = next(h1,P); const vertex_descriptor v3 = target(h2,P); const halfedge_descriptor h3 = next(h2,P); const vertex_descriptor v1 = target(h3,P); //check for regular facet //i.e. if there is a coherent orientation of ppal dir at the facet vertices if ( get(d1,v1)*get(d1,v2) * get(d1,v1)*get(d1,v3) * get(d1,v2)*get(d1,v3) < 0 ) return NO_RIDGE; //determine potential crest color //MAX_CREST_RIDGE if |sum(k1)|>|sum(k2)| sum over facet vertices vi //MIN_CREST_RIDGE if |sum(k1)|<|sum(k2)| Ridge_type crest_color = NO_RIDGE; if (r_type == CREST_RIDGE) { if ( CGAL::abs(get(k1,v1)+get(k1,v2)+get(k1,v3)) > CGAL::abs(get(k2, v1)+get(k2,v2)+get(k2,v3)) ) crest_color = MAX_CREST_RIDGE; if ( CGAL::abs(get(k1,v1)+get(k1,v2)+get(k1,v3)) < CGAL::abs(get(k2,v1)+get(k2,v2)+get(k2,v3)) ) crest_color = MIN_CREST_RIDGE; if ( CGAL::abs(get(k1,v1)+get(k1,v2)+get(k1,v3)) == CGAL::abs(get(k2,v1)+get(k2,v2)+get(k2,v3)) ) return NO_RIDGE; } //compute Xing on the 3 edges bool h1_is_crossed = false, h2_is_crossed = false, h3_is_crossed = false; if ( r_type == MAX_RIDGE || crest_color == MAX_CREST_RIDGE ) { xing_on_edge(h1, h1_is_crossed, MAX_RIDGE); xing_on_edge(h2, h2_is_crossed, MAX_RIDGE); xing_on_edge(h3, h3_is_crossed, MAX_RIDGE); } if ( r_type == MIN_RIDGE || crest_color == MIN_CREST_RIDGE ) { xing_on_edge(h1, h1_is_crossed, MIN_RIDGE); xing_on_edge(h2, h2_is_crossed, MIN_RIDGE); xing_on_edge(h3, h3_is_crossed, MIN_RIDGE); } //there are either 0 or 2 crossed edges if ( !h1_is_crossed && !h2_is_crossed && !h3_is_crossed ) return NO_RIDGE; if (h1_is_crossed && h2_is_crossed && !h3_is_crossed) { he1 = h1; he2 = h2; } if (h1_is_crossed && !h2_is_crossed && h3_is_crossed) { he1 = h1; he2 = h3; } if (!h1_is_crossed && h2_is_crossed && h3_is_crossed) { he1 = h2; he2 = h3; } //check there is no other case (just one edge crossed) CGAL_postcondition ( !( (h1_is_crossed && !h2_is_crossed && !h3_is_crossed) || (!h1_is_crossed && h2_is_crossed && !h3_is_crossed) || (!h1_is_crossed && !h2_is_crossed && h3_is_crossed)) ); //There is a ridge segment in the triangle, determine its type elliptic/hyperbolic bool is_elliptic; if ( r_type == MAX_RIDGE || crest_color == MAX_CREST_RIDGE ) is_elliptic = tag_as_elliptic_hyperbolic(MAX_RIDGE, he1, he2); else is_elliptic = tag_as_elliptic_hyperbolic(MIN_RIDGE, he1, he2); if (r_type == MAX_RIDGE) {if (is_elliptic) return MAX_ELLIPTIC_RIDGE; else return MAX_HYPERBOLIC_RIDGE; } if (crest_color == MAX_CREST_RIDGE && is_elliptic) return MAX_CREST_RIDGE; if (r_type == MIN_RIDGE) {if (is_elliptic) return MIN_ELLIPTIC_RIDGE; else return MIN_HYPERBOLIC_RIDGE; } if (crest_color == MIN_CREST_RIDGE && is_elliptic) return MIN_CREST_RIDGE; return NO_RIDGE; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > void Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: xing_on_edge(const halfedge_descriptor he, bool& is_crossed, Ridge_interrogation_type color) { is_crossed = false; FT sign = 0; FT b_p, b_q; // extremalities at p and q for he: p->q Vector_3 d_p = get(d1,target(opposite(he,P),P)), d_q = get(d1,target(he,P)); //ppal dir if ( color == MAX_RIDGE ) { b_p = get(b0,target(opposite(he,P),P)); b_q = get(b0,target(he,P)); } else { b_p = get(b3,target(opposite(he,P),P)); b_q = get(b3,target(he,P)); } if ( b_p == 0 && b_q == 0 ) return; if ( b_p == 0 && b_q !=0 ) sign = d_p*d_q * b_q; if ( b_p != 0 && b_q ==0 ) sign = d_p*d_q * b_p; if ( b_p != 0 && b_q !=0 ) sign = d_p*d_q * b_p * b_q; if ( sign < 0 ) is_crossed = true; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > bool Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: tag_as_elliptic_hyperbolic(const Ridge_interrogation_type color, const halfedge_descriptor he1, const halfedge_descriptor he2) { const vertex_descriptor v_p1 = target(opposite(he1,P),P), v_q1 = target(he1,P), v_p2 = target(opposite(he2,P),P), v_q2 = target(he2,P); // hei: pi->qi FT coord1, coord2; if (color == MAX_RIDGE) { coord1 = CGAL::abs(get(b0,v_q1)) / ( CGAL::abs(get(b0,v_p1)) + CGAL::abs(get(b0,v_q1)) ); coord2 = CGAL::abs(get(b0,v_q2)) / ( CGAL::abs(get(b0,v_p2)) + CGAL::abs(get(b0,v_q2)) ); } else { coord1 = CGAL::abs(get(b3,v_q1)) / ( CGAL::abs(get(b3,v_p1)) + CGAL::abs(get(b3,v_q1)) ); coord2 = CGAL::abs(get(b3,v_q2)) / ( CGAL::abs(get(b3,v_p2)) + CGAL::abs(get(b3,v_q2)) ); } if ( tag_order == Ridge_order_3 ) { Vector_3 r1 = CGAL::barycenter(get(vpm, v_p1), coord1, get(vpm, v_q1)) - ORIGIN, r2 = CGAL::barycenter(get(vpm,v_p2), coord2, get(vpm,v_q2)) - ORIGIN; //identify the 3 different vertices v_p1, v_q1 and v3 = v_p2 or v_q2 vertex_descriptor v3; if (v_p2 == v_p1 || v_p2 == v_q1) v3 = v_q2; else v3 = v_p2; int b_sign = b_sign_pointing_to_ridge(v_p1, v_q1, v3, r1, r2, color); if (color == MAX_RIDGE) if (b_sign == 1) return true; else return false; else if (b_sign == -1) return true; else return false; } else {//tag_order == Ridge_order_4, check the sign of the meanvalue of the signs // of Pi at the two crossing points FT sign_P; if (color == MAX_RIDGE) sign_P = get(P1,v_p1)*coord1 + get(P1,v_q1)*(1-coord1) + get(P1,v_p2)*coord2 + get(P1,v_q2)*(1-coord2); else sign_P = get(P2,v_p1)*coord1 + get(P2,v_q1)*(1-coord1) + get(P2,v_p2)*coord2 + get(P2,v_q2)*(1-coord2); if ( sign_P < 0 ) return true; else return false; } } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > int Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: b_sign_pointing_to_ridge(const vertex_descriptor v1, const vertex_descriptor v2, const vertex_descriptor v3, const Vector_3 r1, const Vector_3 r2, const Ridge_interrogation_type color) { Vector_3 r = r2 - r1, dv1, dv2, dv3; FT bv1, bv2, bv3; if ( color == MAX_RIDGE ) { bv1 = get(b0,v1); bv2 = get(b0,v2); bv3 = get(b0,v3); dv1 = get(d1,v1); dv2 = get(d1,v2); dv3 = get(d1,v3); } else { bv1 = get(b3,v1); bv2 = get(b3,v2); bv3 = get(b3,v3); dv1 = get(d2,v1); dv2 = get(d2,v2); dv3 = get(d2,v3); } if ( r != CGAL::NULL_VECTOR ) r = r/CGAL::sqrt(r*r); FT sign1, sign2, sign3; sign1 = bv1*(r1 - (get(vpm, v1)-ORIGIN) + (((get(vpm, v1)-ORIGIN)-r1)*r)*r )*dv1; sign2 = bv2*(r1 - (get(vpm, v2)-ORIGIN) + (((get(vpm, v2)-ORIGIN)-r1)*r)*r )*dv2; sign3 = bv3*(r1 - (get(vpm, v3)-ORIGIN) + (((get(vpm, v3)-ORIGIN)-r1)*r)*r )*dv3; int compt = 0; if ( sign1 > 0 ) compt++; else if (sign1 < 0) compt--; if ( sign2 > 0 ) compt++; else if (sign2 < 0) compt--; if ( sign3 > 0 ) compt++; else if (sign3 < 0) compt--; if (compt > 0) return 1; else return -1; } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > void Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: init_ridge_line(Ridge_line* ridge_line, const halfedge_descriptor h1, const halfedge_descriptor h2, const Ridge_type r_type) { ridge_line->line_type() = r_type; ridge_line->line()->push_back(Ridge_halfedge(h1, bary_coord(h1,r_type))); addback(ridge_line, h2, r_type); } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > void Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: addback(Ridge_line* ridge_line, const halfedge_descriptor he, const Ridge_type r_type) { halfedge_descriptor he_cur = ( --(ridge_line->line()->end()) )->first; FT coord_cur = ( --(ridge_line->line()->end()) )->second;//bary_coord(he_cur); FT coord = bary_coord(he,r_type); vertex_descriptor v_p = target(opposite(he,P),P), v_q = target(he,P), v_p_cur = target(opposite(he_cur,P),P), v_q_cur = target(he_cur,P); // he: p->q Vector_3 segment = CGAL::barycenter(get(vpm, v_p), coord, get(vpm, v_q)) - CGAL::barycenter(get(vpm, v_p_cur), coord_cur, get(vpm, v_q_cur)); FT k1x, k2x; //abs value of the ppal curvatures at the Xing point on he. FT k_second = 0; // abs value of the second derivative of the curvature // along the line of curvature k1x = CGAL::abs(get(k1,v_p)) * coord + CGAL::abs(get(k1,v_q)) * (1-coord) ; k2x = CGAL::abs(get(k2,v_p)) * coord + CGAL::abs(get(k2,v_q)) * (1-coord) ; if ( (ridge_line->line_type() == MAX_ELLIPTIC_RIDGE) || (ridge_line->line_type() == MAX_HYPERBOLIC_RIDGE) || (ridge_line->line_type() == MAX_CREST_RIDGE) ) { ridge_line->strength() += k1x * CGAL::sqrt(segment * segment); if (tag_order == Ridge_order_4) { if (k1x != k2x) k_second =CGAL::abs(( CGAL::abs(get(P1,v_p)) * coord + CGAL::abs(get(P1,v_q)) * (1-coord) )/(k1x-k2x)); ridge_line->sharpness() += k_second * CGAL::sqrt(segment * segment) * squared_model_size; } } if ( (ridge_line->line_type() == MIN_ELLIPTIC_RIDGE) || (ridge_line->line_type() == MIN_HYPERBOLIC_RIDGE) || (ridge_line->line_type() == MIN_CREST_RIDGE) ) { ridge_line->strength() += k2x * CGAL::sqrt(segment * segment); if (tag_order == Ridge_order_4) { if (k1x != k2x) k_second =CGAL::abs(( CGAL::abs(get(P2,v_p)) * coord + CGAL::abs(get(P2,v_q)) * (1-coord) )/(k1x-k2x)); ridge_line->sharpness() += k_second * CGAL::sqrt(segment * segment) * squared_model_size; } } ridge_line->line()->push_back( Ridge_halfedge(he, coord)); } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > void Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap >:: addfront(Ridge_line* ridge_line, const halfedge_descriptor he, const Ridge_type r_type) { halfedge_descriptor he_cur = ( ridge_line->line()->begin() )->first; FT coord_cur = ( ridge_line->line()->begin() )->second; FT coord = bary_coord(he,r_type); vertex_descriptor v_p = target(opposite(he,P),P), v_q = target(he,P), v_p_cur = target(opposite(he_cur,P),P), v_q_cur = target(he_cur,P); // he: p->q Vector_3 segment = CGAL::barycenter(get(vpm, v_p), coord, get(vpm, v_q)) - CGAL::barycenter(get(vpm, v_p_cur), coord_cur, get(vpm, v_q_cur)); FT k1x, k2x; //abs value of the ppal curvatures at the Xing point on he. FT k_second = 0.; // abs value of the second derivative of the curvature // along the line of curvature k1x = CGAL::abs(get(k1,v_p)) * coord + CGAL::abs(get(k1,v_q)) * (1-coord) ; k2x = CGAL::abs(get(k2,v_p)) * coord + CGAL::abs(get(k2,v_q)) * (1-coord) ; if ( (ridge_line->line_type() == MAX_ELLIPTIC_RIDGE) || (ridge_line->line_type() == MAX_HYPERBOLIC_RIDGE) || (ridge_line->line_type() == MAX_CREST_RIDGE) ) { ridge_line->strength() += k1x * CGAL::sqrt(segment * segment); if (tag_order == Ridge_order_4) { if (k1x != k2x) k_second =CGAL::abs(( CGAL::abs(get(P1,v_p)) * coord + CGAL::abs(get(P1,v_q)) * (1-coord) )/(k1x-k2x)); ridge_line->sharpness() += k_second * CGAL::sqrt(segment * segment) * squared_model_size; } } if ( (ridge_line->line_type() == MIN_ELLIPTIC_RIDGE) || (ridge_line->line_type() == MIN_HYPERBOLIC_RIDGE) || (ridge_line->line_type() == MIN_CREST_RIDGE) ) { ridge_line->strength() += k2x * CGAL::sqrt(segment * segment); if (tag_order == Ridge_order_4) { if (k1x != k2x) k_second =CGAL::abs(( CGAL::abs(get(P2,v_p)) * coord + CGAL::abs(get(P2,v_q)) * (1-coord) )/(k1x-k2x)); ridge_line->sharpness() += k_second * CGAL::sqrt(segment * segment) * squared_model_size; } } ridge_line->line()->push_front( Ridge_halfedge(he, coord)); } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap > typename Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap>::FT Ridge_approximation< TriangleMesh, VertexFTMap , VertexVectorMap>:: bary_coord(const halfedge_descriptor he, const Ridge_type r_type) { FT b_p = 0., b_q = 0.; // extremalities at p and q for he: p->q if ( (r_type == MAX_ELLIPTIC_RIDGE) || (r_type == MAX_HYPERBOLIC_RIDGE) || (r_type == MAX_CREST_RIDGE) ) { b_p = get(b0,target(opposite(he,P),P)); b_q = get(b0,target(he,P)); } if ( (r_type == MIN_ELLIPTIC_RIDGE) || (r_type == MIN_HYPERBOLIC_RIDGE) || (r_type == MIN_CREST_RIDGE) ) { b_p = get(b3,target(opposite(he,P),P)); b_q = get(b3,target(he,P)); } //denominator cannot be 0 since there is no crossing when both extremalities are 0 return CGAL::abs(b_q) / ( CGAL::abs(b_q) + CGAL::abs(b_p) ); } //--------------------------------------------------------------------------- //Global functions //-------------------------------------------------------------------------- template < class TriangleMesh, class VertexFTMap, class VertexVectorMap, class OutputIterator> OutputIterator compute_max_ridges(const TriangleMesh &P, const VertexFTMap& vertex2k1_pm, const VertexFTMap& vertex2k2_pm, const VertexFTMap& vertex2b0_pm, const VertexFTMap& vertex2b3_pm, const VertexVectorMap& vertex2d1_pm, const VertexVectorMap& vertex2d2_pm, const VertexFTMap& vertex2P1_pm, const VertexFTMap& vertex2P2_pm, OutputIterator it, Ridge_order order = Ridge_order_3) { typedef Ridge_approximation < TriangleMesh, VertexFTMap, VertexVectorMap > Ridge_approximation; Ridge_approximation ridge_approximation(P, vertex2k1_pm, vertex2k2_pm, vertex2b0_pm, vertex2b3_pm, vertex2d1_pm, vertex2d2_pm, vertex2P1_pm, vertex2P2_pm ); return ridge_approximation.compute_max_ridges(it, order); } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap, class OutputIterator> OutputIterator compute_min_ridges(const TriangleMesh &P, const VertexFTMap& vertex2k1_pm, const VertexFTMap& vertex2k2_pm, const VertexFTMap& vertex2b0_pm, const VertexFTMap& vertex2b3_pm, const VertexVectorMap& vertex2d1_pm, const VertexVectorMap& vertex2d2_pm, const VertexFTMap& vertex2P1_pm, const VertexFTMap& vertex2P2_pm, OutputIterator it, Ridge_order order = Ridge_order_3) { typedef Ridge_approximation < TriangleMesh, VertexFTMap, VertexVectorMap > Ridge_approximation; Ridge_approximation ridge_approximation(P, vertex2k1_pm, vertex2k2_pm, vertex2b0_pm, vertex2b3_pm, vertex2d1_pm, vertex2d2_pm, vertex2P1_pm, vertex2P2_pm ); return ridge_approximation.compute_min_ridges(it, order); } template < class TriangleMesh, class VertexFTMap, class VertexVectorMap, class OutputIterator> OutputIterator compute_crest_ridges(const TriangleMesh &P, const VertexFTMap& vertex2k1_pm, const VertexFTMap& vertex2k2_pm, const VertexFTMap& vertex2b0_pm, const VertexFTMap& vertex2b3_pm, const VertexVectorMap& vertex2d1_pm, const VertexVectorMap& vertex2d2_pm, const VertexFTMap& vertex2P1_pm, const VertexFTMap& vertex2P2_pm, OutputIterator it, Ridge_order order = Ridge_order_3) { typedef Ridge_approximation < TriangleMesh, VertexFTMap, VertexVectorMap > Ridge_approximation; Ridge_approximation ridge_approximation(P, vertex2k1_pm, vertex2k2_pm, vertex2b0_pm, vertex2b3_pm, vertex2d1_pm, vertex2d2_pm, vertex2P1_pm, vertex2P2_pm ); return ridge_approximation.compute_crest_ridges(it, order); } } //namespace CGAL #endif