// Copyright (c) 2016 GeometryFactory (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL: https://github.com/CGAL/cgal/blob/v5.1/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h $ // $Id: clip.h c253679 2020-04-18T16:27:58+02:00 Sébastien Loriot // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Sebastien Loriot #ifndef CGAL_POLYGON_MESH_PROCESSING_CLIP_H #define CGAL_POLYGON_MESH_PROCESSING_CLIP_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL{ namespace Polygon_mesh_processing { namespace internal { template int inter_pt_index(int i, int j, const Plane_3& plane, std::vector& points, std::map, int>& id_map) { std::pair, int>::iterator, bool> res = id_map.insert(std::make_pair(make_sorted_pair(i,j), static_cast (points.size()))); if(res.second) points.push_back( typename Geom_traits::Construct_plane_line_intersection_point_3() (plane, points[i], points[j])); return res.first->second; } template Oriented_side clip_to_bbox(const Plane_3& plane, const Bbox_3& bbox, TriangleMesh& tm_out, const NamedParameters& np) { typedef typename GetGeomTraits::type Geom_traits; typedef typename Geom_traits::Point_3 Point_3; typedef typename GetVertexPointMap::type Vpm; Vpm vpm_out = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_property_map(boost::vertex_point, tm_out)); std::vector corners(8); corners[0] = Point_3(bbox.xmin(),bbox.ymin(),bbox.zmin()); corners[1] = Point_3(bbox.xmin(),bbox.ymax(),bbox.zmin()); corners[2] = Point_3(bbox.xmax(),bbox.ymax(),bbox.zmin()); corners[3] = Point_3(bbox.xmax(),bbox.ymin(),bbox.zmin()); corners[4] = Point_3(bbox.xmin(),bbox.ymin(),bbox.zmax()); corners[5] = Point_3(bbox.xmin(),bbox.ymax(),bbox.zmax()); corners[6] = Point_3(bbox.xmax(),bbox.ymax(),bbox.zmax()); corners[7] = Point_3(bbox.xmax(),bbox.ymin(),bbox.zmax()); std::array orientations = {{ plane.oriented_side(corners[0]), plane.oriented_side(corners[1]), plane.oriented_side(corners[2]), plane.oriented_side(corners[3]), plane.oriented_side(corners[4]), plane.oriented_side(corners[5]), plane.oriented_side(corners[6]), plane.oriented_side(corners[7]) }}; // description of faces of the bbox std::array face_indices = {{ 0, 1, 2, 3, 2, 1, 5, 6, 3, 2, 6, 7, 1, 0, 4, 5, 4, 0, 3, 7, 6, 5, 4, 7 }}; std::map, int> id_map; std::vector< std::vector > output_faces(6); bool all_in = true; bool all_out = true; std::set in_point_ids; // to collect the set of points in the clipped bbox // for each face of the bbox, we look for intersection of the plane with its edges for(int i=0; i<6; ++i) { for(int k=0; k< 4; ++k) { int current_id = face_indices[4*i + k]; int next_id = face_indices[4*i + (k+1)%4]; switch(orientations[ current_id ]) { case ON_NEGATIVE_SIDE: { all_out=false; // point on or on the negative side output_faces[i].push_back(current_id); in_point_ids.insert(output_faces[i].back()); // check for intersection of the edge if(orientations[ next_id ] == ON_POSITIVE_SIDE) { output_faces[i].push_back( inter_pt_index(current_id, next_id, plane, corners, id_map)); in_point_ids.insert(output_faces[i].back()); } break; } case ON_POSITIVE_SIDE: { all_in = false; // check for intersection of the edge if(orientations[ next_id ] == ON_NEGATIVE_SIDE) { output_faces[i].push_back( inter_pt_index(current_id, next_id, plane, corners, id_map)); in_point_ids.insert(output_faces[i].back()); } break; } case ON_ORIENTED_BOUNDARY: { output_faces[i].push_back(current_id); in_point_ids.insert(output_faces[i].back()); } } } if(output_faces[i].size() < 3){ CGAL_assertion(output_faces[i].empty() || (output_faces[i].front()<8 && output_faces[i].back()<8)); output_faces[i].clear(); // edge of the bbox included in the plane } } // the intersection is the full bbox if(all_in) return ON_NEGATIVE_SIDE; if(all_out) return ON_POSITIVE_SIDE; // build the clipped bbox typedef boost::graph_traits graph_traits; typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename graph_traits::face_descriptor face_descriptor; std::map out_vertices; for(int i : in_point_ids) { vertex_descriptor v = add_vertex(tm_out); out_vertices.insert(std::make_pair(i, v)); put(vpm_out, v, corners[i]); } std::map< std::pair, halfedge_descriptor> hedge_map; const halfedge_descriptor null_hedge = graph_traits::null_halfedge(); const face_descriptor null_fd = graph_traits::null_face(); for(const std::vector& findices : output_faces) { if(findices.empty()) continue; const face_descriptor fd=add_face(tm_out); int prev_id = findices.back(); // create of recover face boundary halfedges std::vector hedges; hedges.reserve(findices.size()); for(int current_id : findices) { vertex_descriptor src = out_vertices[prev_id], tgt = out_vertices[current_id]; std::pair, halfedge_descriptor>::iterator, bool> res = hedge_map.insert(std::make_pair(std::make_pair(prev_id, current_id), null_hedge)); if(res.second) { res.first->second = halfedge(add_edge(tm_out), tm_out); hedge_map.insert(std::make_pair(std::make_pair(current_id, prev_id), opposite(res.first->second, tm_out))); set_face(opposite(res.first->second, tm_out), null_fd, tm_out); } hedges.push_back(res.first->second); // set edge source and target set_target(hedges.back(), tgt, tm_out); set_target(opposite(hedges.back(), tm_out), src, tm_out); // set face pointer of halfedges set_face(hedges.back(), fd, tm_out); // set vertex halfedge set_halfedge(src, opposite(hedges.back(), tm_out), tm_out); set_halfedge(tgt, hedges.back(), tm_out); if(current_id==findices.front()) set_halfedge(fd, hedges.back(), tm_out); prev_id = current_id; } CGAL_assertion(hedges.size() == findices.size()); // set next/prev relationship halfedge_descriptor prev_h=hedges.back(); for(halfedge_descriptor h : hedges) { set_next(prev_h, h, tm_out); prev_h = h; } } // handle the face of the plane: // look for a border halfedge and reconstruct the face of the plane // by turning around vertices inside the mesh constructed above // until we reach another border halfedge for(halfedge_descriptor h : halfedges(tm_out)) { if(face(h, tm_out) == null_fd) { face_descriptor fd = add_face(tm_out); set_halfedge(fd, h, tm_out); halfedge_descriptor h_prev=h; halfedge_descriptor h_curr=h; do{ h_curr=opposite(h_curr, tm_out); do{ h_curr=opposite(prev(h_curr, tm_out), tm_out); } while(face(h_curr, tm_out) != null_fd && h_curr!=h); set_face(h_prev, fd, tm_out); set_next(h_prev, h_curr, tm_out); if(h_curr==h) break; h_prev=h_curr; } while(true); break; } } CGAL_assertion(is_valid_polygon_mesh(tm_out)); // triangulate the faces CGAL::Polygon_mesh_processing::triangulate_faces(tm_out, np); return ON_ORIENTED_BOUNDARY; } template void split_along_edges(TriangleMesh& tm, Ecm ecm, VPM vpm) { typedef boost::graph_traits GT; typedef typename GT::face_descriptor face_descriptor; typedef typename GT::edge_descriptor edge_descriptor; typedef typename GT::vertex_descriptor vertex_descriptor; typedef typename GT::halfedge_descriptor halfedge_descriptor; std::vector shared_edges; for(edge_descriptor e : edges(tm)) if(get(ecm, e)) shared_edges.push_back(e); std::size_t nb_shared_edges = shared_edges.size(); std::vector hedges_to_update; typedef CGAL::dynamic_halfedge_property_t H_tag; typename boost::property_map::type no_target_update = get(H_tag(), tm); std::vector< std::pair > vertices_to_duplicate; //collect border halfedges having as target one of the edge endpoints std::set extra_border_hedges; for(std::size_t k=0; k& p : vertices_to_duplicate) { vertex_descriptor nv = add_vertex(tm); put(vpm, nv, get(vpm, p.second)); for(halfedge_descriptor h : halfedges_around_target(p.first, tm)) set_target(h, nv, tm); set_halfedge(nv, p.first, tm); } CGAL_assertion_code(for(halfedge_descriptor h : hedges_to_update)) { CGAL_assertion(next(prev(h, tm), tm) == h); CGAL_assertion(prev(next(h, tm), tm) == h); } for(halfedge_descriptor h : hedges_to_update) { for(halfedge_descriptor hh : halfedges_around_target(h, tm)) if(h!=hh) set_target(hh, target(h, tm), tm); } CGAL_assertion(is_valid_polygon_mesh(tm)); } } // end of internal namespace /** * \ingroup PMP_corefinement_grp * * clips `tm` by keeping the part that is inside the volume \link coref_def_subsec bounded \endlink * by `clipper`. * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. * See Subsection \ref coref_clip for more details. * \attention With the current implementation, `clipper` will be modified (refined with the intersection with `tm`). * * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(clipper)` \endlink * \pre \link CGAL::Polygon_mesh_processing::does_bound_a_volume() `CGAL::Polygon_mesh_processing::does_bound_a_volume(clipper)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. * @tparam NamedParameters1 a sequence of \ref bgl_namedparameters "Named Parameters" * @tparam NamedParameters2 a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param clipper triangulated surface mesh used to clip `tm` * @param np_tm an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * @param np_c an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm` (resp. `clipper`)} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm (resp. clipper))`} * \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` * must be available in `TriangleMesh`.} * \cgalParamNEnd * * \cgalParamNBegin{face_index_map} * \cgalParamDescription{a property map associating to each face of `tm` (`clipper`) a unique index between `0` and `num_faces(tm (resp. clipper)) - 1`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%face_descriptor` * as key type and `std::size_t` as value type} * \cgalParamDefault{an automatically indexed internal map} * \cgalParamExtra{if the property map is writable, the indices of the faces of `tm` and `clipper` * will be set after refining `tm` with the intersection with `clipper`} * \cgalParamNEnd * * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` and `clipper` will be * checked for self-intersections and `Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{clip_volume} * \cgalParamDescription{If `true`, and `tm` is closed, the clipping will be done on the volume * \link coref_def_subsec bounded \endlink by `tm` rather than on its surface * (i.e., `tm` will be kept closed).} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{use_compact_clipper} * \cgalParamDescription{if `false`, the parts of `tm` coplanar with `clipper` will not be part of the output.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamExtra{This option has an effect only if a surface and not a volume is clipped, * (i.e., if `clip_volume` is `false` or if `tm` is open).} * \cgalParamNEnd * \cgalNamedParamsEnd * * @return `true` if the output surface mesh is manifold. * If `false` is returned `tm` and `clipper` are only corefined. */ template bool clip(TriangleMesh& tm, TriangleMesh& clipper, const NamedParameters1& np_tm, const NamedParameters2& np_c) { const bool clip_volume = parameters::choose_parameter(parameters::get_parameter(np_tm, internal_np::clip_volume), false); if(clip_volume && is_closed(tm)) return corefine_and_compute_intersection(tm, clipper, tm, np_tm, np_c); return corefine_and_compute_intersection(tm, clipper, tm, np_tm.use_bool_op_to_clip_surface(true), np_c); } /** * \ingroup PMP_corefinement_grp * clips `tm` by keeping the part that is on the negative side of `plane` (side opposite to its normal vector). * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. * See Subsection \ref coref_clip for more details. * * \note In the current implementation it is not possible to set the vertex point map and the default will be used. `Plane_3` must be * from the same %Kernel as the point of the vertex point map. * * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. * An internal property map for `CGAL::vertex_point_t` must be available. * * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param plane plane whose negative side defines the half-space to intersect `tm` with. * `Plane_3` is the plane type for the same CGAL kernel as the point of the vertex point map of `tm`. * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} * \cgalParamNEnd * * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` * and `plane` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{clip_volume} * \cgalParamDescription{If `true`, and `tm` is closed, the clipping will be done on * the volume \link coref_def_subsec bounded \endlink by `tm` * rather than on its surface (i.e., `tm` will be kept closed).} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{use_compact_clipper} * \cgalParamDescription{if `false` the parts of `tm` coplanar with `plane` will not be part of the output} * \cgalParamType{Boolean} * \cgalParamDefault{`true`} * \cgalParamNEnd * * \cgalNamedParamsEnd * * @return `true` if the output surface mesh is manifold. * If `false` is returned `tm` is only refined by the intersection with `plane`. */ template bool clip(TriangleMesh& tm, #ifdef DOXYGEN_RUNNING const Plane_3& plane, #else const typename GetGeomTraits::type::Plane_3& plane, #endif const NamedParameters& np) { if(boost::begin(faces(tm))==boost::end(faces(tm))) return true; CGAL::Bbox_3 bbox = ::CGAL::Polygon_mesh_processing::bbox(tm); //extend the bbox a bit to avoid border cases double xd=(std::max)(1.,(bbox.xmax()-bbox.xmin())/100); double yd=(std::max)(1.,(bbox.ymax()-bbox.ymin())/100); double zd=(std::max)(1.,(bbox.zmax()-bbox.zmin())/100); bbox=CGAL::Bbox_3(bbox.xmin()-xd, bbox.ymin()-yd, bbox.zmin()-zd, bbox.xmax()+xd, bbox.ymax()+yd, bbox.zmax()+zd); TriangleMesh clipper; Oriented_side os = internal::clip_to_bbox(plane, bbox, clipper, parameters::all_default()); switch(os) { case ON_NEGATIVE_SIDE: return true; // nothing to clip, the full mesh is on the negative side case ON_POSITIVE_SIDE: clear(tm); // clear the mesh that is fully on the positive side return true; default: break; } return clip(tm, clipper, np, parameters::all_default()); } /** * \ingroup PMP_corefinement_grp * clips `tm` by keeping the part that is inside `iso_cuboid`. * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. * See Subsection \ref coref_clip for more details. * * \note In the current implementation it is not possible to set the vertex point map and the default will be used. `Iso_cuboid_3` must be * from the same %Kernel as the point of the vertex point map. * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. * An internal property map for `CGAL::vertex_point_t` must be available. * * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param iso_cuboid iso-cuboid used to clip `tm`. * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` * and `iso_cuboid` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{clip_volume} * \cgalParamDescription{If `true`, and `tm` is closed, the clipping will be done on * the volume \link coref_def_subsec bounded \endlink by `tm` * rather than on its surface (i.e., `tm` will be kept closed).} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{use_compact_clipper} * \cgalParamDescription{if `false` the parts of `tm` coplanar with `iso_cuboid` will not be part of the output} * \cgalParamType{Boolean} * \cgalParamDefault{`true`} * \cgalParamNEnd * \cgalNamedParamsEnd * * @return `true` if the output surface mesh is manifold. * If `false` is returned `tm` is only refined by the intersection with `iso_cuboid`. */ template bool clip(TriangleMesh& tm, #ifdef DOXYGEN_RUNNING const Iso_cuboid_3& iso_cuboid, #else const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid, #endif const NamedParameters& np) { if(boost::begin(faces(tm))==boost::end(faces(tm))) return true; TriangleMesh clipper; make_hexahedron(iso_cuboid[0], iso_cuboid[1], iso_cuboid[2], iso_cuboid[3], iso_cuboid[4], iso_cuboid[5], iso_cuboid[6], iso_cuboid[7], clipper); triangulate_faces(clipper); return clip(tm, clipper, np, parameters::all_default()); } /*! * \ingroup PMP_corefinement_grp * corefines `tm` and `splitter` and duplicates edges in `tm` that are on the intersection with `splitter`. * * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(splitter)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. * * @tparam NamedParameters1 a sequence of \ref bgl_namedparameters "Named Parameters" * @tparam NamedParameters2 a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param splitter triangulated surface mesh used to split `tm` * @param np_tm an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * @param np_s an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm` (`splitter`)} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} * \cgalParamDefault{If this parameter is omitted, an internal property map for * `CGAL::vertex_point_t` must be available in `TriangleMesh`.} * \cgalParamNEnd * * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` * and `splitter` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalNamedParamsEnd */ template void split(TriangleMesh& tm, TriangleMesh& splitter, const NamedParameters1& np_tm, const NamedParameters2& np_s) { namespace PMP = CGAL::Polygon_mesh_processing; using parameters::get_parameter; using parameters::choose_parameter; typedef typename GetVertexPointMap::type VPM1; typedef typename GetVertexPointMap::type VPM2; typedef typename boost::template property_map >::type Ecm; VPM1 vpm_tm = choose_parameter(get_parameter(np_tm, internal_np::vertex_point), get_property_map(vertex_point, tm)); VPM2 vpm_s = choose_parameter(get_parameter(np_s, internal_np::vertex_point), get_property_map(vertex_point, splitter)); Ecm ecm = get(CGAL::dynamic_edge_property_t(), tm); // create a constrained edge map and corefine input mesh with the splitter, // and mark edges PMP::corefine(tm, splitter, CGAL::parameters::vertex_point_map(vpm_tm).edge_is_constrained_map(ecm), CGAL::parameters::vertex_point_map(vpm_s)); //split mesh along marked edges internal::split_along_edges(tm, ecm, vpm_tm); } /** * \ingroup PMP_corefinement_grp * adds intersection edges of `plane` and `tm` in `tm` and duplicates those edges. * * \note In the current implementation it is not possible to set the vertex point map and the default will be used. * * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph` * An internal property map for `CGAL::vertex_point_t` must be available. * * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param plane the plane that will be used to split `tm`. * `Plane_3` is the plane type for the same CGAL kernel as the point of the vertex point map of `tm`. * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} * \cgalParamNEnd * * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` * and `plane` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalNamedParamsEnd */ template void split(TriangleMesh& tm, #ifdef DOXYGEN_RUNNING const Plane_3& plane, #else const typename GetGeomTraits::type::Plane_3& plane, #endif const NamedParameters& np) { using parameters::get_parameter; using parameters::choose_parameter; namespace PMP = CGAL::Polygon_mesh_processing; namespace params = PMP::parameters; // create a splitter mesh for the splitting plane using an internal CGAL function CGAL::Bbox_3 bbox = ::CGAL::Polygon_mesh_processing::bbox(tm, np); double xd = (std::max)(1., 0.01 * (bbox.xmax() - bbox.xmin())); double yd = (std::max)(1., 0.01 * (bbox.ymax() - bbox.ymin())); double zd = (std::max)(1., 0.01 * (bbox.zmax() - bbox.zmin())); bbox = CGAL::Bbox_3(bbox.xmin()-xd, bbox.ymin()-yd, bbox.zmin()-zd, bbox.xmax()+xd, bbox.ymax()+yd, bbox.zmax()+zd); TriangleMesh splitter; CGAL::Oriented_side os = PMP::internal::clip_to_bbox(plane, bbox, splitter, PMP::parameters::all_default()); if(os == CGAL::ON_ORIENTED_BOUNDARY) return split(tm, splitter, np, params::all_default()); //else nothing to do, no intersection. } /** * \ingroup PMP_corefinement_grp * adds intersection edges of `iso_cuboid` and `tm` in `tm` and duplicates those edges. * * \note In the current implementation it is not possible to set the vertex point map and the default will be used. * \note `Iso_cuboid_3` must be from the same %Kernel as the point of the vertex point map. * * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph` * An internal property map for `CGAL::vertex_point_t` must be available. * * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh * @param iso_cuboid iso-cuboid used to split `tm`. * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} * \cgalParamNEnd * * \cgalParamNBegin{visitor} * \cgalParamDescription{a visitor used to track the creation of new faces} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd * * \cgalParamNBegin{throw_on_self_intersection} * \cgalParamDescription{If `true`, the set of triangles closed to the intersection of `tm` * and `iso_cuboid` will be checked for self-intersections * and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one self-intersection is found.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{clip_volume} * \cgalParamDescription{If `true`, and `tm` is closed, the clipping will be done on * the volume \link coref_def_subsec bounded \endlink by `tm` * rather than on its surface (i.e., `tm` will be kept closed).} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} * \cgalParamNEnd * * \cgalParamNBegin{use_compact_clipper} * \cgalParamDescription{if `false` the parts of `tm` coplanar with `iso_cuboid` will not be part of the output} * \cgalParamType{Boolean} * \cgalParamDefault{`true`} * \cgalParamNEnd * * \cgalNamedParamsEnd */ template void split(TriangleMesh& tm, #ifdef DOXYGEN_RUNNING const Iso_cuboid_3& iso_cuboid, #else const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid, #endif const NamedParameters& np) { TriangleMesh splitter; make_hexahedron(iso_cuboid[0], iso_cuboid[1], iso_cuboid[2], iso_cuboid[3], iso_cuboid[4], iso_cuboid[5], iso_cuboid[6], iso_cuboid[7], splitter); triangulate_faces(splitter); split(tm, splitter, np, parameters::all_default()); } /// \cond SKIP_IN_MANUAL // convenience overloads template bool clip(TriangleMesh& tm, const typename GetGeomTraits::type::Plane_3& plane) { return clip(tm, plane, parameters::all_default()); } // convenience overloads template bool clip(TriangleMesh& tm, const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid) { return clip(tm, iso_cuboid, parameters::all_default()); } // convenience overload template bool clip(TriangleMesh& tm, TriangleMesh& clipper, const NamedParameters1& np_tm) { return clip(tm, clipper, np_tm, parameters::all_default()); } // convenience overload template bool clip(TriangleMesh& tm, TriangleMesh& clipper) { return clip(tm, clipper, parameters::all_default()); } // convenience overload template void split(TriangleMesh& tm, TriangleMesh& splitter, const NamedParameters1& np_tm) { split(tm, splitter, np_tm, parameters::all_default()); } // convenience overload template void split(TriangleMesh& tm, TriangleMesh& splitter) { split(tm, splitter, parameters::all_default()); } template void split(TriangleMesh& tm, const typename GetGeomTraits::type::Plane_3& plane) { split(tm, plane, parameters::all_default()); } template void split(TriangleMesh& tm, const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid) { split(tm, iso_cuboid, parameters::all_default()); } /// \endcond } } //end of namespace CGAL::Polygon_mesh_processing #endif // CGAL_POLYGON_MESH_PROCESSING_CLIP_H