Merge remote-tracking branch 'cgal/main' into Point_set-reduce_dependencies-GF

This commit is contained in:
Andreas Fabri 2025-11-20 10:14:49 +00:00
commit 970f16913b
256 changed files with 6830 additions and 1424 deletions

View File

@ -164,7 +164,7 @@ jobs:
wget --no-verbose cgal.github.io -O index.html
if ! egrep -qF "/$PR_NUMBER/$ROUND" index.html || [ "$force" = "yes" ]; then
#list impacted packages
LIST_OF_PKGS=$(git diff --name-only origin/master...HEAD |cut -s -d/ -f1 |sort -u | xargs -I {} echo {} && ls -d {}/package_info 2>/dev/null |cut -d/ -f1 |egrep -v Installation||true)
LIST_OF_PKGS=$(git diff --name-only origin/main...HEAD |cut -s -d/ -f1 |sort -u | xargs -I {} echo {} && ls -d {}/package_info 2>/dev/null |cut -d/ -f1 |egrep -v Installation||true)
if [ "$LIST_OF_PKGS" = "" ]; then
echo "DoxygenError=No package affected." >> $GITHUB_OUTPUT
exit 1

View File

@ -221,7 +221,7 @@ a visible circle through an iterator called
`ApolloniusGraphVertexBase_2` concept and is implemented by its
model, the `Apollonius_graph_vertex_base_2<Gt,StoreHidden>`
class. It is also possible to iterate through the entire set of hidden
sites using an homonymous iterator defined by the
sites using a homonymous iterator defined by the
`Apollonius_graph_2<Gt,Agds>` class.
Since storing hidden sites may not be of interest in some cases (e.g.,

View File

@ -56,7 +56,7 @@ public:
/// @{
/*!
Creates an hierarchy of Apollonius graphs using `gt` as
Creates a hierarchy of Apollonius graphs using `gt` as
geometric traits.
*/
Apollonius_graph_hierarchy_2(Gt gt=Gt());

View File

@ -4890,7 +4890,7 @@ using Arrangement = CGAL::Arrangement_2<Traits>;
<!-- ----------------------------------------------------------------------- -->
\cgalFigureBegin{aos_fig-conic_multiplicities,conic_multiplicities.png}
An arrangement of a circular arc and an hyperbolic arc, as constructed
An arrangement of a circular arc and a hyperbolic arc, as constructed
in \ref Arrangement_on_surface_2/conic_multiplicities.cpp.
\cgalFigureEnd
<!-- ----------------------------------------------------------------------- -->
@ -6841,9 +6841,9 @@ arrangement-with-history from a file:
\cgalExample{Arrangement_on_surface_2/io_curve_history.cpp}
\cgalAdvancedBegin
The arrangement package also includes the free functions `write(arr,
os, formatter)` and `read(arr, os, formatter)` that operate on a given
arrangement-with-history instance `arr`. Both functions are
The arrangement package also includes the free functions
`write(arr, os, formatter)` and `read(arr, os, formatter)` that operate
on a given arrangement-with-history instance `arr`. Both functions are
parameterized by a `formatter` object, which defines the I/O
format. The package contains a template called,
`Arr_with_hist_text_formatter<ArranagmentFormatter>`, which extends an
@ -6855,12 +6855,24 @@ and defines a simple textual input/output format.
\subsection arr_ssecarr_io_vis Drawing an Arrangement
<!-- ----------------------------------------------------------------------- -->
An arrangement data structure can be visualized by calling the \link PkgArrangementOnSurface2Draw CGAL::draw<arr>() \endlink function as shown in the following example. This function opens a new window showing the given arrangement. A call to this function is blocking; that is, the program continues execution only after the user closes the window.
An arrangement data structure can be visualized by calling one of the
\link PkgArrangementOnSurface2Draw `CGAL::draw()` \endlink
overloaded template functions. Every variant opens a new window
showing the given arrangement. A call to any \link
PkgArrangementOnSurface2Draw `CGAL::draw()` \endlink function is
blocking; that is, the program continues execution only after the user
closes the window. The most simple variant accepts the arrangement to
draw and an optional string used as the title of the window. In the
following example we exploit a variant that also accepts an object the
type of which is an instance of the class template
`Graphics_scene_options`. It allows us to tune the drawings.
\cgalExample{Arrangement_on_surface_2/draw_arr.cpp}
This function requires `CGAL_Qt6`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined.
Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the definition `CGAL_USE_BASIC_VIEWER`.
This function requires `CGAL_Qt6`, and is only available if the macro
`CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target
`CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the
definition `CGAL_USE_BASIC_VIEWER`.
\cgalFigureBegin{aos_fig-draw_arr,draw_arr.png}
A snapshot of the window created by the program
@ -6868,6 +6880,10 @@ A snapshot of the window created by the program
of 14 vertices, 15 edges, and 3 faces. Notice that the colors are generated at random.
\cgalFigureEnd
Another pair of overloaded \link PkgArrangementOnSurface2Draw
`CGAL::draw()` \endlink functions also accept a bounding box. Each
of these two variants can be ised to draw arrangements induced by
unbounded curves.
<!-- ======================================================================= -->
\section aos_sec-bgl Adapting to Boost Graphs

View File

@ -32,7 +32,7 @@ namespace CGAL {
* same direction as a precondition. Moreover, `Arr_circle_segment_traits_2`
* supports the merging of curves of opposite directions.
*
* \cgalModels{AosTraits_2,AosApproximateTraits_2,AosDirectionalXMonotoneTraits_2}
* \cgalModels{AosTraits_2,AosApproximateTraits_2,AosApproximatePointTraits_2,AosDirectionalXMonotoneTraits_2}
*
*/
template <typename Kernel>

View File

@ -80,7 +80,7 @@ namespace CGAL {
* to have the same direction as a precondition. Moreover, `Arr_conic_traits_2`
* supports the merging of curves of opposite directions.
*
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosApproximateTraits_2,AosDirectionalXMonotoneTraits_2}
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosApproximateTraits_2,AosApproximatePointTraits_2,AosDirectionalXMonotoneTraits_2}
*
* \cgalHeading{Types}
*/

View File

@ -21,7 +21,7 @@ namespace CGAL {
* we can find out its actual type and convert it to the respective kernel
* object (say, to a `Kernel::Ray_2`).
*
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosOpenBoundaryTraits_2}
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosOpenBoundaryTraits_2,AosApproximatePointTraits_2,AosApproximateTraits_2,AosApproximateUnboundedTraits_2}
*/
template <typename Kernel>
class Arr_linear_traits_2 {

View File

@ -3,9 +3,8 @@ namespace CGAL {
/*! \ingroup PkgArrangementOnSurface2Ref
*
* `Arr_observer<Arrangement_2>` is an alias for
* `Aos_observer<Arrangement_on_surface_2>`,
* where `Arrangement_2` derives from `Arrangement_on_surface_2` and the latter
* is an instance of the template
* `Aos_observer<Arrangement_on_surface_2>`, where `Arrangement_2` derives from
* `Arrangement_on_surface_2` and the latter is an instance of the template
* `CGAL::Arrangement_on_surface_2<GeometryTraits, TopologyTraits>`.
*/

View File

@ -52,7 +52,7 @@ namespace CGAL {
* same direction as a precondition. Moreover, `Arr_segment_traits_2` supports
* the merging of curves of opposite directions.
*
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosApproximateTraits_2,AosDirectionalXMonotoneTraits_2}
* \cgalModels{AosTraits_2,AosLandmarkTraits_2,AosApproximateTraits_2,AosApproximatePointTraits_2,AosDirectionalXMonotoneTraits_2}
*/
template <typename Kernel>
class Arr_segment_traits_2 : public Kernel {

View File

@ -19,6 +19,8 @@
#include <CGAL/Qt/Basic_viewer.h>
#include "CGAL/Bbox_2.h"
#ifdef DOXYGEN_RUNNING
namespace CGAL {
@ -26,8 +28,8 @@ namespace CGAL {
/*! \ingroup PkgArrangementOnSurface2Draw
* The function opens a new window and draws `arr`, an instance of the
* `CGAL::Arrangement_2` class template. Parameters of the drawing are taken
* from the optional graphics scene options parameter.
* `CGAL::Arrangement_on_surface_2` class template. Parameters of the drawing
* are taken from the optional graphics scene options parameter.
*
* A call to this function blocks the execution of the program until the drawing
* window is closed. This function requires `CGAL_Qt6`, and is only available if
@ -35,57 +37,64 @@ namespace CGAL {
* `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the definition
* `CGAL_USE_BASIC_VIEWER`.
*
* \tparam GeometryTraits_2 a geometry traits type, a model of a 2D arrangement
* traits concept. At this point it must be an instance of either
* `CGAL::Arr_segment_traits_2` or `CGAL::Arr_conic_traits_2`.
* \tparam Dcel the \dcel type, a model of the `AosDcel` concept.
* \tparam GeometryTraits a geometry traits type, a model of a 2D arrangement
* geometry traits concept. Observe that not all geometery-traits models are
* supported.
*
* \tparam TopologyTraits a topology traits type, a model of the
* `AosTopologyTraits` concept.
*
* \tparam GSOptions a model of `GraphicsSceneOptions` concept.
*
* \param arr the 2D arrangement to draw.
* \param gso the graphics scene options parameter.
* \param bbox a bounding box in parameter space.
* \param gso the graphics scene options.
* \param title the optional title of the window.
*
* \sa `AosDcel`
* \sa `AosTraits_2`
* \sa `AosTopologyTraits`
* \sa `GraphicsSceneOptions`
*/
template <typename GeometryTraits_2, typename Dcel, typename GSOptions>
void draw(const Arrangement_2<GeometryTraits_2, Dcel>& arr,
const GSOptions& gso);
template <typename GeometryTraits, typename TopologyTraits>
void draw(const Arrangement_on_surface_2<GeometryTraits, TopologyTraits>& arr,
const Bbox_2& bbox, const GSOptions& gso,
const char* title = "2D Arrangement on Surface");
/*! \ingroup PkgArrangementOnSurface2Draw
*
* A shortcut to `CGAL::draw(arr, Graphics_scene_options{})`.
* A shortcut to `CGAL::draw(arr, bbox, Graphics_scene_options<Aos,
* Aos::Vertex_const_handle, Aos::Halfedge_const_handle,
* Aos::Face_const_handle>{})`, where `Aos` is
* `Arrangement_on_surface_2<GeometryTraits, TopologyTraits>`.
*/
template <typename GeometryTraits_2, typename Dcel>
void draw(const Arrangement_2<GeometryTraits_2, Dcel>& arr);
template <typename GeometryTraits, typename TopologyTraits>
void draw(const Arrangement_on_surface_2<GeometryTraits, TopologyTraits>& arr,
const Bbox_2& bbox, const char* title = "2D Arrangement on Surface");
/*! \ingroup PkgArrangementOnSurface2Draw
*
* adds the vertices, edges and faces of `arr` into the given graphic scene
* `gs`. Parameters of the cells are taken from the optional graphics scene
* options parameter `gso`. Note that `gs` is not cleared before being filled
* (to enable to draw several data structures in the same basic viewer).
*
* \tparam GeometryTraits_2 a geometry traits type, a model of a 2D arrangement
* traits concept. At this point it must be an instance of either
* `CGAL::Arr_segment_traits_2` or `CGAL::Arr_conic_traits_2`.
* \tparam Dcel the \dcel type, a model of the `AosDcel` concept.
* \tparam GSOptions a model of `GraphicsSceneOptions` concept.
*
* \param arr the 2D arrangement to draw.
* \param gs the graphic scene to fill.
* \param gso the graphics scene options parameter.
* Similar to `CGAL::draw(arr, bbox, gso)`, where the bounding box `bbox` is
* computed to bound all points and curves of the arrangement in parameter
* space.
*/
template <typename GeometryTraits_2, typename Dcel, typename GSOptions>
void add_to_graphics_scene(const Arrangement_2<GeometryTraits_2, Dcel>& arr,
CGAL::Graphics_scene& gs, const GSOptions& gso);
template <typename GeometryTraits, typename TopologyTraits>
void draw(const Arrangement_on_surface_2<GeometryTraits, TopologyTraits>& arr,
const GSOptions& gso, const char* title = "2D Arrangement on Surface");
/*! \ingroup PkgArrangementOnSurface2Draw
* A shortcut to `CGAL::add_to_graphics_scene(arr, gs,
* Graphics_scene_options{})`.
*
* A shortcut to `CGAL::draw(arr, Graphics_scene_options<Aos,
* Aos::Vertex_const_handle, Aos::Halfedge_const_handle,
* Aos::Face_const_handle>{})`, where `Aos` is
* `Arrangement_on_surface_2<GeometryTraits, TopologyTraits>`.
*/
template <typename GeometryTraits_2, typename Dcel>
void add_to_graphics_scene(const Arrangement_2<GeometryTraits_2, Dcel>& arr,
CGAL::Graphics_scene& gs);
template <typename GeometryTraits, typename TopologyTraits>
void draw(const Arrangement_on_surface_2<GeometryTraits, TopologyTraits>& arr,
const char* title = "2D Arrangement on Surface");
} /* namespace CGAL */

View File

@ -18,8 +18,6 @@
* \cgalHasModels{CGAL::Arr_rational_function_traits_2<AlgebraicKernel_d_1>}
* \cgalHasModelsEnd
*
* \sa `AosConstructXMonotoneCurveTraits_2`
* \sa `AosXMonotoneTraits_2`
* \sa `AosTraits_2`
*/
class AosApproximatePointTraits_2 {
@ -35,7 +33,7 @@ public:
/// \name Functor Types
/// @{
/// models the concept `AosTraits::Approximate_2`.
/// models the concept `AosTraits::ApproximatePoint_2`.
typedef unspecified_type Approximate_2;
/// @}

View File

@ -17,7 +17,9 @@
* \cgalHasModelsEnd
*
* \sa `AosApproximatePointTraits_2`
* \sa `draw()`
* \sa `AosConstructXMonotoneCurveTraits_2`
* \sa `AosXMonotoneTraits_2`
* \sa \link PkgArrangementOnSurface2Draw `CGAL::draw()`\endlink
*/
class AosApproximateTraits_2 {
public:

View File

@ -0,0 +1,40 @@
/*! \ingroup PkgArrangementOnSurface2ConceptsTraits
* \cgalConcept
*
* The concept `AosApproximateUnboundedTraits_2` refines the concept
* `AosApproximateTraits_2`. A model of this concept is able to approximate a
* curve constrained to a given bounding box (in addition to the ability to
* approximate a point and a curve without constraints).
*
* \cgalRefines{AosApproximateTraits_2}
*
* \cgalHasModelsBegin
* \cgalHasModels{CGAL::Arr_linear_traits_2<Kernel>}
* \cgalHasModelsEnd
*
* \sa `AosApproximateTraits_2`
* \sa \link PkgArrangementOnSurface2Draw `CGAL::draw()`\endlink
*/
class AosApproximateUnboundedTraits_2 {
public:
/// \name Types
/// @{
/// @}
/// \name Functor Types
/// @{
/// models the concept `AosTraits::ApproximateUnbounded_2`.
typedef unspecified_type Approximate_2;
/// @}
/// \name Accessing Functor Objects
/// @{
///
Approximate_2 approximate_2_object() const;
/// @}
}

View File

@ -0,0 +1,28 @@
namespace AosTraits {
/*! \ingroup PkgArrangementOnSurface2ConceptsFunctionObjects
* \cgalConcept
*
* \cgalRefines{Functor}
*
* \cgalHasModelsBegin
* \cgalHasModels{AosApproximatePointTraits_2::Approximate_2}
* \cgalHasModels{AosApproximateTraits_2::Approximate_2}
* \cgalHasModelsEnd
*/
class ApproximatePoint_2 {
public:
/// \name Operations
/// A model of this concept must provide:
/// @{
/*! obtains an approximation of `p`'s \f$x\f$-coordinate (if `i == 0`), or of
* `p`'s \f$y\f$-coordinate (if `i == 1`).
* \pre `i` is either 0 or 1.
*/
Approximate_number_type operator()(AosTraits::Point_2 p, int i);
/// @}
}; /* end AosTraits::Approximate_2 */
}

View File

@ -0,0 +1,49 @@
namespace AosTraits {
/*! \ingroup PkgArrangementOnSurface2ConceptsFunctionObjects
* \cgalConcept
*
* \cgalRefines{Approximate_2}
*
* \cgalHasModelsBegin
* \cgalHasModels{AosApproximatePointTraits_2::Approximate_2}
* \cgalHasModels{AosApproximateTraits_2::Approximate_2}
* \cgalHasModels{AosApproximateUnboundedTraits_2::Approximate_2}
* \cgalHasModelsEnd
*/
class ApproximateUnbounded_2 {
public:
/// \name Operations
/// A model of this concept must provide:
/// @{
/*! approximates a given \f$x\f$-monotone curve constrained to a bounding
* box. It computes one or more sequences of approximate points that represent
* the disconnected portions of a polyline that approximates `xcv` within the
* bounding box `bbox`, and inserts them into output containers given through
* the output iterator `oi`. The first point of the first sequence and the
* last point of the last sequence are always approximations of the endpoints
* of the given curve.
*
* \param xcv The exact \f$x\f$-monotone curve.
* \param error The error bound of the polyline approximation. This is the
* Hausdorff distance between the curve and the polyline that
* approximates the curve.
* \param oi An output iterator for the output containers.
* \param bbox the bounding box.
* \param l2r A Boolean flag that indicates whether the curve direction is
* left to right.
* \return The past-the-end iterator of the output container.
*
* \pre Dereferencing `oi` must yield an object the type of which is a
* container, where the value type of this container is
* `AosApproximateTraits_2::Approximate_point_2`.
*/
template <typename OutputIterator>
OutputIterator operator()(const X_monotone_curve_2& xcv, double error, OutputIterator oi,
const Bbox_2& bbox, bool l2r = true) const;
/// @}
}; /* end AosTraits::Approximate_2 */
}

View File

@ -3,7 +3,7 @@ namespace AosTraits {
/*! \ingroup PkgArrangementOnSurface2ConceptsFunctionObjects
* \cgalConcept
*
* \cgalRefines{Functor}
* \cgalRefines{ApproximatePoint_2}
*
* \cgalHasModelsBegin
* \cgalHasModels{AosApproximatePointTraits_2::Approximate_2}
@ -16,15 +16,9 @@ public:
/// A model of this concept must provide:
/// @{
/*! obtains an approximation of `p`'s \f$x\f$-coordinate (if `i == 0`), or of
* `p`'s \f$y\f$-coordinate (if `i == 1`).
* \pre `i` is either 0 or 1.
*/
CGAL::Approximate_number_type operator()(AosTraits::Point_2 p, int i);
/*! obtains an approximation of `p`.
*/
CGAL::Approximate_point_2 operator()(AosTraits::Point_2 p);
Approximate_point_2 operator()(AosTraits::Point_2 p);
/*! approximates a given \f$x\f$-monotone curve. It computes a sequence of
* approximate points that represent an approximate polyline, and inserts
@ -42,7 +36,7 @@ public:
* \return The past-the-end iterator of the output container.
*
* \pre Dereferencing `oi` must yield an object of type
* `Arr_conic_traits_2::Approximate_point_2`.
* `AosApproximateTraits_2::Approximate_point_2`.
*/
template <typename OutputIterator>
OutputIterator operator()(const X_monotone_curve_2& xcv, double error,

View File

@ -136,7 +136,7 @@ public:
/// \name Face Creation
/// The following function is invoked whenever a new face is
/// created. It is guaranteed that all halfedges along the face
/// boundary have already been created an have their auxiliary data
/// boundary have already been created and have their auxiliary data
/// fields attached to them:
/// @{

View File

@ -113,6 +113,7 @@ implemented as peripheral classes or as free (global) functions.
- `AosApproximateTraits_2`
- `AosApproximatePointTraits_2`
- `AosApproximateUnboundedTraits_2`
- `AosBasicTopologyTraits`
- `AosBasicTraits_2`
- `AosBottomSideTraits_2`
@ -167,6 +168,8 @@ implemented as peripheral classes or as free (global) functions.
\cgalCRPSection{Function Object Concepts}
- `AosTraits::Approximate_2`
- `AosTraits::ApproximatePoint_2`
- `AosTraits::ApproximateUnbounded_2`
- `AosTraits::AreMergeable_2`
- `AosTraits::CompareX_2`
- `AosTraits::CompareXy_2`
@ -262,6 +265,6 @@ implemented as peripheral classes or as free (global) functions.
- \link PkgArrangementOnSurface2op_right_shift `CGAL::operator<<` \endlink
\cgalCRPSection{Draw an `Arrangemen_2` object}
- \link PkgArrangementOnSurface2Draw CGAL::draw<>() \endlink
- \link PkgArrangementOnSurface2Draw CGAL::draw<>() \endlink
*/

View File

@ -1,52 +1,59 @@
/*!
\example Arrangement_on_surface_2/aggregated_insertion.cpp
\example Arrangement_on_surface_2/algebraic_curves.cpp
\example Arrangement_on_surface_2/algebraic_segments.cpp
\example Arrangement_on_surface_2/batched_point_location.cpp
\example Arrangement_on_surface_2/Bezier_curves.cpp
\example Arrangement_on_surface_2/bgl_dual_adapter.cpp
\example Arrangement_on_surface_2/bgl_primal_adapter.cpp
\example Arrangement_on_surface_2/bounded_vertical_decomposition.cpp
\example Arrangement_on_surface_2/circles.cpp
\example Arrangement_on_surface_2/circular_arcs.cpp
\example Arrangement_on_surface_2/conics.cpp
\example Arrangement_on_surface_2/conic_multiplicities.cpp
\example Arrangement_on_surface_2/consolidated_curve_data.cpp
\example Arrangement_on_surface_2/count_and_trace.cpp
\example Arrangement_on_surface_2/curve_history.cpp
\example Arrangement_on_surface_2/dcel_extension.cpp
\example Arrangement_on_surface_2/dcel_extension_io.cpp
\example Arrangement_on_surface_2/dual_lines.cpp
\example Arrangement_on_surface_2/dual_with_data.cpp
\example Arrangement_on_surface_2/edge_insertion.cpp
\example Arrangement_on_surface_2/edge_manipulation.cpp
\example Arrangement_on_surface_2/edge_manipulation_curve_history.cpp
\example Arrangement_on_surface_2/face_extension.cpp
\example Arrangement_on_surface_2/face_extension_overlay.cpp
\example Arrangement_on_surface_2/generic_curve_data.cpp
\example Arrangement_on_surface_2/global_insertion.cpp
\example Arrangement_on_surface_2/global_removal.cpp
\example Arrangement_on_surface_2/incremental_insertion.cpp
\example Arrangement_on_surface_2/io.cpp
\example Arrangement_on_surface_2/io_curve_history.cpp
\example Arrangement_on_surface_2/isolated_vertices.cpp
\example Arrangement_on_surface_2/observer.cpp
\example Arrangement_on_surface_2/overlay.cpp
\example Arrangement_on_surface_2/overlay_color.cpp
\example Arrangement_on_surface_2/overlay_unbounded.cpp
\example Arrangement_on_surface_2/point_location.cpp
\example Arrangement_on_surface_2/polylines.cpp
\example Arrangement_on_surface_2/predefined_kernel.cpp
\example Arrangement_on_surface_2/predefined_kernel_non_intersecting.cpp
\example Arrangement_on_surface_2/rational_functions.cpp
\example Arrangement_on_surface_2/special_edge_insertion.cpp
\example Arrangement_on_surface_2/spherical_insert.cpp
\example Arrangement_on_surface_2/unbounded_non_intersecting.cpp
\example Arrangement_on_surface_2/unbounded_rational_functions.cpp
\example Arrangement_on_surface_2/vertical_ray_shooting.cpp
\example Arrangement_on_surface_2/draw_arr.cpp
\example Arrangement_on_surface_2/linear_conics.cpp
\example Arrangement_on_surface_2/parabolas.cpp
\example Arrangement_on_surface_2/ellipses.cpp
\example Arrangement_on_surface_2/hyperbolas.cpp
*/
* \example Arrangement_on_surface_2/aggregated_insertion.cpp
* \example Arrangement_on_surface_2/algebraic_curves.cpp
* \example Arrangement_on_surface_2/algebraic_segments.cpp
* \example Arrangement_on_surface_2/batched_point_location.cpp
* \example Arrangement_on_surface_2/Bezier_curves.cpp
* \example Arrangement_on_surface_2/bgl_dual_adapter.cpp
* \example Arrangement_on_surface_2/bgl_primal_adapter.cpp
* \example Arrangement_on_surface_2/bounded_vertical_decomposition.cpp
* \example Arrangement_on_surface_2/circles.cpp
* \example Arrangement_on_surface_2/circular_arcs.cpp
* \example Arrangement_on_surface_2/conics.cpp
* \example Arrangement_on_surface_2/conic_multiplicities.cpp
* \example Arrangement_on_surface_2/consolidated_curve_data.cpp
* \example Arrangement_on_surface_2/count_and_trace.cpp
* \example Arrangement_on_surface_2/curve_history.cpp
* \example Arrangement_on_surface_2/dcel_extension.cpp
* \example Arrangement_on_surface_2/dcel_extension_io.cpp
* \example Arrangement_on_surface_2/draw_agas.cpp
* \example Arrangement_on_surface_2/draw_arr.cpp
* \example Arrangement_on_surface_2/dual_lines.cpp
* \example Arrangement_on_surface_2/dual_with_data.cpp
* \example Arrangement_on_surface_2/edge_insertion.cpp
* \example Arrangement_on_surface_2/edge_manipulation.cpp
* \example Arrangement_on_surface_2/edge_manipulation_curve_history.cpp
* \example Arrangement_on_surface_2/ellipses.cpp
* \example Arrangement_on_surface_2/face_extension.cpp
* \example Arrangement_on_surface_2/face_extension_overlay.cpp
* \example Arrangement_on_surface_2/generic_curve_data.cpp
* \example Arrangement_on_surface_2/global_insertion.cpp
* \example Arrangement_on_surface_2/global_removal.cpp
* \example Arrangement_on_surface_2/hyperbolas.cpp
* \example Arrangement_on_surface_2/incremental_insertion.cpp
* \example Arrangement_on_surface_2/io.cpp
* \example Arrangement_on_surface_2/io_curve_history.cpp
* \example Arrangement_on_surface_2/io_unbounded.cpp
* \example Arrangement_on_surface_2/isolated_vertices.cpp
* \example Arrangement_on_surface_2/linear_conics.cpp
* \example Arrangement_on_surface_2/observer.cpp
* \example Arrangement_on_surface_2/overlay.cpp
* \example Arrangement_on_surface_2/overlay_color.cpp
* \example Arrangement_on_surface_2/overlay_unbounded.cpp
* \example Arrangement_on_surface_2/parabolas.cpp
* \example Arrangement_on_surface_2/point_location.cpp
* \example Arrangement_on_surface_2/polycurve_bezier.cpp
* \example Arrangement_on_surface_2/polycurve_circular_arc.cpp
* \example Arrangement_on_surface_2/polycurve_conic.cpp
* \example Arrangement_on_surface_2/polycurve_geodesic.cpp
* \example Arrangement_on_surface_2/polylines.cpp
* \example Arrangement_on_surface_2/predefined_kernel.cpp
* \example Arrangement_on_surface_2/predefined_kernel_non_intersecting.cpp
* \example Arrangement_on_surface_2/rational_functions.cpp
* \example Arrangement_on_surface_2/special_edge_insertion.cpp
* \example Arrangement_on_surface_2/spherical_insert.cpp
* \example Arrangement_on_surface_2/unbounded_non_intersecting.cpp
* \example Arrangement_on_surface_2/unbounded_rational_functions.cpp
* \example Arrangement_on_surface_2/unb_planar_vertical_decomposition.cpp
* \example Arrangement_on_surface_2/vertical_ray_shooting.cpp
*/

View File

@ -15,16 +15,21 @@ foreach(cppfile ${cppfiles})
create_single_source_cgal_program("${cppfile}")
endforeach()
if(CGAL_Qt6_FOUND)
target_link_libraries(draw_arr PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(linear_conics PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(parabolas PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(ellipses PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(hyperbolas PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(polylines PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(circles PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(circular_arcs PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(draw_arr PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(draw_agas PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(ellipses PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(hyperbolas PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(linear_conics PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(parabolas PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(polylines PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(rational_functions PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(spherical_insert PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(spherical_overlay PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(unbounded_non_intersecting PRIVATE CGAL::CGAL_Basic_viewer)
else()
message(
STATUS

View File

@ -21,14 +21,15 @@ int main() {
// Locate the vertex with maximal degree.
auto vit = arr.vertices_begin();
Arrangement::Vertex_const_handle v_max = vit;
for (++vit; vit != arr.vertices_end(); ++vit)
if (vit->degree() > v_max->degree()) v_max = vit;
for(++vit; vit != arr.vertices_end(); ++vit)
if(vit->degree() > v_max->degree()) v_max = vit;
// Locate the vertex with maximum degree.
std::cout << "The vertex with maximal degree in the arrangement is: "
<< "v_max = (" << v_max->point() << ") "
<< "with degree " << v_max->degree() << "." << std::endl;
CGAL::draw(arr);
CGAL::draw(arr, "circles");
return 0;
}

View File

@ -24,8 +24,7 @@ int main() {
// Create a line segment (C4) with the same supporting line (y = x), but
// having one endpoint with irrational coordinates.
CoordNT sqrt_15 = CoordNT(0, 1, 15); // = sqrt(15)
curves.push_back(Curve(s3.supporting_line(),
Point(3, 3), Point(sqrt_15, sqrt_15)));
curves.push_back(Curve(s3.supporting_line(), Point(3, 3), Point(sqrt_15, sqrt_15)));
// Create a circular arc (C5) that is the upper half of the circle centered at
// (1, 1) with squared radius 3. Create the circle with clockwise orientation,
@ -51,13 +50,12 @@ int main() {
// Create a circular arc (C7) defined by two endpoints and a midpoint,
// all having rational coordinates. This arc is the upper right
// quarter of a circle centered at the origin with radius 5.
curves.push_back(Curve(Rational_point(0, 5), Rational_point(3, 4),
Rational_point(5, 0)));
curves.push_back(Curve(Rational_point(0, 5), Rational_point(3, 4), Rational_point(5, 0)));
// Construct the arrangement of the curves and print its size.
Arrangement arr;
Arrangement arr;
insert(arr, curves.begin(), curves.end());
print_arrangement_size(arr);
CGAL::draw(arr);
CGAL::draw(arr, "circular_arcs");
return 0;
}

View File

@ -0,0 +1,106 @@
#include <vector>
#include <CGAL/draw_arrangement_2.h>
#include "arr_geodesic.h"
#include "arr_print.h"
void draw_face_crossing_boundary() {
Arrangement arr;
const auto& traits = *arr.geometry_traits();
auto ctr_p = traits.construct_point_2_object();
auto ctr_cv = traits.construct_curve_2_object();
auto p1 = ctr_p(-0.95, 0.32, 0), p2 = ctr_p(-0.87, 0.02, 0.49), p3 = ctr_p(-0.93, -0.36, 0),
p4 = ctr_p(-0.81, -0.03, -0.59);
auto arcs = {ctr_cv(p1, p2), ctr_cv(p2, p3), ctr_cv(p3, p4), ctr_cv(p4, p1)};
CGAL::insert(arr, arcs.begin(), arcs.end());
print_arrangement_size(arr);
CGAL::draw(arr, "face crossing boundary");
}
void draw_lakes() {
Arrangement arr;
const auto& traits = *arr.geometry_traits();
auto ctr_p = traits.construct_point_2_object();
auto ctr_cv = traits.construct_curve_2_object();
auto poly1 = {
ctr_p(-0.27, -0.053, -0.96), ctr_p(-0.76, -0.15, -0.63), ctr_p(-0.98, -0.19, -0.063), ctr_p(-0.98, -0.098, 0.2),
ctr_p(-0.44, -0.18, 0.88), ctr_p(0.39, -0.0049, 0.92), ctr_p(-0.01, 0.39, 0.92), ctr_p(-0.54, 0.66, 0.53),
ctr_p(-0.83, 0.56, 0.025), ctr_p(-0.57, 0.32, -0.75), ctr_p(-0.087, 0.048, -1), ctr_p(-0.048, 0.088, -1),
ctr_p(0.12, -0.14, -0.98), ctr_p(-0.12, -0.14, -0.98),
};
auto poly2 = {ctr_p(-0.24, -0.53, -0.81), ctr_p(-0.47, -0.54, -0.69), ctr_p(-0.68, -0.65, -0.32),
ctr_p(-0.71, -0.68, 0.2), ctr_p(-0.54, -0.52, 0.67), ctr_p(-0.18, -0.72, 0.67),
ctr_p(0.31, -0.68, 0.67), ctr_p(0.71, -0.69, 0.11), ctr_p(0.6, -0.58, -0.56),
ctr_p(0.21, -0.62, -0.75)};
auto poly3 = {ctr_p(0.44, 0.27, -0.86), ctr_p(0.58, -0.063, -0.81), ctr_p(0.87, -0.094, -0.48),
ctr_p(0.97, -0.1, 0.2), ctr_p(0.46, 0.77, 0.45), ctr_p(-0.023, 0.89, 0.45),
ctr_p(-0.3, 0.95, 0.11), ctr_p(-0.22, 0.69, -0.69), ctr_p(-0.076, 0.35, -0.93)};
auto poly4 = {
ctr_p(0.4, 0.67, -0.63), ctr_p(0.78, 0.39, -0.48), ctr_p(0.92, 0.35, -0.15),
ctr_p(0.52, 0.86, 0.025), ctr_p(0.068, 0.99, -0.15), ctr_p(0.22, 0.85, -0.48),
};
std::vector<Curve> arcs;
std::vector<std::vector<Point>> polygons{poly1, poly2, poly3, poly4};
for(const auto& poly : polygons) {
for(size_t i = 0; i < poly.size(); ++i) {
size_t next = (i + 1) % poly.size();
arcs.push_back(ctr_cv(poly[i], poly[next]));
}
}
CGAL::insert(arr, arcs.begin(), arcs.end());
print_arrangement_size(arr);
CGAL::draw(arr, "lakes");
}
void draw_gaussian_map() {
Arrangement arr;
const auto& traits = *arr.geometry_traits();
auto ctr_p = traits.construct_point_2_object();
auto ctr_cv = traits.construct_curve_2_object();
auto p1 = ctr_p(1, 1, 1), p2 = ctr_p(-1, -1, 1), p3 = ctr_p(-1, 1, -1), p4 = ctr_p(1, -1, -1);
auto arcs = {ctr_cv(p1, p2), ctr_cv(p2, p3), ctr_cv(p3, p1), ctr_cv(p1, p4), ctr_cv(p2, p4), ctr_cv(p3, p4)};
CGAL::insert(arr, arcs.begin(), arcs.end());
print_arrangement_size(arr);
CGAL::draw(arr, "gaussian map of a tetrahedron");
}
void draw_random_arcs(int n) {
Arrangement arr;
const auto& traits = *arr.geometry_traits();
auto ctr_p = traits.construct_point_2_object();
auto ctr_cv = traits.construct_curve_2_object();
CGAL::Random random;
std::vector<Point> points;
for(int i = 0; i < n; ++i) {
double x = random.get_double(-1.0, 1.0);
double y = random.get_double(-1.0, 1.0);
double z = random.get_double(-1.0, 1.0);
points.push_back(ctr_p(x, y, z));
}
std::vector<Curve> curves;
for(int i = 0; i < n; ++i) {
int j = random.get_int(0, n - 1);
if(i == j)
j = (j + 1) % n;
curves.push_back(ctr_cv(points[i], points[j]));
}
CGAL::insert(arr, curves.begin(), curves.end());
print_arrangement_size(arr);
CGAL::draw(arr, (std::to_string(n) + " random arcs").c_str());
}
int main() {
draw_face_crossing_boundary();
draw_lakes();
draw_gaussian_map();
draw_random_arcs(100);
return 0;
}

View File

@ -1,13 +1,13 @@
#include <string>
#include <CGAL/Random.h>
#include <CGAL/Arr_linear_traits_2.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Arrangement_2.h>
#include <CGAL/Arr_segment_traits_2.h>
#include <CGAL/Arr_non_caching_segment_traits_2.h>
#include <CGAL/draw_arrangement_2.h>
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_segment_traits_2<Kernel>;
using Point = Traits::Point_2;
using Arrangement_2 = CGAL::Arrangement_2<Traits>;
/*! Convert HSV to RGB color space
* Converts a given set of HSV values `h', `s', `v' into RGB coordinates.
* The output RGB values are in the range [0, 255], and the input HSV values
@ -17,9 +17,8 @@ using Arrangement_2 = CGAL::Arrangement_2<Traits>;
* \param sat Saturation component range: [0, 1]
* \param value Value component range: [0, 1]
* \return tuple<red, green, blue>, where each component is in the range [0, 255]
*/
std::tuple<unsigned char, unsigned char, unsigned char>
hsv_to_rgb(float hue, float sat, float value) {
*/
std::tuple<unsigned char, unsigned char, unsigned char> hsv_to_rgb(float hue, float sat, float value) {
float red, green, blue;
float fc = value * sat; // Chroma
float hue_prime = fmod(hue / 60.0f, 6.f);
@ -75,49 +74,175 @@ hsv_to_rgb(float hue, float sat, float value) {
return std::make_tuple(redc, greenc, bluec);
}
int main() {
void draw_rect() {
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_non_caching_segment_traits_2<Kernel>;
using Point = Traits::Point_2;
using Arrangement = CGAL::Arrangement_2<Traits>;
Traits traits;
Arrangement_2 arr(&traits);
Arrangement arr(&traits);
auto ctr_xcv = traits.construct_x_monotone_curve_2_object();
CGAL::insert(arr, ctr_xcv(Point(-2,-2), Point(2,-2)));
CGAL::insert(arr, ctr_xcv(Point(2,-2), Point(2,2)));
CGAL::insert(arr, ctr_xcv(Point(2,2), Point(-2,2)));
CGAL::insert(arr, ctr_xcv(Point(-2,2), Point(-2,-2)));
CGAL::insert(arr, ctr_xcv(Point(-2, -2), Point(2, -2)));
CGAL::insert(arr, ctr_xcv(Point(2, -2), Point(2, 2)));
CGAL::insert(arr, ctr_xcv(Point(2, 2), Point(-2, 2)));
CGAL::insert(arr, ctr_xcv(Point(-2, 2), Point(-2, -2)));
CGAL::insert(arr, ctr_xcv(Point(-1,-1), Point(1,-1)));
CGAL::insert(arr, ctr_xcv(Point(1,-1), Point(1,1)));
CGAL::insert(arr, ctr_xcv(Point(1,1), Point(-1,1)));
CGAL::insert(arr, ctr_xcv(Point(-1,1), Point(-1,-1)));
CGAL::insert(arr, ctr_xcv(Point(-1, -1), Point(1, -1)));
CGAL::insert(arr, ctr_xcv(Point(1, -1), Point(1, 1)));
CGAL::insert(arr, ctr_xcv(Point(1, 1), Point(-1, 1)));
CGAL::insert(arr, ctr_xcv(Point(-1, 1), Point(-1, -1)));
CGAL::insert(arr, ctr_xcv(Point(-2,-2), Point(-2,-4)));
CGAL::insert(arr, ctr_xcv(Point(2,-2), Point(4,-2)));
CGAL::insert(arr, ctr_xcv(Point(-2, -2), Point(-2, -4)));
CGAL::insert(arr, ctr_xcv(Point(2, -2), Point(4, -2)));
CGAL::insert(arr, ctr_xcv(Point(0,0), Point(0,-3)));
CGAL::insert(arr, ctr_xcv(Point(0, 0), Point(0, -3)));
std::cout << arr.number_of_vertices() << ", "
<< arr.number_of_edges() << ", "
<< arr.number_of_faces() << std::endl;
std::cout << arr.number_of_vertices() << ", " << arr.number_of_edges() << ", " << arr.number_of_faces() << std::endl;
std::size_t id(0);
CGAL::Graphics_scene_options<Arrangement, typename Arrangement::Vertex_const_handle,
typename Arrangement::Halfedge_const_handle, typename Arrangement::Face_const_handle>
gso;
gso.colored_face = [](const Arrangement&, Arrangement::Face_const_handle) -> bool { return true; };
CGAL::Graphics_scene_options<Arrangement_2,
typename Arrangement_2::Vertex_const_handle,
typename Arrangement_2::Halfedge_const_handle,
typename Arrangement_2::Face_const_handle> gso;
gso.colored_face=[](const Arrangement_2&, Arrangement_2::Face_const_handle) -> bool
{ return true; };
gso.face_color = [](const Arrangement&, Arrangement::Face_const_handle fh) -> CGAL::IO::Color {
CGAL::Random random((size_t(fh.ptr())));
float h = 360.0f * random.get_double(0, 1);
float s = 0.5;
float v = 0.5;
auto [r, g, b] = hsv_to_rgb(h, s, v);
return CGAL::IO::Color(r, g, b);
};
gso.face_color=[&id](const Arrangement_2& arr, Arrangement_2::Face_const_handle) -> CGAL::IO::Color
{
float h = 360.0f * id++ / arr.number_of_faces();
float s = 0.5;
float v = 0.5;
auto [r, g, b] = hsv_to_rgb(h, s, v);
return CGAL::IO::Color(r,g,b);
};
CGAL::draw(arr, gso, "hsv colors");
return EXIT_SUCCESS;
CGAL::draw(arr, gso, "rect with hsv colors");
}
void draw_nested() {
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_segment_traits_2<Kernel>;
using Point = Traits::Point_2;
using Arrangement = CGAL::Arrangement_2<Traits>;
using X_monotone_curve = Traits::X_monotone_curve_2;
Arrangement arr;
auto traits = arr.traits();
auto ctr_xcv = traits->construct_x_monotone_curve_2_object();
std::vector<X_monotone_curve> curves;
{
// a hexagon centered at the origin
const double r = 10.0;
for (int i = 0; i < 6; ++i) {
int next = (i + 1) % 6;
Point source(r * cos(i * CGAL_PI / 3), r * sin(i * CGAL_PI / 3));
Point target(r * cos(next * CGAL_PI / 3), r * sin(next * CGAL_PI / 3));
curves.push_back(ctr_xcv(source, target));
}
}
{
// a square inside the hexagon
const double size = 4.0;
Point p1(-size, size), p2(size, size), p3(size, -size), p4(-size, -size);
auto rect = {ctr_xcv(p1, p2), ctr_xcv(p2, p3), ctr_xcv(p3, p4), ctr_xcv(p4, p1)};
curves.insert(curves.end(), rect.begin(), rect.end());
}
{
// two adjacent triangle inside the square
Point p1(-1, 0), p2(1, 0), p3(0, sqrt(3)), p4(0, -sqrt(3));
auto tri1 = {ctr_xcv(p1, p2), ctr_xcv(p2, p3), ctr_xcv(p3, p1)};
auto tri2 = {ctr_xcv(p1, p2), ctr_xcv(p2, p4), ctr_xcv(p4, p1)};
curves.insert(curves.end(), tri1.begin(), tri1.end());
curves.insert(curves.end(), tri2.begin(), tri2.end());
}
// a degenerate hole inside the square
auto degen_seg = ctr_xcv({-1, -3}, {1, -3});
curves.push_back(degen_seg);
// an isolated vertex inside the square
auto iso_point = Point{1, -1};
CGAL::insert(arr, curves.begin(), curves.end());
CGAL::insert_point(arr, iso_point);
CGAL::draw(arr, "nested polygons");
}
void draw_unbounded_linear_grid() {
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_linear_traits_2<Kernel>;
using Point = Traits::Point_2;
using Segment = Traits::Segment_2;
using Line = Traits::Line_2;
using X_monotone_curve = Traits::X_monotone_curve_2;
using Arrangement = CGAL::Arrangement_2<Traits>;
Arrangement arr;
// Insert a n*n grid
int n = 5;
for (int i = 0; i < n; ++i) {
Point p1(i * 5, 0);
Point p2(i * 5, 1);
CGAL::insert(arr, X_monotone_curve(Line(p1, p2)));
}
for (int i = 0; i < n; ++i) {
Point p1(0, i * 5);
Point p2(1, i * 5);
CGAL::insert(arr, X_monotone_curve(Line(p1, p2)));
}
// Generate a inner square(2*2) for all cells
// And an inner triangle for each square
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
Point p1(i * 5 + 1, j * 5 + 1);
Point p2(i * 5 + 4, j * 5 + 4);
CGAL::insert(arr, X_monotone_curve(Segment(p1, Point(p2.x(), p1.y()))));
CGAL::insert(arr, X_monotone_curve(Segment(Point(p1.x(), p2.y()), p2)));
CGAL::insert(arr, X_monotone_curve(Segment(p1, Point(p1.x(), p2.y()))));
CGAL::insert(arr, X_monotone_curve(Segment(Point(p2.x(), p1.y()), p2)));
// Insert a triangle inside the square
Point tri_p1(i * 5 + 2, j * 5 + 2);
Point tri_p2(i * 5 + 3, j * 5 + 2);
Point tri_p3(i * 5 + 2.5, j * 5 + 3);
CGAL::insert(arr, X_monotone_curve(Segment(tri_p1, tri_p2)));
CGAL::insert(arr, X_monotone_curve(Segment(tri_p2, tri_p3)));
CGAL::insert(arr, X_monotone_curve(Segment(tri_p3, tri_p1)));
// Connect the triangle to the square
Point top(i * 5 + 2.5, j * 5 + 4);
CGAL::insert(arr, X_monotone_curve(Segment(tri_p1, top)));
}
}
CGAL::draw(arr, "unbounded linear grid");
}
void draw_random_segments(int n) {
using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_segment_traits_2<Kernel>;
using Point = Traits::Point_2;
using Arrangement = CGAL::Arrangement_2<Traits>;
using X_monotone_curve = Traits::X_monotone_curve_2;
Arrangement arr;
auto traits = arr.traits();
auto ctr_xcv = traits->construct_x_monotone_curve_2_object();
CGAL::Random random;
std::vector<X_monotone_curve> curves;
for (int i = 0; i < n; ++i) {
double x1 = random.get_double(-100, 100);
double y1 = random.get_double(-100, 100);
double x2 = random.get_double(-100, 100);
double y2 = random.get_double(-100, 100);
curves.push_back(ctr_xcv(Point(x1, y1), Point(x2, y2)));
}
CGAL::insert(arr, curves.begin(), curves.end());
CGAL::draw(arr, (std::to_string(n) + " random segments").c_str());
}
int main() {
draw_rect();
draw_nested();
draw_unbounded_linear_grid();
draw_random_segments(100);
return EXIT_SUCCESS;
}

View File

@ -17,14 +17,10 @@ int main() {
auto ctr_cv = traits.construct_curve_2_object();
// Insert a full x-major ellipse
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE,
Point(4,0), Point(0,2)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE,
Point(0,2), Point(-4,0)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE,
Point(-4,0), Point(0,-2)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE,
Point(0,-2), Point(4,0)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE, Point(4, 0), Point(0, 2)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE, Point(0, 2), Point(-4, 0)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE, Point(-4, 0), Point(0, -2)));
CGAL::insert(arr, ctr_cv(1, 4, 0, 0, 0, -16, CGAL::COUNTERCLOCKWISE, Point(0, -2), Point(4, 0)));
// Insert a full y-major ellipse
CGAL::insert(arr, ctr_cv(4, 1, 0, 0, 0, -16));
@ -39,7 +35,7 @@ int main() {
print_arrangement_size(arr);
CGAL::draw(arr);
CGAL::draw(arr, "ellipses");
return 0;
}

View File

@ -19,63 +19,46 @@ int main() {
// Insert a hyperbolic arc (C1), supported by the hyperbola y = 1/x
// (or: xy - 1 = 0) with the endpoints (1/4, 4) and (2, 1/2).
// The arc is counterclockwise oriented.
CGAL::insert(arr, ctr_cv(0, 0, 1, 0, 0, -1, CGAL::COUNTERCLOCKWISE,
Point(Rational(1,4), 4), Point(2, Rational(1,2))));
CGAL::insert(arr, ctr_cv(0, 0, -1, 0, 0, -1, CGAL::CLOCKWISE,
Point(Rational(-1,4), 4), Point(-2, Rational(1,2))));
CGAL::insert(arr,
ctr_cv(0, 0, 1, 0, 0, -1, CGAL::COUNTERCLOCKWISE, Point(Rational(1, 4), 4), Point(2, Rational(1, 2))));
CGAL::insert(arr, ctr_cv(0, 0, -1, 0, 0, -1, CGAL::CLOCKWISE, Point(Rational(-1, 4), 4), Point(-2, Rational(1, 2))));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE,
Point(3, 4), Point(1, 0)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE,
Point(1, 0), Point(3, -4)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::CLOCKWISE,
Point(-3, 4), Point(-1, 0)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::CLOCKWISE,
Point(-1, 0), Point(-3, -4)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE, Point(3, 4), Point(1, 0)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE, Point(1, 0), Point(3, -4)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::CLOCKWISE, Point(-3, 4), Point(-1, 0)));
CGAL::insert(arr, ctr_cv(2, -1, 0, 0, 0, -2, CGAL::CLOCKWISE, Point(-1, 0), Point(-3, -4)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::CLOCKWISE,
Point(4, 3), Point(0, 1)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::CLOCKWISE,
Point(0, 1), Point(-4, 3)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE,
Point(4, -3), Point(0, -1)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE,
Point(0, -1), Point(-4, -3)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::CLOCKWISE, Point(4, 3), Point(0, 1)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::CLOCKWISE, Point(0, 1), Point(-4, 3)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE, Point(4, -3), Point(0, -1)));
CGAL::insert(arr, ctr_cv(-1, 2, 0, 0, 0, -2, CGAL::COUNTERCLOCKWISE, Point(0, -1), Point(-4, -3)));
CGAL::insert(arr, ctr_cv(4, 46, -144, 0, 0, -100, CGAL::COUNTERCLOCKWISE,
Point(-5, 0),
CGAL::insert(arr, ctr_cv(4, 46, -144, 0, 0, -100, CGAL::COUNTERCLOCKWISE, Point(-5, 0),
Point(Rational(14, 10), Rational(48, 10))));
CGAL::insert(arr, ctr_cv(4, 46, -144, 0, 0, -100, CGAL::COUNTERCLOCKWISE,
Point(5, 0),
CGAL::insert(arr, ctr_cv(4, 46, -144, 0, 0, -100, CGAL::COUNTERCLOCKWISE, Point(5, 0),
Point(Rational(-14, 10), Rational(-48, 10))));
// 4*x*x + 46*y*y - 144*x*y - 100
CGAL::insert(arr, ctr_cv(46, 4, -144, 0, 0, -100, CGAL::CLOCKWISE,
Point(0, -5),
Point(Rational(48, 10), Rational(14, 10))));
CGAL::insert(arr, ctr_cv(46, 4, -144, 0, 0, -100, CGAL::CLOCKWISE,
Point(0, 5),
Point(Rational(-48, 10), Rational(-14, 10))));
CGAL::insert(
arr, ctr_cv(46, 4, -144, 0, 0, -100, CGAL::CLOCKWISE, Point(0, -5), Point(Rational(48, 10), Rational(14, 10))));
CGAL::insert(
arr, ctr_cv(46, 4, -144, 0, 0, -100, CGAL::CLOCKWISE, Point(0, 5), Point(Rational(-48, 10), Rational(-14, 10))));
// 46*x*x + 4*y*y - 144*x*y - 100
CGAL::insert(arr, ctr_cv(4, 46, 144, 0, 0, -100, CGAL::CLOCKWISE,
Point(-5, 0),
Point(Rational(14,10), Rational(-48,10))));
CGAL::insert(arr, ctr_cv(4, 46, 144, 0, 0, -100, CGAL::CLOCKWISE,
Point(5, 0),
Point(Rational(-14,10), Rational(48,10))));
CGAL::insert(
arr, ctr_cv(4, 46, 144, 0, 0, -100, CGAL::CLOCKWISE, Point(-5, 0), Point(Rational(14, 10), Rational(-48, 10))));
CGAL::insert(
arr, ctr_cv(4, 46, 144, 0, 0, -100, CGAL::CLOCKWISE, Point(5, 0), Point(Rational(-14, 10), Rational(48, 10))));
// 4*x*x + 46*y*y + 144*x*y - 100
CGAL::insert(arr, ctr_cv(46, 4, 144, 0, 0, -100, CGAL::COUNTERCLOCKWISE,
Point(0, -5),
Point(Rational(-48,10), Rational(14,10))));
CGAL::insert(arr, ctr_cv(46, 4, 144, 0, 0, -100, CGAL::COUNTERCLOCKWISE,
Point(0, 5),
Point(Rational(48,10), Rational(-14,10))));
CGAL::insert(arr, ctr_cv(46, 4, 144, 0, 0, -100, CGAL::COUNTERCLOCKWISE, Point(0, -5),
Point(Rational(-48, 10), Rational(14, 10))));
CGAL::insert(arr, ctr_cv(46, 4, 144, 0, 0, -100, CGAL::COUNTERCLOCKWISE, Point(0, 5),
Point(Rational(48, 10), Rational(-14, 10))));
print_arrangement_size(arr);
CGAL::draw(arr);
CGAL::draw(arr, "hyperbolas");
return 0;
}

View File

@ -20,8 +20,8 @@ int main() {
Point p2(0, -1);
Point p3(0, 1);
Point p4(1, 0);
Point p5(Rational(1,2),Rational(1,2));
Point p6(Rational(-1,2),Rational(1,2));
Point p5(Rational(1, 2), Rational(1, 2));
Point p6(Rational(-1, 2), Rational(1, 2));
Rat_point rp0(0, 0);
Rat_point rp1(1, 0);
Rat_point rp2(0, 1);
@ -56,7 +56,7 @@ int main() {
print_arrangement_size(arr);
CGAL::draw(arr);
CGAL::draw(arr, "linear_conics");
return 0;
}

View File

@ -18,62 +18,49 @@ int main() {
// x-major
// insert the parabola y = x^2; (-1,1)--(1,1)
CGAL::insert(arr, ctr_cv(1, 0, 0, 0, -1, 0, CGAL::COUNTERCLOCKWISE,
Point(-1, 1), Point(1, 1)));
CGAL::insert(arr, ctr_cv(1, 0, 0, 0, -1, 0, CGAL::COUNTERCLOCKWISE, Point(-1, 1), Point(1, 1)));
// translated
// Insert the parabola y = x^2 - 2x + 2; (1,1)--(2,2)
CGAL::insert(arr, ctr_cv(1, 0, 0, -2, -1, 2, CGAL::COUNTERCLOCKWISE,
Point(1, 1), Point(2, 2)));
CGAL::insert(arr, ctr_cv(1, 0, 0, 2, -1, 2, CGAL::COUNTERCLOCKWISE,
Point(-2, 2), Point(-1, 1)));
CGAL::insert(arr, ctr_cv(1, 0, 0, -2, -1, 2, CGAL::COUNTERCLOCKWISE, Point(1, 1), Point(2, 2)));
CGAL::insert(arr, ctr_cv(1, 0, 0, 2, -1, 2, CGAL::COUNTERCLOCKWISE, Point(-2, 2), Point(-1, 1)));
// rotated
// Insert the parabola y = x^2 rotated clockwise about theta, such that
// sin(theta) = 0.6, cos(theta) = 0.8
CGAL::insert(arr, ctr_cv(16, 9, -24, -15, -20, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(-2,10), Rational(14,10)),
Point(Rational(14,10), Rational(2,10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, 15, -20, 0, CGAL::CLOCKWISE,
Point(Rational(2,10), Rational(14,10)),
Point(Rational(-14,10), Rational(2,10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, -15, 20, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(14,10), Rational(-2,10)),
Point(Rational(-2,10), Rational(-14,10))));
CGAL::insert(arr, ctr_cv(16, 9, -24, 15, 20, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(2,10), Rational(-14,10)),
Point(Rational(-14,10), Rational(-2,10))));
CGAL::insert(arr, ctr_cv(16, 9, -24, -15, -20, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(-2, 10), Rational(14, 10)),
Point(Rational(14, 10), Rational(2, 10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, 15, -20, 0, CGAL::CLOCKWISE, Point(Rational(2, 10), Rational(14, 10)),
Point(Rational(-14, 10), Rational(2, 10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, -15, 20, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(14, 10), Rational(-2, 10)),
Point(Rational(-2, 10), Rational(-14, 10))));
CGAL::insert(arr, ctr_cv(16, 9, -24, 15, 20, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(2, 10), Rational(-14, 10)),
Point(Rational(-14, 10), Rational(-2, 10))));
// 16*x*x+9*y*y-24*x*y-15*x-20*y
CGAL::insert(arr, ctr_cv(9, 16, -24, -20, -15, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(2,10), Rational(14,10)),
Point(Rational(14,10), Rational(-2,10))));
CGAL::insert(arr, ctr_cv(9, 16, 24, -20, 15, 0, CGAL::CLOCKWISE,
Point(Rational(2,10), Rational(-14,10)),
Point(Rational(14,10), Rational(2,10))));
CGAL::insert(arr, ctr_cv(9, 16, 24, 20, -15, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(-14,10), Rational(-2,10)),
Point(Rational(-2,10), Rational(14,10))));
CGAL::insert(arr, ctr_cv(9, 16, -24, 20, 15, 0, CGAL::COUNTERCLOCKWISE,
Point(Rational(-2,10), Rational(-14,10)),
Point(Rational(-14,10), Rational(2,10))));
CGAL::insert(arr, ctr_cv(9, 16, -24, -20, -15, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(2, 10), Rational(14, 10)),
Point(Rational(14, 10), Rational(-2, 10))));
CGAL::insert(arr, ctr_cv(9, 16, 24, -20, 15, 0, CGAL::CLOCKWISE, Point(Rational(2, 10), Rational(-14, 10)),
Point(Rational(14, 10), Rational(2, 10))));
CGAL::insert(arr, ctr_cv(9, 16, 24, 20, -15, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(-14, 10), Rational(-2, 10)),
Point(Rational(-2, 10), Rational(14, 10))));
CGAL::insert(arr, ctr_cv(9, 16, -24, 20, 15, 0, CGAL::COUNTERCLOCKWISE, Point(Rational(-2, 10), Rational(-14, 10)),
Point(Rational(-14, 10), Rational(2, 10))));
// 9*x*x+16*y*y-24*x*y+20*x+15*y
// rotated & translated
CGAL::insert(arr, ctr_cv(16, 9, -24, -23, -14, 36, CGAL::COUNTERCLOCKWISE,
Point(Rational(8,10), Rational(24,10)),
Point(Rational(24,10), Rational(12,10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, 23, -14, 36, CGAL::CLOCKWISE,
Point(Rational(-8,10), Rational(24,10)),
Point(Rational(-24,10), Rational(12,10))));
CGAL::insert(arr, ctr_cv(16, 9, -24, -23, -14, 36, CGAL::COUNTERCLOCKWISE, Point(Rational(8, 10), Rational(24, 10)),
Point(Rational(24, 10), Rational(12, 10))));
CGAL::insert(arr, ctr_cv(16, 9, 24, 23, -14, 36, CGAL::CLOCKWISE, Point(Rational(-8, 10), Rational(24, 10)),
Point(Rational(-24, 10), Rational(12, 10))));
// 16*x*x+9*y*y-24*x*y-23*x-14*y+36
print_arrangement_size(arr);
CGAL::draw(arr);
CGAL::draw(arr, "parabolas");
return 0;
}

View File

@ -45,7 +45,7 @@ int main() {
insert(arr, pi1);
insert(arr, pi2);
insert(arr, pi3);
print_arrangement_size(arr); // print the arrangement size
CGAL::draw(arr);
print_arrangement_size(arr); // print the arrangement size
CGAL::draw(arr, "polylines");
return 0;
}

View File

@ -17,7 +17,7 @@ int main() {
#include "arr_print.h"
int main() {
CGAL::IO::set_pretty_mode(std::cout); // for nice printouts.
CGAL::IO::set_pretty_mode(std::cout); // for nice printouts.
// Define a traits class object and a constructor for rational functions.
Traits traits;
@ -31,24 +31,24 @@ int main() {
// Create an arc (C1) supported by the polynomial y = x^4 - 6x^2 + 8,
// defined over the (approximate) interval [-2.1, 2.1].
Polynomial P1 = CGAL::ipower(x,4) - 6*x*x + 8;
Polynomial P1 = CGAL::ipower(x, 4) - 6 * x * x + 8;
Alg_real l(Bound(-2.1)), r(Bound(2.1));
arcs.push_back(construct(P1, l, r));
// Create an arc (C2) supported by the function y = x / (1 + x^2),
// defined over the interval [-3, 3].
Polynomial P2 = x;
Polynomial Q2 = 1 + x*x;
Polynomial Q2 = 1 + x * x;
arcs.push_back(construct(P2, Q2, Alg_real(-3), Alg_real(3)));
// Create an arc (C3) supported by the parbola y = 8 - x^2,
// defined over the interval [-2, 3].
Polynomial P3 = 8 - x*x;
Polynomial P3 = 8 - x * x;
arcs.push_back(construct(P3, Alg_real(-2), Alg_real(3)));
// Create an arc (C4) supported by the line y = -2x,
// defined over the interval [-3, 0].
Polynomial P4 = -2*x;
Polynomial P4 = -2 * x;
arcs.push_back(construct(P4, Alg_real(-3), Alg_real(0)));
// Construct the arrangement of the four arcs.

View File

@ -1,6 +1,5 @@
//! \file examples/Arrangement_on_surface_2/spherical_insert.cpp
// Constructing an arrangement of arcs of great circles.
#include <list>
#include <cmath>
#include <cstdio>
@ -9,6 +8,7 @@
#include <CGAL/Arrangement_on_surface_2.h>
#include <CGAL/Arr_geodesic_arc_on_sphere_traits_2.h>
#include <CGAL/Arr_spherical_topology_traits_2.h>
#include <CGAL/draw_arrangement_2.h>
#include "arr_geodesic.h"
#include "arr_print.h"
@ -20,12 +20,11 @@ int main() {
Arrangement arr(&traits);
Point p1 = ctr_p(0, 0, -1), p3 = ctr_p(0, -1, 0), p5 = ctr_p(-1, 0, 0);
Point p2 = ctr_p(0, 0, 1), p4 = ctr_p(0, 1, 0), p6 = ctr_p( 1, 0, 0);
Curve arcs[] = {
ctr_cv(p6, p1), ctr_cv(p6, p2), ctr_cv(p4, p1), ctr_cv(p4, p2),
ctr_cv(p5, p1), ctr_cv(p5, p2), ctr_cv(p3, p1), ctr_cv(p3, p2),
ctr_cv(p6, p4), ctr_cv(p6, p3), ctr_cv(p5, p4), ctr_cv(p5, p3) };
CGAL::insert(arr, arcs, arcs + sizeof(arcs)/sizeof(Curve));
Point p2 = ctr_p(0, 0, 1), p4 = ctr_p(0, 1, 0), p6 = ctr_p(1, 0, 0);
Curve arcs[] = {ctr_cv(p6, p1), ctr_cv(p6, p2), ctr_cv(p4, p1), ctr_cv(p4, p2), ctr_cv(p5, p1), ctr_cv(p5, p2),
ctr_cv(p3, p1), ctr_cv(p3, p2), ctr_cv(p6, p4), ctr_cv(p6, p3), ctr_cv(p5, p4), ctr_cv(p5, p3)};
CGAL::insert(arr, arcs, arcs + sizeof(arcs) / sizeof(Curve));
print_arrangement_size(arr);
CGAL::draw(arr, "spherical insert");
return 0;
}

View File

@ -3,6 +3,7 @@
#include <CGAL/Arr_overlay_2.h>
#include <CGAL/Arr_default_overlay_traits.h>
#include <CGAL/draw_arrangement_2.h>
#include "arr_geodesic_on_sphere.h"
@ -34,8 +35,8 @@ int main() {
Arrangement overlay_arr;
Overlay_traits overlay_traits;
overlay(arr1, arr2, overlay_arr, overlay_traits);
std::cout << "No. of vertices: " << overlay_arr.number_of_vertices()
<< std::endl;
std::cout << "No. of vertices: " << overlay_arr.number_of_vertices() << std::endl;
CGAL::draw(overlay_arr, "spherical overlay");
return 0;
}

View File

@ -4,6 +4,7 @@
#include <cassert>
#include "CGAL/draw_arrangement_2.h"
#include "arr_linear.h"
#include "arr_print.h"
@ -14,8 +15,8 @@ int main() {
// then, insert a point that lies on the line splitting it into two.
X_monotone_curve c1 = Line(Point(-1, 0), Point(1, 0));
arr.insert_in_face_interior(c1, arr.unbounded_face());
Vertex_handle v = insert_point(arr, Point(0,0));
assert(! v->is_at_open_boundary());
Vertex_handle v = insert_point(arr, Point(0, 0));
assert(!v->is_at_open_boundary());
// Add two more rays using the specialized insertion functions.
arr.insert_from_right_vertex(Ray(Point(0, 0), Point(-1, 1)), v); // c2
@ -30,25 +31,27 @@ int main() {
// Print the outer CCBs of the unbounded faces.
int k = 1;
for (auto it = arr.unbounded_faces_begin(); it != arr.unbounded_faces_end();
++it)
{
std::cout << "Face no. " << k++ << "(" << it->is_unbounded() << ","
<< it->number_of_holes() << ")" << ": ";
for(auto it = arr.unbounded_faces_begin(); it != arr.unbounded_faces_end(); ++it) {
std::cout << "Face no. " << k++ << "(" << it->is_unbounded() << "," << it->number_of_holes() << ")" << ": ";
Arrangement::Ccb_halfedge_const_circulator first = it->outer_ccb();
auto curr = first;
if (! curr->source()->is_at_open_boundary())
if(!curr->source()->is_at_open_boundary())
std::cout << "(" << curr->source()->point() << ")";
do {
Arrangement::Halfedge_const_handle e = curr;
if (! e->is_fictitious()) std::cout << " [" << e->curve() << "] ";
else std::cout << " [ ... ] ";
if(!e->is_fictitious())
std::cout << " [" << e->curve() << "] ";
else
std::cout << " [ ... ] ";
if (! e->target()->is_at_open_boundary())
if(!e->target()->is_at_open_boundary())
std::cout << "(" << e->target()->point() << ")";
} while (++curr != first);
} while(++curr != first);
std::cout << std::endl;
}
CGAL::draw(arr, "unbounded_non_intersecting");
return 0;
}

View File

@ -685,7 +685,7 @@ point_position(const Point_2& p, Bezier_cache& cache) const
return EQUAL;
}
// Then check whether the bezier is an horizontal segment or
// Then check whether the bezier is a horizontal segment or
// if p has the same x-coordinate as one of the endpoint
const Comparison_result res1 = p.compare_x(_ps, cache);

View File

@ -28,11 +28,13 @@
#include <variant>
#include <CGAL/Cartesian.h>
#include <CGAL/tags.h>
#include <CGAL/intersections.h>
#include <CGAL/Arr_tags.h>
#include <CGAL/Arr_enums.h>
#include <CGAL/Arr_geometry_traits/Segment_assertions.h>
#include "CGAL/number_utils.h"
namespace CGAL {
@ -1517,9 +1519,24 @@ public:
/// \name Functor definitions for the landmarks point-location strategy.
//@{
typedef double Approximate_number_type;
typedef double Approximate_number_type;
typedef CGAL::Cartesian<Approximate_number_type> Approximate_kernel;
typedef Approximate_kernel::Point_2 Approximate_point_2;
class Approximate_2 {
protected:
using Traits = Arr_linear_traits_2<Kernel>;
/*! The traits (in case it has state) */
const Traits& m_traits;
/*! constructs
* \param traits the traits.
*/
Approximate_2(const Traits& traits) : m_traits(traits) {}
friend class Arr_linear_traits_2<Kernel>;
public:
/*! obtains an approximation of a point coordinate.
* \param p The exact point.
@ -1533,10 +1550,103 @@ public:
CGAL_precondition((i == 0) || (i == 1));
return (i == 0) ? CGAL::to_double(p.x()) : CGAL::to_double(p.y());
}
/*! obtains an approximation of a point.
*/
Approximate_point_2 operator()(const Point_2& p) const
{ return Approximate_point_2(operator()(p, 0), operator()(p, 1)); }
/*! obtains an approximation of an \f$x\f$-monotone curve.
*/
template <typename OutputIterator>
OutputIterator operator()(const X_monotone_curve_2& xcv, double /* error */,
OutputIterator oi, bool l2r = true) const {
if(xcv.is_ray() || xcv.is_line()) return oi;
auto min_vertex = m_traits.construct_min_vertex_2_object();
auto max_vertex = m_traits.construct_max_vertex_2_object();
const auto& src = (l2r) ? min_vertex(xcv) : max_vertex(xcv);
const auto& trg = (l2r) ? max_vertex(xcv) : min_vertex(xcv);
auto xs = CGAL::to_double(src.x());
auto ys = CGAL::to_double(src.y());
auto xt = CGAL::to_double(trg.x());
auto yt = CGAL::to_double(trg.y());
*oi++ = Approximate_point_2(xs, ys);
*oi++ = Approximate_point_2(xt, yt);
return oi;
}
/*! obtains an approximation of an \f$x\f$-monotone curve.
*/
template <typename OutputIterator>
OutputIterator operator()(const X_monotone_curve_2& xcv, double /* error */,
OutputIterator oi, const Bbox_2& bbox,
bool l2r = true) const
{
using Approx_pnt = Approximate_point_2;
using Approx_seg = Approximate_kernel::Segment_2;
using Approx_ray = Approximate_kernel::Ray_2;
using Approx_lin = Approximate_kernel::Line_2;
auto xmin = bbox.xmin();
auto ymin = bbox.ymin();
auto xmax = bbox.xmax();
auto ymax = bbox.ymax();
Approximate_kernel::Iso_rectangle_2 rect(xmin, ymin, xmax, ymax);
if (xcv.is_ray()) {
auto ray = xcv.ray();
Kernel kernel;
auto construct_vertex = kernel.construct_point_on_2_object();
Approx_pnt s = this->operator()(construct_vertex(ray, 0));
Approx_pnt t = this->operator()(construct_vertex(ray, 1));
const auto result = CGAL::intersection(rect, Approx_ray(s, t));
if (! result) return oi;
if (const auto* res_seg = std::get_if<Approx_seg>(&*result)) {
*oi++ = l2r ? (res_seg->min)() : (res_seg->max)();
*oi++ = l2r ? (res_seg->max)() : (res_seg->min)();
return oi;
}
const auto* res_pnt = std::get_if<Approx_pnt>(&*result);
CGAL_assertion(res_pnt != nullptr);
*oi++ = *res_pnt;
return oi;
}
if (xcv.is_line()) {
const Line_2 & supp_line = xcv.supp_line();
Approx_lin approx_supp_line(
CGAL::to_double(supp_line.a()),
CGAL::to_double(supp_line.b()),
CGAL::to_double(supp_line.c()));
const auto result = CGAL::intersection(rect, approx_supp_line);
if (! result) return oi;
if (const auto* res_seg = std::get_if<Approx_seg>(&*result)) {
*oi++ = l2r ? (res_seg->min)() : (res_seg->max)();
*oi++ = l2r ? (res_seg->max)() : (res_seg->min)();
return oi;
}
const auto* res_pnt = std::get_if<Approx_pnt>(&*result);
CGAL_assertion(res_pnt != nullptr);
*oi++ = *res_pnt;
return oi;
}
Approx_seg seg(this->operator()(xcv.source()), this->operator()(xcv.target()));
const auto result = CGAL::intersection(rect, seg);
if (! result) return oi;
if (const auto* res_seg = std::get_if<Approx_seg>(&*result)) {
*oi++ = l2r ? (res_seg->min)() : (res_seg->max)();
*oi++ = l2r ? (res_seg->max)() : (res_seg->min)();
return oi;
}
const auto* res_pnt = std::get_if<Approx_pnt>(&*result);
CGAL_assertion(res_pnt != nullptr);
*oi++ = *res_pnt;
return oi;
}
};
/*! obtains an `Approximate_2` functor object. */
Approximate_2 approximate_2_object() const { return Approximate_2(); }
Approximate_2 approximate_2_object() const { return Approximate_2(*this); }
//! Functor
class Construct_x_monotone_curve_2 {

View File

@ -1115,6 +1115,7 @@ public:
using Approximate_number_type = void;
using Approximate_point_2 = void;
using Approximate_2 = void;
using Approximate_kernel = void;
};
template <typename T>
@ -1123,6 +1124,7 @@ public:
using Approximate_number_type = typename T::Approximate_number_type;
using Approximate_2 = typename T::Approximate_2;
using Approximate_point_2 = typename T::Approximate_point_2;
using Approximate_kernel = typename T::Approximate_kernel;
};
using Approximate_number_type =
@ -1131,6 +1133,8 @@ public:
typename has_approximate_2<Subcurve_traits_2>::Approximate_2;
using Approximate_point_2 =
typename has_approximate_2<Subcurve_traits_2>::Approximate_point_2;
using Approximate_kernel =
typename has_approximate_2<Subcurve_traits_2>::Approximate_kernel;
/*! obtains an Approximate_2 functor object. */
Approximate_2 approximate_2_object_impl(std::false_type) const

View File

@ -597,6 +597,7 @@ public:
//
using Approximate_number_type = typename Base::Approximate_number_type;
using Approximate_point_2 = typename Base::Approximate_point_2;
using Approximate_kernel = typename Base::Approximate_kernel;
class Approximate_2 : public Base::Approximate_2 {
protected:

View File

@ -37,12 +37,12 @@ init_with_hint(const X_monotone_curve_2& cv, Pl_result_type obj) {
// associated with valid endpoints.
m_cv = cv;
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto psy = m_geom_traits->parameter_space_in_y_2_object();
if (m_geom_traits->is_closed_2_object()(m_cv, ARR_MIN_END)) {
// The left endpoint is valid.
const Arr_parameter_space ps_x1 =
m_geom_traits->parameter_space_in_x_2_object()(m_cv, ARR_MIN_END);
const Arr_parameter_space ps_y1 =
m_geom_traits->parameter_space_in_y_2_object()(m_cv, ARR_MIN_END);
const Arr_parameter_space ps_x1 = psx(m_cv, ARR_MIN_END);
const Arr_parameter_space ps_y1 = psy(m_cv, ARR_MIN_END);
m_has_left_pt = true;
m_left_on_boundary = (ps_x1 != ARR_INTERIOR || ps_y1 != ARR_INTERIOR);
m_left_pt = m_geom_traits->construct_min_vertex_2_object()(m_cv);
@ -55,10 +55,8 @@ init_with_hint(const X_monotone_curve_2& cv, Pl_result_type obj) {
if (m_geom_traits->is_closed_2_object()(m_cv, ARR_MAX_END)) {
// The right endpoint is valid.
const Arr_parameter_space ps_x2 =
m_geom_traits->parameter_space_in_x_2_object()(m_cv, ARR_MAX_END);
const Arr_parameter_space ps_y2 =
m_geom_traits->parameter_space_in_y_2_object()(m_cv, ARR_MAX_END);
const Arr_parameter_space ps_x2 = psx(m_cv, ARR_MAX_END);
const Arr_parameter_space ps_y2 = psy(m_cv, ARR_MAX_END);
m_has_right_pt = true;
m_right_on_boundary = (ps_x2 != ARR_INTERIOR || ps_y2 != ARR_INTERIOR);
m_right_pt = m_geom_traits->construct_max_vertex_2_object()(m_cv);
@ -252,11 +250,12 @@ bool Arrangement_zone_2<Arrangement, ZoneVisitor>::
do_overlap_impl(const X_monotone_curve_2& cv1,
const X_monotone_curve_2& cv2,
const Point_2& p, Arr_not_all_sides_oblivious_tag) const {
typename Traits_adaptor_2::Compare_y_at_x_right_2 cmp_right =
m_geom_traits->compare_y_at_x_right_2_object();
auto cmp_right = m_geom_traits->compare_y_at_x_right_2_object();
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto psy = m_geom_traits->parameter_space_in_y_2_object();
auto psx1 = m_geom_traits->parameter_space_in_x_2_object()(cv1, ARR_MIN_END);
auto psy1 = m_geom_traits->parameter_space_in_y_2_object()(cv1, ARR_MIN_END);
auto psx1 = psx(cv1, ARR_MIN_END);
auto psy1 = psy(cv1, ARR_MIN_END);
if ((psx1 == ARR_INTERIOR) && (psy1 == ARR_INTERIOR))
return (cmp_right(cv1, cv2, p) == EQUAL);
@ -265,8 +264,8 @@ do_overlap_impl(const X_monotone_curve_2& cv1,
bool vertical2 = m_geom_traits->is_vertical_2_object()(cv2);
if (vertical1 != vertical2) return false;
auto psx2 = m_geom_traits->parameter_space_in_x_2_object()(cv2, ARR_MIN_END);
auto psy2 = m_geom_traits->parameter_space_in_y_2_object()(cv2, ARR_MIN_END);
auto psx2 = psx(cv2, ARR_MIN_END);
auto psy2 = psy(cv2, ARR_MIN_END);
// If, for example, both curves are vertical and the bottom boundary is
// contracted, they may have different parameter space in x values.
@ -277,8 +276,7 @@ do_overlap_impl(const X_monotone_curve_2& cv1,
// left boundary, they completely lie on the left boundary and they overlap.
if (vertical1) return true;
typename Traits_adaptor_2::Compare_y_near_boundary_2 cmp_near =
m_geom_traits->compare_y_near_boundary_2_object();
auto cmp_near = m_geom_traits->compare_y_near_boundary_2_object();
return (cmp_near(cv1, cv2, ARR_MIN_END) == EQUAL);
}
@ -407,7 +405,7 @@ _direct_intersecting_edge_to_right(const X_monotone_curve_2& cv_ins,
// Check whether the curve lies above of below the edge immediately to
// the right of its left endpoint.
const Comparison_result pos_res =
const Comparison_result pos_res =
m_geom_traits->compare_y_at_x_right_2_object()(cv_ins, query_he->curve(),
cv_left_pt);
@ -459,7 +457,7 @@ _direct_intersecting_edge_to_left(const X_monotone_curve_2& cv_ins,
// Check whether the curve lies above of below the edge (we use the curve
// position predicate, as we know they cruves do not overlap and intersect
// only at the split point).
Comparison_result pos_res =
Comparison_result pos_res =
m_geom_traits->compare_y_position_2_object()(cv_ins, query_he->curve());
if (pos_res == EQUAL) {
@ -729,14 +727,14 @@ _is_to_left_impl(const Point_2& p, Halfedge_handle he,
// Check the boundary conditions of the minimal end of the curve associated
// with the given halfedge.
auto ps_in_x = m_geom_traits->parameter_space_in_x_2_object();
auto ps_x_min = ps_in_x(he->curve(), ARR_MIN_END);
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto ps_x_min = psx(he->curve(), ARR_MIN_END);
// Any point is not to the left of the left boundary.
if (ps_x_min == ARR_LEFT_BOUNDARY) return false;
auto ps_in_y = m_geom_traits->parameter_space_in_y_2_object();
auto ps_y_min = ps_in_y(he->curve(), ARR_MIN_END);
auto psy = m_geom_traits->parameter_space_in_y_2_object();
auto ps_y_min = psy(he->curve(), ARR_MIN_END);
if (ps_y_min != ARR_INTERIOR) {
// Check if p is to the left of the minimal curve-end:
auto cmp_x = m_geom_traits->compare_x_point_curve_end_2_object();
@ -766,16 +764,16 @@ _is_to_right_impl(const Point_2& p, Halfedge_handle he,
// Check the boundary conditions of the maximal end of the curve associated
// with the given halfedge.
auto ps_in_x = m_geom_traits->parameter_space_in_x_2_object();
auto ps_x_max = ps_in_x(he->curve(), ARR_MAX_END);
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto ps_x_max = psx(he->curve(), ARR_MAX_END);
// Any point is not to the right of the right boundary.
if (ps_x_max == ARR_RIGHT_BOUNDARY) return false;
// Any interior point is to the right of the left boundary.
if (ps_x_max == ARR_LEFT_BOUNDARY) return true;
auto ps_in_y = m_geom_traits->parameter_space_in_y_2_object();
auto ps_y_max = ps_in_y(he->curve(), ARR_MAX_END);
auto psy = m_geom_traits->parameter_space_in_y_2_object();
auto ps_y_max = psy(he->curve(), ARR_MAX_END);
if (ps_y_max != ARR_INTERIOR) {
// Check if p is to the right of the maximal curve-end:
auto cmp_x = m_geom_traits->compare_x_point_curve_end_2_object();
@ -790,6 +788,59 @@ _is_to_right_impl(const Point_2& p, Halfedge_handle he,
return (m_geom_traits->compare_xy_2_object()(p, v_right->point()) == LARGER);
}
//! checks whether a point lies to the left of another point.
template <typename Arrangement, typename ZoneVisitor>
bool Arrangement_zone_2<Arrangement, ZoneVisitor>::
is_to_left_impl(const Point_2& p1, Arr_parameter_space /* ps1 */,
const Point_2& p2, Arr_parameter_space /* ps2 */,
Arr_all_sides_oblivious_tag) const {
auto cmp_xy = m_geom_traits->compare_xy_2_object();
return (cmp_xy(p2, p1) == SMALLER);
}
//! checks whether a point lies to the left of another point.
template <typename Arrangement, typename ZoneVisitor>
bool Arrangement_zone_2<Arrangement, ZoneVisitor>::
is_to_left_impl(const Point_2& p1, Arr_parameter_space /* ps1 */,
const Point_2& p2, Arr_parameter_space /* ps2 */,
Arr_has_identified_side_tag) const {
auto is_on_y_ident = m_geom_traits->is_on_y_identification_2_object();
if (is_on_y_ident(p1)) {
if (is_on_y_ident(p2)) {
auto cmp_y_on_boundary = m_geom_traits->compare_y_on_boundary_2_object();
return (cmp_y_on_boundary(p2, p1) == SMALLER);
}
return false;
}
if (is_on_y_ident(p2)) return true;
auto cmp_xy = m_geom_traits->compare_xy_2_object();
return (cmp_xy(p2, p1) == SMALLER);
}
//! checks whether a point lies to the left of another point.
template <typename Arrangement, typename ZoneVisitor>
bool Arrangement_zone_2<Arrangement, ZoneVisitor>::
is_to_left_impl(const Point_2& p1, Arr_parameter_space ps1,
const Point_2& p2, Arr_parameter_space ps2,
Arr_boundary_cond_tag) const {
if (ps1 == ARR_LEFT_BOUNDARY) {
if (ps2 == ARR_LEFT_BOUNDARY) {
auto cmp_y_on_boundary = m_geom_traits->compare_y_on_boundary_2_object();
return (cmp_y_on_boundary(p2, p1) == SMALLER);
}
return false;
}
if (ps1 == ARR_RIGHT_BOUNDARY) {
if (ps2 == ARR_RIGHT_BOUNDARY) {
auto cmp_y_on_boundary = m_geom_traits->compare_y_on_boundary_2_object();
return (cmp_y_on_boundary(p2, p1) == SMALLER);
}
return true;
}
auto cmp_xy = m_geom_traits->compare_xy_2_object();
return (cmp_xy(p2, p1) == SMALLER);
}
//-----------------------------------------------------------------------------
// Compute the (lexicographically) leftmost intersection of the query
// curve with a given halfedge on the boundary of a face in the arrangement.
@ -866,11 +917,7 @@ _leftmost_intersection(Ccb_halfedge_circulator he_curr, bool on_boundary,
// Found a simple intersection point. Check if it is the leftmost
// intersection point so far.
if (! m_found_intersect ||
((intersection_location != ARR_RIGHT_BOUNDARY) &&
((leftmost_location == ARR_RIGHT_BOUNDARY) ||
compare_xy(ip, m_intersect_p) == SMALLER)))
{
if (! m_found_intersect || is_to_left(m_intersect_p, leftmost_location, ip, intersection_location)) {
// Store the leftmost intersection point and the halfedge handle.
m_intersect_p = ip;
m_ip_multiplicity = int_p->second;
@ -1034,9 +1081,14 @@ _zone_in_face(Face_handle face, bool on_boundary) {
// Set m_cv to be the remaining portion.
m_has_left_pt = true;
m_left_on_boundary = false;
m_left_pt = m_intersect_p;
m_cv = m_sub_cv2;
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto psy = m_geom_traits->parameter_space_in_y_2_object();
auto ps_x = psx(m_left_pt);
auto ps_y = psy(m_left_pt);
m_left_on_boundary = (ps_x != ARR_INTERIOR || ps_y != ARR_INTERIOR);
}
const X_monotone_curve_2* p_orig_curve = nullptr;
@ -1220,11 +1272,9 @@ bool Arrangement_zone_2<Arrangement, ZoneVisitor>::_zone_in_overlap() {
#endif
// Obtain some geometry-traits functors.
typename Traits_adaptor_2::Equal_2 equal = m_geom_traits->equal_2_object();
typename Traits_adaptor_2::Is_closed_2 is_closed =
m_geom_traits->is_closed_2_object();
typename Traits_adaptor_2::Construct_max_vertex_2 ctr_max_vertex =
m_geom_traits->construct_max_vertex_2_object();
auto equal = m_geom_traits->equal_2_object();
auto is_closed = m_geom_traits->is_closed_2_object();
auto ctr_max_vertex = m_geom_traits->construct_max_vertex_2_object();
// Check if the right end of m_overlap_cv is bounded. If so, compute its
// right endpoint.
@ -1310,8 +1360,13 @@ bool Arrangement_zone_2<Arrangement, ZoneVisitor>::_zone_in_overlap() {
// Set m_cv to be the remaining portion.
m_has_left_pt = true;
m_left_on_boundary = false;
m_left_pt = cv_right_pt;
auto psx = m_geom_traits->parameter_space_in_x_2_object();
auto psy = m_geom_traits->parameter_space_in_y_2_object();
auto ps_x = psx(m_left_pt);
auto ps_y = psy(m_left_pt);
m_left_on_boundary = (ps_x != ARR_INTERIOR || ps_y != ARR_INTERIOR);
m_cv = m_sub_cv2;
// Move to the remaining portion of the curve, whose left endpoint is the

View File

@ -415,6 +415,24 @@ private:
Arr_parameter_space& intersection_location,
Arr_boundary_cond_tag) const;
/*! checks whether an point lies to the left of another point.
*/
bool is_to_left(const Point_2& p1, Arr_parameter_space ps1,
const Point_2& p2, Arr_parameter_space ps2) const
{ return is_to_left_impl(p1, ps1, p2, ps2, Left_or_right_sides_category()); }
bool is_to_left_impl(const Point_2& p1, Arr_parameter_space ps1,
const Point_2& p2, Arr_parameter_space ps2,
Arr_all_sides_oblivious_tag) const;
bool is_to_left_impl(const Point_2& p1, Arr_parameter_space ps1,
const Point_2& p2, Arr_parameter_space ps2,
Arr_has_identified_side_tag) const;
bool is_to_left_impl(const Point_2& p1, Arr_parameter_space ps1,
const Point_2& p2, Arr_parameter_space ps2,
Arr_boundary_cond_tag) const;
/*! computes the (lexicographically) leftmost intersection of the query
* curve with a given halfedge on the boundary of a face in the arrangement.
*/

View File

@ -0,0 +1,72 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_APPROXIMATION_CACHE_H
#define CGAL_DRAW_AOS_ARR_APPROXIMATION_CACHE_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <boost/range/iterator_range.hpp>
#include <CGAL/Arr_enums.h>
#include <CGAL/unordered_flat_map.h>
#include <CGAL/Draw_aos/type_utils.h>
namespace CGAL {
namespace draw_aos {
/** @brief Cache class for approximating arrangement on surface.
*
* When iterating over the arrangement dcel, a feature(vertex, halfedge, face) might be visited multiple times.
* This cache stores the approximated geometry for each feature to avoid redundant calculations.
* @tparam Arrangement
*/
template <typename Arrangement>
class Arr_approximation_cache {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Vertex_cache_obj = typename Approx_traits::Point;
using Halfedge_cache_obj = typename Approx_traits::Polyline;
using Face_cache_obj = typename Approx_traits::Triangle_soup;
using Vertex_const_handle = typename Arrangement::Vertex_const_handle;
using Halfedge_const_handle = typename Arrangement::Halfedge_const_iterator;
using Face_const_handle = typename Arrangement::Face_const_handle;
using Vertex_cache = unordered_flat_map<Vertex_const_handle, Vertex_cache_obj>;
using Halfedge_cache = unordered_flat_map<Halfedge_const_handle, Halfedge_cache_obj>;
using Face_cache = unordered_flat_map<Face_const_handle, Face_cache_obj>;
public:
Arr_approximation_cache() = default;
const Vertex_cache& vertices() const { return m_vertices; }
const Halfedge_cache& halfedges() const { return m_halfedges; }
const Face_cache& faces() const { return m_faces; }
Vertex_cache& vertices() { return m_vertices; }
Halfedge_cache& halfedges() { return m_halfedges; }
Face_cache& faces() { return m_faces; }
private:
Vertex_cache m_vertices;
Halfedge_cache m_halfedges;
Face_cache m_faces;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,258 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_FACE_H
#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_FACE_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <iterator>
#include <optional>
#include <utility>
#include <algorithm>
#include <boost/iterator/function_output_iterator.hpp>
#include <CGAL/Arr_enums.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Draw_aos/Arr_bounded_approximate_halfedge.h>
#include <CGAL/Draw_aos/Arr_bounded_approximate_vertex.h>
#include <CGAL/Draw_aos/Arr_bounded_face_triangulator.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
#include <CGAL/Draw_aos/type_utils.h>
namespace CGAL {
namespace draw_aos {
/*! \brief Functor to approximate arrangement face with triangles within a bounding box.
*
* \tparam Arrangement
*/
template <typename Arrangement>
class Arr_bounded_approximate_face {
using Face_const_handle = typename Arrangement::Face_const_handle;
using Halfedge_const_handle = typename Arrangement::Halfedge_const_handle;
using Vertex_const_handle = typename Arrangement::Vertex_const_handle;
using Ccb_halfedge_const_circulator = typename Arrangement::Ccb_halfedge_const_circulator;
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Point = typename Approx_traits::Point;
using Polyline = typename Approx_traits::Polyline;
using Triangle_soup = typename Approx_traits::Triangle_soup;
using Bounded_approximate_vertex = Arr_bounded_approximate_vertex<Arrangement>;
using Bounded_approximate_halfedge = Arr_bounded_approximate_halfedge<Arrangement>;
using Bounded_render_context = Arr_bounded_render_context<Arrangement>;
using Triangulator = Arr_bounded_face_triangulator<Arrangement>;
static constexpr bool Is_on_curved_surface = is_or_derived_from_curved_surf_traits_v<Geom_traits>;
struct Left_to_right_tag {};
struct Right_to_left_tag {};
private:
/*! \brief A stateful geometry simplifier that simplifies horizontal and vertical segments
*
* \tparam OutputIterator
*/
template <typename OutputIterator>
class Colinear_simplifier {
public:
Colinear_simplifier(OutputIterator out_it) : m_out_it(out_it) {}
void dump() {
if (m_start.has_value()) {
*m_out_it++ = m_start.value();
m_start.reset();
}
if (m_mid.has_value()) {
*m_out_it++ = m_mid.value();
m_mid.reset();
}
}
void push_back(Point p) {
if (m_mid.has_value()) {
if (((p.y() == m_mid->y()) && (p.y() == m_start->y())) || ((p.x() == m_mid->x()) && (p.x() == m_start->x())))
// Three points are collinear horizontally or vertically.
m_mid = p;
else {
*m_out_it++ = m_start.value();
m_start = m_mid;
m_mid = p;
}
return;
}
if (m_start.has_value())
m_mid = p;
else
m_start = p;
}
~Colinear_simplifier() { dump(); }
private:
OutputIterator m_out_it;
std::optional<Point> m_start, m_mid;
};
class Context : public Bounded_render_context {
using Simplifier = Colinear_simplifier<std::back_insert_iterator<Triangulator>>;
public:
Context(const Bounded_render_context& ctx, Triangulator& triangulator) :
Bounded_render_context(ctx),
m_triangulator(triangulator) {
if constexpr(!Is_on_curved_surface) m_simplifier.emplace(std::back_inserter(m_triangulator));
}
// Let's not accidentally copy this object.
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
void insert(Point pt) {
if (Approx_traits::is_null(pt) || pt == m_last_pt) return;
pt = Point(pt.x(), std::clamp(pt.y(), this->ymin(), this->ymax()));
if constexpr(!Is_on_curved_surface) {
m_simplifier->push_back(pt);
return;
}
m_triangulator.push_back(pt);
m_last_pt = pt;
}
void start_ccb() { m_triangulator.start_constraint(); }
void end_ccb() {
if constexpr(!Is_on_curved_surface) m_simplifier->dump();
m_triangulator.end_constraint();
}
const std::optional<Point>& last_pt() const { return m_last_pt; }
private:
Triangulator& m_triangulator;
// Colinear simplifier is only used for optimizing planar arrangements.
std::optional<Simplifier> m_simplifier;
std::optional<Point> m_last_pt;
};
private:
static Arr_parameter_space side_of_fict_edge(const Halfedge_const_handle& he) {
const auto& source = he->source();
const auto& target = he->target();
auto sx = source->parameter_space_in_x();
auto sy = source->parameter_space_in_y();
auto tx = target->parameter_space_in_x();
auto ty = target->parameter_space_in_y();
if (sx == tx && sx != ARR_INTERIOR) return sx;
if (sy == ty && sy != ARR_INTERIOR) return sy;
CGAL_assertion(false && "Unexpected parameter space for fictitious edge ends.");
return ARR_INTERIOR;
}
// Generate dummy segment for fictitious edge he at its corresponding boundary.
static Polyline approximate_fict_edge(const Context& ctx, const Halfedge_const_handle& he) {
auto side = side_of_fict_edge(he);
// There's no need to handle fictitious edges on left or right boundaries.
if (side == ARR_LEFT_BOUNDARY || side == ARR_RIGHT_BOUNDARY) return Polyline{};
if (side == ARR_BOTTOM_BOUNDARY) return Polyline{ctx.bottom_left(), ctx.bottom_right()};
if (side == ARR_TOP_BOUNDARY) return Polyline{ctx.top_right(), ctx.top_left()};
CGAL_assertion(false && "Unexpected side for a fictitious edge.");
return Polyline{};
}
void approximate_vertex(Context& /* ctx */, const Vertex_const_handle& vh) const {
if (vh->is_at_open_boundary()) return;
m_bounded_approx_vertex(vh);
}
void approximate_halfedge(Context& ctx, const Halfedge_const_handle& he) const {
const Polyline& polyline = he->is_fictitious() ? approximate_fict_edge(ctx, he) : m_bounded_approx_halfedge(he);
for (const auto& curr_pt : polyline) ctx.insert(curr_pt);
}
void approximate_ccb(Context& ctx, Ccb_halfedge_const_circulator start) const {
// Try to start on a concrete halfedge.
// For any unbounded face, there can't be more than 4 adjacent fictitious edges.
for (int i = 0; i < 4 && start->is_fictitious(); ++i) ++start;
ctx.start_ccb();
auto circ = start;
do {
approximate_halfedge(ctx, circ);
approximate_vertex(ctx, circ->target());
} while(++circ != start);
ctx.end_ccb();
}
public:
Arr_bounded_approximate_face(const Bounded_render_context& ctx) :
m_ctx(ctx),
m_bounded_approx_halfedge(ctx),
m_bounded_approx_vertex(ctx)
{}
/*! \brief Approximate an arrangement face with a bunch of triangles.
*
* \param fh
* \return const Triangulated_face&
*/
const Triangle_soup& operator()(const Face_const_handle& fh) const {
CGAL_precondition_msg(!fh->is_fictitious(), "Cannot approximate a fictitious face.");
auto [iter, inserted] = m_ctx.m_cache.faces().try_emplace(fh);
Triangle_soup& ts = iter->second;
if (! inserted || m_ctx.is_cancelled()) return ts;
auto triangulator = Triangulator(m_ctx, fh);
auto ctx = Context(m_ctx, triangulator);
if (! Is_on_curved_surface && !fh->has_outer_ccb()) {
// Skip approximation of the unbounded face in planar arrangements.
// However, degenerate holes still need to be approximated.
for (auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb) {
auto circ = *inner_ccb;
do {
if (circ->face() != circ->twin()->face()) continue;
m_bounded_approx_halfedge(circ);
} while(++circ != *inner_ccb);
}
for (auto vh = fh->isolated_vertices_begin(); vh != fh->isolated_vertices_end(); ++vh) m_bounded_approx_vertex(vh);
return ts;
}
for (auto outer_ccb = fh->outer_ccbs_begin(); outer_ccb != fh->outer_ccbs_end(); ++outer_ccb)
approximate_ccb(ctx, *outer_ccb);
for (auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb)
approximate_ccb(ctx, *inner_ccb);
for (auto iso_vertex = fh->isolated_vertices_begin(); iso_vertex != fh->isolated_vertices_end(); ++iso_vertex)
approximate_vertex(ctx, iso_vertex);
return ts = std::move(triangulator);
}
private:
const Bounded_render_context& m_ctx;
const Bounded_approximate_halfedge m_bounded_approx_halfedge;
const Bounded_approximate_vertex m_bounded_approx_vertex;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,337 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_HALFEDGE_H
#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_HALFEDGE_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <algorithm>
#include <array>
#include <cstdlib>
#include <optional>
#include <type_traits>
#include <boost/iterator/function_output_iterator.hpp>
#include <CGAL/enum.h>
#include <CGAL/Arr_enums.h>
#include <CGAL/Arr_has.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
#include <CGAL/Draw_aos/type_utils.h>
namespace CGAL {
namespace draw_aos {
/** @brief Functor to approximate an x-monotone curve within an bounding box.
*
* The Approximation is done from xmin to xmax with a given step. For parts outbound the y limits and precedes or
* succeeds a part within, the approximation may be skipped but there will be at least one point outside the bbox
* for indication.
*/
template <typename Arrangement>
class Arr_bounded_approximate_halfedge {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Halfedge_const_handle = typename Arrangement::Halfedge_const_handle;
using Gt_point = typename Geom_traits::Point_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_nt = typename Approx_traits::Approx_nt;
using Approx_point = typename Approx_traits::Approx_point;
using Point = typename Approx_traits::Point;
using Polyline = typename Approx_traits::Polyline;
using Approx_kernel = typename Approx_traits::Approx_kernel;
using Approx_line_2 = typename Approx_kernel::Line_2;
using X_monotone_curve_2 = typename Geom_traits::X_monotone_curve_2;
using Bounded_render_context = Arr_bounded_render_context<Arrangement>;
using Boundary_lines = std::array<Approx_line_2, 4>;
constexpr static bool Has_approximate_xcv_with_bounds =
has_approximate_xcv_with_bounds_v<Geom_traits, typename Geom_traits::Approximate_2>;
private:
struct Context : public Bounded_render_context {
Context(const Bounded_render_context& ctx, const X_monotone_curve_2& curve, Polyline& polyline) :
Bounded_render_context(ctx),
m_polyline(polyline), m_curve(curve)
{}
// Prevent accidental copying.
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
public:
/*! \brief Insert a point to the polyline if it is within the x-range of the curve
* \note Will be replaced after AosApproximateUnboundedTraits_2 is fully available.
* \param pt
*/
void insert(Point pt) {
if (pt.x() < this->xmin()) {
// We need the last point if not yet x-inbound.
m_last_pt = pt;
return;
}
else if (pt.x() > this->xmax()) return;
m_polyline.push_back(pt);
m_last_pt = pt;
}
const std::optional<Point>& last_pt() const { return m_last_pt; }
private:
std::optional<Point> m_last_pt;
public:
Polyline& m_polyline;
const X_monotone_curve_2& m_curve;
};
/*! \brief Computes the intersection point between the given boundary side and the line segment from last_pt to pt.
*/
Point boundary_intersection(const Context& ctx, Point pt, Boundary_side side) const {
std::optional<double> x, y;
const Approx_line_2* line = nullptr;
switch(side) {
case Boundary_side::Left:
x = ctx.xmin();
line = &m_left;
break;
case Boundary_side::Right:
x = ctx.xmax();
line = &m_right;
break;
case Boundary_side::Top:
y = ctx.ymax();
line = &m_top;
break;
case Boundary_side::Bottom:
y = ctx.ymin();
line = &m_bottom;
break;
default:
CGAL_assertion(false && "Unexpected side of boundary.");
}
Point inter = std::get<Point>(*CGAL::intersection(Approx_line_2(*ctx.last_pt(), pt), *line));
if (x.has_value()) return Point(*x, inter.y());
return Point(inter.x(), *y);
}
/*! \brief Trace approximated curve point in ltr ordering, adding boundary intersections if necessary.
*
* \note This method will eventually be replaced by AosApproximateUnboundedTraits_2.
*/
void trace_add(Context& ctx, Point pt) const {
if (! ctx.last_pt().has_value()) {
ctx.insert(pt);
return;
}
if (ctx.last_pt()->x() < ctx.xmin() && pt.x() >= ctx.xmin())
ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Left));
if (ctx.last_pt()->y() < ctx.ymin()) {
if (pt.y() > ctx.ymin()) ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Bottom));
if (pt.y() > ctx.ymax()) ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Top));
}
else if (ctx.last_pt()->y() > ctx.ymax()) {
if (pt.y() < ctx.ymax()) ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Top));
if (pt.y() < ctx.ymin()) ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Bottom));
}
else {
if (pt.y() < ctx.ymin())
ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Bottom));
else if (pt.y() > ctx.ymax())
ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Top));
}
if (ctx.last_pt()->x() <= ctx.xmax() && pt.x() > ctx.xmax())
ctx.insert(boundary_intersection(ctx, pt, Boundary_side::Right));
ctx.insert(pt);
}
/*! \brief Check if the point is within the x-range of the curve.
*/
static bool is_in_x_range(const Context& ctx, const Gt_point& pt) {
const Geom_traits& traits = ctx.m_traits;
const X_monotone_curve_2& curve = ctx.m_curve;
if constexpr(has_is_in_x_range_v<Geom_traits>) return curve.is_in_x_range(pt);
if constexpr(!has_parameter_space_in_x_2<Geom_traits>::value) {
const auto& min_pt = traits.construct_min_vertex_2_object()(curve);
const auto& max_pt = traits.construct_max_vertex_2_object()(curve);
return ((traits.compare_x_2_object()(pt, min_pt) != CGAL::SMALLER) &&
(traits.compare_x_2_object()(pt, max_pt) != CGAL::LARGER));
}
Comparison_result left_cmp;
if (auto left_loc = traits.parameter_space_in_x_2_object()(curve, ARR_MIN_END); left_loc == ARR_INTERIOR)
left_cmp = traits.compare_x_2_object()(pt, traits.construct_min_vertex_2_object()(curve));
else if (left_loc == ARR_LEFT_BOUNDARY)
left_cmp = CGAL::LARGER;
else
left_cmp = traits.compare_x_on_boundary_2_object()(pt, curve, ARR_MIN_END);
if (left_cmp == CGAL::SMALLER) return false;
if (left_cmp == CGAL::EQUAL) return true;
Comparison_result right_cmp;
if (auto right_loc = traits.parameter_space_in_x_2_object()(curve, ARR_MAX_END); right_loc == ARR_INTERIOR)
right_cmp = traits.compare_x_2_object()(pt, traits.construct_max_vertex_2_object()(curve));
else if (right_loc == ARR_RIGHT_BOUNDARY)
right_cmp = CGAL::SMALLER;
else
right_cmp = traits.compare_x_on_boundary_2_object()(pt, curve, ARR_MAX_END);
return right_cmp != CGAL::LARGER;
}
/*! \brief transform approximated curve points(ltr ordering) in place based on the halfedge, giving correct
* ordering, continuity, etc.
*/
static void transform_polyline(Context& ctx, Polyline& polyline, const Halfedge_const_handle& he)
{ transform_polyline_impl<Geom_traits>(ctx, polyline, he); }
// For planar arrangements, we only need to reverse the polyline if the halfedge is rtl.
template <typename Gt, std::enable_if_t<!is_or_derived_from_curved_surf_traits_v<Gt>, int> = 0>
static void transform_polyline_impl(Context&, Polyline& polyline, const Halfedge_const_handle& he) {
if (he->direction() == CGAL::ARR_LEFT_TO_RIGHT) return;
std::reverse(polyline.begin(), polyline.end());
}
template <typename Gt, std::enable_if_t<is_or_derived_from_agas_v<Gt>, int> = 0>
static void transform_polyline_impl(Context& ctx, Polyline& polyline, const Halfedge_const_handle& he) {
using Direction_3 = typename Geom_traits::Direction_3;
using Vector_3 = typename Geom_traits::Vector_3;
if (polyline.size() < 2) return;
const X_monotone_curve_2& curve = he->curve();
const auto& traits = ctx.m_traits;
if (curve.is_vertical()) {
Direction_3 normal_dir = curve.is_directed_right() ? curve.normal() : -curve.normal();
Direction_3 azimuth_dir(CGAL::cross_product(Vector_3(0, 0, 1), normal_dir.vector()));
Approx_nt azimuth = ctx.to_uv(traits.approximate_2_object()(traits.construct_point_2_object()(azimuth_dir))).x();
if (azimuth == 0 && he->direction() == ARR_LEFT_TO_RIGHT) azimuth = 2 * CGAL_PI;
std::transform(polyline.begin(), polyline.end(), polyline.begin(),
[azimuth](Point pt) { return Point(azimuth, pt.y()); });
}
else if (polyline.back().x() == 0) {
// For strictly x-monotone arcs whose target point sits on the boundary, the x should be set to 2 * CGAL_PI
polyline.back() = Point(2 * CGAL_PI, polyline.back().y());
}
if (he->direction() == CGAL::ARR_LEFT_TO_RIGHT) return;
std::reverse(polyline.begin(), polyline.end());
}
void approximate_curve(Context& ctx) const { approximate_curve_impl<Geom_traits>(ctx); }
// If Approximate_2 supports curve approximation with bounding box
template <typename Gt, std::enable_if_t<has_approximate_xcv_with_bounds_v<Gt, typename Gt::Approximate_2>, int> = 0>
void approximate_curve_impl(Context& ctx) const {
const Geom_traits& traits = ctx.m_traits;
const X_monotone_curve_2& curve = ctx.m_curve;
Polyline& polyline = ctx.m_polyline;
auto compare_y_at_x_2 = traits.compare_y_at_x_2_object();
if (is_in_x_range(ctx, m_top_left)) {
if (compare_y_at_x_2(m_top_left, curve) == CGAL::SMALLER) {
polyline.insert(polyline.end(), {Approx_traits::Null_point, Point(ctx.xmin(), ctx.ymax())});
}
else if (compare_y_at_x_2(m_bottom_left, curve) == CGAL::LARGER) {
polyline.insert(polyline.end(), {Approx_traits::Null_point, Point(ctx.xmin(), ctx.ymin())});
}
}
traits.approximate_2_object()(curve, ctx.m_approx_error,
boost::make_function_output_iterator([&ctx, this](Approx_point approx_pt)
{ ctx.m_polyline.push_back(snap_to_boundary(ctx, ctx.to_uv(approx_pt))); }),
ctx.bbox(), true);
if (is_in_x_range(ctx, m_top_right)) {
if (compare_y_at_x_2(m_top_right, curve) == CGAL::SMALLER) {
polyline.insert(polyline.end(), {Point(ctx.xmax(), ctx.ymax()), Approx_traits::Null_point});
}
else if (compare_y_at_x_2(m_bottom_right, curve) == CGAL::LARGER) {
polyline.insert(polyline.end(), {Point(ctx.xmax(), ctx.ymin()), Approx_traits::Null_point});
}
}
}
// If Approximate_2 does not support curve approximation with bounding box
template <typename Gt, std::enable_if_t<!has_approximate_xcv_with_bounds_v<Gt, typename Gt::Approximate_2>, int> = 0>
void approximate_curve_impl(Context& ctx) const {
auto approx = m_ctx.m_traits.approximate_2_object();
approx(ctx.m_curve, ctx.m_approx_error,
boost::make_function_output_iterator([&ctx, this](Approx_point pt) { trace_add(ctx, ctx.to_uv(pt)); }), true);
}
/*! \brief Adjusts a point by snapping it to the nearest boundary to reduce floating-point error.
*
* \return The adjusted (snapped) point if it lies within snapping tolerance, or the original point otherwise.
*/
Point snap_to_boundary(const Context& ctx, Point pt) const {
Approx_nt x = pt.x(), y = pt.y();
if (std::abs(x - ctx.xmin()) < m_ep_left) x = ctx.xmin();
else if (std::abs(x - ctx.xmax()) < m_ep_right) x = ctx.xmax();
if (std::abs(y - ctx.ymin()) < m_ep_bottom) y = ctx.ymin();
else if (std::abs(y - ctx.ymax()) < m_ep_top) y = ctx.ymax();
return Point(x, y);
}
public:
Arr_bounded_approximate_halfedge(const Bounded_render_context& ctx) :
m_ctx(ctx),
m_left(ctx.bottom_left(), ctx.top_left()),
m_right(ctx.bottom_right(), ctx.top_right()),
m_bottom(ctx.bottom_left(), ctx.bottom_right()),
m_top(ctx.top_left(), ctx.top_right()) {
Construct_gt_point_2<Geom_traits> ctr_p;
m_top_left = ctr_p(ctx.to_cartesian(ctx.top_left()));
m_top_right = ctr_p(ctx.to_cartesian(ctx.top_right()));
m_bottom_left = ctr_p(ctx.to_cartesian(ctx.bottom_left()));
m_bottom_right = ctr_p(ctx.to_cartesian(ctx.bottom_right()));
Approx_nt ep_base = std::numeric_limits<Approx_nt>::epsilon();
m_ep_left = std::max(std::abs(ep_base * ctx.xmin()), ep_base);
m_ep_right = std::max(std::abs(ep_base * ctx.xmax()), ep_base);
m_ep_bottom = std::max(std::abs(ep_base * ctx.ymin()), ep_base);
m_ep_top = std::max(std::abs(ep_base * ctx.ymax()), ep_base);
}
const Polyline& operator()(const Halfedge_const_handle& he) const {
CGAL_assertion(!he->is_fictitious());
auto& cache = m_ctx.m_cache.halfedges();
auto [iter, inserted] = cache.try_emplace(he, Polyline());
Polyline& polyline = iter->second;
if (!inserted) return polyline;
if (m_ctx.is_cancelled()) return polyline;
const X_monotone_curve_2& curve = he->curve();
Context ctx(m_ctx, curve, polyline);
approximate_curve(ctx);
Polyline poly_copy(polyline);
transform_polyline(ctx, polyline, he);
// also approximate the twin halfedge
auto [twin_iter, twin_inserted] = cache.try_emplace(he->twin(), std::move(poly_copy));
if (twin_inserted) transform_polyline(ctx, twin_iter->second, he->twin());
// The previous iterator might have been invalidated by the second try_emplace call, so we do an extra lookup.
return cache.at(he);
}
private:
const Bounded_render_context& m_ctx;
Approx_line_2 m_left, m_right, m_bottom, m_top;
Gt_point m_top_left, m_top_right, m_bottom_left, m_bottom_right;
Approx_nt m_ep_left, m_ep_right, m_ep_bottom, m_ep_top;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,60 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_VERTEX_H
#define CGAL_DRAW_AOS_ARR_BOUNDED_APPROXIMATE_VERTEX_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <CGAL/Draw_aos/type_utils.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
namespace CGAL {
namespace draw_aos {
template <typename Arrangement>
class Arr_bounded_approximate_vertex {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Point_2 = typename Geom_traits::Point_2;
using Vertex_const_handle = typename Arrangement::Vertex_const_handle;
using Point_geom = typename Arr_approximate_traits<Geom_traits>::Point;
using Bounded_render_context = Arr_bounded_render_context<Arrangement>;
public:
Arr_bounded_approximate_vertex(const Bounded_render_context& ctx) : m_ctx(ctx) {}
/** @brief Approximate a vertex within the x-bounded range.
*
* The function uses cached values if available.
* @precondition: The vertex must have an associated point.
*
* @param vh the vertex handle
* @return const Point_geom&
*/
const Point_geom& operator()(const Vertex_const_handle& vh) const {
auto [iter, inserted] = m_ctx.m_cache.vertices().try_emplace(vh);
Point_geom& point = iter->second;
if (! inserted) return point;
return point = m_ctx.to_uv(m_ctx.m_traits.approximate_2_object()(vh->point()));
}
private:
const Bounded_render_context& m_ctx;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,317 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H
#define CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <algorithm>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
#include <boost/iterator/function_output_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
#include <CGAL/mark_domain_in_triangulation.h>
#include <CGAL/unordered_flat_map.h>
#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
#include <CGAL/Draw_aos/type_utils.h>
#if defined(CGAL_DRAW_AOS_DEBUG) && defined(CGAL_DRAW_AOS_TRIANGULATOR_DEBUG_FILE_DIR)
#include <fstream>
#include <filesystem>
template <typename Arrangement>
class Arr_bounded_face_triangulator;
template <typename Arrangement>
void debug_print(const Arr_bounded_face_triangulator<Arrangement>& triangulator);
#endif
namespace CGAL {
namespace draw_aos {
/**
* @brief Triangulator for a face of an arrangement within a bounding box.
*/
template <typename Arrangement>
class Arr_bounded_face_triangulator {
using Geom_traits = typename Arrangement::Geometry_traits_2;
constexpr static bool Is_on_curved_surface = is_or_derived_from_curved_surf_traits_v<Geom_traits>;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Point = typename Approx_traits::Point;
using Approx_point = typename Approx_traits::Approx_point;
using Approx_kernel = typename Approx_traits::Approx_kernel;
using Triangle_soup = typename Approx_traits::Triangle_soup;
using Triangle = typename Triangle_soup::Triangle;
using Face_const_handle = typename Arrangement::Face_const_handle;
#if defined(CGAL_DRAW_AOS_DEBUG)
template <typename T>
friend void debug_print(const Arr_bounded_face_triangulator<T>& triangulator);
#endif
enum Point_type { Vertex_only, Constraint_only, Vertex_and_constraint };
/*! \brief A index wrapper defaulted to invalid.
*/
class Index {
public:
Index() = default;
Index(int idx) : m_index(idx) {}
bool is_valid() const { return m_index != Invalid_index; }
operator int() const { return m_index; }
private:
constexpr static int Invalid_index = -1;
int m_index{Invalid_index};
};
using Epick = Exact_predicates_inexact_constructions_kernel;
using Vb = Triangulation_vertex_base_with_info_2<Index, Epick>;
using Fb = Constrained_triangulation_face_base_2<Epick>;
using Tds = Triangulation_data_structure_2<Vb, Fb>;
// For planar arrangements, Constrained_triangulation_2 is enough.
using Ct = std::conditional_t<Is_on_curved_surface,
Constrained_Delaunay_triangulation_2<Epick, Tds, Exact_predicates_tag>,
Constrained_triangulation_2<Epick, Tds, Exact_predicates_tag>>;
using KPoint = Epick::Point_2;
using KPoint_with_index = std::pair<KPoint, Index>;
using Bounded_render_context = Arr_bounded_render_context<Arrangement>;
public:
using value_type = Point;
private:
static KPoint to_kpoint(Point pt) { return KPoint(pt.x(), pt.y()); }
/*! \brief Offset a point on a specific boundary outward by a given offset.
*
* \pre side != Boundary_side::None
*/
static Point offset_boundary_point(Point pt, Boundary_side side, double offset) {
CGAL_precondition(side != Boundary_side::None);
switch(side) {
case Boundary_side::Left: return Point(pt.x() - offset, pt.y());
case Boundary_side::Right: return Point(pt.x() + offset, pt.y());
case Boundary_side::Top: return Point(pt.x(), pt.y() + offset);
case Boundary_side::Bottom: return Point(pt.x(), pt.y() - offset);
default: return pt; // Should not reach here
}
}
/*! \brief Find the shared boundary side of two points, or None if they are not on the same boundary.
*/
Boundary_side shared_boundary(const Point& pt1, const Point& pt2) const {
if (m_ctx.is_on_left(pt1) && m_ctx.is_on_left(pt2)) return Boundary_side::Left;
if (m_ctx.is_on_right(pt1) && m_ctx.is_on_right(pt2)) return Boundary_side::Right;
if (m_ctx.is_on_bottom(pt1) && m_ctx.is_on_bottom(pt2)) return Boundary_side::Bottom;
if (m_ctx.is_on_top(pt1) && m_ctx.is_on_top(pt2)) return Boundary_side::Top;
return Boundary_side::None;
}
/*! \brief Add a helper point on the shared boundary of two points if they are on the same boundary side.
*
* When triangulating a arrangement face within a bounding box, curves outside the bounding box are projected on the
* four sides of the bbox. Topological errors could be introduced if several segments are lying on the same side.
* Thus we add the midpoint in between the two points on boundary and move it outward with an increasing offset.
*/
void add_boundary_helper_point(Point from, Point to) {
// Arrangements on curved surfaces currently draws the entire parameter space, so there's no need to add
// helper points.
if constexpr(Is_on_curved_surface) return;
if (from == to) return;
auto shared_side = shared_boundary(from, to);
if (shared_side == Boundary_side::None) return;
Point mid = CGAL::midpoint(from, to);
m_points.push_back(offset_boundary_point(mid, shared_side, m_offset += 0.1));
m_point_types.push_back(Constraint_only);
}
void insert_all_vertices() {
auto vertex_filter = [this](int idx) { return m_point_types[idx] != Constraint_only; };
auto index_to_point_with_info = [this](int idx) -> KPoint_with_index {
return std::make_pair(to_kpoint(m_points[idx]), idx);
};
auto indexes_begin = boost::make_counting_iterator<int>(0);
auto indexes_end = boost::make_counting_iterator<int>(m_points.size());
auto filtered_begin = boost::make_filter_iterator(vertex_filter, indexes_begin, indexes_end);
auto filtered_end = boost::make_filter_iterator(vertex_filter, indexes_end, indexes_end);
auto transformed_begin = boost::make_transform_iterator(filtered_begin, index_to_point_with_info);
auto transformed_end = boost::make_transform_iterator(filtered_end, index_to_point_with_info);
// Constrained_triangulation_2 and Constrained_Delaunay_triangulation_2 have slightly different interfaces.
if constexpr(Is_on_curved_surface)
m_ct.insert(transformed_begin, transformed_end);
else
m_ct.template insert_with_info<KPoint_with_index>(transformed_begin, transformed_end);
}
void insert_all_constraints() {
auto constraint_filter = [this](int idx) { return m_point_types[idx] != Vertex_only; };
auto index_to_point = [this](int idx) -> KPoint { return to_kpoint(m_points[idx]); };
for (auto [start_idx, end_idx] : m_cst_ranges) {
auto indexes_begin = boost::make_counting_iterator<int>(start_idx);
auto indexes_end = boost::make_counting_iterator<int>(end_idx);
auto filtered_begin = boost::make_filter_iterator(constraint_filter, indexes_begin, indexes_end);
auto filtered_end = boost::make_filter_iterator(constraint_filter, indexes_end, indexes_end);
auto transformed_begin = boost::make_transform_iterator(filtered_begin, index_to_point);
auto transformed_end = boost::make_transform_iterator(filtered_end, index_to_point);
m_ct.insert_constraint(transformed_begin, transformed_end, true);
}
}
public:
Arr_bounded_face_triangulator(const Bounded_render_context& ctx, Face_const_handle fh) :
m_ctx(ctx),
m_fh(fh)
{}
void push_back(Point pt) {
CGAL_assertion_msg(m_curr_cst_begin.has_value(), "Call start_constraint() before push_back().");
if (m_points.size() - *m_curr_cst_begin >= 1) add_boundary_helper_point(m_points.back(), pt);
m_points.push_back(pt);
m_point_types.push_back(Vertex_and_constraint);
}
void start_constraint() { m_curr_cst_begin = m_points.size(); }
void end_constraint() {
CGAL_assertion_msg(m_curr_cst_begin.has_value(), "Call start_constraint() before end_constraint().");
int cst_begin = *m_curr_cst_begin;
m_curr_cst_begin.reset();
if (m_points.size() - cst_begin <= 2) {
m_points.erase(m_points.begin() + cst_begin, m_points.end());
m_point_types.erase(m_point_types.begin() + cst_begin, m_point_types.end());
return;
}
add_boundary_helper_point(m_points.back(), m_points[cst_begin]);
m_cst_ranges.emplace_back(cst_begin, m_points.size());
}
/*! \brief Converts the triangulator to a triangulated face, moving internal data to the result.
*
* \return Triangulated_face
*/
operator Triangle_soup() && {
CGAL_assertion_msg(!m_curr_cst_begin.has_value(), "Call end_constraint() before conversion");
if (m_points.empty()) return Triangle_soup();
if constexpr(Is_on_curved_surface) {
if (auto it = m_ctx.m_face_points.find(m_fh); it != m_ctx.m_face_points.end()) {
m_points.insert(m_points.end(), it->second.begin(), it->second.end());
m_point_types.insert(m_point_types.end(), it->second.size(), Vertex_only);
}
}
insert_all_vertices();
insert_all_constraints();
if (m_ct.number_of_faces() == 0) return Triangle_soup();
#if defined(CGAL_DRAW_AOS_DEBUG)
debug_print(*this);
#endif
unordered_flat_map<typename Ct::Face_handle, bool> in_domain_map;
in_domain_map.reserve(m_ct.number_of_faces());
boost::associative_property_map<decltype(in_domain_map)> in_domain(in_domain_map);
CGAL::mark_domain_in_triangulation(m_ct, in_domain);
// Collect triangles within the constrained domain.
Triangle_soup ts;
ts.triangles.reserve(m_ct.number_of_faces());
for (auto fit = m_ct.finite_faces_begin(); fit != m_ct.finite_faces_end(); ++fit) {
Index v1 = fit->vertex(0)->info();
Index v2 = fit->vertex(1)->info();
Index v3 = fit->vertex(2)->info();
if (! v1.is_valid() || !v2.is_valid() || !v3.is_valid()) continue;
if (! get(in_domain, fit)) continue;
ts.triangles.push_back(Triangle{v1, v2, v3});
}
ts.points = std::move(m_points);
return ts;
}
private:
const Bounded_render_context& m_ctx;
Face_const_handle m_fh;
Ct m_ct;
std::vector<Point> m_points;
std::vector<Point_type> m_point_types;
std::vector<std::pair<int, int>> m_cst_ranges;
std::optional<int> m_curr_cst_begin;
double m_offset{0};
};
#if defined(CGAL_DRAW_AOS_DEBUG) && defined(CGAL_DRAW_AOS_TRIANGULATOR_DEBUG_FILE_DIR)
template <typename Arrangement>
void debug_print(const Arr_bounded_face_triangulator<Arrangement>& triangulator) {
const auto& ctx = triangulator.m_ctx;
const auto& m_points = triangulator.m_points;
const auto& m_point_types = triangulator.m_point_types;
using Point_type = typename Arr_bounded_face_triangulator<Arrangement>::Point_type;
using Path = std::filesystem::path;
Path debug_dir(CGAL_DRAW_AOS_TRIANGULATOR_DEBUG_FILE_DIR);
std::string index_file_name = "index.txt";
Path index_file_path = debug_dir / index_file_name;
std::string points_file_name_prefix = "face_" + std::to_string(*ctx.debug_counter) + "_points";
std::string ccb_constraint_file_name_prefix = "face_" + std::to_string(*ctx.debug_counter) + "_constraint";
const_cast<int&>(*ctx.debug_counter)++;
std::ofstream ofs_index(index_file_path, std::ios::app);
auto points_filename = points_file_name_prefix + ".txt";
auto points_path = debug_dir / points_filename;
std::ofstream ofs_points(points_path);
ofs_index << points_filename << std::endl;
for (int i = 0; i < triangulator.m_points.size(); ++i) {
if (m_point_types[i] == Point_type::Constraint_only) continue;
const auto& pt = m_points[i];
ofs_points << pt.x() << " " << pt.y() << "\n";
}
int counter = 0;
for (auto [start_idx, end_idx] : triangulator.m_cst_ranges) {
auto filename = ccb_constraint_file_name_prefix + "_" + std::to_string(counter++) + ".txt";
auto filepath = debug_dir / filename;
ofs_index << filename << std::endl;
std::ofstream ofs_ccb_constraint(filepath);
for (int i = start_idx; i < end_idx; ++i) {
if (m_point_types[i] == Point_type::Vertex_only) continue;
const auto& pt = m_points[i];
ofs_ccb_constraint << pt.x() << " " << pt.y() << "\n";
}
}
}
#endif
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,67 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_BOUNDED_RENDERER_H
#define CGAL_DRAW_AOS_ARR_BOUNDED_RENDERER_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Draw_aos/Arr_approximation_cache.h>
#include <CGAL/Draw_aos/Arr_bounded_approximate_face.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
#include <CGAL/Draw_aos/type_utils.h>
namespace CGAL {
namespace draw_aos {
/** @brief Render arrangement on surface within a bounding box.
*/
template <typename Arrangement>
class Arr_bounded_renderer {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Face_const_handle = typename Arrangement::Face_const_handle;
using Render_context = Arr_render_context<Arrangement>;
using Approx_cache = Arr_approximation_cache<Arrangement>;
public:
Arr_bounded_renderer(const Render_context& ctx, Bbox_2 bbox) :
m_ctx(ctx),
m_bbox(bbox)
{}
Approx_cache render() const {
Approx_cache cache;
if(m_ctx.is_cancelled()) return cache;
cache.vertices().reserve(m_ctx.m_arr.number_of_vertices());
cache.halfedges().reserve(m_ctx.m_arr.number_of_halfedges());
cache.faces().reserve(m_ctx.m_arr.number_of_faces());
Arr_bounded_render_context<Arrangement> derived_ctx(m_ctx, m_bbox, cache);
Arr_bounded_approximate_face<Arrangement> bounded_approx_face(derived_ctx);
for(Face_const_handle fh = m_ctx.m_arr.faces_begin(); fh != m_ctx.m_arr.faces_end(); ++fh) bounded_approx_face(fh);
return cache;
}
private:
const Render_context& m_ctx;
const Bbox_2 m_bbox;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,112 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_COORDINATE_CONVERTER_H
#define CGAL_DRAW_AOS_ARR_COORDINATE_CONVERTER_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <cmath>
#include <CGAL/number_type_config.h>
#include <CGAL/Arr_geodesic_arc_on_sphere_traits_2.h>
#include <CGAL/Draw_aos/type_utils.h>
namespace CGAL {
namespace draw_aos {
/*! \brief class handling coordinate conversion between 2D parameterized surface coordinates and cartesian coordinates.
*
* \tparam GeomTraits
*/
template <typename GeomTraits>
class Arr_coordinate_converter {
using Geom_traits = GeomTraits;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_point = typename Approx_traits::Approx_point;
using Point = typename Approx_traits::Point;
public:
Arr_coordinate_converter(const GeomTraits& traits) : m_traits(traits) {}
/*! \brief converts a point in cartesian coordinates to parameterized surface coordinates.
*
* \param pt
* \return Point
*/
Point to_uv(Approx_point pt) const { return pt; }
/*! \brief Converts a point in parameterized surface coordinates to cartesian coordinates.
*
* \param pt
* \return Approx_point
*/
Approx_point to_cartesian(Point pt) const { return pt; }
private:
const GeomTraits& m_traits;
};
/*! \brief Converter specialization for geodesic arc on sphere traits.
*
* provides conversions between spherical coordinates and right-handed Cartesian coordinates. Sphercial coordinates are
* represented as azimuth ( [0, 2 Pi) ) and polar ( [0, Pi] ) angle in radians. Points on the identification curve have
* azimuth == 0. The south pole has polar == 0.
*
* \tparam Kernel
* \tparam atanX
* \tparam atanY
*/
template <typename Kernel, int atanX, int atanY>
class Arr_coordinate_converter<Arr_geodesic_arc_on_sphere_traits_2<Kernel, atanX, atanY>> {
using Geom_traits = Arr_geodesic_arc_on_sphere_traits_2<Kernel>;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_point = typename Approx_traits::Approx_point;
using Approx_nt = typename Approx_traits::Approx_nt;
using Point = typename Approx_traits::Point;
public:
Arr_coordinate_converter(const Geom_traits& traits) : m_traits(traits) {}
Point to_uv(Approx_point point) const {
if(point.location() == Approx_point::MAX_BOUNDARY_LOC) return Point(0, CGAL_PI);
if(point.location() == Approx_point::MIN_BOUNDARY_LOC) return Point(0, 0);
Approx_nt azimuth_from_id =
std::fmod(std::atan2(point.dy(), point.dx()) - std::atan2(atanY, atanX) + 2 * CGAL_PI, 2 * CGAL_PI);
return Point(azimuth_from_id, std::acos(-point.dz()));
}
Approx_point to_cartesian(Point point) const {
using Direction_3 = typename Geom_traits::Approximate_kernel::Direction_3;
Approx_nt polar = point.y();
if(point.y() == CGAL_PI) return Approx_point(Direction_3(0, 0, 1), Approx_point::MAX_BOUNDARY_LOC);
if(point.y() == 0) return Approx_point(Direction_3(0, 0, -1), Approx_point::MIN_BOUNDARY_LOC);
Approx_nt azimuth = point.x() + std::atan2(atanY, atanX);
Approx_nt x = std::sin(polar) * std::cos(azimuth);
Approx_nt y = std::sin(polar) * std::sin(azimuth);
Approx_nt z = -std::cos(polar);
Direction_3 dir(x, y, z);
return Approx_point(dir, azimuth == 0 ? Approx_point::MID_BOUNDARY_LOC : Approx_point::NO_BOUNDARY_LOC);
}
private:
const Geom_traits& m_traits;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,101 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_FACE_POINT_GENERATOR_H
#define CGAL_DRAW_AOS_ARR_FACE_POINT_GENERATOR_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <utility>
#include <variant>
#include <vector>
#include <boost/iterator/function_output_iterator.hpp>
#include "CGAL/unordered_flat_map.h"
#include "CGAL/Arr_batched_point_location.h"
#include "CGAL/Arr_point_location_result.h"
#include "CGAL/Draw_aos/Arr_coordinate_converter.h"
#include "CGAL/Draw_aos/type_utils.h"
namespace CGAL {
namespace draw_aos {
/*! \brief Generate face interior points.
*
* \tparam Arrangement
*/
template <typename Arrangement, typename = void>
class Arr_face_point_generator;
template <typename Arrangement>
class Arr_face_point_generator<Arrangement,
std::enable_if_t<!is_or_derived_from_curved_surf_traits_v
<typename Arrangement::Geometry_traits_2>>> {
using Point_geom = typename Arr_approximate_traits<typename Arrangement::Geometry_traits_2>::Point;
using Face_const_handle = typename Arrangement::Face_const_handle;
public:
using Face_points_map = unordered_flat_map<Face_const_handle, std::vector<Point_geom>>;
// No-op implementation for non-curved surface arrangements.
Face_points_map operator()(const Arrangement&, double) { return {}; }
};
template <typename Arrangement>
class Arr_face_point_generator<Arrangement,
std::enable_if_t<is_or_derived_from_agas_v<typename Arrangement::Geometry_traits_2>>> {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_nt = typename Approx_traits::Approx_nt;
using Point = typename Approx_traits::Point;
using Face_const_handle = typename Arrangement::Face_const_handle;
using Gt_point = typename Geom_traits::Point_2;
using Query_result = std::pair<Gt_point, typename Arr_point_location_result<Arrangement>::Type>;
public:
using Face_points_map = unordered_flat_map<Face_const_handle, std::vector<Point>>;
Face_points_map operator()(const Arrangement& arr, double error) {
const Geom_traits& traits = *arr.geometry_traits();
// Grid sampling in parameter space.
Approx_nt cell_size = 2.0 * std::acos(1 - error);
std::vector<Gt_point> points;
Arr_coordinate_converter<Geom_traits> coords(traits);
points.reserve(2 * CGAL_PI / cell_size * CGAL_PI / cell_size);
for (Approx_nt x = 0; x < 2 * CGAL_PI; x += cell_size) {
for (Approx_nt y = 0; y < CGAL_PI; y += cell_size) {
auto pt = coords.to_cartesian(Point(x, y));
points.push_back(traits.construct_point_2_object()(pt.dx(), pt.dy(), pt.dz()));
}
}
unordered_flat_map<Face_const_handle, std::vector<Point>> face_points;
CGAL::locate(arr, points.begin(), points.end(),
boost::make_function_output_iterator([&face_points, &traits, &coords](const Query_result& res) {
if (! std::holds_alternative<Face_const_handle>(res.second)) return;
Face_const_handle fh = std::get<Face_const_handle>(res.second);
auto [it, _] = face_points.try_emplace(fh, std::vector<Point>());
it->second.push_back(coords.to_uv(traits.approximate_2_object()(res.first)));
}));
return face_points;
}
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,178 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Li <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_ARR_RENDER_CONTEXT_H
#define CGAL_DRAW_AOS_ARR_RENDER_CONTEXT_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <cstdlib>
#include <memory>
#include <atomic>
#include <chrono>
#include <CGAL/Bbox_2.h>
#include <CGAL/Arr_point_location_result.h>
#include <CGAL/Arr_trapezoid_ric_point_location.h>
#include <CGAL/Arrangement_2.h>
#include <CGAL/Draw_aos/Arr_approximation_cache.h>
#include <CGAL/Draw_aos/type_utils.h>
#include <CGAL/Draw_aos/Arr_face_point_generator.h>
#include <CGAL/Draw_aos/Arr_coordinate_converter.h>
#if defined(CGAL_DRAW_AOS_DEBUG)
#include <fstream>
#endif
namespace CGAL {
namespace draw_aos {
/** @brief A cancellable context mixin for asynchronous operations. It also tracks elapsed time for performance
* profiling.
*
* The idea is borrowed from golang with a simple implementation.
* @see https://pkg.go.dev/context
*/
class Arr_cancellable_context_mixin {
using Clock = std::chrono::steady_clock;
using Duration = Clock::duration;
using Time_point = std::chrono::time_point<Clock, Duration>;
protected:
Arr_cancellable_context_mixin() :
m_start_time(Clock::now()),
m_cancelled(std::make_shared<std::atomic<bool>>(false))
{}
public:
Time_point start_time() const { return m_start_time; }
Time_point end_time() const { return m_end_time; }
Duration elapsed_time() const { return Clock::now() - m_start_time; }
bool is_cancelled() const { return m_cancelled->load(); }
void cancel() {
m_cancelled->store(true, std::memory_order_relaxed);
m_end_time = Clock::now();
}
private:
Time_point m_start_time, m_end_time;
std::shared_ptr<std::atomic<bool>> m_cancelled;
};
/** @brief Boundary context mixin for rendering arrangements within a bounding box.
* Provides extended functionality for checking point-bbox relations.
*
* @tparam GeomTraits the geometry traits class.
*/
template <typename GeomTraits>
class Arr_bounds_context_mixin {
using Geom_traits = GeomTraits;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Point = typename Approx_traits::Point;
using Approx_nt = typename Approx_traits::Approx_nt;
protected:
Arr_bounds_context_mixin(const Bbox_2& bbox) : m_bbox(bbox) {}
public:
double xmin() const { return m_bbox.xmin(); }
double xmax() const { return m_bbox.xmax(); }
double ymin() const { return m_bbox.ymin(); }
double ymax() const { return m_bbox.ymax(); }
const Bbox_2& bbox() const { return m_bbox; }
bool contains_x(Approx_nt x) const { return xmin() <= x && x <= xmax(); }
bool contains_y(Approx_nt y) const { return ymin() <= y && y <= ymax(); }
bool contains(Point pt) const { return contains_x(pt.x()) && contains_y(pt.y()); }
Point top_left() const { return Point(xmin(), ymax()); }
Point top_right() const { return Point(xmax(), ymax()); }
Point bottom_left() const { return Point(xmin(), ymin()); }
Point bottom_right() const { return Point(xmax(), ymin()); }
bool is_on_left(Point pt) const { return pt.x() == xmin() && contains_y(pt.y()); }
bool is_on_right(Point pt) const { return pt.x() == xmax() && contains_y(pt.y()); }
bool is_on_bottom(Point pt) const { return pt.y() == ymin() && contains_x(pt.x()); }
bool is_on_top(Point pt) const { return pt.y() == ymax() && contains_x(pt.x()); }
bool is_on_boundary(Point pt) const { return is_on_left(pt) || is_on_right(pt) || is_on_bottom(pt) || is_on_top(pt); }
private:
const Bbox_2 m_bbox;
};
template <typename GeomTraits>
using Arr_parameterization_context_mixin = Arr_coordinate_converter<GeomTraits>;
template <typename Arrangement>
class Arr_render_context :
public Arr_cancellable_context_mixin,
public Arr_parameterization_context_mixin<typename Arrangement::Geometry_traits_2> {
using Cancellable_context_mixin = Arr_cancellable_context_mixin;
using Param_context_mixin = Arr_parameterization_context_mixin<typename Arrangement::Geometry_traits_2>;
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Face_points_map = typename Arr_face_point_generator<Arrangement>::Face_points_map;
public:
Arr_render_context(const Arrangement& arr, double approx_error, Face_points_map& face_points) :
Cancellable_context_mixin(),
Param_context_mixin(*arr.geometry_traits()),
m_arr(arr),
m_traits(*arr.geometry_traits()),
m_approx_error(approx_error),
m_face_points(face_points) {
#if defined(CGAL_DRAW_AOS_DEBUG) && defined(CGAL_DRAW_AOS_TRIANGULATOR_DEBUG_FILE_DIR)
std::filesystem::path debug_file_dir(CGAL_DRAW_AOS_TRIANGULATOR_DEBUG_FILE_DIR);
// clear the index file.
std::filesystem::remove(debug_file_dir / "index.txt");
#endif
}
public:
const Arrangement& m_arr;
const Geom_traits& m_traits;
const double m_approx_error;
const Face_points_map& m_face_points;
#if defined(CGAL_DRAW_AOS_DEBUG)
std::shared_ptr<int> debug_counter = std::make_shared<int>(0);
#endif
};
template <typename Arrangement>
class Arr_bounded_render_context :
public Arr_render_context<Arrangement>,
public Arr_bounds_context_mixin<typename Arrangement::Geometry_traits_2> {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_point = typename Geom_traits::Approximate_point_2;
using Render_context = Arr_render_context<Arrangement>;
using Bounds_context_mixin = Arr_bounds_context_mixin<Geom_traits>;
using Approx_cache = Arr_approximation_cache<Arrangement>;
public:
Arr_bounded_render_context(const Render_context& ctx, const Bbox_2& bbox, Approx_cache& cache) :
Render_context(ctx),
Bounds_context_mixin(bbox),
m_cache(cache)
{}
public:
Approx_cache& m_cache;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,358 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef ARR_VIEWER_H
#define ARR_VIEWER_H
#include <algorithm>
#include <array>
#include <cstdlib>
#include <type_traits>
#include <QWidget>
#include <CGAL/Qt/Basic_viewer.h>
#include <CGAL/Qt/camera.h>
#include <CGAL/IO/Color.h>
#include <CGAL/Basic_viewer.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Graphics_scene.h>
#include <CGAL/Qt/camera.h>
#include <CGAL/Graphics_scene.h>
#include <CGAL/Graphics_scene_options.h>
#include <CGAL/Buffer_for_vao.h>
#include <CGAL/Arr_enums.h>
#include <CGAL/Draw_aos/type_utils.h>
#include <CGAL/Draw_aos/Arr_render_context.h>
#include <CGAL/Draw_aos/Arr_bounded_renderer.h>
#include <CGAL/Draw_aos/Arr_coordinate_converter.h>
#include <CGAL/Draw_aos/Arr_face_point_generator.h>
namespace CGAL {
namespace draw_aos {
/*! \brief Viewport helper functions
*
* \tparam Arrangement
*/
template <typename Arrangement, typename = void>
class Arr_viewport_helpers;
// Specialization for planar arrangements
template <typename Arrangement>
class Arr_viewport_helpers<Arrangement,
std::enable_if_t<! is_or_derived_from_curved_surf_traits_v
<typename Arrangement::Geometry_traits_2>>> {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_point = typename Approx_traits::Approx_point;
using Camera = qglviewer::Camera;
using Point = typename Approx_traits::Point;
using Local_point = Buffer_for_vao::Local_point;
protected:
Arr_viewport_helpers(const Arrangement& arr) : m_arr(arr) {}
/*! \brief Computes a subpixel-level approximation error based on the bounding box and viewport width.
*
* \param bbox
* \param viewport_width width of the viewport in pixels
* \return double
*/
double approximation_error(const Bbox_2& bbox, int viewport_width) const
{ return bbox.x_span() / viewport_width; }
/*! \brief Computes a parameter space bounding box that contains everything in the arrangement with some margin.
*
* \note For arrangement induced by unbounded curves, the bounding box only fits all vertices.
* \return Bbox_2
*/
Bbox_2 arr_bbox() const {
const auto& traits = *m_arr.geometry_traits();
Bbox_2 bbox;
// Computes a rough bounding box from the vertices.
for (const auto& vh : m_arr.vertex_handles())
bbox += traits.approximate_2_object()(vh->point()).bbox();
double approx_error = approximation_error(bbox, 100);
// Computes a more precise bounding box from the halfedges.
auto approx = traits.approximate_2_object();
for (const auto& he : m_arr.halfedge_handles()) {
approx(he->curve(), approx_error,
boost::make_function_output_iterator([&bbox](Approx_point pt) { bbox += pt.bbox(); }));
}
// Place margin around the bbox.
double dx = bbox.x_span() * 0.1;
double dy = bbox.y_span() * 0.1;
bbox = Bbox_2(bbox.xmin() - dx, bbox.ymin() - dy, bbox.xmax() + dx, bbox.ymax() + dy);
// Make sure the bbox is not degenerate.
if (bbox.x_span() == 0) bbox += Bbox_2(bbox.xmin() - 1, bbox.ymin(), bbox.xmax() + 1, bbox.ymax());
if (bbox.y_span() == 0) bbox += Bbox_2(bbox.xmin(), bbox.ymin() - 1, bbox.xmax(), bbox.ymax() + 1);
return bbox;
}
/*! \brief Fits the camera to bbox.
*
* \param bbox
* \param camera
*/
void fit_camera(const Bbox_2& bbox, Camera& cam) const {
using Vec = qglviewer::Vec;
cam.fitBoundingBox(Vec(bbox.xmin(), bbox.ymin(), 0.0), Vec(bbox.xmax(), bbox.ymax(), 0.0));
}
/*! \brief Computes parameter space axis aligned bounding box from camera parameters.
*
* \param cam
* \return Bbox_2
*/
Bbox_2 screen_to_world(const Camera& cam) const {
QMatrix4x4 mvp;
cam.getModelViewProjectionMatrix(mvp.data());
QMatrix4x4 inverse_mvp = mvp.inverted();
// Define 4 corners of the near plane in NDC (-1 to 1 in x and y)
std::array<QVector4D, 4> clip_space_corners{QVector4D(-1.0, -1.0, 0.0, 1.0), QVector4D(-1.0, 1.0, 0.0, 1.0),
QVector4D(1.0, -1.0, 0.0, 1.0), QVector4D(1.0, 1.0, 0.0, 1.0)};
double xmin = std::numeric_limits<double>::max();
double xmax = std::numeric_limits<double>::lowest();
double ymin = std::numeric_limits<double>::max();
double ymax = std::numeric_limits<double>::lowest();
for (const QVector4D& corner : clip_space_corners) {
QVector4D world = inverse_mvp * corner;
if (world.w() != 0.0) world /= world.w();
double x = world.x();
double y = world.y();
xmin = std::min(xmin, x);
xmax = std::max(xmax, x);
ymin = std::min(ymin, y);
ymax = std::max(ymax, y);
}
return Bbox_2(xmin, ymin, xmax, ymax);
}
/*! \brief Converts a parameter space point to a local point of the buffer object.
*
* \param pt
* \return Local_point
*/
Local_point to_local_point(Point pt) const { return Local_point(pt.x(), pt.y(), 0.0); }
private:
const Arrangement& m_arr;
};
// Spherical arrangement specialization
template <typename Arrangement>
class Arr_viewport_helpers<Arrangement,
std::enable_if_t<is_or_derived_from_agas_v<typename Arrangement::Geometry_traits_2>>> {
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_point = typename Approx_traits::Approx_point;
using Camera = qglviewer::Camera;
using Point = typename Approx_traits::Point;
using Local_point = Buffer_for_vao::Local_point;
protected:
Arr_viewport_helpers(const Arrangement& arr) : m_arr(arr) {}
Bbox_2 arr_bbox() const { return Bbox_2(0, 0, 2 * CGAL_PI, CGAL_PI); }
Bbox_2 screen_to_world(const Camera& /* cam */) const { return Bbox_2(0, 0, 2 * CGAL_PI, CGAL_PI); }
void fit_camera(const Bbox_2&, Camera& cam) {
using Vec = qglviewer::Vec;
cam.setSceneCenter(Vec(0, 0, 0));
cam.fitSphere(Vec(0, 0, 0), 1.1); // slightly larger than the unit sphere
}
double approximation_error(const Bbox_2& bbox, int viewport_width) const {
// If crossing hemisphere
if (bbox.x_span() >= CGAL_PI) return 1.0 / viewport_width;
// Otherwise we evaluate the error bound with respect to the longest longitude arc
double theta =
std::abs(bbox.ymin() - CGAL_PI / 2.0) < std::abs(bbox.ymax() - CGAL_PI / 2.0) ? bbox.ymin() : bbox.ymax();
return bbox.x_span() * std::sin(theta) / viewport_width;
}
Buffer_for_vao::Local_point to_local_point(Point pt) const {
auto approx_pt = Arr_coordinate_converter<Geom_traits>(*m_arr.geometry_traits()).to_cartesian(pt);
return Buffer_for_vao::Local_point(approx_pt.dx(), approx_pt.dy(), approx_pt.dz());
}
private:
const Arrangement& m_arr;
};
/*! Viewer for visualizing arrangements on surface.
*
* \tparam Arrangement
* \tparam GSOptions
*/
template <typename Arrangement, typename GSOptions>
class Arr_viewer : public Qt::Basic_viewer, Arr_viewport_helpers<Arrangement> {
using Basic_viewer = Qt::Basic_viewer;
using Helpers = Arr_viewport_helpers<Arrangement>;
using Vertex_const_handle = typename Arrangement::Vertex_const_handle;
using Halfedge_const_handle = typename Arrangement::Halfedge_const_handle;
using Face_const_handle = typename Arrangement::Face_const_handle;
using Geom_traits = typename Arrangement::Geometry_traits_2;
using Approx_traits = Arr_approximate_traits<Geom_traits>;
using Approx_point = typename Approx_traits::Approx_point;
using Point = typename Approx_traits::Point;
using Point_generator = Arr_face_point_generator<Arrangement>;
using Faces_point_map = typename Point_generator::Face_points_map;
struct Render_params {
bool operator==(const Render_params& other) const
{ return bbox == other.bbox && approx_error == other.approx_error; }
Bbox_2 bbox;
double approx_error{0};
};
constexpr static bool Is_on_curved_surface = is_or_derived_from_curved_surf_traits_v<Geom_traits>;
private:
static bool contains(const Bbox_2& bbox, const Point& pt)
{ return bbox.xmin() <= pt.x() && pt.x() <= bbox.xmax() && bbox.ymin() <= pt.y() && pt.y() <= bbox.ymax(); }
int viewport_width() const {
std::array<GLint, 4> viewport;
this->camera_->getViewport(viewport.data());
return viewport[2];
}
Render_params compute_render_params() {
Render_params params;
params.bbox = this->screen_to_world(*this->camera_);
params.approx_error = this->approximation_error(params.bbox, viewport_width());
return params;
}
void render_arr(const Render_params& params) {
const Bbox_2& bbox = params.bbox;
auto face_points = Point_generator()(m_arr, params.approx_error);
Arr_render_context<Arrangement> ctx(m_arr, params.approx_error, face_points);
Arr_bounded_renderer<Arrangement> renderer(ctx, bbox);
auto cache = renderer.render();
// add faces
for (const auto& [fh, tf] : cache.faces()) {
if (! m_gso.draw_face(m_arr, fh)) continue;
bool colored_face = m_gso.colored_face(m_arr, fh);
auto color = colored_face ? m_gso.face_color(m_arr, fh) : CGAL::IO::Color();
for (const auto& tri : tf.triangles) {
if (colored_face) m_gs.face_begin(color);
else m_gs.face_begin();
for (const auto i : tri) m_gs.add_point_in_face(this->to_local_point(tf.points[i]));
m_gs.face_end();
}
}
// add edges
for (const auto& [he, polyline] : cache.halfedges()) {
if (he->direction() == ARR_RIGHT_TO_LEFT || !m_gso.draw_edge(m_arr, he) || polyline.size() < 2) continue;
bool colored_edge = m_gso.colored_edge(m_arr, he);
auto color = colored_edge ? m_gso.edge_color(m_arr, he) : CGAL::IO::Color();
// skip first two if starts with a sep point.
int start_idx = Approx_traits::is_null(polyline.front()) ? 2 : 0;
// skip last two if ends with a sep point.
int end_idx = Approx_traits::is_null(polyline.back()) ? polyline.size() - 2 : polyline.size();
for (int i = start_idx; i < end_idx - 1; ++i) {
const auto& src = polyline[i];
const auto& tgt = polyline[i + 1];
if (Approx_traits::is_null(src) || Approx_traits::is_null(tgt)) continue;
if (! contains(bbox, src) || !contains(bbox, tgt)) continue;
if (colored_edge)
m_gs.add_segment(this->to_local_point(src), this->to_local_point(tgt), color);
else
m_gs.add_segment(this->to_local_point(src), this->to_local_point(tgt));
}
}
// add vertices
for (const auto& [vh, pt] : cache.vertices()) {
if (! m_gso.draw_vertex(m_arr, vh) || !contains(bbox, pt)) continue;
if (m_gso.colored_vertex(m_arr, vh))
m_gs.add_point(this->to_local_point(pt), m_gso.vertex_color(m_arr, vh));
else
m_gs.add_point(this->to_local_point(pt));
}
}
/*! \brief Rerender scene within the given bounding box.
*
* \param bbox
*/
void rerender(const Render_params& params) {
if (params == m_last_params) return;
m_last_params = params;
m_gs.clear();
render_arr(params);
Basic_viewer::redraw();
}
public:
Arr_viewer(QWidget* parent, const Arrangement& arr, const GSOptions& gso, const char* title, Bbox_2 initial_bbox) :
Basic_viewer(parent, m_gs, title),
Helpers(arr),
m_gso(gso),
m_arr(arr),
m_coords(*arr.geometry_traits()) {
if ((initial_bbox.x_span() == 0) || (initial_bbox.y_span() == 0) || (Is_on_curved_surface))
m_initial_bbox = this->arr_bbox();
else
m_initial_bbox = initial_bbox;
}
virtual void draw() override {
Render_params params = compute_render_params();
#if defined(CGAL_DRAW_AOS_DEBUG)
if constexpr(! is_or_derived_from_agas_v<Geom_traits>) {
Bbox_2& bbox = params.bbox;
double dx = (bbox.xmax() - bbox.xmin()) * 0.1;
double dy = (bbox.ymax() - bbox.ymin()) * 0.1;
bbox = Bbox_2(bbox.xmin() + dx, bbox.ymin() + dy, bbox.xmax() - dx, bbox.ymax() - dy);
std::cout << "Camera changed, recomputing arrangement bounding box: " << bbox << std::endl;
}
#endif
rerender(params);
if (! m_initialized) {
// The initial render must be done with original camera parameters or the width of edges gets exaggerated.
// So we fit the camera after initial render.
this->fit_camera(m_initial_bbox, *this->camera_);
m_initialized = true;
}
Basic_viewer::draw();
}
virtual ~Arr_viewer() {}
private:
Graphics_scene m_gs;
GSOptions m_gso;
const Arrangement& m_arr;
bool m_initialized{false};
Bbox_2 m_initial_bbox;
const Arr_coordinate_converter<Geom_traits> m_coords;
Render_params m_last_params;
};
} // namespace draw_aos
} // namespace CGAL
#endif

View File

@ -0,0 +1,223 @@
// Copyright (c) 2025
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). 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): Shepard Liu <shepard0liu@gmail.com>
#ifndef CGAL_DRAW_AOS_TYPE_UTILS_H
#define CGAL_DRAW_AOS_TYPE_UTILS_H
#include <CGAL/license/Arrangement_on_surface_2.h>
#include <limits>
#include <vector>
#include <type_traits>
#include <cmath>
#include <CGAL/Arr_geodesic_arc_on_sphere_traits_2.h>
namespace CGAL {
namespace draw_aos {
enum class Boundary_side {
Top = 0,
Left = 1,
Bottom = 2,
Right = 3,
None = -1,
};
template <typename, typename = std::void_t<>>
struct has_approximate_2_object : std::false_type {};
template <typename Gt>
struct has_approximate_2_object<Gt, std::void_t<decltype(std::declval<Gt>().approximate_2_object())>> : std::true_type
{};
// Detect whether Gt has defined a member function approximate_2_object()
template <typename Gt>
inline constexpr bool has_approximate_2_object_v = has_approximate_2_object<Gt>::value;
template <typename, typename, typename = std::void_t<>>
struct has_approximate_point : std::false_type {};
template <typename Gt, typename A>
struct has_approximate_point<Gt,
A,
std::void_t<decltype(std::declval<A>()(std::declval<const typename Gt::Point_2&>()))>> :
std::true_type
{};
// Detect whether A has operator()(const Gt::Point_2&)
template <typename Gt, typename A>
inline constexpr bool has_approximate_point_v = has_approximate_point<Gt, A>::value;
template <typename, typename, typename, typename = std::void_t<>>
struct has_approximate_xcv : std::false_type {};
template <typename Gt, typename A, typename O>
struct has_approximate_xcv
<Gt,
A,
O,
std::void_t<decltype(std::declval<A&>()(std::declval<const typename Gt::X_monotone_curve_2&>(),
std::declval<double>(),
std::declval<O>(),
std::declval<bool>()))>> : std::true_type
{};
// Detect whether A has operator()(const Gt::X_monotone_curve_2&, double, OutputIterator, bool)?
template <typename Gt, typename A>
constexpr bool has_approximate_xcv_v = has_approximate_xcv<Gt, A, void*>::value;
template <typename, typename, typename, typename = std::void_t<>>
struct has_approximate_xcv_with_bounds : std::false_type
{};
template <typename Gt, typename A, typename O>
struct has_approximate_xcv_with_bounds
<Gt,
A,
O,
std::void_t<decltype(std::declval<A&>()(std::declval<const typename Gt::X_monotone_curve_2&>(),
std::declval<double>(),
std::declval<O>(),
std::declval<Bbox_2>(),
std::declval<bool>()))>> : std::true_type
{};
// Detect whether A has operator()(const X_monotone_curve&, double, OutputIterator, Bbox_2, bool)
template <typename Gt, typename A>
inline constexpr bool has_approximate_xcv_with_bounds_v = has_approximate_xcv_with_bounds<Gt, A, void*>::value;
// Detect whether a geometry traits has all the necessary types and functions for approximation
template <typename Gt>
constexpr bool has_approximate_traits_v =
has_approximate_2_object_v<Gt> && has_approximate_point_v<Gt, typename Gt::Approximate_2> &&
(has_approximate_xcv_v<Gt, typename Gt::Approximate_2> ||
has_approximate_xcv_with_bounds_v<Gt, typename Gt::Approximate_2>);
template <typename Gt, typename = std::void_t<>>
struct has_is_in_x_range : std::false_type
{};
template <typename Gt>
struct has_is_in_x_range<Gt,
std::void_t<decltype(std::declval<typename Gt::X_monotone_curve_2>().is_in_x_range
(std::declval<const typename Gt::Point_2&>()))>> : std::true_type
{};
// Detect whether Gt::X_monotone_curve_2 has a member function bool is_in_x_range(const Gt::Point_2&)
template <typename Gt>
inline constexpr bool has_is_in_x_range_v = has_is_in_x_range<Gt>::value;
// Detect whether Gt is or derives from Arr_geodesic_arc_on_sphere_traits_2<*, *, *>
template <typename Gt>
struct is_or_derived_from_agas {
private:
template <typename Kernel_, int AtanX, int AtanY>
static std::true_type test(const Arr_geodesic_arc_on_sphere_traits_2<Kernel_, AtanX, AtanY>*);
static std::false_type test(...);
public:
static constexpr bool value = decltype(test(static_cast<const Gt*>(nullptr)))::value;
};
template <typename Gt>
inline constexpr bool is_or_derived_from_agas_v = is_or_derived_from_agas<Gt>::value;
// Detect whether T is or derives from a geometry traits on curved surfaces
template <typename Gt>
inline constexpr bool is_or_derived_from_curved_surf_traits_v = is_or_derived_from_agas_v<Gt>;
// Static helpers to get template arguments from a geometry traits
template <typename Gt>
struct tmpl_args {};
template <typename Kernel_, int AtanX, int AtanY>
struct tmpl_args<Arr_geodesic_arc_on_sphere_traits_2<Kernel_, AtanX, AtanY>> {
using Kernel = Kernel_;
static constexpr int atan_x = AtanX;
static constexpr int atan_y = AtanY;
};
/*! \brief Approximation data types
*
* \tparam Gt Geometry traits
*/
template <typename Gt>
class Arr_approximate_traits {
using Geom_traits = Gt;
template <typename P, typename I>
struct Triangle_soup_ {
using Index = I;
using Triangle = std::array<Index, 3>;
using Point = P;
std::vector<Point> points;
std::vector<Triangle> triangles;
};
public:
using Approx_point = typename Geom_traits::Approximate_point_2;
using Approx_nt = typename Geom_traits::Approximate_number_type;
using Approx_kernel = typename Geom_traits::Approximate_kernel;
// 2D parameter space point
using Point = typename Approx_kernel::Point_2;
using Polyline = std::vector<Point>;
using Triangle_soup = Triangle_soup_<Point, int>;
// A null point with NaN coordinates. Use ::is_null(pt) to check if a point is null.
inline static const Point Null_point =
Point(std::numeric_limits<Approx_nt>::signaling_NaN(), std::numeric_limits<Approx_nt>::signaling_NaN());
static bool is_null(Point pt) { return std::isnan(pt.x()) || std::isnan(pt.y()); }
};
/*!
* \brief Functor to construct a Point_2 from an Approximate_point_2.
*
* \tparam Gt Geometry traits
*/
template <typename Gt>
class Construct_gt_point_2 {
using Approx_traits = Arr_approximate_traits<Gt>;
using Approx_point = typename Approx_traits::Approx_point;
using Gt_point = typename Gt::Point_2;
public:
Gt_point operator()(const Approx_point& pt) const { return Gt_point(pt.x(), pt.y()); }
};
// Specialization for Arr_geodesic_arc_on_sphere_traits_2
template <typename Kernel, int AtanX, int AtanY>
class Construct_gt_point_2<Arr_geodesic_arc_on_sphere_traits_2<Kernel, AtanX, AtanY>> {
using Geom_traits = Arr_geodesic_arc_on_sphere_traits_2<Kernel, AtanX, AtanY>;
using Approx_traits = Arr_approximate_traits<Arr_geodesic_arc_on_sphere_traits_2<Kernel, AtanX, AtanY>>;
using Approx_point = typename Approx_traits::Approx_point;
using Gt_point = typename Geom_traits::Point_2;
public:
Gt_point operator()(const Approx_point& pt) const {
using Direction_3 = typename Kernel::Direction_3;
return Gt_point(Direction_3(pt.dx(), pt.dy(), pt.dz()),
static_cast<typename Gt_point::Location_type>(pt.location()));
}
};
} // namespace draw_aos
} // namespace CGAL
#endif // CGAL_DRAW_AOS_TYPE_UTILS_H

File diff suppressed because it is too large Load Diff

View File

@ -1491,4 +1491,4 @@ if(CGAL_DISABLE_GMP)
foreach(_test ${LIST_OF_TESTS})
set_property(TEST ${_test} APPEND PROPERTY ENVIRONMENT CGAL_DISABLE_GMP=1)
endforeach()
endif()
endif()

View File

@ -31,7 +31,7 @@ the dot operator, and a typical usage is thus:
\code {.cpp}
Graph g1, g2;
Vertex_point_map_2 vpm_2; // an hypothetical custom property map assigning a Point to the vertices of g2
Vertex_point_map_2 vpm_2; // a hypothetical custom property map assigning a Point to the vertices of g2
// without any named parameter (default values are used)
CGAL::copy_face_graph(g1, g2);

View File

@ -1563,10 +1563,10 @@ does_satisfy_link_condition(typename boost::graph_traits<Graph>::edge_descriptor
*
* After the collapse of edge `e` the following holds:
* - The edge `e` is no longer in `g`.
* - The faces incident to edge `e` are no longer in `g`.
* - The triangle faces incident to edge `e` are no longer in `g`.
* - `v0` is no longer in `g`.
* - If `h` is not a border halfedge, `p_h` is no longer in `g` and is replaced by `o_n_h`.
* - If the opposite of `h` is not a border halfedge, `p_o_h` is no longer in `g` and is replaced by `o_n_o_h`.
* - If `h` is part of a triangle face, `p_h` is no longer in `g` and is replaced by `o_n_h`.
* - If the opposite of `h` is part of a triangle face, `p_o_h` is no longer in `g` and is replaced by `o_n_o_h`.
* - The halfedges kept in `g` that had `v0` as target and source now have `v1` as target and source, respectively.
* - No other incidence information is changed in `g`.
*
@ -1595,9 +1595,8 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
bool lBottomFaceExists = ! is_border(qp,g);
bool lTopLeftFaceExists = lTopFaceExists && ! is_border(pt,g);
bool lBottomRightFaceExists = lBottomFaceExists && ! is_border(qb,g);
CGAL_precondition( !lTopFaceExists || (lTopFaceExists && ( degree(target(pt, g), g) > 2 ) ) ) ;
CGAL_precondition( !lBottomFaceExists || (lBottomFaceExists && ( degree(target(qb, g), g) > 2 ) ) ) ;
bool lBottomIsTriangle = lBottomFaceExists && is_triangle(qp,g);
bool lTopIsTriangle = lTopFaceExists && is_triangle(pq,g);
vertex_descriptor q = target(pq, g);
vertex_descriptor p = source(pq, g);
@ -1605,7 +1604,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
bool lP_Erased = false;
if ( lTopFaceExists )
if ( lTopIsTriangle)
{
CGAL_precondition( ! is_border(opposite(pt, g),g) ) ; // p-q-t is a face of the mesh
if ( lTopLeftFaceExists )
@ -1632,7 +1631,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
}
}
if ( lBottomFaceExists )
if ( lBottomIsTriangle)
{
CGAL_precondition( ! is_border(opposite(qb, g),g) ) ; // p-q-b is a face of the mesh
if ( lBottomRightFaceExists )
@ -1679,7 +1678,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
* collapses an edge in a graph having non-collapsable edges.
*
* Let `h` be the halfedge of `e`, and let `v0` and `v1` be the source and target vertices of `h`.
* Collapses the edge `e` replacing it with `v1`, as described in the paragraph above
* Collapses the edge `e` replacing it with `v1`, as described in the other overload
* and guarantees that an edge `e2`, for which `get(edge_is_constrained_map, e2)==true`,
* is not removed after the collapse.
*
@ -1689,14 +1688,14 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
*
* \returns vertex `v1`.
* \pre This function requires `g` to be an oriented 2-manifold with or without boundaries.
* Furthermore, the edge `v0v1` must satisfy the link condition, which guarantees that the surface mesh is also 2-manifold after the edge collapse.
* \pre `get(edge_is_constrained_map, v0v1) == false`.
* Furthermore, the edge `e` must satisfy the link condition, which guarantees that the surface mesh is also 2-manifold after the edge collapse.
* \pre `get(edge_is_constrained_map, e) == false`.
* \pre `v0` and `v1` are not both incident to a constrained edge.
*/
template<typename Graph, typename EdgeIsConstrainedMap>
typename boost::graph_traits<Graph>::vertex_descriptor
collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
Graph& g,
EdgeIsConstrainedMap Edge_is_constrained_map)
{
@ -1704,11 +1703,11 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
typedef typename Traits::vertex_descriptor vertex_descriptor;
typedef typename Traits::halfedge_descriptor halfedge_descriptor;
CGAL_precondition(is_valid_edge_descriptor(v0v1, g));
CGAL_precondition(does_satisfy_link_condition(v0v1,g));
CGAL_precondition(!get(Edge_is_constrained_map, v0v1));
CGAL_precondition(is_valid_edge_descriptor(e, g));
CGAL_precondition(does_satisfy_link_condition(e,g));
CGAL_precondition(!get(Edge_is_constrained_map, e));
halfedge_descriptor pq = halfedge(v0v1,g);
halfedge_descriptor pq = halfedge(e,g);
halfedge_descriptor qp = opposite(pq,g);
halfedge_descriptor pt = opposite(prev(pq,g),g);
@ -1718,6 +1717,8 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
bool lTopFaceExists = ! is_border(pq,g) ;
bool lBottomFaceExists = ! is_border(qp,g) ;
bool lTopIsTriangle = lTopFaceExists && is_triangle(pq,g);
bool lBottomIsTriangle = lBottomFaceExists && is_triangle(qp,g);
vertex_descriptor q = target(pq,g);
vertex_descriptor p = source(pq,g);
@ -1728,7 +1729,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
// If the top facet exists, we need to choose one out of the two edges which one disappears:
// p-t if it is not constrained and t-q otherwise
if ( lTopFaceExists )
if ( lTopIsTriangle )
{
if ( !get(Edge_is_constrained_map,edge(pt,g)) )
{
@ -1742,7 +1743,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
// If the bottom facet exists, we need to choose one out of the two edges which one disappears:
// q-b if it is not constrained and b-p otherwise
if ( lBottomFaceExists )
if ( lBottomIsTriangle )
{
if ( !get(Edge_is_constrained_map,edge(qb,g)) )
{
@ -1753,7 +1754,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
}
}
if (lTopFaceExists && lBottomFaceExists)
if (lTopIsTriangle && lBottomIsTriangle)
{
if ( face(edges_to_erase[0],g) == face(edges_to_erase[1],g)
&& (! is_border(edges_to_erase[0],g)) )
@ -1800,7 +1801,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
}
else
{
if (lTopFaceExists)
if (lTopIsTriangle)
{
if (!(is_border(edges_to_erase[0],g))){
join_face(edges_to_erase[0],g);
@ -1815,21 +1816,32 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
remove_face(opposite(edges_to_erase[0],g),g);
return q;
}
if (! (is_border(edges_to_erase[0],g))){
// q will be removed, swap it with p
internal::swap_vertices(p, q, g);
join_face(edges_to_erase[0],g);
join_vertex(qp,g);
return q;
}
if(!is_border(opposite(next(qp,g),g),g))
else
{
// q will be removed, swap it with p
internal::swap_vertices(p, q, g);
if (lBottomIsTriangle)
{
if (! (is_border(edges_to_erase[0],g))){
// q will be removed, swap it with p
internal::swap_vertices(p, q, g);
join_face(edges_to_erase[0],g);
CGAL_assertion(source(qp,g)==p);
join_vertex(qp,g);
return q;
}
if(!is_border(opposite(next(qp,g),g),g))
{
// q will be removed, swap it with p
internal::swap_vertices(p, q, g);
}
remove_face(opposite(edges_to_erase[0],g),g);
return q;
}
else
{
join_vertex(pq,g);
return q;
}
}
remove_face(opposite(edges_to_erase[0],g),g);
return q;
}
}
@ -1896,7 +1908,7 @@ bool satisfies_link_condition(typename boost::graph_traits<Graph>::edge_descript
* \param h halfedge descriptor
* \param g the graph
*
* \returns an halfedge linking the two vertices adjacent to the vertex being removed.
* \returns a halfedge linking the two vertices adjacent to the vertex being removed.
*
* \pre `degree(target(h, g), g) == 2`.
*

View File

@ -18,6 +18,7 @@
#include <CGAL/boost/graph/IO/Generic_facegraph_builder.h>
#include <CGAL/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <fstream>
#include <string>
@ -44,7 +45,7 @@ class PLY_builder
typedef typename Base::Face_container Face_container;
public:
PLY_builder(std::istream& is) : Base(is) { }
PLY_builder(std::istream& is, std::string& comments) : Base(is), comments(comments) { }
template <typename NamedParameters>
bool read(std::istream& is,
@ -52,19 +53,22 @@ public:
Face_container& faces,
const NamedParameters& np)
{
return read_PLY(is, points, faces, np);
return read_PLY(is, points, faces, comments, np);
}
std::string& comments;
};
template <typename Graph, typename CGAL_NP_TEMPLATE_PARAMETERS>
bool read_PLY_BGL(std::istream& is,
Graph& g,
std::string& comments,
const CGAL_NP_CLASS& np = parameters::default_values())
{
typedef typename CGAL::GetVertexPointMap<Graph, CGAL_NP_CLASS>::type VPM;
typedef typename boost::property_traits<VPM>::value_type Point;
internal::PLY_builder<Graph, Point> builder(is);
internal::PLY_builder<Graph, Point> builder(is, comments);
return builder(g, np);
}
@ -84,6 +88,7 @@ bool read_PLY_BGL(std::istream& is,
\param is the input stream
\param g the graph to be built from the input data
\param comments a string included line by line in the header of the PLY stream (each line will be precedeed by "comment ")
\param np optional \ref bgl_namedparameters "Named Parameters" described below
\cgalNamedParamsBegin
@ -132,15 +137,31 @@ template <typename Graph,
typename CGAL_NP_TEMPLATE_PARAMETERS>
bool read_PLY(std::istream& is,
Graph& g,
std::string& comments,
const CGAL_NP_CLASS& np = parameters::default_values()
#ifndef DOXYGEN_RUNNING
, std::enable_if_t<!internal::is_Point_set_or_Range_or_Iterator<Graph>::value>* = nullptr
#endif
)
{
return internal::read_PLY_BGL(is, g, np);
return internal::read_PLY_BGL(is, g, comments, np);
}
template <typename Graph,
typename CGAL_NP_TEMPLATE_PARAMETERS>
bool read_PLY(std::istream& is,
Graph& g,
const CGAL_NP_CLASS& np = parameters::default_values()
#ifndef DOXYGEN_RUNNING
, std::enable_if_t<!internal::is_Point_set_or_Range_or_Iterator<Graph>::value>* = nullptr
#endif
)
{
std::string unused_comments;
return internal::read_PLY_BGL(is, g, unused_comments, np);
}
/*!
\ingroup PkgBGLIoFuncsPLY
@ -153,6 +174,7 @@ bool read_PLY(std::istream& is,
\param fname the name of the input file
\param g the graph to be built from the input data
\param comments a string included line by line in the header of the PLY stream (each line will be precedeed by "comment" )
\param np optional \ref bgl_namedparameters "Named Parameters" described below
\cgalNamedParamsBegin
@ -207,6 +229,7 @@ template <typename Graph,
typename CGAL_NP_TEMPLATE_PARAMETERS>
bool read_PLY(const std::string& fname,
Graph& g,
std::string& comments,
const CGAL_NP_CLASS& np = parameters::default_values()
#ifndef DOXYGEN_RUNNING
, std::enable_if_t<!internal::is_Point_set_or_Range_or_Iterator<Graph>::value>* = nullptr
@ -218,16 +241,29 @@ bool read_PLY(const std::string& fname,
{
std::ifstream is(fname, std::ios::binary);
CGAL::IO::set_mode(is, CGAL::IO::BINARY);
return internal::read_PLY_BGL(is, g, np);
return read_PLY(is, g, comments, np);
}
else
{
std::ifstream is(fname);
CGAL::IO::set_mode(is, CGAL::IO::ASCII);
return internal::read_PLY_BGL(is, g, np);
return read_PLY(is, g, comments, np);
}
}
template <typename Graph,
typename CGAL_NP_TEMPLATE_PARAMETERS>
bool read_PLY(const std::string& fname,
Graph& g,
const CGAL_NP_CLASS& np = parameters::default_values()
#ifndef DOXYGEN_RUNNING
, std::enable_if_t<!internal::is_Point_set_or_Range_or_Iterator<Graph>::value>* = nullptr
#endif
)
{
std::string unused_comment;
return read_PLY(fname, g, unused_comment, np);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Write
@ -259,6 +295,15 @@ bool read_PLY(const std::string& fname,
must be available in `Graph`.}
\cgalParamNEnd
\cgalParamNBegin{vertex_normal_map}
\cgalParamDescription{a property map associating normals to the vertices of `g`}
\cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<Graph>::%vertex_descriptor`
as key type and `%Vector_3` as value type}
\cgalParamDefault{`boost::get(CGAL::vertex_point, g)`}
\cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
must be available in `Graph`.}
\cgalParamNEnd
\cgalParamNBegin{vertex_index_map}
\cgalParamDescription{a property map associating to each vertex of `graph` a unique index}
\cgalParamType{a class model of `WritablePropertyMap` with `boost::graph_traits<Graph>::%vertex_descriptor`
@ -326,6 +371,8 @@ bool write_PLY(std::ostream& os,
bool has_vcolor = !is_default_parameter<CGAL_NP_CLASS, internal_np::vertex_color_map_t>::value;
bool has_fcolor = !is_default_parameter<CGAL_NP_CLASS, internal_np::face_color_map_t>::value;
constexpr bool has_vnormal = !is_default_parameter<CGAL_NP_CLASS, internal_np::vertex_normal_map_t>::value;
VIMap vim = CGAL::get_initialized_vertex_index_map(g, np);
Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, g));
@ -351,8 +398,20 @@ bool write_PLY(std::ostream& os,
}
}
os << "element vertex " << vertices(g).size() << std::endl;
internal::output_property_header(os, make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
if constexpr (std::is_same<typename Kernel_traits<Point_3>::Kernel::FT, float>::value)
{
internal::output_property_header(os, make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
}
else
{
typedef typename Kernel_traits<Point_3>::Kernel K;
typedef decltype(std::declval<CGAL::Cartesian_converter<K, Epick> >().operator()(std::declval<Point_3>())) Target_point;
auto fvpm = CGAL::make_cartesian_converter_property_map<Target_point>(vpm);
internal::output_property_header(os, make_ply_point_writer (fvpm));
}
//if vcm is not default add v:color property
if(has_vcolor)
{
@ -362,10 +421,30 @@ bool write_PLY(std::ostream& os,
<< "property uchar alpha" << std::endl;
}
if constexpr (has_vnormal)
{
auto vnm = get_parameter(np, internal_np::vertex_normal_map);
typedef decltype(vnm) Normal_map;
typedef typename Normal_map::value_type Vector_3;
typedef typename Kernel_traits<Vector_3>::Kernel K;
typedef typename K::FT FT;
if constexpr (std::is_same<FT, float>::value)
{
internal::output_property_header(os, make_ply_normal_writer (CGAL::Identity_property_map<Vector_3>()));
}
else
{
typedef decltype(std::declval<CGAL::Cartesian_converter<K, Epick> >().operator()(std::declval<Vector_3>())) Target_vector;
auto fvnm = CGAL::make_cartesian_converter_property_map<Target_vector>(vnm);
internal::output_property_header(os, make_ply_normal_writer (fvnm));
}
}
os << "element face " << faces(g).size() << std::endl;
internal::output_property_header(
os, std::make_pair(CGAL::Identity_property_map<std::vector<std::size_t> >(),
PLY_property<std::vector<int> >("vertex_indices")));
//if fcm is not default add f:color property
if(has_fcolor)
{
@ -378,8 +457,42 @@ bool write_PLY(std::ostream& os,
for(vertex_descriptor vd : vertices(g))
{
const Point_3& p = get(vpm, vd);
internal::output_properties(os, &p, make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
if constexpr (std::is_same<typename Kernel_traits<Point_3>::Kernel::FT, float>::value)
{
decltype(auto) p = get(vpm, vd);
internal::output_properties(os, &p, make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
}
else
{
typedef typename Kernel_traits<Point_3>::Kernel K;
typedef CGAL::cpp20::remove_cvref_t<decltype(std::declval<CGAL::Cartesian_converter<K, Epick> >().operator()(std::declval<Point_3>()))> Target_point;
CGAL::Cartesian_converter_property_map<Target_point, Vpm> fvpm = CGAL::make_cartesian_converter_property_map<Target_point>(vpm);
decltype(auto) fp = get(fvpm, vd);
internal::output_properties(os, &fp, make_ply_point_writer (CGAL::Identity_property_map<Target_point>()));
}
std::cout << "using generic writer" << std::endl;
if constexpr (!parameters::is_default_parameter<CGAL_NP_CLASS, internal_np::vertex_normal_map_t>::value)
{
auto vnm = get_parameter(np, internal_np::vertex_normal_map);
typedef decltype(vnm) Normal_map;
typedef typename Normal_map::value_type Vector_3;
if constexpr (std::is_same<typename Kernel_traits<Vector_3>::Kernel::FT, float>::value)
{
decltype(auto) vec = get(vnm,vd);
internal::output_properties(os, &vec, make_ply_normal_writer (CGAL::Identity_property_map<Vector_3>()));
}
else
{
typedef typename Kernel_traits<Vector_3>::Kernel K;
typedef CGAL::cpp20::remove_cvref_t<decltype(std::declval<CGAL::Cartesian_converter<K, Epick> >().operator()(std::declval<Vector_3>()))> Target_vector;
auto fvnm = CGAL::make_cartesian_converter_property_map<Target_vector>(vnm);
decltype(auto) fvec = get(fvnm, vd);
internal::output_properties(os, &fvec, make_ply_normal_writer (CGAL::Identity_property_map<Target_vector>()));
}
}
if(has_vcolor)
{
const CGAL::IO::Color& c = get(vcm, vd);
@ -455,6 +568,15 @@ bool write_PLY(std::ostream& os, const Graph& g, const CGAL_NP_CLASS& np = param
must be available in `Graph`.}
\cgalParamNEnd
\cgalParamNBegin{vertex_normal_map}
\cgalParamDescription{a property map associating normals to the vertices of `g`}
\cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<Graph>::%vertex_descriptor`
as key type and `%Vector_3` as value type}
\cgalParamDefault{`boost::get(CGAL::vertex_point, g)`}
\cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
must be available in `Graph`.}
\cgalParamNEnd
\cgalParamNBegin{vertex_index_map}
\cgalParamDescription{a property map associating to each vertex of `graph` a unique index between `0` and `num_vertices(graph) - 1`}
\cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<Graph>::%vertex_descriptor`

View File

@ -537,7 +537,7 @@ bool is_valid_polygon_mesh(const Mesh& g, bool verb = false)
return false;
// test for 2-manifoldness
// Distinct facets on each side of an halfedge.
// Distinct facets on each side of a halfedge.
for(halfedge_descriptor i : halfedges(g))
{
valid = (face(i, g) != face(opposite(i, g), g));

View File

@ -1,13 +1,19 @@
Algebraic_foundations
Arithmetic_kernel
BGL
Cartesian_kernel
Circulator
Distance_2
Distance_3
Filtered_kernel
Homogeneous_kernel
Hash_map
Installation
Intersections_2
Intersections_3
Interval_support
Kernel_23
Kernel_d
Modular_arithmetic
Number_types
Profiling_tools
@ -15,3 +21,4 @@ Property_map
Random_numbers
STL_Extension
Stream_support
CGAL_Core

View File

@ -0,0 +1,42 @@
OFF
25 13 0
0.39160239696502686 1.3864846229553223 4.8046874923102223e-08
0.053782559931278229 1.3864846229553223 4.8046874923102223e-08
-0.94644606113433838 1.6651756763458252 4.8046874923102223e-08
-1.3082554340362549 1.7385153770446777 4.8046874923102223e-08
-1.3033660650253296 1.1860226392745972 4.8046874923102223e-08
1.61628258228302 -0.17601536214351654 4.8046874923102223e-08
0.55834579467773438 -0.19216139614582062 4.8046874923102223e-08
0.053782559931278229 -0.17601536214351654 4.8046874923102223e-08
-0.24240998923778534 -0.22679123282432556 4.8046874923102223e-08
-0.58168435096740723 -0.25845989584922791 4.8046874923102223e-08
-1.2915089130401611 -0.17601536214351654 4.8046874923102223e-08
-1.50871741771698 -0.17601536214351654 4.8046874923102223e-08
1.61628258228302 -1.7385153770446777 4.8046874923102223e-08
1.1978726387023926 -1.7385153770446777 4.8046874923102223e-08
0.71942150592803955 -1.7385153770446777 4.8046874923102223e-08
0.053782559931278229 -1.7385153770446777 4.8046874923102223e-08
-0.73973840475082397 -1.7385153770446777 4.8046874923102223e-08
1.61628258228302 0.36264327168464661 4.8046874923102223e-08
-0.26156377792358398 0.45463424921035767 4.8046874923102223e-08
-0.028661971911787987 -0.78840988874435425 4.8046874923102223e-08
0.053782559931278229 -1.2213115692138672 4.8046874923102223e-08
-1.5918357372283936 1.5331641435623169 4.8046874923102223e-08
-1.6162823438644409 0.87338578701019287 4.8046874923102223e-08
-1.50871741771698 -0.0072435899637639523 4.8046874923102223e-08
-1.50871741771698 -1.3000825643539429 4.8046874923102223e-08
7 18 2 3 4 22 9 8
3 2 18 1
7 18 7 6 5 17 0 1
7 12 5 6 7 8 19 13
6 11 24 16 15 20 10
3 9 19 8
4 10 20 19 9
3 7 18 8
3 14 20 15
4 13 19 20 14
3 3 21 4
4 9 22 23 10
3 10 23 11

View File

@ -2,7 +2,6 @@
#include <CGAL/boost/graph/Euler_operations.h>
#include <CGAL/boost/graph/IO/OFF.h>
#include <boost/range/distance.hpp>
#include <string>
@ -213,12 +212,30 @@ collapse_edge_test()
assert(found == 2);
CGAL::clear(test_mesh);
}
// Case 6 non pure triangle mesh
{
Mesh ref;
if(!CGAL::IO::read_OFF("data/polygon_mesh_to_collapse.off", ref))
{
std::cout << "Error reading file: data/polygon_mesh_to_collapse.off" << std::endl;
exit(1);
}
std::size_t nbe=halfedges(ref).size();
for (std::size_t i=0; i< nbe; ++i)
{
Mesh m = ref;
auto h = *std::next(halfedges(m).begin(), i);
if (CGAL::Euler::does_satisfy_link_condition(edge(h,m),m))
CGAL::Euler::collapse_edge(edge(h,m), m);
assert(CGAL::is_valid_polygon_mesh(m));
}
}
}
int main()
{
collapse_edge_test<Polyhedron>();
collapse_edge_test<SM>();

View File

@ -1,9 +1,7 @@
# Created by the script cgal_create_cmake_script.
# This is the CMake script for compiling a CGAL application.
project(Barycentric_coordinates_2_Benchmarks)
cmake_minimum_required(VERSION 3.12...3.31)
project(Barycentric_coordinates_2_Benchmarks)
find_package(CGAL REQUIRED COMPONENTS Core)
@ -14,8 +12,9 @@ create_single_source_cgal_program("benchmark_polygon_16_vertices.cpp")
create_single_source_cgal_program("benchmark_polygon_100_vertices.cpp")
create_single_source_cgal_program("benchmark_mv_34_vertices.cpp")
find_package(Eigen3 3.1.0 QUIET) # (3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program("benchmark_hm_4_vertices.cpp")
target_link_libraries(benchmark_hm_4_vertices PRIVATE CGAL::Eigen3_support)

View File

@ -17,8 +17,9 @@ create_single_source_cgal_program("terrain_height_modeling.cpp")
# this code is deprecated:
create_single_source_cgal_program("deprecated_coordinates.cpp")
find_package(Eigen3 3.1.0 QUIET) # (3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program("affine_coordinates.cpp")
target_link_libraries(affine_coordinates PRIVATE CGAL::Eigen3_support)

View File

@ -42,8 +42,9 @@ create_single_source_cgal_program("test_wp_deprecated_api.cpp")
create_single_source_cgal_program("test_mv_deprecated_api.cpp")
create_single_source_cgal_program("test_dh_deprecated_api.cpp")
find_package(Eigen3 3.1.0 QUIET) # (3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program("test_hm_unit_square.cpp")
target_link_libraries(test_hm_unit_square PRIVATE CGAL::Eigen3_support)

View File

@ -11,7 +11,7 @@ project(Basic_viewer_Examples)
#CGAL_Qt6 is needed for the drawing.
find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6)
find_package(Eigen3 3.1.0)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
create_single_source_cgal_program("draw_lcc.cpp")

View File

@ -1193,7 +1193,7 @@ protected:
// accessor for low-level arrangement functionalities
CGAL::Arr_accessor<Aos_2> accessor(*arr);
// the face field of outer and inner ccb are used in the loop to access the old face an halfedge
// the face field of outer and inner ccb are used in the loop to access the old face a halfedge
// used to contribute to. These two vectors are used to delay the association to the new face to
// avoid overwriting a field that is still needed
typedef std::pair<typename Aos_2::Dcel::Outer_ccb*, typename Aos_2::Dcel::Face*> Outer_ccb_and_face;

View File

@ -4,7 +4,7 @@ project(Approximate_min_ellipsoid_d_Examples)
find_package(CGAL REQUIRED)
# Use Eigen
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
# create a target per cppfile

View File

@ -7,7 +7,7 @@ project(Bounding_volumes_Tests)
find_package(CGAL REQUIRED COMPONENTS Core)
# Use Eigen
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
# create a target per cppfile

View File

@ -20,8 +20,9 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
find_package(CGAL REQUIRED COMPONENTS Core)
find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: This project requires the Eigen library, and will not be compiled.")
return()

View File

@ -116,13 +116,13 @@ Draws a theta-graph with k cones
<li><b>Yao-k-graph:</b>
Draws a Yao-graph with k cones
<li><b>Half-theta-k-graph with even cones:</b>
Draws an half-theta-graph with the even of k cones.
Draws a half-theta-graph with the even of k cones.
<li><b>Half-Yao-k-graph with even cones:</b>
Draws an half-Yao-graph with the even of k cones.
Draws a half-Yao-graph with the even of k cones.
<li><b>Half-theta-k-graph with odd cones:</b>
Draws an half-theta-graph with the odd of k cones.
Draws a half-theta-graph with the odd of k cones.
<li><b>Half-Yao-k-graph with odd cones:</b>
Draws an half-Yao-graph with the odd of k cones.
Draws a half-Yao-graph with the odd of k cones.
<li><b>k cones:</b> For each selected point.
Draws the k cones around the point.
</ul>

View File

@ -23,8 +23,9 @@ if(NOT TARGET CGAL::Boost_iostreams_support)
set(Classification_dependencies_met FALSE)
endif()
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: This project requires the Eigen library, and will not be compiled.")
set(Classification_dependencies_met FALSE)

View File

@ -23,8 +23,9 @@ if(NOT TARGET CGAL::Boost_iostreams_support)
set(Classification_dependencies_met FALSE)
endif()
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: This project requires the Eigen library, and will not be compiled.")
set(Classification_dependencies_met FALSE)

View File

@ -472,7 +472,7 @@ The output is:
Number of 2-free darts: 4
\endverbatim
We can verify that there are 6 2-cells after the insertion since the squared face was inserted as a hole in one face of the hexahedron. We can also see that there are 4 2-free darts, which are the darts of the squared face. Since they bound an hole, there is no face filling the hole and thus 4 darts are 2-free.
We can verify that there are 6 2-cells after the insertion since the squared face was inserted as a hole in one face of the hexahedron. We can also see that there are 4 2-free darts, which are the darts of the squared face. Since they bound a hole, there is no face filling the hole and thus 4 darts are 2-free.
See also a similar example for Linear cell complex \ref Linear_cell_complexInsert "Insert an Edge Between Two Different Faces".

View File

@ -572,7 +572,7 @@ namespace CGAL {
}
}
/** Import the given hds which should be a model of an halfedge graph. */
/** Import the given hds which should be a model of a halfedge graph. */
template<class HEG>
void import_from_halfedge_graph(const HEG& heg,
std::unordered_map
@ -3710,8 +3710,8 @@ namespace CGAL {
void set_automatic_attributes_management_without_correction(bool newval)
{ this->automatic_attributes_management = newval; }
/** Create an half-edge.
* @return a dart of the new half-edge.
/** Create a halfedge.
* @return a dart of the new halfedge.
*/
Dart_descriptor make_half_edge()
{ return create_dart(); }

View File

@ -45,7 +45,7 @@ In any way, a package has to work with both representations.
Since \cgal uses homogeneous representation for affine geometry and not
for projective geometry, the homogenizing coordinate is non zero.
The cartesian
representation corresponding to an homogeneous point
representation corresponding to a homogeneous point
\f$ (x_0,x_1,...,x_d,w)\f$ is
\f$ (x_0/w,x_1/w,...,x_d/w)\f$. Hence, homogeneous representation is not unique;
\f$ (\alpha x,\alpha y,\alpha z,\alpha w)\f$ is an alternative

View File

@ -117,14 +117,14 @@ Qt is a cross-platform application and UI framework.
The component `CGAL_Qt6` is essential to run the \cgal demos and basic viewers.
It requires \qt6 installed on your system.
In case \qt is not yet installed on your system, you can download
it from <A HREF="https://www.qt-project.org/">`https://www.qt-project.org/`</A>.
it from <A HREF="https://contribute.qt-project.org/">`https://contribute.qt-project.org/`</A>.
The exhaustive list of \qt6 components used in demos is:
`Core`, `Gui`, `Help`, `OpenGL`, `OpenGLWidgets`, `Qml`, `Svg`, `Widgets`,
`WebSockets`, `Network`, and `qcollectiongenerator` (with `sqlite` driver plugin).
\subsection thirdpartyEigen Eigen
<b>Version 3.3.7 or later</b>
<b>Version 3.3.7 or later (including Eigen3 5.0.0)</b>
\eigen is a `C++` template library for linear algebra. \eigen supports all
matrix sizes, various matrix decomposition methods and sparse linear solvers.
@ -138,7 +138,7 @@ Overview</a> page. In order to use Eigen in \cgal programs, the
executables should be linked with the CMake imported target
`CGAL::Eigen3_support` provided in `CGAL_Eigen3_support.cmake`.
The \eigen web site is <A HREF="https://eigen.tuxfamily.org/index.php?title=Main_Page">`https://eigen.tuxfamily.org`</A>.
The \eigen web site is <A HREF="https://libeigen.gitlab.io/">`https://libeigen.gitlab.io/`</A>.
\subsection thirdpartyOpenGR OpenGR
@ -167,17 +167,6 @@ Alternatively, version 1.3.1 of \libpointmatcher is supported with version 3.3.7
`https://github.com/ethz-asl/libpointmatcher/blob/master/doc/Compilation.md`:`NABO_INCLUDE_DIR` becomes `libnabo_INCLUDE_DIRS`
and `NABO_LIBRARY` becomes `libnabo_LIBRARIES` in the "Build libpointmatcher" section.
\subsection thirdpartyLeda LEDA
<b>Version 6.2 or later</b>
\leda is a library of efficient data structures and
algorithms. Like \core, \leda offers a real number data type.
In \cgal this library is optional, and its number types can
be used as an alternative to \gmp, \mpfr, and \core.
Free and commercial editions of \leda are available from <A HREF="https://www.algorithmic-solutions.com">`https://www.algorithmic-solutions.com`</A>.
\subsection thirdpartyMPFI Multiple Precision Floating-point Interval (MPFI)
<b>Version 1.4 or later</b>
@ -265,7 +254,7 @@ vcpkg install suitesparse
\subsection thirdpartyMETIS METIS
<b>Version 5.1 or later</b>
\metis is a library developed by the <A HREF="http://glaros.dtc.umn.edu/gkhome/">Karypis Lab</A>
\metis is a library developed by the <A HREF="https://github.com/KarypisLab/">Karypis Lab</A>
and designed to partition graphs and produce fill-reducing matrix orderings.
\cgal offers wrappers around some of the methods of the \metis library
@ -274,7 +263,7 @@ to allow the partitioning of graphs that are models of the concepts of the
and, by extension, of surface meshes (see Section \ref BGLPartitioning of the package \ref PkgBGL).
More information is available on the METIS library
at <A HREF="http://glaros.dtc.umn.edu/gkhome/metis/metis/overview">`http://glaros.dtc.umn.edu/gkhome/metis/metis/overview`</A>.
at <A HREF="https://github.com/KarypisLab/METIS">`https://github.com/KarypisLab/METIS`</A>.
\subsection thirdpartyzlib zlib

View File

@ -83,12 +83,12 @@ a newline character and each coordinate separated by a white
space. Other formats available are 'OFF', 'PLY' and 'LAS'. \cgal
provides functions to read such formats:
- `read_XYZ()`
- `read_OFF()`
- `read_PLY()`
- `read_PLY_with_properties()` to read additional PLY properties
- `read_LAS()`
- `read_LAS_with_properties()` to read additional LAS properties
- `CGAL::IO::read_XYZ()`
- `CGAL::IO::read_OFF()`
- `CGAL::IO::read_PLY()`
- `CGAL::IO::read_PLY_with_properties()` to read additional PLY properties
- `CGAL::IO::read_LAS()`
- `CGAL::IO::read_LAS_with_properties()` to read additional LAS properties
\cgal also provides a dedicated container `CGAL::Point_set_3` to
handle point sets with additional properties such as normal

View File

@ -152118,3 +152118,15 @@ keywords = {polygonal surface mesh, Surface reconstruction, kinetic framework, s
pages={11},
year={2015}
}
@article{liu2025linequadrics,
journal = {Computer Graphics Forum},
title = {{Controlling Quadric Error Simplification with Line Quadrics}},
author = {Liu, Hsueh-Ti Derek and Rahimzadeh, Mehdi and Zordan, Victor},
year = {2025},
publisher = {The Eurographics Association and John Wiley & Sons Ltd.},
ISSN = {1467-8659},
DOI = {10.1111/cgf.70184},
url = {https://doi.org/10.1111/cgf.70184},
year={2025}
}

View File

@ -7,7 +7,7 @@ project( Frechet_distance_Examples )
find_package(CGAL REQUIRED QUIET OPTIONAL_COMPONENTS Core )
find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
create_single_source_cgal_program( "Frechet_distance_2.cpp" )

View File

@ -514,6 +514,7 @@ inline void FrechetLight<C>::continueQSimpleSearch(QSimpleInterval& qsimple,
// TODO: uncritical for correctness or speed but unelegant coding style: stripping down information added by getInterval
CInterval temp_interval = FrechetLight::getInterval<IndexType>(
fixed_curve, fixed, curve, cur);
Interval interval = Interval(temp_interval.begin.getPoint() == cur
? temp_interval.begin.getFraction()
: 1,

View File

@ -113,7 +113,10 @@ struct Lambda<Curve<FilteredTraits,true>>
// fill_lambda returns a pair and we are only interested in a bound
bool update_exact() const
{
if (is_exact) {
if (is_exact){
if (!exact.has_value()){
exact = (is_one) ? std::make_optional(Exact(1)) : std::make_optional(Exact(0));
}
return true;
}
@ -198,7 +201,9 @@ struct Lambda<Curve<FilteredTraits,true>>
{
if ((is_zero && other.is_zero) || (is_one && other.is_one))
return false;
if ((is_zero && (!other.is_zero)) || (!is_one && other.is_one))
// AF this may be wrong if approx is [0.9,1.1] and other.is_one:
// if ((is_zero && (!other.is_zero)) || (!is_one && other.is_one))
if(is_zero && other.is_one)
return true;
CGAL::Uncertain<bool> res = approx < other.approx;
if (CGAL::is_certain(res)) {
@ -206,7 +211,8 @@ struct Lambda<Curve<FilteredTraits,true>>
}
update_exact();
other.update_exact();
return exact < other.exact;
bool eres = exact.value() < other.exact.value();
return eres;
}
};

View File

@ -6,8 +6,12 @@ project( Frechet_distance_Tests )
find_package(CGAL REQUIRED)
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
create_single_source_cgal_program( "Frechet-IssueOct25.cpp" )
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program( "Frechet_distance_test.cpp" )
target_link_libraries(Frechet_distance_test PRIVATE CGAL::Eigen3_support)

View File

@ -0,0 +1,40 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Frechet_distance.h>
#include <fstream>
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = Kernel::Point_2;
using Segment = Kernel::Segment_2;
bool load_polyline(const std::string &filename, std::vector<Point> &polyline) {
polyline.clear();
std::ifstream ifs(filename);
if (!ifs)
return false;
while (ifs.good()) {
Point p;
ifs >> p;
if (ifs.good())
polyline.push_back(p);
}
return true;
}
int main() {
std::vector<Point> poly1, poly2;
if (!load_polyline("poly1.txt", poly1) || !load_polyline("poly2.txt", poly2)) {
std::cout << "input files could not be loaded" << std::endl;
return -1;
}
std::pair<double, double> res = CGAL::bounded_error_Frechet_distance(poly1, poly2, 0.000001);
std::cout << "Frechet distance: [" << res.first << ", " << res.second << "]" << std::endl;
return 0;
}

View File

@ -6,8 +6,7 @@ project( classical_Frechet_distance )
find_package(CGAL REQUIRED)
find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
create_single_source_cgal_program("Compute_classical_Frechet_distance_3.cpp")

View File

@ -0,0 +1,185 @@
-126 70
-138 58
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-39 -18
-42.6 -14
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70

View File

@ -0,0 +1,625 @@
-126 70
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
70 -138
61.4 -118.4
61.4 -118
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70
-126 70

View File

@ -494,7 +494,7 @@ The output is:
Number of 2-free darts: 8
\endverbatim
We can verify that there are 6 2-cells after the insertion since the squared face was inserted as a hole in one face of the hexahedron. We can also see that there are 8 2-free darts, which are the darts of the squared face. Since they bound an hole, there is no face filling the hole and thus 8 darts are 2-free.
We can verify that there are 6 2-cells after the insertion since the squared face was inserted as a hole in one face of the hexahedron. We can also see that there are 8 2-free darts, which are the darts of the squared face. Since they bound a hole, there is no face filling the hole and thus 8 darts are 2-free.
See also a similar example for Linear cell complex \ref Linear_cell_complexInsert "Insert an Edge Between Two Different Faces".

View File

@ -2949,8 +2949,8 @@ namespace CGAL {
void set_automatic_attributes_management_without_correction(bool newval)
{ this->automatic_attributes_management = newval; }
/** Create an half-edge.
* @return a dart of the new half-edge.
/** Create a halfedge.
* @return a dart of the new halfedge.
*/
Dart_descriptor make_half_edge()
{

View File

@ -103,7 +103,7 @@ namespace CGAL {
/*!
The class `Random_points_in_cube_d` is an input iterator creating points uniformly
distributed in an half-open cube.
distributed in a half-open cube.
\cgalModels{InputIterator,PointGenerator}

View File

@ -28,8 +28,9 @@ create_single_source_cgal_program("random_segments1.cpp")
create_single_source_cgal_program("random_segments2.cpp")
create_single_source_cgal_program("sphere_d.cpp")
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program("random_points_in_tetrahedral_mesh_3.cpp")
target_link_libraries(random_points_in_tetrahedral_mesh_3 PRIVATE CGAL::Eigen3_support)

View File

@ -15,8 +15,9 @@ create_single_source_cgal_program("test_tetrahedron_3.cpp")
create_single_source_cgal_program("test_triangle_2.cpp")
create_single_source_cgal_program("test_triangle_3.cpp")
find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program("generic_random_test.cpp")
target_link_libraries(generic_random_test PRIVATE CGAL::Eigen3_support)

View File

@ -8,7 +8,7 @@ find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6)
find_package(Qt6 QUIET COMPONENTS Widgets)
find_package(Eigen3 3.1.91 QUIET) #(requires 3.1.91 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)

View File

@ -6,8 +6,9 @@ project(Polygon_Demo)
find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6 Core)
find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: This demo requires the Eigen library, and will not be compiled.")
return()

View File

@ -221,7 +221,10 @@ void
DemosMainWindow::popupAboutBox(QString title, QString html_resource_name)
{
QFile about_CGAL(html_resource_name);
about_CGAL.open(QIODevice::ReadOnly);
if (!about_CGAL.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Error"), tr("Could not open resource file: %1").arg(html_resource_name));
return;
}
QString about_CGAL_txt = QTextStream(&about_CGAL).readAll();
#ifdef CGAL_VERSION_STR
QString cgal_version(CGAL_VERSION_STR);

View File

@ -222,7 +222,7 @@ private:
QTimer flyTimer_;
bool rotatesAroundUpVector_;
// Inverse the direction of an horizontal mouse motion. Depends on the
// Inverse the direction of a horizontal mouse motion. Depends on the
// projected screen orientation of the vertical axis when the mouse button is
// pressed.
bool constrainedRotationIsReversed_;

View File

@ -743,7 +743,7 @@ public:
typedef std::vector<Halfedge_handle> HVector;
HVector stack;
// Algorithm: The next() pointer is used as visited tag
// for a graph search. If the next pointer of an halfedge
// for a graph search. If the next pointer of a halfedge
// or its opposite halfedge is set to Halfedge_handle(),
// this edge has already been visited and must not be put
// on the stack again.

View File

@ -7,8 +7,9 @@ project(Heat_method_3_Examples)
# CGAL and its components
find_package(CGAL REQUIRED)
find_package(Eigen3 3.3.0 QUIET)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: These examples require the Eigen library (3.3 or greater), and will not be compiled.")
return()

View File

@ -7,8 +7,9 @@ project(Heat_method_3_Tests)
# CGAL and its components
find_package(CGAL REQUIRED)
find_package(Eigen3 3.3.0 QUIET)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
if(NOT TARGET CGAL::Eigen3_support)
message("NOTICE: These tests require the Eigen library (3.3 or greater), and will not be compiled.")
return()

View File

@ -47,7 +47,7 @@ The Delaunay triangulation of a set of points \f$P\f$ in the hyperbolic plane \f
- `HyperbolicDelaunayTriangulationTraits_2` describes the requirements for an interface for geometric objects, constructions, and predicates in the hyperbolic plane.
- `HyperbolicTriangulationFaceBase_2` describes the requirements for faces of the hyperbolic Delaunay triangulation.
- `HyperbolicFaceData` describes the requirements for an hyperbolic marker of faces of the hyperbolic Delaunay triangulation, used to filter faces of the Euclidean Delaunay triangulation.
- `HyperbolicFaceData` describes the requirements for a hyperbolic marker of faces of the hyperbolic Delaunay triangulation, used to filter faces of the Euclidean Delaunay triangulation.
\cgalCRPSection{Classes}

View File

@ -4,6 +4,13 @@
Release date: July 2026
### [2D Arrangements](https://doc.cgal.org/6.1/Manual/packages.html#PkgArrangementOnSurface2)
- Introduced a Geometry Traits concept for arrangement on surfaces that enables the provision of the disconnected portions of an approximation of a curve within a given bounding box.
- Made the `Arr_linear_traits_2` a model of the new concept.
- Added overloads of `draw(Arrangement_on_surface_2& arr, Bbox& bbox, ...)` that enable the drawing of arrangements induced by unbounded curves.
### [Linear Cell Complex](https://doc.cgal.org/6.2/Manual/packages.html#PkgLinearCellComplex)
- **API Changes**: The following import functions have been deprecated and renamed for better naming clarity and consistency:
@ -12,6 +19,15 @@ Release date: July 2026
- `import_from_triangulation_3()``triangulation_3_to_lcc()`
- The old function names are still available but marked as deprecated for backward compatibility.
### [Surface Mesh Simplification](https://doc.cgal.org/6.2/Manual/packages.html#PkgSurfaceMeshSimplification)
- Added the class `CGAL::Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies`, which provides improved output for `CGAL::Surface_mesh_simplification::edge_collapse()`.
That class works the same as previous `GarlandHeckbert_policies`.
Its constructor accepts a `Mesh` and optional named parameters to set the weight of the line policy relative to the plane policy, set the boundary cost multiplier or provide vertex normals.
- **Breaking change**: `CGAL::Surface_mesh_simplification::GarlandHeckbert_policies.h` is now an alias of `CGAL::Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies.h` and is no longer deprecated.
## [Release 6.1](https://github.com/CGAL/cgal/releases/tag/v6.1)
Release date: Sept 2025
@ -58,6 +74,71 @@ Release date: Sept 2025
See also the associated [news entry](https://www.cgal.org/2025/06/24/triangulations-on-hyperbolic-surfaces/).
### 3D Isosurfacing (new package)
- This package provides algorithms to extract isosurfaces from different inputs. The input is represented
as a 3D domain and can be an implicit function or a Cartesian grid. The output is an indexed face
set that stores an isosurface in the form of a surface mesh. The provided algorithms include Marching Cubes,
topologically correct Marching Cubes, and Dual Contouring.
### [Polygon Mesh Processing](https://doc.cgal.org/6.1/Manual/packages.html#PkgPolygonMeshProcessing)
- Added the function `CGAL::Polygon_mesh_processing::discrete_mean_curvature` and `CGAL::Polygon_mesh_processing::discrete_Guassian_curvature` to evaluate the discrete curvature at a vertex of a mesh.
- Added the function `CGAL::Polygon_mesh_processing::angle_sum` to compute the sum of the angles around a vertex.
- Added a function in the [visitor of the corefinement based methods](https://doc.cgal.org/6.1/Polygon_mesh_processing/classPMPCorefinementVisitor.html)
to know faces in the output meshes that are corresponding to input coplanar faces.
- Added the function `CGAL::Polygon_mesh_processing::approximated_centroidal_Voronoi_diagram_remeshing()`
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.
### [Algebraic Kernel](https://doc.cgal.org/6.1/Manual/packages.html#PkgAlgebraicKernelD)
- **Breaking change**: Classes based on the RS Library are no longer provided.
### [BGL](https://doc.cgal.org/6.1/Manual/packages.html#PkgBGL)
- Added the function `CGAL::Euler::remove_degree_2_vertex()`, which enables users to remove vertices which have exactly two incident edges.
### [2D Arrangements](https://doc.cgal.org/6.1/Manual/packages.html#PkgArrangementOnSurface2)
- Introduces two traits decorators, namely `Arr_tracing_traits_2` and `Arr_counting_traits_2`, which can be used to extract debugging and informative metadata about the traits in use while a program is being executed.
- Fixed the Landmark point-location strategy so that it can be applied to arrangements on a sphere.
- Fixed a bug in the extensions of vertex and halfedge types of the DCEL when used to instantiate Arrangement_with_history_2 or similar arrangement classes that derive from Arrangement_2.
- Renamed the prefix of the names of all concepts in the Arrangement_on_surface_2 package from "Arrangement" to "Aos".
- Renamed the old concept `AosApproximateTraits_2` to `AosApproximatePointTraits_2` to make room for the new concept `AosApproximateTraits_2`. This concept requires the provision of a functor called `Approximate_2` that has an operator that approximates the coordinates of a point.
- Introduced a new concept called `AosApproximateTraits_2`. It refines the concept `AosApproximatePointTraits_2`. This concept requires the provision of a functor called `Approximate_2`. In addition to an operator that approximates the coordinates of a point, it also requires the provision of (i) an operator that approximates a points, and (ii) an operator that approximates a curve.
- Changed all "typedef" style statements in the user manual to "using" style. (Observe that a similar update to the examples has already been made in a previous release.)
- Fixed do_intersect() of a 2D Arrangement and a curve.
- Added overloads of `draw(Arrangement_on_surface_2& arr, Bbox& bbox, ...)` that enable the drawing of arrangements induced by unbounded curves.
- Introduced a Geometry Traits concept for arrangement on surfaces that enables the provision of the disconnected portions of an approximation of a curve within a given bounding box.
- Made the `Arr_linear_traits_2` a model of the new concept.
### [3D Mesh Generation](https://doc.cgal.org/6.1/Manual/packages.html#PkgMesh3)
- Added two new meshing parameters that enable mesh initialization customization :
- `initial_points_generator` : enables the user to specify a functor that generates initial points,
- `initial_points` : enables the user to specify a `Range` of initial points.
- Added a new meshing parameter `surface_only`, to improve performances when the user is only interested in surface mesh generation.
### [Poisson Surface Reconstruction](https://doc.cgal.org/6.1/Manual/packages.html#PkgPoissonSurfaceReconstruction3)
- Added a new mesh domain `Poisson_mesh_domain_3` that integrates some optimizations from the deprecated 3D Surface Mesh Generation package.
### [3D Subdivision Methods](https://doc.cgal.org/6.1/Manual/packages.html#PkgSurfaceSubdivisionMethod3)
- Added a new named parameter for `CGAL::Subdivision_method_3::Loop_subdivision()` and
`CGAL::Subdivision_method_3::CatmullClark_subdivision()`, which enables users to subdivide
a mesh without modifying its geometry.
### [2D Triangulations](https://doc.cgal.org/6.1/Manual/packages.html#PkgTriangulation2)
- **Breaking change**: In the class template `Constrained_triangulation_plus_2`, the value type of the range returned
by `subconstraints()` has changed from `const std::pair<const Subconstraint, std::list<Context>*>` to `Subconstraint`.
The old range type is now returned by a new function named `subconstraints_and_contexts()`.
### [Polygon Repair](https://doc.cgal.org/6.1/Manual/packages.html#PkgPolygonRepair)
- Added the [non-zero rule](https://doc.cgal.org/6.1/Polygon_repair/structCGAL_1_1Polygon__repair_1_1Non__zero__rule.html)

View File

@ -3,5 +3,5 @@ if(CERES_FOUND AND NOT TARGET CGAL::Ceres_support)
set_target_properties(CGAL::Ceres_support PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "CGAL_PMP_USE_CERES_SOLVER"
INTERFACE_INCLUDE_DIRECTORIES "${CERES_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "ceres")
INTERFACE_LINK_LIBRARIES "Ceres::ceres")
endif()

View File

@ -1,3 +1,10 @@
if((EIGEN3_FOUND OR Eigen3_FOUND) AND NOT TARGET CGAL::Eigen3_support)
if ("${Eigen3_VERSION}" VERSION_LESS "3.3.7")
set (EIGEN3_FOUND 0)
find_package(Eigen3 3.3.7 QUIET) # (3.3.7 or greater)
endif()
endif()
if((EIGEN3_FOUND OR Eigen3_FOUND) AND NOT TARGET CGAL::Eigen3_support)
if(NOT TARGET Threads::Threads)
find_package(Threads REQUIRED)

Some files were not shown because too many files have changed in this diff Show More