mirror of https://github.com/CGAL/cgal
Merge pull request #8721 from sloriot/PMP-new_clip
Add refine_with_plane and new fast clip method
This commit is contained in:
commit
cb747e8201
|
|
@ -28,6 +28,9 @@
|
|||
to remesh triangle meshes. This remeshing algorithm uses clustering on polygonal meshes as to
|
||||
approximate a Centroidal Voronoi Diagram construction, and can move vertices as to recover
|
||||
sharp features and corners.
|
||||
- New implementation of `CGAL::Polygon_mesh_processing::clip()` with a plane as clipper that is much faster and is now able to handle non-triangulated surface meshes.
|
||||
- New implementation of `CGAL::Polygon_mesh_processing::split()` with a plane as clipper that is much faster and is now able to handle non-triangulated surface meshes.
|
||||
- Added the function `CGAL::Polygon_mesh_processing::refine_with_plane()`, which enables users to refine a mesh with their intersection with a plane.
|
||||
|
||||
### [Point Set Processing](https://doc.cgal.org/6.1/Manual/packages.html#PkgPointSetProcessing3)
|
||||
- Added `poisson_eliminate()` to downsample a point cloud to a target size while providing Poisson disk property, i.e., a larger minimal distance between points.
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ public :
|
|||
|
||||
connect(ui_widget.clip_radioButton, &QRadioButton::toggled,
|
||||
[this](bool b){
|
||||
ui_widget.close_checkBox->setEnabled(!ui_widget.do_not_modify_CheckBox->isChecked() &&b);
|
||||
ui_widget.close_checkBox->setEnabled(!ui_widget.do_not_modify_CheckBox->isChecked() && b);
|
||||
ui_widget.coplanarCheckBox->setEnabled(ui_widget.clip_radioButton->isChecked());
|
||||
});
|
||||
connect(actionClipPolyhedra , SIGNAL(triggered()),
|
||||
this, SLOT(pop_widget()));
|
||||
|
|
@ -235,7 +236,7 @@ public Q_SLOTS:
|
|||
for(int id : scene->selectionIndices())
|
||||
{
|
||||
Scene_surface_mesh_item *sm_item = qobject_cast<Scene_surface_mesh_item*>(scene->item(id));
|
||||
if(sm_item && CGAL::is_triangle_mesh(*sm_item->polyhedron()))
|
||||
if(sm_item)
|
||||
{
|
||||
if(!ui_widget.do_not_modify_CheckBox->isChecked() && CGAL::Polygon_mesh_processing::does_self_intersect(*sm_item->face_graph()))
|
||||
CGAL::Three::Three::warning(tr("%1 has not been clipped because it has self intersections.").arg(sm_item->name()));
|
||||
|
|
@ -255,10 +256,10 @@ public Q_SLOTS:
|
|||
{
|
||||
CGAL::Polygon_mesh_processing::clip(*(sm_item->face_graph()),
|
||||
plane->plane(),
|
||||
CGAL::parameters::clip_volume(
|
||||
ui_widget.close_checkBox->isChecked()).
|
||||
throw_on_self_intersection(true).
|
||||
use_compact_clipper(
|
||||
CGAL::parameters::clip_volume(ui_widget.close_checkBox->isChecked())
|
||||
.do_not_triangulate_faces(!ui_widget.triangulated_checkBox->isChecked())
|
||||
.throw_on_self_intersection(true)
|
||||
.use_compact_clipper(
|
||||
!ui_widget.coplanarCheckBox->isChecked())
|
||||
.allow_self_intersections(ui_widget.do_not_modify_CheckBox->isChecked()));
|
||||
}
|
||||
|
|
@ -280,6 +281,7 @@ public Q_SLOTS:
|
|||
CGAL::Polygon_mesh_processing::split(*(sm_item->face_graph()),
|
||||
plane->plane(),
|
||||
CGAL::parameters::throw_on_self_intersection(true)
|
||||
.do_not_triangulate_faces(!ui_widget.triangulated_checkBox->isChecked())
|
||||
.allow_self_intersections(ui_widget.do_not_modify_CheckBox->isChecked()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>386</width>
|
||||
<height>231</height>
|
||||
<width>326</width>
|
||||
<height>358</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -56,33 +56,86 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0" columnstretch="1,0">
|
||||
<item row="6" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="clip_radioButton">
|
||||
<property name="text">
|
||||
<string>Clip</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</spacer>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="split_radioButton">
|
||||
<property name="text">
|
||||
<string>Split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="close_checkBox">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Keep Closed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="triangulated_checkBox">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Keep Triangulated</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="coplanarCheckBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
|
|
@ -99,37 +152,7 @@ Only available in Split mode.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="close_checkBox">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Keep Closed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="split_radioButton">
|
||||
<property name="text">
|
||||
<string>Split</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="clip_radioButton">
|
||||
<property name="text">
|
||||
<string>Clip</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="do_not_modify_CheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Allow the use of a self-intersected clipper, but prevent the closing of the result.</string>
|
||||
|
|
@ -139,20 +162,11 @@ Only available in Split mode.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="flip_Button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Flip plane</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="clipButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
|
|
@ -165,23 +179,36 @@ Only available in Split mode.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="flip_Button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Flip plane</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ The page \ref bgl_namedparameters "Named Parameters" describes their usage.
|
|||
- `CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces()`
|
||||
- `CGAL::Polygon_mesh_processing::detect_corners_of_regions()`
|
||||
- `CGAL::Polygon_mesh_processing::refine_mesh_at_isolevel()`
|
||||
- `CGAL::Polygon_mesh_processing::refine_with_plane()`
|
||||
|
||||
\cgalCRPSection{I/O Functions}
|
||||
- \link PMP_IO_grp `CGAL::Polygon_mesh_processing::IO::read_polygon_mesh()`\endlink
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2016 GeometryFactory (France).
|
||||
// Copyright (c) 2016-2025 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
|
|
@ -43,7 +43,9 @@
|
|||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
namespace CGAL{
|
||||
#include <CGAL/Polygon_mesh_processing/refine_with_plane.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Polygon_mesh_processing {
|
||||
namespace internal {
|
||||
|
||||
|
|
@ -514,6 +516,49 @@ generic_clip_impl(
|
|||
functor(CGAL::Emptyset_iterator(), false, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION
|
||||
template <class PolygonMesh, class Clip_visitor>
|
||||
struct Visitor_wrapper_for_triangulate_face
|
||||
: public ::CGAL::Polygon_mesh_processing::Hole_filling::Default_visitor
|
||||
//TODO: @afabri --> I shouldn't be the one doing the inheritance...
|
||||
//TODO: @afabri --> nothing on edge?
|
||||
{
|
||||
using face_descriptor = typename boost::graph_traits<PolygonMesh>::face_descriptor;
|
||||
|
||||
Clip_visitor& clip_visitor;
|
||||
const PolygonMesh& pm;
|
||||
std::vector<face_descriptor> triangulation_faces;
|
||||
|
||||
Visitor_wrapper_for_triangulate_face(const PolygonMesh& pm, Clip_visitor& clip_visitor)
|
||||
: clip_visitor(clip_visitor)
|
||||
, pm(pm)
|
||||
{}
|
||||
|
||||
void before_subface_creations(face_descriptor f_split)
|
||||
{
|
||||
CGAL_assertion(triangulation_faces.empty());
|
||||
triangulation_faces.push_back(f_split);
|
||||
}
|
||||
void after_subface_creations()
|
||||
{
|
||||
for (face_descriptor f : triangulation_faces)
|
||||
{
|
||||
clip_visitor.before_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, pm);
|
||||
clip_visitor.after_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, f, pm);
|
||||
}
|
||||
triangulation_faces.clear();
|
||||
}
|
||||
|
||||
void after_subface_created(face_descriptor f_new)
|
||||
{
|
||||
triangulation_faces.push_back(f_new);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // end of internal namespace
|
||||
|
||||
/**
|
||||
|
|
@ -521,7 +566,7 @@ generic_clip_impl(
|
|||
*
|
||||
* \brief 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`.
|
||||
* If `tm` is closed, the clipped part can be kept closed by setting the named parameter `clip_volume` 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`).
|
||||
|
|
@ -584,10 +629,11 @@ generic_clip_impl(
|
|||
* \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`}
|
||||
* \cgalParamDefault{`true`}
|
||||
* \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
|
||||
*
|
||||
* \cgalParamNBegin{do_not_modify}
|
||||
* \cgalParamDescription{(`np_c` only) if `true`, `clipper` will not be modified.}
|
||||
* \cgalParamType{Boolean}
|
||||
|
|
@ -633,119 +679,224 @@ clip(TriangleMesh& tm,
|
|||
/**
|
||||
* \ingroup PMP_corefinement_grp
|
||||
*
|
||||
* \brief clips `tm` by keeping the part that is on the negative side of `plane` (side opposite to its normal vector).
|
||||
* \brief clips `pm` by keeping the part that is on the negative side of `plane` (the 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`.
|
||||
* If `pm` is closed, the clipped part can be kept closed by setting the named parameter `clip_volume`to `true`.
|
||||
* See Subsection \ref coref_clip for more details.
|
||||
*
|
||||
* \note `Plane_3` must be from the same %Kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Plane_3` must be from the same %Kernel as the point of the vertex point map of `tm`.
|
||||
*
|
||||
* \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`.
|
||||
* @tparam PolygonMesh 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 pm input surface mesh
|
||||
* @param plane plane whose negative side defines the halfspace to intersect `pm` with.
|
||||
* `Plane_3` is the plane type for the same CGAL kernel as the point of the vertex point map of `pm`.
|
||||
* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
*
|
||||
* \cgalParamNBegin{concurrency_tag}
|
||||
* \cgalParamDescription{a tag indicating if the task should be performed using one or several threads.}
|
||||
* \cgalParamType{Either `CGAL::Sequential_tag`, or `CGAL::Parallel_tag`, or `CGAL::Parallel_if_available_tag`}
|
||||
* \cgalParamDefault{`CGAL::Sequential_tag`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \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<TriangleMesh>::%vertex_descriptor`
|
||||
* \cgalParamDescription{a property map associating points to the vertices of `pm`}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%vertex_descriptor`
|
||||
* as key type and `%Point_3` as value type}
|
||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`}
|
||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, pm)`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{visitor}
|
||||
* \cgalParamDescription{a visitor used to track the creation of new faces}
|
||||
* \cgalParamDescription{a visitor used to track the creation of new faces, edges, and faces.
|
||||
* Note that as there are no mesh associated with `plane`,
|
||||
* `boost::graph_traits<PolygonMesh>::null_halfedge()` and `boost::graph_traits<PolygonMesh>::null_face()` will be used when calling
|
||||
* functions of the visitor expecting a halfedge or a face from `plane`. Similarly, `pm` will be used as the mesh of `plane`.}
|
||||
* \cgalParamType{a class model of `PMPCorefinementVisitor`}
|
||||
* \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`}
|
||||
* \cgalParamDefault{`Corefinement::Default_visitor<PolygonMesh>`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm`
|
||||
* \cgalParamDescription{If `true`, the set of triangles close to the intersection of `pm`
|
||||
* 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.}
|
||||
* will be thrown if at least one self-intersection is found.
|
||||
* This option is only taken into account if `pm` is a triangle mesh.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections in `pm` are accepted.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `pm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false` (overwriting any value provided)}
|
||||
* \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).}
|
||||
* \cgalParamDescription{If `true`, and if `pm` is closed, the clipping will be done on
|
||||
* the volume \link coref_def_subsec bounded \endlink by `pm`
|
||||
* rather than on its surface (i.e., `pm` will remain 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}
|
||||
* \cgalParamDescription{If `false`, the parts of `pm` coplanar with `plane` will not be part of the output.
|
||||
* Always `true` if `clip_volume` is `true`.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`true`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections are accepted for `tm`.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamNBegin{do_not_triangulate_faces}
|
||||
* \cgalParamDescription{If the input mesh is triangulated and this parameter is set to `false`, the mesh will be kept triangulated.
|
||||
* Always `true` if `pm` is not a triangle mesh.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @return `true` if the output surface mesh is manifold.
|
||||
* If `false` is returned `tm` is only refined by the intersection with `plane`.
|
||||
* @return `true`
|
||||
*
|
||||
* @see `split()`
|
||||
*/
|
||||
template <class TriangleMesh,
|
||||
template <class PolygonMesh,
|
||||
class NamedParameters = parameters::Default_named_parameters>
|
||||
bool clip(TriangleMesh& tm,
|
||||
bool clip(PolygonMesh& pm,
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
const Plane_3& plane,
|
||||
#else
|
||||
const typename GetGeomTraits<TriangleMesh, NamedParameters>::type::Plane_3& plane,
|
||||
const typename GetGeomTraits<PolygonMesh, NamedParameters>::type::Plane_3& plane,
|
||||
#endif
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
namespace params = CGAL::parameters;
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
using parameters::get_parameter_reference ;
|
||||
|
||||
using params::get_parameter;
|
||||
using params::choose_parameter;
|
||||
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
|
||||
|
||||
if(std::begin(faces(tm))==std::end(faces(tm))) return true;
|
||||
using GT = typename GetGeomTraits<PolygonMesh, NamedParameters>::type;
|
||||
GT traits = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, pm));
|
||||
|
||||
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::default_values());
|
||||
switch(os)
|
||||
using Default_visitor = Corefinement::Default_visitor<PolygonMesh>;
|
||||
Default_visitor default_visitor;
|
||||
using Visitor_ref = typename internal_np::Lookup_named_param_def<internal_np::visitor_t, NamedParameters, Default_visitor>::reference;
|
||||
Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor);
|
||||
constexpr bool has_visitor = !std::is_same_v<Default_visitor, std::remove_cv_t<std::remove_reference_t<Visitor_ref>>>;
|
||||
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::concurrency_tag_t,
|
||||
NamedParameters,
|
||||
Sequential_tag
|
||||
> ::type Concurrency_tag;
|
||||
|
||||
// config flags
|
||||
bool clip_volume =
|
||||
parameters::choose_parameter(parameters::get_parameter(np, internal_np::clip_volume), false);
|
||||
bool use_compact_clipper =
|
||||
choose_parameter(get_parameter(np, internal_np::use_compact_clipper), true);
|
||||
const bool throw_on_self_intersection =
|
||||
choose_parameter(get_parameter(np, internal_np::throw_on_self_intersection), false);
|
||||
const bool allow_self_intersections =
|
||||
choose_parameter(get_parameter(np, internal_np::allow_self_intersections), false);
|
||||
bool triangulate = !choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false);
|
||||
|
||||
auto vos = get(dynamic_vertex_property_t<Oriented_side>(), pm);
|
||||
auto ecm = get(dynamic_edge_property_t<bool>(), pm, false);
|
||||
|
||||
if (triangulate && !is_triangle_mesh(pm))
|
||||
triangulate = false;
|
||||
|
||||
refine_with_plane(pm, plane, parameters::vertex_oriented_side_map(vos)
|
||||
.edge_is_marked_map(ecm)
|
||||
.vertex_point_map(vpm)
|
||||
.geom_traits(traits)
|
||||
.do_not_triangulate_faces(!triangulate)
|
||||
.throw_on_self_intersection(!allow_self_intersections &&
|
||||
throw_on_self_intersection)
|
||||
.visitor(std::ref(visitor))
|
||||
.concurrency_tag(Concurrency_tag()));
|
||||
|
||||
CGAL_assertion(is_valid_polygon_mesh(pm));
|
||||
|
||||
if (allow_self_intersections)
|
||||
clip_volume=false;
|
||||
|
||||
if (clip_volume && !is_closed(pm)) clip_volume=false;
|
||||
if (clip_volume && !use_compact_clipper) use_compact_clipper=true;
|
||||
|
||||
auto fcc = get(dynamic_face_property_t<std::size_t>(), pm);
|
||||
|
||||
std::size_t nbcc = connected_components(pm, fcc, CGAL::parameters::edge_is_constrained_map(ecm));
|
||||
|
||||
std::vector<bool> classified(nbcc, false);
|
||||
std::vector<std::size_t> ccs_to_remove;
|
||||
|
||||
for (auto f : faces(pm))
|
||||
{
|
||||
case ON_NEGATIVE_SIDE:
|
||||
return true; // nothing to clip, the full mesh is on the negative side
|
||||
case ON_POSITIVE_SIDE:
|
||||
remove_all_elements(tm); // clear the mesh that is fully on the positive side
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
std::size_t ccid = get(fcc, f);
|
||||
if (classified[ccid]) continue;
|
||||
halfedge_descriptor hf = halfedge(f, pm);
|
||||
for(halfedge_descriptor h : CGAL::halfedges_around_face(hf, pm))
|
||||
{
|
||||
CGAL::Oriented_side os = get(vos, target(h,pm));
|
||||
if (os==CGAL::ON_ORIENTED_BOUNDARY) continue;
|
||||
classified[ccid]=true;
|
||||
if (os==CGAL::ON_POSITIVE_SIDE) ccs_to_remove.push_back(ccid);
|
||||
}
|
||||
|
||||
if (!classified[ccid])
|
||||
{
|
||||
if (!use_compact_clipper) ccs_to_remove.push_back(ccid);
|
||||
classified[ccid]=true;
|
||||
}
|
||||
}
|
||||
|
||||
const bool do_not_modify = choose_parameter(get_parameter(np, internal_np::allow_self_intersections), false);
|
||||
return clip(tm, clipper, np, params::do_not_modify(do_not_modify));
|
||||
remove_connected_components(pm, ccs_to_remove, fcc);
|
||||
|
||||
if (clip_volume)
|
||||
{
|
||||
std::vector<halfedge_descriptor> borders;
|
||||
extract_boundary_cycles(pm, std::back_inserter(borders));
|
||||
|
||||
for (halfedge_descriptor h : borders)
|
||||
{
|
||||
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION
|
||||
if (triangulate)
|
||||
{
|
||||
Euler::fill_hole(h, pm); // visitor call done in the triangulation visitor
|
||||
if constexpr (!has_visitor)
|
||||
{
|
||||
triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits));
|
||||
}
|
||||
else
|
||||
{
|
||||
using Base_visitor = std::remove_cv_t<std::remove_reference_t<Visitor_ref>>;
|
||||
internal::Visitor_wrapper_for_triangulate_face<PolygonMesh, Base_visitor> visitor_wrapper(pm, visitor);
|
||||
triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits).visitor(visitor_wrapper));
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
visitor.before_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, pm);
|
||||
Euler::fill_hole(h, pm);
|
||||
visitor.after_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, face(h, pm), pm);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -753,11 +904,11 @@ bool clip(TriangleMesh& tm,
|
|||
*
|
||||
* \brief 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`.
|
||||
* If `tm` is closed, the clipped part can be kept closed by setting the named parameter `clip_volume` to `true`.
|
||||
* See Subsection \ref coref_clip for more details.
|
||||
*
|
||||
* \note `Iso_cuboid_3` must be from the same %Kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Iso_cuboid_3` must be from the same %Kernel as the point of the vertex point map of `tm`.
|
||||
* \note `Iso_cuboid_3` must be from the same kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Iso_cuboid_3` must be from the same kernel as the point of the vertex point map of `tm`.
|
||||
*
|
||||
* \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink
|
||||
*
|
||||
|
|
@ -777,15 +928,6 @@ bool clip(TriangleMesh& tm,
|
|||
* \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close 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`
|
||||
|
|
@ -800,14 +942,24 @@ bool clip(TriangleMesh& tm,
|
|||
* \cgalParamDefault{`true`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close 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{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections are accepted for `tm`.}
|
||||
* \cgalParamDescription{If `true`, self-intersections in `tm` are accepted.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @return `true` if the output surface mesh is manifold.
|
||||
|
|
@ -949,90 +1101,109 @@ void split(TriangleMesh& tm,
|
|||
/**
|
||||
* \ingroup PMP_corefinement_grp
|
||||
*
|
||||
* adds intersection edges of `plane` and `tm` in `tm` and duplicates those edges.
|
||||
* splits a polygon mesh with a plane.
|
||||
*
|
||||
* \note `Plane_3` must be from the same %Kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Plane_3` must be from the same %Kernel as the point of the vertex point map of `tm`.
|
||||
* The polygon mesh is refined with the intersection edges, and those edges are duplicated as to create a boundary,
|
||||
* and thus separate connected components on either side of the plane.
|
||||
*
|
||||
* \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`.
|
||||
* @tparam PolygonMesh 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 pm input surface mesh
|
||||
* @param plane the plane that will be used to split `pm`.
|
||||
* `Plane_3` is the plane type for the same CGAL kernel as the point of the vertex point map of `pm`.
|
||||
* @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`}
|
||||
* \cgalParamDescription{a property map associating points to the vertices of `pm`}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<TriangleMesh>::%vertex_descriptor`
|
||||
* as key type and `%Point_3` as value type}
|
||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`}
|
||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, pm)`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{visitor}
|
||||
* \cgalParamDescription{a visitor used to track the creation of new faces}
|
||||
* \cgalParamDescription{a visitor used to track the creation of new faces, edges, and vertices.
|
||||
* Note that as there are no mesh associated with `plane`,
|
||||
* `boost::graph_traits<PolygonMesh>::null_halfedge()` and `boost::graph_traits<PolygonMesh>::null_face()` will be used when calling
|
||||
* functions of the visitor expecting a halfedge or a face from `plane`. Similarly, `pm` will be used as the mesh of `plane`.}}
|
||||
* \cgalParamType{a class model of `PMPCorefinementVisitor`}
|
||||
* \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close to the intersection of `tm`
|
||||
* \cgalParamDescription{If `true`, the set of triangles close to the intersection of `pm`
|
||||
* 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.}
|
||||
* will be thrown if at least one self-intersection is found.
|
||||
* This option is only taken into account if `pm` is a triangle mesh.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections are accepted for `tm`.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalParamNBegin{do_not_triangulate_faces}
|
||||
* \cgalParamDescription{If the input mesh is triangulated and this parameter is set to `false`, the mesh will be kept triangulated.
|
||||
* Always `true` if `pm` is not a triangle mesh.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @see `clip()`
|
||||
*/
|
||||
template <class TriangleMesh,
|
||||
template <class PolygonMesh,
|
||||
class NamedParameters = parameters::Default_named_parameters>
|
||||
void split(TriangleMesh& tm,
|
||||
void split(PolygonMesh& pm,
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
const Plane_3& plane,
|
||||
const Plane_3& plane,
|
||||
#else
|
||||
const typename GetGeomTraits<TriangleMesh, NamedParameters>::type::Plane_3& plane,
|
||||
const typename GetGeomTraits<PolygonMesh, NamedParameters>::type::Plane_3& plane,
|
||||
#endif
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
namespace params = CGAL::parameters;
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
using parameters::get_parameter_reference;
|
||||
|
||||
using params::get_parameter;
|
||||
using params::choose_parameter;
|
||||
using GT = typename GetGeomTraits<PolygonMesh, NamedParameters>::type;
|
||||
GT traits = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, pm));
|
||||
|
||||
// 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);
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::concurrency_tag_t,
|
||||
NamedParameters,
|
||||
Sequential_tag
|
||||
> ::type Concurrency_tag;
|
||||
|
||||
TriangleMesh splitter;
|
||||
CGAL::Oriented_side os = PMP::internal::clip_to_bbox(plane, bbox, splitter, params::default_values());
|
||||
// config flags
|
||||
const bool throw_on_self_intersection =
|
||||
choose_parameter(get_parameter(np, internal_np::throw_on_self_intersection), false);
|
||||
bool triangulate = !choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false);
|
||||
|
||||
if(os == CGAL::ON_ORIENTED_BOUNDARY)
|
||||
{
|
||||
const bool do_not_modify = choose_parameter(get_parameter(np, internal_np::allow_self_intersections), false);
|
||||
return split(tm, splitter, np, params::do_not_modify(do_not_modify));
|
||||
}
|
||||
auto vos = get(dynamic_vertex_property_t<Oriented_side>(), pm);
|
||||
auto ecm = get(dynamic_edge_property_t<bool>(), pm, false);
|
||||
|
||||
//else nothing to do, no intersection.
|
||||
if (triangulate && !is_triangle_mesh(pm))
|
||||
triangulate = false;
|
||||
|
||||
using Default_visitor = Corefinement::Default_visitor<PolygonMesh>;
|
||||
Default_visitor default_visitor;
|
||||
using Visitor_ref = typename internal_np::Lookup_named_param_def<internal_np::visitor_t, NamedParameters, Default_visitor>::reference;
|
||||
Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor);
|
||||
|
||||
refine_with_plane(pm, plane, parameters::vertex_oriented_side_map(vos)
|
||||
.edge_is_marked_map(ecm)
|
||||
.vertex_point_map(vpm)
|
||||
.geom_traits(traits)
|
||||
.do_not_triangulate_faces(!triangulate)
|
||||
.throw_on_self_intersection(throw_on_self_intersection)
|
||||
.concurrency_tag(Concurrency_tag())
|
||||
.visitor(std::ref(visitor)));
|
||||
|
||||
//split mesh along marked edges
|
||||
internal::split_along_edges(pm, ecm, vpm, visitor);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1041,8 +1212,8 @@ void split(TriangleMesh& tm,
|
|||
*
|
||||
* adds intersection edges of `iso_cuboid` and `tm` in `tm` and duplicates those edges.
|
||||
*
|
||||
* \note `Iso_cuboid_3` must be from the same %Kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Iso_cuboid_3` must be from the same %Kernel as the point of the vertex point map of `tm`.
|
||||
* \note `Iso_cuboid_3` must be from the same kernel as the point of the internal vertex point map of `TriangleMesh`.
|
||||
* \note `Iso_cuboid_3` must be from the same kernel as the point of the vertex point map of `tm`.
|
||||
*
|
||||
* \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink
|
||||
*
|
||||
|
|
@ -1068,15 +1239,6 @@ void split(TriangleMesh& tm,
|
|||
* \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close 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`
|
||||
|
|
@ -1091,14 +1253,24 @@ void split(TriangleMesh& tm,
|
|||
* \cgalParamDefault{`true`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* * \cgalParamNBegin{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections are accepted for `tm`.}
|
||||
* \cgalParamNBegin{throw_on_self_intersection}
|
||||
* \cgalParamDescription{If `true`, the set of triangles close 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{allow_self_intersections}
|
||||
* \cgalParamDescription{If `true`, self-intersections in `tm` are accepted.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`false`}
|
||||
* \cgalParamExtra{If this option is set to `true`, `tm` is no longer required to be without self-intersection.
|
||||
* Setting this option to `true` will automatically set `throw_on_self_intersection` to `false`
|
||||
* and `clip_volume` to `false`.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @see `clip()`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2016 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
//
|
||||
// Author(s) : Sebastien Loriot
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_INTERNAL_SELF_INTERSECTION_EXCEPTION_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_INTERNAL_SELF_INTERSECTION_EXCEPTION_H
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/corefinement.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Polygon_mesh_processing {
|
||||
namespace Corefinement {
|
||||
|
||||
struct Self_intersection_exception :
|
||||
public std::runtime_error
|
||||
{
|
||||
Self_intersection_exception()
|
||||
: std::runtime_error("Self-intersection detected in input mesh")
|
||||
{}
|
||||
};
|
||||
|
||||
} } } // end of CGAL::Polygon_mesh_processing::Corefinement
|
||||
|
||||
#endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_SELF_INTERSECTION_EXCEPTION_H
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/enum.h>
|
||||
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/Corefinement/Self_intersection_exception.h>
|
||||
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
#include <memory>
|
||||
|
|
@ -28,14 +29,6 @@ namespace CGAL {
|
|||
namespace Polygon_mesh_processing {
|
||||
namespace Corefinement {
|
||||
|
||||
struct Self_intersection_exception :
|
||||
public std::runtime_error
|
||||
{
|
||||
Self_intersection_exception()
|
||||
: std::runtime_error("Self-intersection detected in input mesh")
|
||||
{}
|
||||
};
|
||||
|
||||
template<class TriangleMesh, class EdgeToFaces>
|
||||
class Collect_face_bbox_per_edge_bbox {
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,516 @@
|
|||
// Copyright (c) 2024-2025 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
//
|
||||
// Author(s) : Sébastien Loriot
|
||||
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_REFINE_WITH_PLANE_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_REFINE_WITH_PLANE_H
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/corefinement.h>
|
||||
|
||||
#include <CGAL/Named_function_parameters.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/Corefinement/Self_intersection_exception.h>
|
||||
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_BOX_INTERSECTION_D
|
||||
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
|
||||
#endif
|
||||
|
||||
namespace CGAL {
|
||||
namespace Polygon_mesh_processing {
|
||||
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
template <class PolygonMesh>
|
||||
struct Default_cut_visitor
|
||||
{
|
||||
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
|
||||
using face_descriptor = typename boost::graph_traits<PolygonMesh>::face_descriptor;
|
||||
using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
|
||||
|
||||
/// @name Functions used for tracking face splits
|
||||
/// @{
|
||||
void before_subface_creations(face_descriptor /* f_split */, const PolygonMesh& /* pm */){}
|
||||
void after_subface_creations(const PolygonMesh& /* pm */){}
|
||||
void before_subface_created(const PolygonMesh& /* pm */){}
|
||||
void after_subface_created(face_descriptor /* f_new */, const PolygonMesh& /* pm */){}
|
||||
/// @}
|
||||
|
||||
/// @name Functions used for tracking edge splits and edge creation
|
||||
/// @{
|
||||
void before_edge_split(halfedge_descriptor, PolygonMesh&) {}
|
||||
void edge_split(halfedge_descriptor, PolygonMesh&) {}
|
||||
void after_edge_split(){}
|
||||
void add_retriangulation_edge(halfedge_descriptor, const PolygonMesh&){}
|
||||
/// @}
|
||||
|
||||
/// @name Functions used when a new vertex is created
|
||||
///@{
|
||||
void intersection_point_detected(std::size_t /* i_id */,
|
||||
int /* sdim */,
|
||||
halfedge_descriptor /* h_e */,
|
||||
halfedge_descriptor /* h_f */,
|
||||
const PolygonMesh& /* tm_e */,
|
||||
const PolygonMesh& /* tm_f */,
|
||||
bool /* is_target_coplanar */,
|
||||
bool /* is_source_coplanar */){}
|
||||
void new_vertex_added(std::size_t /* i_id */, vertex_descriptor /* v */, const PolygonMesh& /* pm */) {}
|
||||
///@}
|
||||
};
|
||||
|
||||
// TODO: doc me or hide me in the np
|
||||
template <class Kernel>
|
||||
struct Orthogonal_cut_plane_traits
|
||||
{
|
||||
using FT = typename Kernel::FT;
|
||||
using Plane_3 = std::pair<int, FT>;
|
||||
using Point_3 = typename Kernel::Point_3;
|
||||
|
||||
struct Oriented_side_3
|
||||
{
|
||||
Oriented_side operator()(const Plane_3& plane, const Point_3& p) const
|
||||
{
|
||||
if (p[plane.first]==plane.second) return ON_ORIENTED_BOUNDARY;
|
||||
return p[plane.first]<plane.second ? ON_NEGATIVE_SIDE : ON_POSITIVE_SIDE;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO here we should integrate the epsilon to reuse existing points (moving them on the grid as an option?)
|
||||
struct Construct_plane_line_intersection_point_3
|
||||
{
|
||||
Point_3 operator()(const Plane_3& plane, const Point_3& p, const Point_3& q)
|
||||
{
|
||||
//TODO: divide by largest value?
|
||||
FT alpha = (plane.second - q[plane.first]) / (p[plane.first] - q[plane.first]);
|
||||
std::array<FT,3> coords;
|
||||
for (int i=0;i<3;++i)
|
||||
{
|
||||
if (i==plane.first)
|
||||
coords[i] = plane.second;
|
||||
else
|
||||
coords[i] = (p[i]==q[i])?p[i]:p[i]*alpha +(1-alpha)*q[i];
|
||||
}
|
||||
return Point_3(coords[0], coords[1], coords[2]);
|
||||
}
|
||||
};
|
||||
|
||||
Oriented_side_3 oriented_side_3_object() const
|
||||
{
|
||||
return Oriented_side_3();
|
||||
}
|
||||
|
||||
Construct_plane_line_intersection_point_3 construct_plane_line_intersection_point_3_object() const
|
||||
{
|
||||
return Construct_plane_line_intersection_point_3();
|
||||
}
|
||||
|
||||
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_BOX_INTERSECTION_D
|
||||
// for does self-intersect
|
||||
using Segment_3 = typename Kernel::Segment_3;
|
||||
using Triangle_3 = typename Kernel::Triangle_3;
|
||||
using Construct_segment_3 = typename Kernel::Construct_segment_3;
|
||||
using Construct_triangle_3 =typename Kernel::Construct_triangle_3;
|
||||
using Do_intersect_3 = typename Kernel::Do_intersect_3;
|
||||
Construct_segment_3 construct_segment_3_object() const { return Construct_segment_3(); }
|
||||
Construct_triangle_3 construct_triangle_3_object() const { return Construct_triangle_3(); }
|
||||
Do_intersect_3 do_intersect_3_object() const { return Do_intersect_3(); }
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \ingroup PMP_corefinement_grp
|
||||
*
|
||||
* refines `pm` by inserting new vertices and new edges at the intersection of `plane` with `pm`.
|
||||
*
|
||||
* \tparam PolygonMesh a model of `HalfedgeListGraph`, `FaceListGraph`, and `MutableFaceGraph`
|
||||
* \tparam Plane_3 plane type, equal to `GeomTraits::Plane_3`, `GeomTraits` being the type of the parameter `geom_traits`.
|
||||
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param pm input mesh to be refined
|
||||
* \param plane the plane used to refine the mesh
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below:
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
*
|
||||
* \cgalParamNBegin{edge_is_constrained_map}
|
||||
* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `pm`.
|
||||
* If an edge marked as constrained is split, the two resulting edges will be marked as constrained.}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%edge_descriptor`
|
||||
* as key type and `bool` as value type}
|
||||
* \cgalParamDefault{unused}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{edge_is_marked_map}
|
||||
* \cgalParamDescription{a property map filled by this function with `true` for all intersection edges of faces
|
||||
* of `pm` and `plane`, and `false` for all other edges.}
|
||||
* \cgalParamType{a class model of `WritablePropertyMap` with `boost::graph_traits<PolygonMesh>::%edge_descriptor`
|
||||
* as key type and `bool` as value type}
|
||||
* \cgalParamDefault{unused}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{vertex_oriented_side_map}
|
||||
* \cgalParamDescription{a property map filled by this function containing the position
|
||||
* of each vertex relative to the oriented plane `plane`.}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%vertex_descriptor`
|
||||
* as key type and `Oriented_side` as value type}
|
||||
* \cgalParamDefault{Dynamic vertex property map}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{do_not_triangulate_faces}
|
||||
* \cgalParamDescription{If the input mesh is triangulated and this parameter is set to `false`, the mesh will be kept triangulated.}
|
||||
* \cgalParamType{Boolean}
|
||||
* \cgalParamDefault{`true`}
|
||||
* \cgalParamExtra{The function `triangulate_faces()` can be used to triangule faces before calling this function.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{vertex_point_map}
|
||||
* \cgalParamDescription{a property map associating points to the vertices of `pm`}
|
||||
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%vertex_descriptor`
|
||||
* as key type and `GeomTraits::Point_3` as value type, `GeomTraits` being the type of the parameter `geom_traits`}
|
||||
* \cgalParamDefault{`boost::get(CGAL::vertex_point, pm)`}
|
||||
* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` must be available in `PolygonMesh`.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \sa `split()`
|
||||
* \sa `clip()`
|
||||
*
|
||||
*/
|
||||
template <class PolygonMesh, class Plane_3, class NamedParameters = parameters::Default_named_parameters>
|
||||
void refine_with_plane(PolygonMesh& pm,
|
||||
const Plane_3& plane,
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
{
|
||||
// TODO: concurrency tag
|
||||
/*
|
||||
* \cgalParamNBegin{concurrency_tag}
|
||||
* \cgalParamDescription{a tag indicating if the task should be done using one or several threads.}
|
||||
* \cgalParamType{Either `CGAL::Sequential_tag`, or `CGAL::Parallel_tag`, or `CGAL::Parallel_if_available_tag`}
|
||||
* \cgalParamDefault{`CGAL::Sequential_tag`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* + for vertex_oriented_side_map
|
||||
* ---> \cgalParamExtra{If parallelism is used, concurrent accesses to the property map must be safe.}
|
||||
|
||||
*/
|
||||
// TODO: if you want to clip with many planes (**Kernel**),
|
||||
// it might be interesting to first classify all vertices with all planes
|
||||
// to limit the number of intersection points computed: several classify done lazily
|
||||
// on points not already eliminated (verices all out with adjacent vertices out too)
|
||||
// actually might be a global classifier to filter out edges, testing all planes at once per vertex and stop as soon as one is out
|
||||
// TODO: doc visitor
|
||||
/*
|
||||
* \cgalParamNBegin{visitor}
|
||||
* \cgalParamDescription{TODO add concept}
|
||||
* \cgalParamType{reference wrapper recommeded if it has state TODO}
|
||||
* \cgalParamDefault{None}
|
||||
* \cgalParamNEnd
|
||||
*/
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
using parameters::get_parameter_reference;
|
||||
using parameters::is_default_parameter;
|
||||
|
||||
// graph typedefs
|
||||
using BGT = boost::graph_traits<PolygonMesh>;
|
||||
using face_descriptor = typename BGT::face_descriptor;
|
||||
using edge_descriptor = typename BGT::edge_descriptor;
|
||||
using halfedge_descriptor = typename BGT::halfedge_descriptor;
|
||||
using vertex_descriptor = typename BGT::vertex_descriptor;
|
||||
|
||||
// np typedefs
|
||||
using Default_ecm = Static_boolean_property_map<edge_descriptor, false>;
|
||||
using Default_visitor = Default_cut_visitor<PolygonMesh>;
|
||||
using Visitor_ref = typename internal_np::Lookup_named_param_def<internal_np::visitor_t, NamedParameters, Default_visitor>::reference;
|
||||
using GT = typename GetGeomTraits<PolygonMesh, NamedParameters>::type;
|
||||
GT traits = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
static_assert(std::is_same_v<typename GT::Plane_3,Plane_3>);
|
||||
|
||||
auto ecm = choose_parameter<Default_ecm>(get_parameter(np, internal_np::edge_is_constrained));
|
||||
auto edge_is_marked = choose_parameter<Default_ecm>(get_parameter(np, internal_np::edge_is_marked_map));
|
||||
|
||||
Default_visitor default_visitor;
|
||||
Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor);
|
||||
constexpr bool has_visitor = !std::is_same_v<Default_visitor, std::remove_cv_t<std::remove_reference_t<Visitor_ref>>>;
|
||||
|
||||
auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, pm));
|
||||
|
||||
bool triangulate = !choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), true);
|
||||
if (triangulate && !is_triangle_mesh(pm))
|
||||
triangulate = false;
|
||||
|
||||
bool throw_on_self_intersection = choose_parameter(get_parameter(np, internal_np::use_compact_clipper), false);
|
||||
if (throw_on_self_intersection && !is_triangle_mesh(pm))
|
||||
throw_on_self_intersection = false;
|
||||
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::concurrency_tag_t,
|
||||
NamedParameters,
|
||||
Sequential_tag
|
||||
> ::type Concurrency_tag;
|
||||
|
||||
// constexpr bool parallel_execution = std::is_same_v<Parallel_tag, Concurrency_tag>;
|
||||
|
||||
auto oriented_side = traits.oriented_side_3_object();
|
||||
auto intersection_point = traits.construct_plane_line_intersection_point_3_object();
|
||||
|
||||
// TODO: the default is not thread-safe for example for Polyhedron
|
||||
using V_os_tag = dynamic_vertex_property_t<Oriented_side>;
|
||||
static constexpr bool use_default_vosm =
|
||||
is_default_parameter<NamedParameters, internal_np::vertex_oriented_side_map_t>::value;
|
||||
|
||||
using Vertex_oriented_side_map =
|
||||
std::conditional_t<use_default_vosm,
|
||||
typename boost::property_map<PolygonMesh, V_os_tag>::type,
|
||||
typename internal_np::Get_param<typename NamedParameters::base,
|
||||
internal_np::vertex_oriented_side_map_t>::type>;
|
||||
|
||||
|
||||
Vertex_oriented_side_map vertex_os;
|
||||
if constexpr (use_default_vosm)
|
||||
vertex_os = get(V_os_tag(), pm);
|
||||
else
|
||||
vertex_os = get_parameter(np, internal_np::vertex_oriented_side_map);
|
||||
|
||||
std::vector<edge_descriptor> inters;
|
||||
|
||||
bool all_in = true;
|
||||
bool all_out = true;
|
||||
bool at_least_one_on = false;
|
||||
std::vector<vertex_descriptor> on_obnd;
|
||||
//TODO: parallel for
|
||||
for (vertex_descriptor v : vertices(pm))
|
||||
{
|
||||
Oriented_side os = oriented_side(plane, get(vpm, v));
|
||||
put(vertex_os,v,os);
|
||||
switch(os)
|
||||
{
|
||||
case ON_POSITIVE_SIDE:
|
||||
all_in = false;
|
||||
break;
|
||||
case ON_NEGATIVE_SIDE:
|
||||
all_out = false;
|
||||
break;
|
||||
case ON_ORIENTED_BOUNDARY:
|
||||
at_least_one_on=true;
|
||||
visitor.intersection_point_detected(on_obnd.size(), 2, halfedge(v, pm), boost::graph_traits<PolygonMesh>::null_halfedge(),
|
||||
pm, pm, true, false);
|
||||
on_obnd.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (at_least_one_on || (!all_in && !all_out))
|
||||
{
|
||||
//TODO: parallel for
|
||||
for(edge_descriptor e : edges(pm))
|
||||
{
|
||||
vertex_descriptor src = source(e,pm), tgt = target(e,pm);
|
||||
if (get(vertex_os, src)==CGAL::ON_ORIENTED_BOUNDARY)
|
||||
{
|
||||
if (get(vertex_os, tgt)==CGAL::ON_ORIENTED_BOUNDARY)
|
||||
{
|
||||
bool pure_coplanar=true;
|
||||
if (!is_border(e, pm))
|
||||
{
|
||||
halfedge_descriptor he=halfedge(e, pm);
|
||||
for (halfedge_descriptor h : halfedges_around_face(he,pm))
|
||||
if (get(vertex_os,target(h, pm))!=CGAL::ON_ORIENTED_BOUNDARY)
|
||||
{
|
||||
pure_coplanar=false;
|
||||
break;
|
||||
}
|
||||
if (pure_coplanar)
|
||||
{
|
||||
he=opposite(he, pm);
|
||||
for (halfedge_descriptor h : halfedges_around_face(he,pm))
|
||||
if (get(vertex_os, target(h, pm))!=CGAL::ON_ORIENTED_BOUNDARY)
|
||||
{
|
||||
pure_coplanar=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!pure_coplanar)
|
||||
put(edge_is_marked, e, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (get(vertex_os, tgt)!=CGAL::ON_ORIENTED_BOUNDARY &&
|
||||
get(vertex_os, src)!=get(vertex_os, tgt))
|
||||
{
|
||||
visitor.intersection_point_detected(on_obnd.size()+inters.size(), 2, halfedge(e, pm), boost::graph_traits<PolygonMesh>::null_halfedge(),
|
||||
pm, pm, false, false);
|
||||
inters.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_in || all_out)
|
||||
{
|
||||
//visitor.vertices_on_cut(on_obnd, pm);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<face_descriptor, std::vector<halfedge_descriptor> > splitted_faces;
|
||||
|
||||
if (throw_on_self_intersection)
|
||||
{
|
||||
std::vector<face_descriptor> test_faces;
|
||||
for (edge_descriptor e : inters)
|
||||
{
|
||||
halfedge_descriptor h = halfedge(e, pm);
|
||||
if (!is_border(h,pm))
|
||||
test_faces.push_back(face(h,pm));
|
||||
h=opposite(h, pm);
|
||||
if (!is_border(h,pm))
|
||||
test_faces.push_back(face(h,pm));
|
||||
}
|
||||
std::sort(test_faces.begin(), test_faces.end());
|
||||
auto last = std::unique(test_faces.begin(), test_faces.end());
|
||||
test_faces.erase(last, test_faces.end());
|
||||
if (does_self_intersect<Concurrency_tag>(test_faces, pm, np))
|
||||
throw Corefinement::Self_intersection_exception();
|
||||
}
|
||||
|
||||
//TODO: parallel for
|
||||
std::size_t vid=on_obnd.size();
|
||||
for (edge_descriptor e : inters)
|
||||
{
|
||||
halfedge_descriptor h = halfedge(e, pm);
|
||||
auto pts = CGAL::make_sorted_pair(get(vpm, source(h,pm)), get(vpm, target(h, pm)));
|
||||
typename GT::Point_3 ip = intersection_point(plane, pts.first, pts.second);
|
||||
|
||||
bool was_marked = get(ecm, edge(h, pm));
|
||||
visitor.before_edge_split(h, pm);
|
||||
h = CGAL::Euler::split_edge(h, pm);
|
||||
put(vpm, target(h, pm), ip);
|
||||
visitor.new_vertex_added(vid, target(h,pm), pm);
|
||||
put(vertex_os, target(h, pm), ON_ORIENTED_BOUNDARY);
|
||||
visitor.edge_split(h, pm);
|
||||
visitor.after_edge_split();
|
||||
if (was_marked)
|
||||
put(ecm, edge(h, pm), true);
|
||||
|
||||
if (!is_border(h, pm))
|
||||
splitted_faces[face(h, pm)].push_back(h);
|
||||
h=prev(opposite(h,pm),pm);
|
||||
if (!is_border(h, pm))
|
||||
splitted_faces[face(h, pm)].push_back(h);
|
||||
++vid;
|
||||
}
|
||||
|
||||
// visitor.vertices_on_cut(on_obnd, pm);
|
||||
|
||||
// collect faces to be cut that have one vertex on the cut plane
|
||||
for (vertex_descriptor v : on_obnd)
|
||||
{
|
||||
halfedge_descriptor hv = halfedge(v, pm);
|
||||
for (halfedge_descriptor h : halfedges_around_target(hv, pm))
|
||||
{
|
||||
if (is_border(h, pm)) continue;
|
||||
Oriented_side prev_ori = get(vertex_os, source(h, pm)),
|
||||
next_ori = get(vertex_os, target(next(h, pm), pm));
|
||||
if (prev_ori == ON_ORIENTED_BOUNDARY || next_ori == ON_ORIENTED_BOUNDARY) continue; // skip full edge
|
||||
if (prev_ori!=next_ori) splitted_faces[face(h, pm)].push_back(h); // skip tangency point
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: parallel for
|
||||
for (std::pair<const face_descriptor, std::vector<halfedge_descriptor>>& f_and_hs : splitted_faces)
|
||||
{
|
||||
std::size_t nb_hedges = f_and_hs.second.size();
|
||||
|
||||
CGAL_assertion( nb_hedges%2 ==0 );
|
||||
|
||||
visitor.before_subface_creations(f_and_hs.first, pm);
|
||||
|
||||
if (nb_hedges==2)
|
||||
{
|
||||
halfedge_descriptor h1=f_and_hs.second[0], h2=f_and_hs.second[1];
|
||||
CGAL_assertion(next(h1,pm)!=h2 && next(h2,pm)!=h1); // the edge does not already exist
|
||||
visitor.before_subface_created(pm);
|
||||
halfedge_descriptor res = CGAL::Euler::split_face(h1, h2, pm);
|
||||
visitor.after_subface_created(face(h2, pm), pm);
|
||||
put(edge_is_marked, edge(res, pm), true);
|
||||
visitor.add_retriangulation_edge(res, pm);
|
||||
|
||||
if (triangulate)
|
||||
{
|
||||
if (!is_triangle(res, pm))
|
||||
{
|
||||
// TODO: take the criteria in triangulate_faces ?
|
||||
visitor.before_subface_created(pm);
|
||||
halfedge_descriptor newh =
|
||||
CGAL::Euler::split_face(res, next(next(res, pm), pm), pm);
|
||||
visitor.after_subface_created(face(opposite(newh, pm), pm), pm);
|
||||
visitor.add_retriangulation_edge(newh, pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = opposite(res, pm);
|
||||
if (!is_triangle(res, pm))
|
||||
{
|
||||
// TODO: take the criteria in triangulate_faces ?
|
||||
visitor.before_subface_created(pm);
|
||||
halfedge_descriptor newh =
|
||||
CGAL::Euler::split_face(res, next(next(res, pm), pm), pm);
|
||||
visitor.after_subface_created(face(opposite(newh, pm), pm), pm);
|
||||
visitor.add_retriangulation_edge(newh, pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// sort hedges to make them match
|
||||
CGAL_assertion(!triangulate);
|
||||
// TODO: need mechanism to make it robust even with EPICK
|
||||
auto less_hedge = [&pm, vpm](halfedge_descriptor h1, halfedge_descriptor h2)
|
||||
{
|
||||
return lexicographically_xyz_smaller(get(vpm,target(h1,pm)), get(vpm,target(h2,pm)));
|
||||
};
|
||||
std::sort(f_and_hs.second.begin(), f_and_hs.second.end(), less_hedge);
|
||||
|
||||
for (std::size_t i=0; i<nb_hedges; i+=2)
|
||||
{
|
||||
halfedge_descriptor h1=f_and_hs.second[i], h2=f_and_hs.second[i+1];
|
||||
CGAL_assertion(next(h1,pm)!=h2 && next(h2,pm)!=h1); // the edge does not already exist
|
||||
visitor.before_subface_created(pm);
|
||||
halfedge_descriptor res = CGAL::Euler::split_face(h1, h2, pm);
|
||||
if constexpr (has_visitor)
|
||||
{
|
||||
if (face(h1,pm)!=face(h2,pm))
|
||||
visitor.after_subface_created(face(h2, pm), pm);
|
||||
visitor.add_retriangulation_edge(res, pm);
|
||||
}
|
||||
// put(edge_is_marked, edge(res, pm), true);
|
||||
}
|
||||
}
|
||||
|
||||
visitor.after_subface_creations(pm);
|
||||
}
|
||||
}
|
||||
|
||||
} } // CGAL::Polygon_mesh_processing
|
||||
|
||||
|
||||
#endif // CGAL_POLYGON_MESH_PROCESSING_REFINE_WITH_PLANE_H
|
||||
|
|
@ -119,9 +119,10 @@ private:
|
|||
if(first)
|
||||
first = false;
|
||||
else
|
||||
{
|
||||
f = add_face(pmesh);
|
||||
|
||||
visitor.after_subface_created(f);
|
||||
visitor.after_subface_created(f);
|
||||
}
|
||||
|
||||
std::array<int, 4> indices = make_array(triangle.first,
|
||||
triangle.second,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
OFF
|
||||
6 1 0
|
||||
|
||||
3 1 0
|
||||
3 0 0
|
||||
1 1 0
|
||||
0 0 0
|
||||
0 2 0
|
||||
2 2 0
|
||||
6 0 2 5 4 3 1
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
OFF
|
||||
16 1 0
|
||||
|
||||
1 5 0
|
||||
1 2 0
|
||||
4 1 0
|
||||
1 6 0
|
||||
1 1 0
|
||||
4 0 0
|
||||
4 4 0
|
||||
1 4 0
|
||||
1 3 0
|
||||
4 6 0
|
||||
4 7 0
|
||||
0 0 0
|
||||
4 2 0
|
||||
4 3 0
|
||||
0 7 0
|
||||
4 5 0
|
||||
16 8 7 6 15 0 3 9 10 14 11 5 2 4 1 12 13
|
||||
|
||||
|
|
@ -106,5 +106,5 @@ void test()
|
|||
int main()
|
||||
{
|
||||
test<Surface_mesh>();
|
||||
// test<Polyhedron>();
|
||||
test<Polyhedron>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,11 +148,10 @@ void test()
|
|||
assert(false);
|
||||
return ;
|
||||
}
|
||||
|
||||
PMP::clip(tm1, K::Plane_3(0,0,1,-4.5),
|
||||
params::throw_on_self_intersection(true)
|
||||
.allow_self_intersections(true));
|
||||
assert(vertices(tm1).size() == 16);
|
||||
assert(vertices(tm1).size() == 13);
|
||||
}
|
||||
|
||||
// clipping with identity
|
||||
|
|
@ -321,7 +320,7 @@ void test()
|
|||
TriangleMesh tm1;
|
||||
make_triangle( K::Point_3(0, 0, 0), K::Point_3(0, 1, 0), K::Point_3(1, 0, 0), tm1 );
|
||||
PMP::clip(tm1, K::Plane_3(0, -1, 0 , 0));
|
||||
assert(vertices(tm1).size() == 4);
|
||||
assert(vertices(tm1).size() == 3);
|
||||
}
|
||||
|
||||
// test with clipper on border edge: full triangle
|
||||
|
|
@ -394,31 +393,31 @@ void test()
|
|||
|
||||
// -> closed mesh, true/true
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(true).clip_volume(true));
|
||||
assert(faces(tm1).size() == 14);
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(CGAL::is_closed(tm1));
|
||||
|
||||
// -> closed mesh, false/true
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(false).clip_volume(true));
|
||||
assert(faces(tm1).size() == 14);
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(CGAL::is_closed(tm1));
|
||||
|
||||
// -> closed mesh, true/false
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(true).clip_volume(false));
|
||||
assert(faces(tm1).size() == 14);
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(CGAL::is_closed(tm1));
|
||||
|
||||
// -> closed mesh, false/false
|
||||
PMP::clip(tm1, K::Plane_3(1,0,0,-1), params::use_compact_clipper(false).clip_volume(false));
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(faces(tm1).size() == 10);
|
||||
assert(!CGAL::is_closed(tm1));
|
||||
|
||||
// -> open mesh true/true
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(true).clip_volume(true));
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(faces(tm1).size() == 10);
|
||||
|
||||
// -> open mesh true/false
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(true).clip_volume(false));
|
||||
assert(faces(tm1).size() == 12);
|
||||
assert(faces(tm1).size() == 10);
|
||||
|
||||
// -> open mesh false/false
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,0), params::use_compact_clipper(false).clip_volume(false));
|
||||
|
|
@ -463,7 +462,7 @@ void test()
|
|||
ss >> tm1;
|
||||
CGAL::Euler::remove_face(halfedge(*std::prev(faces(tm1).end()),tm1),tm1);
|
||||
PMP::clip(tm1, K::Plane_3(-1,0,0,2));
|
||||
assert(vertices(tm1).size() == 6);
|
||||
assert(vertices(tm1).size() == 5);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -511,7 +510,7 @@ void test()
|
|||
std::ifstream("data-coref/open_large_cube.off") >> tm1;
|
||||
std::size_t nbv = vertices(tm1).size();
|
||||
PMP::clip(tm1, K::Plane_3(0,0,1,-1), CGAL::parameters::use_compact_clipper(true));
|
||||
assert(vertices(tm1).size()==nbv+2); // because of the plane diagonal
|
||||
assert(vertices(tm1).size()==nbv);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -526,7 +525,7 @@ void test()
|
|||
std::ifstream("data-coref/open_large_cube.off") >> tm1;
|
||||
std::size_t nbv = vertices(tm1).size();
|
||||
PMP::clip(tm1, K::Plane_3(0,0,1,-1), CGAL::parameters::use_compact_clipper(true).allow_self_intersections(true));
|
||||
assert(vertices(tm1).size()==nbv+2); // because of the plane diagonal
|
||||
assert(vertices(tm1).size()==nbv);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -540,7 +539,7 @@ void test()
|
|||
TriangleMesh tm1;
|
||||
std::ifstream("data-coref/open_large_cube.off") >> tm1;
|
||||
PMP::clip(tm1, K::Plane_3(0,0,-1,1), CGAL::parameters::use_compact_clipper(true));
|
||||
assert(vertices(tm1).size()==178);
|
||||
assert(vertices(tm1).size()==176);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -554,7 +553,7 @@ void test()
|
|||
TriangleMesh tm1;
|
||||
std::ifstream("data-coref/open_large_cube.off") >> tm1;
|
||||
PMP::clip(tm1, K::Plane_3(0,0,-1,1), CGAL::parameters::use_compact_clipper(true).allow_self_intersections(true));
|
||||
assert(vertices(tm1).size()==178);
|
||||
assert(vertices(tm1).size()==176);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -581,9 +580,10 @@ void test_split_plane()
|
|||
assert(meshes.size() == 3);
|
||||
//if the order is not deterministc, put the num_vertices in a list and check
|
||||
//if the list does contain all those numbers.
|
||||
assert(num_vertices(meshes[2]) == 48);
|
||||
assert(num_vertices(meshes[0]) == 1527);
|
||||
assert(num_vertices(meshes[1]) == 1674);
|
||||
|
||||
assert(num_vertices(meshes[2]) == 46);
|
||||
assert(num_vertices(meshes[0]) == 1523);
|
||||
assert(num_vertices(meshes[1]) == 1668);
|
||||
|
||||
CGAL::clear(tm1);
|
||||
meshes.clear();
|
||||
|
|
@ -602,7 +602,7 @@ void test_split_plane()
|
|||
|
||||
PMP::split(tm1,K::Plane_3(0,0,1,-1));
|
||||
PMP::split_connected_components(tm1, meshes, params::default_values());
|
||||
assert(meshes.size() == 281);
|
||||
assert(meshes.size() == 2);
|
||||
|
||||
CGAL::clear(tm1);
|
||||
meshes.clear();
|
||||
|
|
@ -642,8 +642,8 @@ void test_split_plane()
|
|||
assert(meshes.size() == 2);
|
||||
//if the order is not deterministc, put the num_vertices in a list and check
|
||||
//if the list does contain all those numbers.
|
||||
assert(num_vertices(meshes[0]) == 16);
|
||||
assert(num_vertices(meshes[1]) == 16);
|
||||
assert(num_vertices(meshes[0]) == 12);
|
||||
assert(num_vertices(meshes[1]) == 12);
|
||||
|
||||
CGAL::clear(tm1);
|
||||
meshes.clear();
|
||||
|
|
@ -856,6 +856,286 @@ void test_isocuboid()
|
|||
assert(vertices(meshes[0]).size() == 20);
|
||||
assert(vertices(meshes[1]).size() == 4);
|
||||
}
|
||||
|
||||
template <class TriangleMesh>
|
||||
void test_new_clip()
|
||||
{
|
||||
{
|
||||
TriangleMesh e;
|
||||
std::ifstream("data-clip/ee.off") >> e;
|
||||
PMP::refine_with_plane(e, K::Plane_3(1,0,0,-2));
|
||||
assert(faces(e).size()==5);
|
||||
assert(vertices(e).size()==24);
|
||||
}
|
||||
|
||||
{
|
||||
TriangleMesh c;
|
||||
std::ifstream("data-clip/c.off") >> c;
|
||||
PMP::refine_with_plane(c, K::Plane_3(1,0,0,-2));
|
||||
assert(faces(c).size()==2);
|
||||
assert(vertices(c).size()==8);
|
||||
}
|
||||
|
||||
{
|
||||
TriangleMesh e;
|
||||
std::ifstream("data-clip/ee.off") >> e;
|
||||
PMP::triangulate_faces(e);
|
||||
PMP::refine_with_plane(e, K::Plane_3(1,0,0,-2), CGAL::parameters::do_not_triangulate_faces(false));
|
||||
assert(faces(e).size()==30);
|
||||
assert(vertices(e).size()==28);
|
||||
}
|
||||
|
||||
{
|
||||
TriangleMesh c;
|
||||
std::ifstream("data-clip/c.off") >> c;
|
||||
PMP::triangulate_faces(c);
|
||||
PMP::refine_with_plane(c, K::Plane_3(1,0,0,-2), CGAL::parameters::do_not_triangulate_faces(false));
|
||||
assert(faces(c).size()==8);
|
||||
assert(vertices(c).size()==9);
|
||||
}
|
||||
|
||||
{
|
||||
TriangleMesh ele;
|
||||
std::ifstream(CGAL::data_file_path("meshes/elephant.off")) >> ele;
|
||||
PMP::clip(ele, K::Plane_3(1,0,0,0), CGAL::parameters::do_not_triangulate_faces(true).clip_volume(true));
|
||||
PMP::clip(ele, K::Plane_3(0,1,0,0), CGAL::parameters::do_not_triangulate_faces(true).clip_volume(true));
|
||||
PMP::clip(ele, K::Plane_3(0,0,1,0), CGAL::parameters::do_not_triangulate_faces(true).clip_volume(true));
|
||||
assert(faces(ele).size()==1220);
|
||||
assert(vertices(ele).size()==691);
|
||||
}
|
||||
}
|
||||
|
||||
struct Clip_and_split_visitor
|
||||
{
|
||||
using halfedge_descriptor = typename boost::graph_traits<Surface_mesh>::halfedge_descriptor;
|
||||
using face_descriptor = typename boost::graph_traits<Surface_mesh>::face_descriptor;
|
||||
using vertex_descriptor = typename boost::graph_traits<Surface_mesh>::vertex_descriptor;
|
||||
|
||||
Surface_mesh& sm;
|
||||
Surface_mesh::Property_map<Surface_mesh::Face_index, int> fid_map;
|
||||
Surface_mesh::Property_map<Surface_mesh::Vertex_index, int> vid_map;
|
||||
Surface_mesh::Property_map<Surface_mesh::Halfedge_index, int> hid_map;
|
||||
|
||||
int fid=-1;
|
||||
int hid=-1;
|
||||
std::size_t nbf=0;
|
||||
std::size_t nbe=0;
|
||||
std::size_t nb_input_v=sm.number_of_vertices();
|
||||
std::size_t nb_input_v_on=0;
|
||||
std::size_t nb_new_v_on=0;
|
||||
|
||||
Clip_and_split_visitor(Surface_mesh& sm)
|
||||
: sm(sm)
|
||||
{
|
||||
bool is_new=false;
|
||||
std::tie(fid_map, is_new)=sm.add_property_map<Surface_mesh::Face_index,int>("f:id", -1);
|
||||
assert(is_new);
|
||||
int i=0; for (auto f : faces(sm)) put(fid_map, f, i++);
|
||||
std::tie(vid_map, is_new)=sm.add_property_map<Surface_mesh::Vertex_index,int>("v:id", -1);
|
||||
assert(is_new);
|
||||
i=0; for (auto v : vertices(sm)) put(vid_map, v, i++);
|
||||
std::tie(hid_map, is_new)=sm.add_property_map<Surface_mesh::Halfedge_index,int>("h:id", -1);
|
||||
assert(is_new);
|
||||
i=0;
|
||||
for (auto e : edges(sm))
|
||||
{
|
||||
auto h = halfedge(e, sm);
|
||||
put(hid_map, opposite(h, sm), i);
|
||||
put(hid_map, h, i++);
|
||||
}
|
||||
}
|
||||
|
||||
void before_subface_creations(face_descriptor f_split, const Surface_mesh& pm)
|
||||
{
|
||||
fid=get(fid_map, f_split);
|
||||
assert(fid!=-1);
|
||||
assert(&pm==&sm);
|
||||
}
|
||||
void after_subface_creations(const Surface_mesh& pm)
|
||||
{
|
||||
assert(&pm==&sm);
|
||||
}
|
||||
void before_subface_created(const Surface_mesh& pm)
|
||||
{
|
||||
nbf=sm.number_of_faces();
|
||||
assert(&pm==&sm);
|
||||
}
|
||||
void after_subface_created(face_descriptor f_new, const Surface_mesh& pm)
|
||||
{
|
||||
assert(&pm==&sm);
|
||||
assert(get(fid_map, f_new)==-1);
|
||||
put(fid_map, f_new, fid);
|
||||
assert(nbf+1==sm.number_of_faces());
|
||||
}
|
||||
|
||||
void before_edge_split(halfedge_descriptor h, Surface_mesh& pm)
|
||||
{
|
||||
hid=get(hid_map, h);
|
||||
assert(hid!=-1);
|
||||
assert(&pm==&sm);
|
||||
nbe=sm.number_of_edges();
|
||||
}
|
||||
void edge_split(halfedge_descriptor hnew, Surface_mesh& pm)
|
||||
{
|
||||
assert(&pm==&sm);
|
||||
assert(get(hid_map, hnew)==-1);
|
||||
assert(hid!=-1);
|
||||
put(hid_map, hnew, hid);
|
||||
put(hid_map, opposite(hnew, sm), hid);
|
||||
assert(nbe+1==sm.number_of_edges());
|
||||
}
|
||||
void after_edge_split()
|
||||
{
|
||||
assert(nbe+1==sm.number_of_edges());
|
||||
}
|
||||
void add_retriangulation_edge(halfedge_descriptor hnew, const Surface_mesh& pm)
|
||||
{
|
||||
assert(get(hid_map, hnew)==-1);
|
||||
put(hid_map, hnew, -2);
|
||||
put(hid_map, opposite(hnew, sm), -2);
|
||||
assert(&pm==&sm);
|
||||
}
|
||||
|
||||
void intersection_point_detected(std::size_t /* i_id */,
|
||||
int /* sdim */,
|
||||
halfedge_descriptor h_e,
|
||||
halfedge_descriptor h_f,
|
||||
const Surface_mesh& tm_e,
|
||||
const Surface_mesh& tm_f,
|
||||
bool is_target_coplanar,
|
||||
bool is_source_coplanar)
|
||||
{
|
||||
assert(is_source_coplanar==false);
|
||||
assert(h_f==boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
assert(h_e!=boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
if (!is_target_coplanar)
|
||||
++nb_new_v_on;
|
||||
else
|
||||
++nb_input_v_on;
|
||||
assert(&tm_e==&sm);
|
||||
assert(&tm_f==&sm);
|
||||
}
|
||||
|
||||
void new_vertex_added(std::size_t id, vertex_descriptor v, const Surface_mesh& pm)
|
||||
{
|
||||
assert(&pm==&sm);
|
||||
assert(get(vid_map, v)==-1);
|
||||
put(vid_map, v, nb_input_v+id-nb_input_v_on);
|
||||
}
|
||||
|
||||
void before_face_copy(face_descriptor f, const Surface_mesh& src, const Surface_mesh& tgt)
|
||||
{
|
||||
assert(f==boost::graph_traits<Surface_mesh>::null_face());
|
||||
assert(&src==&sm);
|
||||
assert(&tgt==&sm);
|
||||
}
|
||||
|
||||
void after_face_copy(face_descriptor fsrc, const Surface_mesh& src, face_descriptor ftgt, const Surface_mesh& tgt)
|
||||
{
|
||||
assert(fsrc==boost::graph_traits<Surface_mesh>::null_face());
|
||||
assert(ftgt!=boost::graph_traits<Surface_mesh>::null_face());
|
||||
//assert(get(fid_map, ftgt)==-1);
|
||||
put(fid_map, ftgt, -2);
|
||||
assert(&src==&sm);
|
||||
assert(&tgt==&sm);
|
||||
for (auto h : halfedges_around_face(halfedge(ftgt, sm), sm))
|
||||
{
|
||||
if (get(hid_map, h)==-1)
|
||||
put(hid_map, h, -2);
|
||||
}
|
||||
}
|
||||
|
||||
void before_edge_duplicated(halfedge_descriptor h, Surface_mesh& tm)
|
||||
{
|
||||
hid = get(hid_map, h);
|
||||
assert(hid!=-1);
|
||||
assert(&tm==&sm);
|
||||
}
|
||||
|
||||
void after_edge_duplicated(halfedge_descriptor h, halfedge_descriptor new_hedge, Surface_mesh& tm)
|
||||
{
|
||||
assert(hid==get(hid_map, h));
|
||||
assert(&tm==&sm);
|
||||
put(hid_map, new_hedge, hid);
|
||||
put(hid_map, opposite(new_hedge, sm), hid);
|
||||
}
|
||||
|
||||
void before_vertex_copy(vertex_descriptor v, Surface_mesh& src, Surface_mesh& tgt)
|
||||
{
|
||||
assert(&src==&sm);
|
||||
assert(&tgt==&sm);
|
||||
assert(get(vid_map, v)!=-1);
|
||||
}
|
||||
void after_vertex_copy(vertex_descriptor v, Surface_mesh& src, vertex_descriptor nv, Surface_mesh& tgt)
|
||||
{
|
||||
assert(&src==&sm);
|
||||
assert(&tgt==&sm);
|
||||
assert(get(vid_map, v)!=-1);
|
||||
put(vid_map, nv, get(vid_map, v));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check()
|
||||
{
|
||||
for (auto f :faces(sm))
|
||||
{
|
||||
if (get(fid_map, f)==-1) std::cout << sm.point(source(halfedge(f, sm), sm)) << " " << sm.point(target(halfedge(f, sm), sm)) << " " << sm.point(target(next(halfedge(f, sm), sm), sm)) << "\n";
|
||||
assert(get(fid_map, f)!=-1);
|
||||
}
|
||||
for (auto h :halfedges(sm))
|
||||
{
|
||||
if (get(hid_map, h)==-1)
|
||||
std::cout << sm.point(source(h, sm)) << " " << sm.point(target(h,sm)) << "\n";
|
||||
assert(get(hid_map, h)!=-1);
|
||||
}
|
||||
std::size_t nbv_max=nb_input_v+nb_new_v_on;
|
||||
for (auto v :vertices(sm))
|
||||
assert(get(vid_map, v)!=-1 && get(vid_map, v)<(int)nbv_max);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void test_clip_and_split_with_plane_visitor()
|
||||
{
|
||||
auto test_clip =[](std::string fname, const K::Plane_3& plane, bool triangulate, bool clip_volume)
|
||||
{
|
||||
std::cout << " testing with clip " << fname << " vs " << plane << " (" << triangulate << "," << clip_volume << ")\n";
|
||||
Surface_mesh sm;
|
||||
std::ifstream(fname) >> sm;
|
||||
Clip_and_split_visitor visitor(sm);
|
||||
visitor.check();
|
||||
PMP::clip(sm, plane, params::visitor(std::ref(visitor)).do_not_triangulate_faces(!triangulate).clip_volume(clip_volume));
|
||||
std::ofstream ("/tmp/out.off") << sm;
|
||||
visitor.check();
|
||||
};
|
||||
|
||||
auto test_split =[](std::string fname, const K::Plane_3& plane, bool triangulate)
|
||||
{
|
||||
std::cout << " testing with split" << fname << " vs " << plane << " (" << triangulate << ")\n";
|
||||
Surface_mesh sm;
|
||||
std::ifstream(fname) >> sm;
|
||||
Clip_and_split_visitor visitor(sm);
|
||||
visitor.check();
|
||||
PMP::split(sm, plane, params::visitor(std::ref(visitor)).do_not_triangulate_faces(!triangulate));
|
||||
visitor.check();
|
||||
};
|
||||
|
||||
auto test = [&](std::string fname, const K::Plane_3& plane)
|
||||
{
|
||||
test_clip(fname, plane, true, true);
|
||||
test_clip(fname, plane, true, false);
|
||||
test_clip(fname, plane, false, false);
|
||||
test_clip(fname, plane, false, true);
|
||||
test_split(fname, plane, false);
|
||||
test_split(fname, plane, true);
|
||||
};
|
||||
|
||||
test(CGAL::data_file_path("meshes/torus_quad.off"), K::Plane_3(0,0,1,0));
|
||||
test(CGAL::data_file_path("meshes/elephant.off"), K::Plane_3(0.137304, -0.293668, 0.945995, 0));
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "Surface Mesh" << std::endl;
|
||||
|
|
@ -878,5 +1158,14 @@ int main()
|
|||
std::cout << "running test_iso_cuboid with Polyhedron\n";
|
||||
test_isocuboid<Polyhedron>();
|
||||
std::cout << "Done!" << std::endl;
|
||||
std::cout << "running test_new_clip with Surface_mesh\n";
|
||||
test_new_clip<Surface_mesh>();
|
||||
std::cout << "Done!" << std::endl;
|
||||
std::cout << "running test_new_clip with Polyhedron\n";
|
||||
test_new_clip<Polyhedron>();
|
||||
std::cout << "Done!" << std::endl;
|
||||
std::cout << "running test_clip_and_split_with_plane_visitor\n";
|
||||
test_clip_and_split_with_plane_visitor();
|
||||
std::cout << "Done!" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ CGAL_add_named_parameter(visitor_t, visitor, visitor)
|
|||
CGAL_add_named_parameter(point_t, point_map, point_map)
|
||||
|
||||
CGAL_add_named_parameter(edge_is_constrained_t, edge_is_constrained, edge_is_constrained_map)
|
||||
CGAL_add_named_parameter(edge_is_marked_map_t, edge_is_marked_map, edge_is_marked_map)
|
||||
CGAL_add_named_parameter(first_index_t, first_index, first_index)
|
||||
CGAL_add_named_parameter(number_of_iterations_t, number_of_iterations, number_of_iterations)
|
||||
CGAL_add_named_parameter(verbosity_level_t, verbosity_level, verbosity_level)
|
||||
|
|
@ -58,6 +59,8 @@ CGAL_add_named_parameter(face_color_output_iterator_t, face_color_output_iterato
|
|||
CGAL_add_named_parameter(vertex_normal_map_t, vertex_normal_map, vertex_normal_map)
|
||||
CGAL_add_named_parameter(vertex_color_map_t, vertex_color_map, vertex_color_map)
|
||||
CGAL_add_named_parameter(vertex_texture_map_t, vertex_texture_map, vertex_texture_map)
|
||||
CGAL_add_named_parameter(vertex_oriented_side_map_t, vertex_oriented_side_map, vertex_oriented_side_map)
|
||||
|
||||
CGAL_add_named_parameter(face_color_map_t, face_color_map, face_color_map)
|
||||
CGAL_add_named_parameter(repair_polygon_soup_t, repair_polygon_soup, repair_polygon_soup)
|
||||
CGAL_add_named_parameter(output_color_t, output_color, output_color)
|
||||
|
|
|
|||
Loading…
Reference in New Issue