fix drawing degenerate paths

This commit is contained in:
Shepard Liu 2025-07-01 18:12:17 +08:00
parent 3f90d36604
commit cd1951be26
21 changed files with 1517 additions and 1443 deletions

View File

@ -12,39 +12,44 @@
#include "ArrangementDemoGraphicsView.h"
#include <QCoreApplication>
#include <QFontMetrics>
#include <QKeyEvent>
#include <QPen>
#include <QVarLengthArray>
#include <cmath>
#include <iostream>
#include <cmath>
#include <QVarLengthArray>
#include <QPen>
#include <QCoreApplication>
#include <QKeyEvent>
#include <QFontMetrics>
//! Member function to setup the viewport of the screen
/*!
\param parent a Qwidget pointer to the class
*/
ArrangementDemoGraphicsView::ArrangementDemoGraphicsView(QWidget* parent)
: QGraphicsView(parent)
, maxScale(500000)
, minScale(0.0002) {
ArrangementDemoGraphicsView::ArrangementDemoGraphicsView( QWidget* parent ) :
QGraphicsView( parent ),
maxScale( 500000 ),
minScale( 0.0002 )
{
this->resetTransform();
this->setResizeAnchor(QGraphicsView::AnchorUnderMouse);
this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
this->setMouseTracking(true);
this->setMouseTracking( true );
this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// TODO: Make options menu work
this->setRenderHint(QPainter::Antialiasing);
}
void ArrangementDemoGraphicsView::paintEvent(QPaintEvent* event) {
void ArrangementDemoGraphicsView::paintEvent(QPaintEvent* event)
{
qreal scale = std::sqrt(std::abs(this->transform().determinant()));
if(scale > this->maxScale)
if (scale > this->maxScale)
this->scale(this->maxScale / scale, this->maxScale / scale);
else if(scale < this->minScale)
else if (scale < this->minScale)
this->scale(this->minScale / scale, this->minScale / scale);
QGraphicsView::paintEvent(event);
}
void ArrangementDemoGraphicsView::resetTransform() { this->setTransform({1.0, 0.0, 0.0, -1.0, 0.0, 0.0}); }
void ArrangementDemoGraphicsView::resetTransform()
{
this->setTransform({1.0, 0.0, 0.0, -1.0, 0.0, 0.0});
}

View File

@ -84,16 +84,19 @@ public:
Vertex_cache_range vertex_cache() const {
return boost::make_iterator_range(vertex_cache_begin(), vertex_cache_end());
}
std::size_t vertex_cache_size() const { return m_vertex_cache.size(); }
Halfedge_cache_const_iterator halfedge_cache_begin() const { return m_halfedge_cache.begin(); }
Halfedge_cache_const_iterator halfedge_cache_end() const { return m_halfedge_cache.end(); }
Halfedge_cache_range halfedge_cache() const {
return boost::make_iterator_range(halfedge_cache_begin(), halfedge_cache_end());
}
std::size_t halfedge_cache_size() const { return m_halfedge_cache.size(); }
Face_cache_const_iterator face_cache_begin() const { return m_face_cache.begin(); }
Face_cache_const_iterator face_cache_end() const { return m_face_cache.end(); }
Face_cache_range face_cache() const { return boost::make_iterator_range(face_cache_begin(), face_cache_end()); }
std::size_t face_cache_size() const { return m_face_cache.size(); }
private:
Vertex_cache m_vertex_cache;

View File

@ -3,7 +3,7 @@
#include "CGAL/Arr_enums.h"
#include "CGAL/Draw_aos/Arr_bounded_approximate_point_2.h"
#include "CGAL/Draw_aos/Arr_bounded_compute_y_at_x.h"
#include "CGAL/Draw_aos/Arr_compute_y_at_x.h"
#include "CGAL/Draw_aos/Arr_construct_curve_end.h"
#include "CGAL/Draw_aos/Arr_construct_segments.h"
#include "CGAL/Draw_aos/Arr_render_context.h"
@ -48,13 +48,13 @@ class Arr_bounded_approximate_curve_2
Execution_context(const Arr_bounded_render_context& ctx,
const X_monotone_curve_2& curve,
const Arr_bounded_approximate_point_2& approx_pt,
const Arr_bounded_compute_y_at_x& compute_y_at_x,
const Arr_compute_y_at_x& compute_y_at_x,
const Intersections_vector& top_inters,
const Intersections_vector& bottom_inters,
Polyline_geom& polyline)
: Arr_context_delegator(ctx)
, curve(curve)
, bounded_compute_y_at_x(compute_y_at_x)
, compute_y_at_x(compute_y_at_x)
, bounded_approx_pt(approx_pt)
, top_inters(top_inters)
, bottom_inters(bottom_inters)
@ -72,7 +72,7 @@ class Arr_bounded_approximate_curve_2
bool is_bounded_curve() const { return is_min_end_bounded() && is_max_end_bounded(); }
const X_monotone_curve_2& curve;
const Arr_bounded_compute_y_at_x& bounded_compute_y_at_x;
const Arr_compute_y_at_x& compute_y_at_x;
const Arr_bounded_approximate_point_2& bounded_approx_pt;
const Intersections_vector &top_inters, bottom_inters;
const std::optional<Point_2> min_end, max_end;
@ -132,7 +132,7 @@ private:
*/
static void approximate_simple_curve_segment(Execution_context& ctx, const FT& start, const FT& end, double step) {
for(FT x = start + step; x < end; x += step) {
auto y = ctx.bounded_compute_y_at_x(ctx.curve, x);
auto y = ctx.compute_y_at_x(ctx.curve, x);
if(!y.has_value()) {
// break as soon as there's no more intersections
break;
@ -143,14 +143,14 @@ private:
}
*ctx.out_it++ = ctx->approx_pt(Point_2(x, y.value()));
if(y > ctx->ymax() || y < ctx->ymin()) {
// We are outside the bbox. The dummy point is inserted to indicate the curve is outside the bbox.
// We are outside the bbox. The dummy point was already inserted to indicate that.
break;
}
}
};
static void approximate_vertical_curve(Execution_context& ctx) {
if(ctx.is_bounded_curve() && ctx.min_end->x() < ctx->xmin() && ctx.min_end->x() > ctx->xmax()) {
if(ctx.is_bounded_curve() && (ctx.min_end->x() < ctx->xmin() || ctx.min_end->x() > ctx->xmax())) {
// The curve is outside the bbox in x direction, no need to approximate
return;
}
@ -178,7 +178,7 @@ private:
public:
Arr_bounded_approximate_curve_2(const Arr_bounded_render_context& ctx,
const Arr_bounded_approximate_point_2& point_approx)
: m_bounded_compute_y_at_x(ctx)
: m_compute_y_at_x(ctx)
, m_approx_pt(point_approx)
, m_ctx(ctx)
, m_top(ctx.cst_horizontal_segment(ctx.ymax(), ctx.xmin(), ctx.xmax()))
@ -210,7 +210,7 @@ public:
auto top_inters = compute_intersections(curve, m_top, m_ctx.intersect_2, m_ctx.cst_curve_end);
auto bottom_inters = compute_intersections(curve, m_bottom, m_ctx.intersect_2, m_ctx.cst_curve_end);
Execution_context ctx(m_ctx, curve, m_approx_pt, m_bounded_compute_y_at_x, top_inters, bottom_inters, polyline);
Execution_context ctx(m_ctx, curve, m_approx_pt, m_compute_y_at_x, top_inters, bottom_inters, polyline);
if(ctx->is_vertical_2(curve)) {
approximate_vertical_curve(ctx);
@ -224,7 +224,7 @@ public:
FT last_x;
std::optional<Point_2> first_inter = first_intersection(ctx);
if(auto y_at_txmin = ctx.bounded_compute_y_at_x(curve, txmin);
if(auto y_at_txmin = ctx.compute_y_at_x(curve, txmin);
y_at_txmin.has_value() && y_at_txmin != ctx->ymin() && y_at_txmin != ctx->ymax())
{
// The tight starting point of the curve is within the bbox and
@ -237,16 +237,7 @@ public:
} else if(first_inter.has_value()) {
last_x = first_inter->x();
} else {
// We assert that the curve is outbound.
// If the min end is bounded, it's obvious.
//
// If the min end is unbounded, we know that the curve has no intersections with top, bottom or left edge(txmin ==
// xmin when the min end is unbounded) and the min end of the curve is outbound (it approaches infinity in one or
// both dimension).
// Assume that the curve does has one point within the bbox. Note that it's a contiguous
// x-monotone curve. So it must cross the top, bottom or left edge to reach the min end from right to left,
// which is a contradiction.
return polyline;
return polyline; // The curve is entirely outside the bbox in x direction.
}
// iterate through the intersections and insert segments in-between.
@ -258,7 +249,7 @@ public:
}),
[&ctx](const Point_2& pt1, const Point_2& pt2) { return ctx->compare_xy_2(pt1, pt2) == CGAL::SMALLER; });
if(auto y_at_txmax = ctx.bounded_compute_y_at_x(curve, txmax);
if(auto y_at_txmax = ctx.compute_y_at_x(curve, txmax);
y_at_txmax.has_value() && y_at_txmax != ctx->ymin() && y_at_txmax != ctx->ymax())
{
approximate_simple_curve_segment(ctx, last_x, txmax, ctx->approx_error);
@ -272,7 +263,7 @@ public:
private:
const Arr_bounded_render_context& m_ctx;
const Arr_bounded_approximate_point_2& m_approx_pt;
const Arr_bounded_compute_y_at_x m_bounded_compute_y_at_x;
const Arr_compute_y_at_x m_compute_y_at_x;
const X_monotone_curve_2 m_top;
const X_monotone_curve_2 m_bottom;
};

View File

@ -22,7 +22,6 @@
namespace CGAL {
namespace internal {
/**
* @brief Patches corners between two boundary points of the bbox
* counter-clockwisely.
@ -118,6 +117,7 @@ public:
continue;
}
*out_it++ = corner;
std::cout << "Patching corner: " << corner << std::endl;
}
return;
}
@ -126,6 +126,64 @@ private:
const Bbox_2 m_bbox;
};
template <typename OutputIterator>
class Geom_simplifier
{
using Approx_point = Arr_approximation_geometry_traits::Approx_point;
private:
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();
}
}
public:
Geom_simplifier(OutputIterator& out_it, const Bbox_2& bbox)
: m_out_it(out_it)
, m_bbox(bbox) {}
decltype(auto) insert_iterator() {
return boost::make_function_output_iterator([this](const Approx_point& p) {
if(m_mid.has_value()) {
if(m_mid.value() == p) {
return;
}
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()) {
if(m_start.value() == p) {
return;
}
m_mid = p;
} else {
m_start = p;
}
});
}
~Geom_simplifier() { dump(); }
private:
OutputIterator& m_out_it;
std::optional<Approx_point> m_start, m_mid;
Bbox_2 m_bbox;
};
} // namespace internal
/**
@ -141,8 +199,11 @@ class Arr_bounded_approximate_face_2
using Polyline_geom = Approx_geom_traits::Polyline_geom;
using Ccb_halfedge_const_circulator = Arrangement::Ccb_halfedge_const_circulator;
using Approx_point = Approx_geom_traits::Approx_point;
using Patch_boundary = internal::Patch_boundary;
using Triangulated_face = Approx_geom_traits::Triangulated_face;
using Patch_boundary = internal::Patch_boundary;
template <typename OutputIterator>
using Geom_simplifier = internal::Geom_simplifier<OutputIterator>;
struct Left_to_right_tag
{};
@ -155,9 +216,9 @@ private:
public:
Execution_context(const Arr_bounded_render_context& ctx,
Arr_bounded_face_triangulator& triangulator,
const Patch_boundary& patch_boundary,
const Arr_bounded_approximate_point_2& bounded_approx_pt,
const Arr_bounded_approximate_curve_2& bounded_approx_curve,
const Patch_boundary& patch_boundary)
const Arr_bounded_approximate_curve_2& bounded_approx_curve)
: Arr_context_delegator(ctx)
, triangulator(triangulator)
, patch_boundary(patch_boundary)
@ -167,8 +228,8 @@ private:
public:
const Arr_bounded_approximate_point_2& bounded_approx_pt;
const Arr_bounded_approximate_curve_2& bounded_approx_curve;
const Patch_boundary& patch_boundary;
Arr_bounded_face_triangulator& triangulator;
const Patch_boundary& patch_boundary;
};
private:
@ -194,46 +255,75 @@ private:
}
}
template <typename Ccb_tag>
static void approximate_ccb(Execution_context& ctx, const Ccb_halfedge_const_circulator& start_circ) {
constexpr bool Is_outer_ccb = std::is_same_v<Ccb_tag, Outer_ccb_tag>;
auto ccb_constraint = ctx.triangulator.make_ccb_constraint<Ccb_tag>();
auto out_it = ccb_constraint.insert_iterator();
template <typename CcbTag, bool Bounded = true>
static void approximate_ccb(Execution_context& ctx, Ccb_halfedge_const_circulator start_circ) {
constexpr bool Is_outer_ccb = std::is_same_v<CcbTag, Outer_ccb_tag>;
static_assert(Is_outer_ccb || Bounded, "Inner CCBs are impossible to be unbounded.");
std::optional<Approx_point> ccb_last_pt, ccb_first_pt;
auto counter_clockwise_start_circ = Is_outer_ccb ? start_circ : Ccb_halfedge_const_circulator(start_circ->twin());
auto circ = counter_clockwise_start_circ;
do {
bool is_he_first_pt = true;
auto patch_out_it = boost::make_function_output_iterator([&](const Approx_point& pt) {
if(ccb_last_pt == pt || !ctx->contains(pt)) {
return;
}
if(is_he_first_pt && ccb_last_pt.has_value()) {
ctx.patch_boundary(ccb_last_pt.value(), pt, out_it);
}
*out_it++ = pt;
ccb_last_pt = pt;
if(!ccb_first_pt.has_value()) {
ccb_first_pt = pt;
}
is_he_first_pt = false;
});
approximate_halfedge_of_ccb(ctx, circ, patch_out_it);
approximate_vertex(ctx, circ->target());
} while(++circ != counter_clockwise_start_circ);
if(Is_outer_ccb && !ccb_first_pt.has_value()) {
*out_it++ = Approx_point(ctx->xmin(), ctx->ymin());
*out_it++ = Approx_point(ctx->xmax(), ctx->ymin());
*out_it++ = Approx_point(ctx->xmax(), ctx->ymax());
*out_it++ = Approx_point(ctx->xmin(), ctx->ymax());
// For unbound ccb, we start on a fictitious edge
if constexpr(!Bounded) {
while(!start_circ->is_fictitious()) {
++start_circ;
}
}
if(ccb_first_pt.has_value() && ccb_first_pt != ccb_last_pt) {
// Close the ccb
ctx.patch_boundary(ccb_last_pt.value(), ccb_first_pt.value(), out_it);
auto ccb_constraint = ctx.triangulator.make_ccb_constraint<CcbTag>();
auto constraint_out_it = ccb_constraint.insert_iterator();
auto simplifier = Geom_simplifier(constraint_out_it, ctx->bbox());
auto simplifier_out_it = simplifier.insert_iterator();
auto circ = start_circ;
std::optional<Approx_point> last_pt;
// These vars are used only in unbounded ccb.
std::optional<Approx_point> first_pt;
bool passed_fictitious_edge = false;
auto he_process_out_it = boost::make_function_output_iterator([&](const Approx_point& pt) {
Approx_point regulated_pt(pt.x(), std::clamp(pt.y(), ctx->ymin(), ctx->ymax()));
if(last_pt == regulated_pt) {
return;
}
*simplifier_out_it++ = regulated_pt;
if constexpr(!Bounded) {
// TODO: nesting too deep and looks ugly
if(passed_fictitious_edge) {
passed_fictitious_edge = false;
if(last_pt.has_value()) {
ctx.patch_boundary(last_pt.value(), regulated_pt, simplifier_out_it);
}
}
if(!first_pt.has_value()) {
first_pt = regulated_pt;
}
}
last_pt = regulated_pt;
});
do {
if constexpr(!Bounded) {
if(circ->is_fictitious()) {
passed_fictitious_edge = true;
}
}
approximate_halfedge_of_ccb(ctx, circ, he_process_out_it);
approximate_vertex(ctx, circ->target());
} while(++circ != start_circ);
if constexpr(!Bounded) {
if(!first_pt.has_value()) {
*simplifier_out_it++ = Approx_point(ctx->xmin(), ctx->ymin());
*simplifier_out_it++ = Approx_point(ctx->xmin(), ctx->ymax());
*simplifier_out_it++ = Approx_point(ctx->xmax(), ctx->ymax());
*simplifier_out_it++ = Approx_point(ctx->xmax(), ctx->ymin());
} else {
ctx.patch_boundary(last_pt.value(), first_pt.value(), simplifier_out_it);
}
}
}
@ -257,18 +347,34 @@ public:
}
CGAL_assertion_msg(!fh->is_fictitious(), "Cannot approximate a fictitious face.");
if(!fh->has_outer_ccb()) {
// The face is the unbounded face of bounded arrangements
// The face is the unbounded face of bounded arrangements, we skip approximating any non degenerate features.
for(auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb) {
if((*inner_ccb)->twin()->face() != (*inner_ccb)->face()) {
continue;
}
m_curve_approx(*inner_ccb);
}
for(auto isolated_vh = fh->isolated_vertices_begin(); isolated_vh != fh->isolated_vertices_end(); ++isolated_vh) {
m_point_approx(isolated_vh);
}
return triangulated_face;
}
Arr_bounded_face_triangulator triangulator(m_ctx);
Execution_context ctx(m_ctx, triangulator, m_point_approx, m_curve_approx, m_patch_boundary);
Execution_context ctx(m_ctx, triangulator, m_patch_boundary, m_point_approx, m_curve_approx);
if(fh->is_unbounded()) {
approximate_ccb<Outer_ccb_tag, false>(ctx, fh->outer_ccb());
} else {
approximate_ccb<Outer_ccb_tag, true>(ctx, fh->outer_ccb());
}
approximate_ccb<Outer_ccb_tag>(ctx, fh->outer_ccb());
for(auto inner_ccb = fh->inner_ccbs_begin(); inner_ccb != fh->inner_ccbs_end(); ++inner_ccb) {
approximate_ccb<Inner_ccb_tag>(ctx, *inner_ccb);
}
for(auto isolated_vh = fh->isolated_vertices_begin(); isolated_vh != fh->isolated_vertices_begin(); ++isolated_vh) {
approximate_vertex(ctx, isolated_vh);
}

View File

@ -7,73 +7,9 @@
#include <CGAL/Draw_aos/helpers.h>
#include <CGAL/Draw_aos/Arr_construct_segments.h>
#include <CGAL/Draw_aos/Arr_construct_curve_end.h>
#include <CGAL/Draw_aos/Arr_compute_y_at_x.h>
#include <boost/iterator/function_output_iterator.hpp>
#include <optional>
namespace CGAL {
/**
* @brief Functor to compute the y-coordinate at a given x-coordinate for an x-monotone curve within a bounding box.
*/
class Arr_bounded_compute_y_at_x
{
public:
using Point_2 = Geom_traits::Point_2;
using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2;
using Intersect_2 = Geom_traits::Intersect_2;
using Construct_min_vertex_2 = Geom_traits::Construct_min_vertex_2;
using FT = Geom_traits::FT;
using Approximate_2 = Geom_traits::Approximate_2;
using Is_vertical_2 = Geom_traits::Is_vertical_2;
Arr_bounded_compute_y_at_x(const Arr_bounded_render_context& ctx)
: m_ctx(ctx)
// TODO: some traits does not have approximate_2_object. we'll need a specialization for them.
, m_approx(ctx.traits.approximate_2_object()) {}
/**
* @brief Computes the y-coordinate at a given x-coordinate for an x-monotone curve trimmed
* to the bounding box.
*
* The bounding box here is considered as closed.
*
* @precondition The curve is not verical
* @param curve
* @param x
* @return true if there is an intersection at given x within the bounding box,
* @return false otherwise.
*/
std::optional<FT> operator()(const X_monotone_curve_2& curve, const FT& x) const {
CGAL_assertion(!m_ctx.is_vertical_2(curve));
if(!m_ctx.contains_x(x)) {
return false;
}
auto min_pt = m_ctx.cst_curve_end(curve, ARR_MIN_END);
auto max_pt = m_ctx.cst_curve_end(curve, ARR_MAX_END);
if(min_pt.has_value() && min_pt->x() == x) {
return min_pt->y();
}
if(max_pt.has_value() && max_pt->x() == x) {
return max_pt->y();
}
using Multiplicity = Geom_traits::Multiplicity;
using Intersect_point = std::pair<Point_2, Multiplicity>;
using Intersect_curve = X_monotone_curve_2;
using Intersect_type = std::variant<Intersect_point, Intersect_curve>;
auto vertical_line = m_ctx.cst_vertical_segment(x, m_ctx.ymin(), m_ctx.ymax());
std::optional<FT> y;
auto func_out_iter = boost::make_function_output_iterator(
[&y, this](const Intersect_type& res) { y = std::get<Intersect_point>(res).first.y(); });
m_ctx.intersect_2(curve, vertical_line, func_out_iter);
return y;
}
private:
const Arr_bounded_render_context& m_ctx;
Approximate_2 m_approx;
};
} // namespace CGAL
namespace CGAL {} // namespace CGAL
#endif // CGAL_DRAW_AOS_ARR_BOUNDED_COMPUTE_Y_AT_X_H

View File

@ -1,13 +1,15 @@
#ifndef CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H
#define CGAL_DRAW_AOS_ARR_FACE_TRIANGULATOR_H
#include "CGAL/Constrained_Delaunay_triangulation_2.h"
#include "CGAL/Constrained_triangulation_2.h"
#include "CGAL/Constrained_triangulation_face_base_2.h"
#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h"
#include "CGAL/Draw_aos/Arr_render_context.h"
#include "CGAL/Exact_predicates_inexact_constructions_kernel.h"
#include "CGAL/Triangulation_vertex_base_with_info_2.h"
#include "CGAL/basic.h"
#include "CGAL/mark_domain_in_triangulation.h"
#include "CGAL/number_utils.h"
#include "CGAL/unordered_flat_map.h"
#include <cstddef>
#include <CGAL/Draw_aos/helpers.h>
@ -49,8 +51,8 @@ class Arr_bounded_face_triangulator
using Fb = CGAL::Constrained_triangulation_face_base_2<Epick>;
using Tds = CGAL::Triangulation_data_structure_2<Vb, Fb>;
using Ct = Constrained_triangulation_2<Epick, Tds, Exact_predicates_tag>;
using Point = Epick::Point_2;
using Point_with_info = std::pair<Point, Point_index>;
using KPoint = Epick::Point_2;
using KPoint_with_info = std::pair<KPoint, Point_index>;
std::size_t counter{0};
@ -63,12 +65,14 @@ class Arr_bounded_face_triangulator
class Ccb_constraint
{
constexpr static bool Is_outer_ccb = std::is_same_v<Ccb_tag, Outer_ccb_tag>;
friend class Arr_bounded_face_triangulator;
using Side_of_boundary = Arr_bounded_render_context::Side_of_boundary;
std::ofstream m_ofs;
Ccb_constraint(Arr_bounded_face_triangulator& triangulator)
: m_triangulator(&triangulator) {
m_ccb_start = m_triangulator->m_points.size();
: m_triangulator(&triangulator)
, m_ccb_start(m_triangulator->m_points.size()) {
triangulator.m_has_active_constraint = true;
if(Is_outer_ccb) {
@ -80,24 +84,28 @@ class Arr_bounded_face_triangulator
}
private:
Point offset_boundary_point(const Point& pt) const {
constexpr double offset = 1; // It doesn't matter how much we offset the point
double x = pt.x(), y = pt.y();
const auto& ctx = m_triangulator->m_ctx;
std::vector<KPoint_with_info>& points() { return m_triangulator->m_points; }
const std::vector<KPoint_with_info>& points() const { return m_triangulator->m_points; }
auto first_point() const { return points()[m_ccb_start].first; }
auto last_point() const { return points().back().first; }
const Arr_bounded_render_context& ctx() const { return m_triangulator->m_ctx; }
void add_point(const KPoint& pt) { points().emplace_back(pt, points().size()); }
std::size_t ccb_size() const { return m_triangulator->m_points.size() - m_ccb_start - m_helper_indices.size(); }
if(x == ctx.xmin()) {
x -= offset;
KPoint offset_boundary_point(const KPoint& pt, Side_of_boundary side, double offset) const {
CGAL_precondition(side != Side_of_boundary::None);
switch(side) {
case Side_of_boundary::Left:
return KPoint(pt.x() - offset, pt.y());
case Side_of_boundary::Right:
return KPoint(pt.x() + offset, pt.y());
case Side_of_boundary::Top:
return KPoint(pt.x(), pt.y() + offset);
case Side_of_boundary::Bottom:
return KPoint(pt.x(), pt.y() - offset);
default:
return pt; // Should not reach here
}
if(x == ctx.xmax()) {
x += offset;
}
if(y == ctx.ymin()) {
y -= offset;
}
if(y == ctx.ymax()) {
y += offset;
}
return Point(x, y);
}
void insert_ccb() {
@ -123,30 +131,39 @@ class Arr_bounded_face_triangulator
};
auto index_to_point_with_info = [&points](std::size_t idx) { return points[idx]; };
auto indexes_begin = boost::make_counting_iterator<std::size_t>(m_ccb_start);
auto indexes_end = boost::make_counting_iterator<std::size_t>(points.size());
auto filtered_begin = boost::make_filter_iterator(concrete_pt_filter, indexes_begin, indexes_end);
auto filtered_end = boost::make_filter_iterator(concrete_pt_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);
// {
// std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::app);
// auto& ctx = const_cast<Arr_bounded_render_context&>(m_triangulator->m_ctx);
// ofs_index << "ccb_" << ctx.counter << ".txt" << std::endl;
// std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/ccb_" + std::to_string(ctx.counter++) + ".txt",
// std::ios::out | std::ios::trunc);
// for(auto it = filtered_begin; it != filtered_end; ++it) {
// const auto& pt = it->first;
// ofs << pt.x() << " " << pt.y() << std::endl;
// }
// }
ct.insert_with_info<Point_with_info>(transformed_begin, transformed_end);
ct.insert_with_info<KPoint_with_info>(transformed_begin, transformed_end);
} else {
ct.insert_with_info<Point_with_info>(begin, end);
ct.insert_with_info<KPoint_with_info>(begin, end);
}
}
void try_add_offset(const KPoint& from, const KPoint& to) {
if(from == to) {
return;
}
auto shared_side = ctx().shared_boundary_side(from, to);
if(shared_side == Arr_bounded_render_context::Side_of_boundary::None) {
return;
}
// m_helper_indices.push_back(points().size());
// add_point(offset_boundary_point(from, shared_side, m_offset));
m_helper_indices.push_back(points().size());
add_point(offset_boundary_point(KPoint((from.x() + to.x()) / 2, (from.y() + to.y()) / 2), shared_side, m_offset));
// TODO: we'll come back to find out if we do need to offset the second point.
// m_helper_indices.push_back(points().size());
// add_point(offset_boundary_point(to, shared_side, m_offset));
// Doesn't matter how much we increase the offset.
m_offset += 0.5;
}
public:
Ccb_constraint(const Ccb_constraint&) = delete;
Ccb_constraint& operator=(const Ccb_constraint&) = delete;
@ -162,22 +179,18 @@ class Arr_bounded_face_triangulator
return *this;
}
decltype(auto) insert_iterator() {
return boost::make_function_output_iterator([&, this](const Approx_point& pt) {
auto& points = m_triangulator->m_points;
auto insert_iterator() {
return boost::make_function_output_iterator([this](const Approx_point& pt) {
CGAL_assertion_msg(m_triangulator != nullptr, "Use of destructed or moved Ccb_constraint object.");
CGAL_assertion_msg(m_triangulator->m_ctx.contains(pt), "Outbound point in Ccb_constraint.");
CGAL_assertion_msg(ctx().contains(pt), "Outbound point in Ccb_constraint.");
KPoint kp(pt.x(), pt.y());
if constexpr(Is_outer_ccb) {
bool is_on_boundary = m_triangulator->m_ctx.is_on_boundary(pt);
if(is_on_boundary && m_is_last_on_boundary) {
m_helper_indices.push_back(points.size());
points.emplace_back(offset_boundary_point(points.back().first), Point_index());
}
m_is_last_on_boundary = is_on_boundary;
if(Is_outer_ccb && ccb_size() != 0) {
try_add_offset(last_point(), kp);
m_ofs << pt.x() << " " << pt.y() << std::endl;
}
points.emplace_back(Point(pt.x(), pt.y()), points.size());
add_point(kp);
});
}
@ -185,15 +198,9 @@ class Arr_bounded_face_triangulator
if(m_triangulator == nullptr) {
return;
}
if(Is_outer_ccb && m_is_last_on_boundary && m_triangulator->m_points.size() != m_ccb_start) {
auto& points = m_triangulator->m_points;
const auto& first_pt = points[m_ccb_start].first;
const auto& last_pt = points.back().first;
bool is_first_on_boundary = m_triangulator->m_ctx.is_on_boundary(first_pt);
if(m_is_last_on_boundary && is_first_on_boundary) {
m_helper_indices.push_back(points.size());
points.emplace_back(offset_boundary_point(last_pt), Point_index());
}
if(Is_outer_ccb && ccb_size() != 0) {
try_add_offset(last_point(), first_point());
}
insert_ccb();
@ -203,8 +210,10 @@ class Arr_bounded_face_triangulator
private:
Arr_bounded_face_triangulator* m_triangulator;
std::size_t m_ccb_start;
bool m_is_last_on_boundary = false;
const std::size_t m_ccb_start;
// These are used only for outer CCBs.
double m_offset = 0.5; // Doesn't matter how much we offset.
std::vector<std::size_t> m_helper_indices; // The offseted point indices when inserting outer ccb constraint
};
@ -217,12 +226,15 @@ public:
operator Triangulated_face() && {
CGAL_assertion(!m_has_active_constraint && "There is an active constraint in the triangulator.");
CGAL_assertion(m_outer_ccb_processed && "Outer CCB has not been processed yet.");
if(m_points.empty() || m_ct.number_of_faces() == 0) {
return Triangulated_face();
}
// insert_constraint() should be called after insert_with_info(), or info will not be set correctly.
auto first_of_pair = [](const Point_with_info& pt_with_info) { return pt_with_info.first; };
auto first_of_pair = [](const KPoint_with_info& pt_with_info) { return pt_with_info.first; };
for(std::size_t i = 0; i < m_ccb_start_indices.size(); ++i) {
auto begin = m_points.begin() + m_ccb_start_indices[i];
auto end = m_points.begin() + (i + 1 < m_ccb_start_indices.size() ? m_ccb_start_indices[i + 1] : m_points.size());
auto end = i + 1 < m_ccb_start_indices.size() ? m_points.begin() + m_ccb_start_indices[i + 1] : m_points.end();
{
std::ofstream ofs_index("/Users/shep/codes/aos_2_js_helper/shapes.txt", std::ios::app);
auto& ctx = const_cast<Arr_bounded_render_context&>(m_ctx);
@ -258,12 +270,11 @@ public:
tf.triangles.emplace_back(tri);
}
auto transform_first_of_pair = [](const Point_with_info& pt_with_info) {
return Approx_point(pt_with_info.first.x(), pt_with_info.first.y());
auto transform_first_of_pair = [](const KPoint_with_info& pt_with_info) {
return Approx_point(CGAL::to_double(pt_with_info.first.x()), CGAL::to_double(pt_with_info.first.y()));
};
tf.points = Point_vec(boost::make_transform_iterator(m_points.begin(), transform_first_of_pair),
boost::make_transform_iterator(m_points.end(), transform_first_of_pair));
m_ct.clear();
return tf;
}
@ -286,7 +297,7 @@ public:
private:
const Arr_bounded_render_context& m_ctx;
Ct m_ct;
std::vector<Point_with_info> m_points;
std::vector<KPoint_with_info> m_points;
std::vector<std::size_t> m_ccb_start_indices;
bool m_has_active_constraint = false;
bool m_outer_ccb_processed = false;

View File

@ -129,11 +129,10 @@ private:
auto he = *inner_ccb;
auto inner_face = he->twin()->face();
bool is_degenerate = inner_face == fh;
bool within_bounds = ctx->strictly_contains(inner_face->outer_ccb()->source()->point());
if(is_degenerate || !within_bounds) {
if(inner_face == fh || !ctx->strictly_contains(inner_face->outer_ccb()->source()->point())) {
continue;
}
discover_faces(ctx, inner_face);
}

View File

@ -0,0 +1,77 @@
#ifndef CGAL_DRAW_AOS_ARR_COMPUTE_Y_AT_X_H
#define CGAL_DRAW_AOS_ARR_COMPUTE_Y_AT_X_H
#include "CGAL/Bbox_2.h"
#include "CGAL/Draw_aos/Arr_render_context.h"
#include "CGAL/Draw_aos/helpers.h"
#include <boost/iterator/function_output_iterator.hpp>
namespace CGAL {
/**
* @brief Functor to compute the y-coordinate at a given x-coordinate for an x-monotone curve within a bounding box.
*/
class Arr_compute_y_at_x
{
public:
using Point_2 = Geom_traits::Point_2;
using X_monotone_curve_2 = Geom_traits::X_monotone_curve_2;
using Intersect_2 = Geom_traits::Intersect_2;
using Construct_min_vertex_2 = Geom_traits::Construct_min_vertex_2;
using FT = Geom_traits::FT;
using Approximate_2 = Geom_traits::Approximate_2;
using Is_vertical_2 = Geom_traits::Is_vertical_2;
Arr_compute_y_at_x(const Arr_render_context& ctx)
: m_ctx(ctx)
// TODO: some traits does not have approximate_2_object. we'll need a specialization for them.
, m_approx(ctx.traits.approximate_2_object()) {}
/**
* @brief Computes the y-coordinate at a given x-coordinate for an x-monotone curve trimmed
* to the bounding box.
*
* The bounding box here is considered as closed.
*
* @precondition The curve is not verical
* @param curve
* @param x
* @return true if there is an intersection at given x within the bounding box,
* @return false otherwise.
*/
std::optional<FT> operator()(const X_monotone_curve_2& curve, const FT& x) const {
CGAL_assertion(!m_ctx.is_vertical_2(curve));
auto min_pt = m_ctx.cst_curve_end(curve, ARR_MIN_END);
auto max_pt = m_ctx.cst_curve_end(curve, ARR_MAX_END);
if(min_pt.has_value() && min_pt->x() == x) {
return min_pt->y();
}
if(max_pt.has_value() && max_pt->x() == x) {
return max_pt->y();
}
using Multiplicity = Geom_traits::Multiplicity;
using Intersect_point = std::pair<Point_2, Multiplicity>;
using Intersect_curve = X_monotone_curve_2;
using Intersect_type = std::variant<Intersect_point, Intersect_curve>;
auto vertical_line = m_ctx.cst_vertical_segment(x, m_ymin, m_ymax);
std::optional<FT> y;
auto func_out_iter = boost::make_function_output_iterator(
[&y, this](const Intersect_type& res) { y = std::get<Intersect_point>(res).first.y(); });
m_ctx.intersect_2(curve, vertical_line, func_out_iter);
return y;
}
private:
Approximate_2 m_approx;
const Arr_render_context& m_ctx;
// Should be enough for visualization purposes.
constexpr static double m_ymin = std::numeric_limits<double>::lowest();
constexpr static double m_ymax = std::numeric_limits<double>::max();
};
} // namespace CGAL
#endif // CGAL_DRAW_AOS_ARR_COMPUTE_Y_AT_X_H

View File

@ -59,6 +59,14 @@ protected:
: m_bbox(bbox) {}
public:
enum class Side_of_boundary {
Left,
Right,
Bottom,
Top,
None,
};
double xmin() const { return m_bbox.xmin(); }
double xmax() const { return m_bbox.xmax(); }
double ymin() const { return m_bbox.ymin(); }
@ -102,6 +110,20 @@ public:
(pt.y() == ymin() || pt.y() == ymax()) && contains_x(pt.x());
}
template <typename Point>
Side_of_boundary shared_boundary_side(const Point& pt1, const Point& pt2) const {
if(pt1.x() == xmin() && pt2.x() == xmin() && contains_y(pt1.y()) && contains_y(pt2.y())) {
return Side_of_boundary::Left;
} else if(pt1.x() == xmax() && pt2.x() == xmax() && contains_y(pt1.y()) && contains_y(pt2.y())) {
return Side_of_boundary::Right;
} else if(pt1.y() == ymin() && pt2.y() == ymin() && contains_x(pt1.x()) && contains_x(pt2.x())) {
return Side_of_boundary::Bottom;
} else if(pt1.y() == ymax() && pt2.y() == ymax() && contains_x(pt1.x()) && contains_x(pt2.x())) {
return Side_of_boundary::Top;
}
return Side_of_boundary::None;
}
private:
const Bbox_2 m_bbox;
};

View File

@ -5,12 +5,14 @@
#include <array>
#include <boost/iterator/function_output_iterator.hpp>
#include "CGAL/Arr_trapezoid_ric_point_location.h"
#include "CGAL/Arrangement_on_surface_2.h"
#include "CGAL/Bbox_2.h"
#include "CGAL/Draw_aos/Arr_bounded_renderer.h"
#include "CGAL/Draw_aos/Arr_render_context.h"
#include "CGAL/Graphics_scene.h"
#include "CGAL/Graphics_scene_options.h"
#include "CGAL/Qt/camera.h"
#include "CGAL/unordered_flat_map.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QtOpenGLWidgets/QtOpenGLWidgets>
@ -20,6 +22,10 @@
#include <CGAL/Basic_viewer.h>
#include <cstddef>
#include <boost/range/iterator_range.hpp>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
namespace CGAL {
@ -31,6 +37,7 @@ class Arr_viewer : public Qt::Basic_viewer
using Face_const_handle = Arrangement::Face_const_handle;
using Graphics_scene_options =
Graphics_scene_options<Arrangement, Vertex_const_handle, Halfedge_const_handle, Face_const_handle>;
using Point_location = Arr_trapezoid_ric_point_location<Arrangement>;
private:
// Function to check if the camera's state has changed
@ -48,17 +55,17 @@ private:
return true;
}
Bbox_2 initial_bbox() const {
Bbox_2 bbox;
for(const auto& vh : m_arr.vertex_handles()) {
bbox += vh->point().bbox();
}
if(bbox.x_span() == 0 || bbox.y_span() == 0) {
// make a default bbox around the degenrate rect
bbox = Bbox_2(bbox.xmin() - 1, bbox.ymin() - 1, bbox.xmax() + 1, bbox.ymax() + 1);
}
return bbox;
}
// Bbox_2 initial_bbox() const {
// Bbox_2 bbox;
// for(const auto& vh : m_arr.vertex_handles()) {
// bbox += vh->point().bbox();
// }
// if(bbox.x_span() == 0 || bbox.y_span() == 0) {
// // make a default bbox around the degenrate rect
// bbox = Bbox_2(bbox.xmin() - 1, bbox.ymin() - 1, bbox.xmax() + 1, bbox.ymax() + 1);
// }
// return bbox;
// }
/**
* @brief Computes the bounding box of the view from orthogonal camera.
@ -101,7 +108,9 @@ private:
std::array<GLint, 4> viewport;
camera_->getViewport(viewport.data());
double width = static_cast<double>(viewport[2]);
return 0.5;
// return bbox.x_span() / std::min(600.0, width);
// We are testing linear traits, lets set it to inf
return 10000;
}
public:
@ -111,26 +120,22 @@ public:
const char* title = "Arrangement Viewer")
: Basic_viewer(parent, m_scene, title)
, m_scene_options(options)
, m_pl(arr)
, m_arr(arr) {}
, m_arr(arr)
, m_pl(arr) {}
void rerender(Bbox_2 bbox) {
Arr_render_context ctx(m_arr, m_pl, get_approx_error(bbox));
void render_arr(const Arrangement& arr, const Point_location& pl, const Bbox_2& bbox) {
Arr_render_context ctx(arr, pl, get_approx_error(bbox));
Arr_bounded_renderer renderer(ctx, bbox);
const auto& cache = renderer.render();
m_scene.clear();
int counter = 0;
// add faces
for(const auto& [fh, face_tris] : cache.face_cache()) {
const auto& points = face_tris.points;
const auto& tris = face_tris.triangles;
bool draw_face = m_scene_options.colored_face(m_arr, fh);
bool draw_face = m_scene_options.colored_face(arr, fh);
for(const auto& t : tris) {
if(draw_face) {
m_scene.face_begin(m_scene_options.face_color(m_arr, fh));
m_scene.face_begin(m_scene_options.face_color(arr, fh));
} else {
m_scene.face_begin();
}
@ -142,15 +147,13 @@ public:
}
// add edges
counter = 0;
for(const auto& [he, polyline] : cache.halfedge_cache()) {
if(polyline.size() < 2) {
continue; // skip degenerate edges
}
// ofs_index << "polyline_" << counter << ".txt" << std::endl;
// std::ofstream ofs("/Users/shep/codes/aos_2_js_helper/polyline_" + std::to_string(counter++) + ".txt");
bool draw_colored_edge = m_scene_options.colored_edge(m_arr, he);
auto color = draw_colored_edge ? m_scene_options.edge_color(m_arr, he) : CGAL::IO::Color();
bool draw_colored_edge = m_scene_options.colored_edge(arr, he);
auto color = draw_colored_edge ? m_scene_options.edge_color(arr, he) : CGAL::IO::Color();
for(size_t i = 0; i < polyline.size() - 1; ++i) {
const auto& cur_pt = polyline[i];
const auto& next_pt = polyline[i + 1];
@ -160,25 +163,26 @@ public:
{
continue;
}
// ofs << cur_pt << "\n";
if(draw_colored_edge) {
m_scene.add_segment(cur_pt, next_pt, color);
} else {
m_scene.add_segment(cur_pt, next_pt);
}
}
// ofs << polyline.back() << "\n";
// ofs << std::endl;
}
// add vertices
for(const auto& [vh, pt] : cache.vertex_cache()) {
// std::cout << pt << std::endl;
if(m_scene_options.colored_vertex(m_arr, vh)) {
m_scene.add_point(pt, m_scene_options.vertex_color(m_arr, vh));
if(m_scene_options.colored_vertex(arr, vh)) {
m_scene.add_point(pt, m_scene_options.vertex_color(arr, vh));
} else {
m_scene.add_point(pt);
}
}
}
void rerender(Bbox_2 bbox) {
m_scene.clear();
render_arr(m_arr, m_pl, bbox);
Basic_viewer::redraw();
}
@ -200,8 +204,8 @@ public:
private:
Graphics_scene m_scene;
Graphics_scene_options m_scene_options;
const Arrangement& m_arr;
Arr_trapezoid_ric_point_location<Arrangement> m_pl;
Arrangement m_arr;
Point_location m_pl;
QMatrix4x4 m_last_proj_matrix;
QMatrix4x4 m_last_modelview_matrix;
};
@ -225,7 +229,9 @@ void draw_viewer(const Arrangement& arr) {
CGAL::Random random((size_t(&*vh)));
return get_random_color(random);
};
// Arr_viewer viewer(app.activeWindow(), move_degenerate_features(arr), gso, "Arrangement Viewer");
Arr_viewer viewer(app.activeWindow(), arr, gso, "Arrangement Viewer");
std::cout << "Preprocess complete" << std::endl;
viewer.show();
app.exec();
}

View File

@ -6,7 +6,7 @@
namespace CGAL {
using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Geom_traits = Arr_linear_traits_2<Exact_kernel>;
// using Geom_traits = Arr_segment_traits_2<Exact_kernel>;
using Geom_traits = CGAL::Arr_linear_traits_2<Exact_kernel>;
using Arrangement = Arrangement_2<Geom_traits>;

View File

@ -1,11 +0,0 @@
#ifndef APPROX_Y_AT_X_H
#define APPROX_Y_AT_X_H
#include <CGAL/Arr_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Segment_traits_2 = CGAL::Arr_segment_traits_2<Exact_kernel>;
using Arr = CGAL::Arrangement_2<Segment_traits_2>;
double approx_y_at_x(const Arr::Traits_2& traits, const Arr::X_monotone_curve_2& curve, double x) {}
#endif // APPROX_Y_AT_X_H

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
0.200004 -1.24026
3.73824 -1.24026
3.73824 1.94415
0.200004 1.94415

View File

@ -1,122 +0,0 @@
#ifndef _PRINT_ARR_H_
#define _PRINT_ARR_H_
#include <CGAL/Arrangement_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <iostream>
//-----------------------------------------------------------------------------
// Print all neighboring vertices to a given arrangement vertex.
//
template <class Arrangement>
void print_neighboring_vertices(typename Arrangement::Vertex_const_handle v) {
if(v->is_isolated()) {
std::cout << "The vertex (" << v->point() << ") is isolated\n";
return;
}
std::cout << "The neighbors of the vertex (" << v->point() << ") are:";
typename Arrangement::Halfedge_around_vertex_const_circulator first, curr;
first = curr = v->incident_halfedges();
do
std::cout << " (" << curr->source()->point() << ")";
while(++curr != first);
std::cout << std::endl;
}
//-----------------------------------------------------------------------------
// Print all vertices (points) and edges (curves) along a connected component
// boundary.
//
template <typename Arrangement>
void print_ccb(typename Arrangement::Ccb_halfedge_const_circulator circ) {
std::cout << "(" << circ->source()->point() << ")";
typename Arrangement::Ccb_halfedge_const_circulator curr = circ;
do {
typename Arrangement::Halfedge_const_handle e = curr;
std::cout << " [" << e->curve() << "] "
<< "(" << e->target()->point() << ")";
} while(++curr != circ);
std::cout << std::endl;
}
//-----------------------------------------------------------------------------
// Print the boundary description of an arrangement face.
//
template <typename Arrangement>
void print_face(typename Arrangement::Face_const_handle f) {
// Print the outer boundary.
if(f->is_unbounded()) {
std::cout << "Unbounded face.\n";
} else {
std::cout << "Outer boundary: ";
print_ccb<Arrangement>(f->outer_ccb());
}
// Print the boundary of each of the holes.
size_t index = 1;
for(auto hole = f->holes_begin(); hole != f->holes_end(); ++hole, ++index) {
std::cout << " Hole #" << index << ": ";
// The following statement pacifies msvc.
typename Arrangement::Ccb_halfedge_const_circulator circ = *hole;
print_ccb<Arrangement>(circ);
}
// Print the isolated vertices.
index = 1;
for(auto iv = f->isolated_vertices_begin(); iv != f->isolated_vertices_end(); ++iv, ++index) {
std::cout << " Isolated vertex #" << index << ": "
<< "(" << iv->point() << ")" << std::endl;
}
}
//-----------------------------------------------------------------------------
// Print the given arrangement.
//
template <typename Arrangement>
void print_arrangement(const Arrangement& arr) {
CGAL_precondition(arr.is_valid());
// Print the arrangement vertices.
std::cout << arr.number_of_vertices() << " vertices:\n";
for(auto vit = arr.vertices_begin(); vit != arr.vertices_end(); ++vit) {
std::cout << "(" << vit->point() << ")";
if(vit->is_isolated())
std::cout << " - Isolated.\n";
else
std::cout << " - degree " << vit->degree() << std::endl;
}
// Print the arrangement edges.
std::cout << arr.number_of_edges() << " edges:\n";
for(auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit)
std::cout << "[" << eit->curve() << "]\n";
// Print the arrangement faces.
std::cout << arr.number_of_faces() << " faces:\n";
for(auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit)
print_face<Arrangement>(fit);
}
//-----------------------------------------------------------------------------
// Print the size of the given arrangement.
//
template <typename Arrangement>
void print_arrangement_size(const Arrangement& arr) {
std::cout << "The arrangement size:\n"
<< " |V| = " << arr.number_of_vertices() << ", |E| = " << arr.number_of_edges()
<< ", |F| = " << arr.number_of_faces() << std::endl;
}
//-----------------------------------------------------------------------------
// Print the size of the given unbounded arrangement.
//
template <typename Arrangement>
void print_unbounded_arrangement_size(const Arrangement& arr) {
std::cout << "The arrangement size:\n"
<< " |V| = " << arr.number_of_vertices() << " (plus " << arr.number_of_vertices_at_infinity()
<< " at infinity)"
<< ", |E| = " << arr.number_of_edges() << ", |F| = " << arr.number_of_faces() << " ("
<< arr.number_of_unbounded_faces() << " unbounded)\n\n";
}
#endif

View File

@ -1,194 +0,0 @@
#include "CGAL/Bbox_2.h"
#include "CGAL/Draw_aos/Arr_approximation_geometry_traits.h"
#include <CGAL/Draw_aos/Arr_bounded_approximate_face_2.h>
#include <cstddef>
#include <iterator>
#include <vector>
using Approx_point = CGAL::Arr_approximation_geometry_traits::Approx_point;
struct Test_case
{
std::vector<Approx_point> points_to_insert;
std::vector<Approx_point> expected_points;
CGAL::Bbox_2 bbox;
Test_case(std::vector<Approx_point> points, std::vector<Approx_point> expected, CGAL::Bbox_2 b)
: points_to_insert(std::move(points))
, expected_points(std::move(expected))
, bbox(b) {}
};
void test_geom_fill_corners_output_iterator(const Test_case& test_case) {
std::vector<Approx_point> points{};
auto inserter = std::back_inserter(points);
CGAL::Geom_fill_corners_output_iterator<std::back_insert_iterator<decltype(points)>> geom_inserter(
inserter, CGAL::Bbox_2(test_case.bbox));
for(const auto& pt : test_case.points_to_insert) {
geom_inserter++ = pt;
}
if(points != test_case.expected_points) {
std::cerr << "Test failed for bbox: " << test_case.bbox << std::endl;
std::cerr << "Points inserted: \n";
for(const auto& pt : test_case.points_to_insert) {
std::cerr << pt << "\n";
}
std::cerr << "\nExpected: \n";
for(const auto& pt : test_case.expected_points) {
std::cerr << pt << "\n";
}
std::cerr << "\nGot:\n";
for(const auto& pt : points) {
std::cerr << pt << "\n";
}
std::cerr << std::endl;
assert(false);
}
}
int main() {
CGAL::Bbox_2 bbox(0, 0, 10, 10);
std::vector<Test_case> cases{
/**
* --------------------
* | |
* | |
* | |
* | |
* | |
* | |
* 1-------------------
*/
Test_case({Approx_point(0, 0)}, {Approx_point(0, 0)}, bbox),
/**
* 2-------------------
* | |
* | |
* | |
* | |
* | |
* | |
* 1-------------------
*
* Expected:
*
* 4------------------3
* | |
* | |
* | |
* | |
* | |
* | |
* 1------------------2
*/
Test_case({Approx_point(0, 0), Approx_point(0, 10)},
{Approx_point(0, 0), Approx_point(10, 0), Approx_point(10, 10), Approx_point(0, 10)}, bbox),
/**
* --------------------
* | |
* | |
* | |
* | |
* | |
* | |
* 1------------------2
*
* Expected:
*
* 2-------------------
* | |
* | |
* | |
* | |
* | |
* | |
* 1------------------2
*/
Test_case({Approx_point(0, 0), Approx_point(10, 0)}, {Approx_point(0, 0), Approx_point(10, 0)}, bbox),
/**
* --------------------
* | |
* | |
* | |
* | |
* | |
* | |
* 1---------2---------
*
* Expected:
*
* --------------------
* | |
* | |
* 2 |
* | |
* | |
* | |
* 1-------------------
*/
Test_case({Approx_point(0, 0), Approx_point(5, 0)}, {Approx_point(0, 0), Approx_point(5, 0)}, bbox),
/**
* --------------------
* | |
* | |
* | 1
* | |
* | 2
* | |
* --------------------
*
* Expected:
*
* 3------------------2
* | |
* | |
* | 1
* | |
* | 6
* | |
* 4------------------5
*/
Test_case({Approx_point(10, 5), Approx_point(10, 3)},
{Approx_point(10, 5), Approx_point(10, 10), Approx_point(0, 10), Approx_point(0, 0),
Approx_point(10, 0), Approx_point(10, 3)},
bbox),
/**
* -------------------1
* | |
* | |
* | |
* | |
* | 2
* | |
* -------------------|
*
* Expected:
*
* 2------------------1
* | |
* | |
* | |
* | |
* | 5
* | |
* 3------------------4
*/
Test_case({Approx_point(10, 10), Approx_point(10, 3)},
{
Approx_point(10, 10),
Approx_point(0, 10),
Approx_point(0, 0),
Approx_point(10, 0),
Approx_point(10, 3),
},
bbox),
};
for(size_t i = 0; i < cases.size(); ++i) {
const auto& test_case = cases[i];
test_geom_fill_corners_output_iterator(test_case);
}
}

View File

@ -18,6 +18,7 @@
#include "CGAL/Exact_predicates_exact_constructions_kernel.h"
#include "CGAL/Exact_predicates_inexact_constructions_kernel.h"
#include <CGAL/draw_arrangement_2.h>
#include <vector>
// void draw_segments_arr_1() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
@ -111,26 +112,120 @@
// // CGAL::draw_viewer(arr);
// }
void draw_linear_arr_1() {
using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Traits = CGAL::Arr_linear_traits_2<Exact_kernel>;
using Point_2 = Traits::Point_2;
using Line_2 = Traits::Line_2;
using Ray_2 = Traits::Ray_2;
using Curve_2 = Traits::Curve_2;
using Arrangement = CGAL::Arrangement_2<Traits>;
using Face_const_handle = Arrangement::Face_const_handle;
using Halfedge_const_handle = Arrangement::Halfedge_const_iterator;
using X_monotone_curve_2 = Traits::X_monotone_curve_2;
// void draw_segments_arr_4() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Segment_traits = CGAL::Arr_segment_traits_2<Exact_kernel>;
// using Point_2 = Segment_traits::Point_2;
// using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2;
// using Arrangement = CGAL::Arrangement_2<Segment_traits>;
// Arrangement arr;
// auto traits = arr.traits();
// auto cst_x_curve = traits->construct_x_monotone_curve_2_object();
Arrangement arr;
auto x_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(1, 0)));
auto y_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(0, 1)));
CGAL::insert(arr, x_axis);
CGAL::insert(arr, y_axis);
// std::vector<X_monotone_curve_2> segments;
CGAL::draw_viewer(arr);
}
// std::vector<Point_2> polyline1{
// {6, -21}, {6, -3}, {13, -3}, {13, -12}, {18, -12}, {18, -4}, {18, -3}, {25, -3}, {25, -21}, {6, -21},
// };
// for(size_t i = 0; i < polyline1.size() - 1; ++i) {
// segments.push_back(cst_x_curve(polyline1[i], polyline1[i + 1]));
// }
// std::vector<Point_2> polyline2{
// {-27, -14}, {-24, -14}, {-21, -16}, {-19, -18}, {-18, -21}, {-17, -24}, {-18, -28}, {-19, -30}, {-22, -32},
// {-24, -33}, {-29, -33}, {-34, -33}, {-38, -32}, {-43, -30}, {-46, -29}, {-50, -25}, {-53, -21}, {-53, -17},
// {-54, -12}, {-53, -7}, {-52, -2}, {-50, 2}, {-45, 5}, {-40, 6}, {-34, 7}, {-29, 7}, {-23, 7},
// {-20, 6}, {-18, 5}, {-16, 2}, {-15, 0}, {-16, -3}, {-17, -3}, {-18, -1}, {-19, 1}, {-20, 4},
// {-22, 4}, {-26, 4}, {-28, 4}, {-31, 4}, {-33, 4}, {-37, 4}, {-41, 3}, {-44, 2}, {-47, 1},
// {-48, 0}, {-49, -2}, {-50, -5}, {-51, -8}, {-51, -13}, {-51, -16}, {-50, -18}, {-49, -22}, {-46, -24},
// {-42, -27}, {-40, -29}, {-36, -29}, {-32, -30}, {-28, -31}, {-24, -31}, {-21, -30}, {-19, -28}, {-19, -25},
// {-20, -23}, {-21, -21}, {-24, -18}, {-26, -16}, {-27, -15}, {-27, -14},
// };
// for(size_t i = 0; i < polyline2.size() - 1; ++i) {
// segments.push_back(cst_x_curve(polyline2[i], polyline2[i + 1]));
// }
// CGAL::insert(arr, segments.begin(), segments.end());
// CGAL::draw_viewer(arr);
// }
// void draw_segments_arr_5() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Segment_traits = CGAL::Arr_segment_traits_2<Exact_kernel>;
// using Point_2 = Segment_traits::Point_2;
// using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2;
// using Arrangement = CGAL::Arrangement_2<Segment_traits>;
// Arrangement arr;
// auto traits = arr.traits();
// auto cst_x_curve = traits->construct_x_monotone_curve_2_object();
// std::vector<X_monotone_curve_2> segments;
// std::vector<Point_2> polyline1{
// {11, 23}, {12, -13}, {17, -13}, {18, 1}, {18, 16}, {18, 23}, {15, 35}, {15, 44}, {18, 47}, {19, 42},
// {19, 37}, {20, 28}, {23, 21}, {23, 9}, {24, -7}, {22, -15}, {20, -19}, {15, -19}, {10, -16}, {7, -14},
// {7, -3}, {5, 11}, {1, 20}, {0, 35}, {1, 45}, {3, 51}, {5, 53}, {11, 53}, {19, 53}, {23, 49},
// {25, 40}, {28, 31}, {34, 16}, {35, 7}, {38, -11}, {44, -11}, {43, -7}, {43, -5}, {42, 7}, {40, 23},
// {38, 30}, {35, 49}, {19, 57}, {8, 58}, {-2, 55}, {-2, 30}, {-3, 15}, {-3, -16}, {1, -20}, {12, -22},
// {20, -22}, {25, -21}, {26, -14}, {28, -3}, {27, 15}, {23, 31}, {22, 44}, {20, 49}, {15, 49}, {10, 46},
// {8, 39}, {8, 32}, {8, 28}, {11, 23},
// };
// for(size_t i = 0; i < polyline1.size() - 1; ++i) {
// segments.push_back(cst_x_curve(polyline1[i], polyline1[i + 1]));
// }
// CGAL::insert(arr, segments.begin(), segments.end());
// CGAL::draw_viewer(arr);
// }
// void draw_segments_arr_6() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Segment_traits = CGAL::Arr_segment_traits_2<Exact_kernel>;
// using Point_2 = Segment_traits::Point_2;
// using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2;
// using Arrangement = CGAL::Arrangement_2<Segment_traits>;
// Arrangement arr;
// auto traits = arr.traits();
// auto cst_x_curve = traits->construct_x_monotone_curve_2_object();
// std::vector<X_monotone_curve_2> segments;
// std::vector<Point_2> polyline1{
// {9, -3}, {11, -34}, {26, -34}, {26, -8}, {26, -34}, {36, -34}, {36, -31}, {36, -27}, {37, -23}, {41, -23},
// {47, -23}, {49, -16}, {49, -2}, {42, 10}, {41, 0}, {41, -19}, {41, 0}, {42, 10}, {30, 10}, {28, 6},
// {28, 2}, {29, -30}, {28, 2}, {28, 6}, {30, 10}, {42, 10}, {29, 16}, {18, 12}, {9, -3},
// };
// for(size_t i = 0; i < polyline1.size() - 1; ++i) {
// segments.push_back(cst_x_curve(polyline1[i], polyline1[i + 1]));
// }
// CGAL::insert(arr, segments.begin(), segments.end());
// CGAL::draw_viewer(arr);
// }
// void draw_linear_arr_1() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Traits = CGAL::Arr_linear_traits_2<Exact_kernel>;
// using Point_2 = Traits::Point_2;
// using Line_2 = Traits::Line_2;
// using Ray_2 = Traits::Ray_2;
// using Curve_2 = Traits::Curve_2;
// using Arrangement = CGAL::Arrangement_2<Traits>;
// using Face_const_handle = Arrangement::Face_const_handle;
// using Halfedge_const_handle = Arrangement::Halfedge_const_iterator;
// using X_monotone_curve_2 = Traits::X_monotone_curve_2;
// Arrangement arr;
// auto x_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(1, 0)));
// auto y_axis = X_monotone_curve_2(Ray_2(Point_2(0, 0), Point_2(0, 1)));
// CGAL::insert(arr, x_axis);
// CGAL::insert(arr, y_axis);
// CGAL::draw_viewer(arr);
// }
void draw_linear_arr_2() {
using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
@ -148,17 +243,17 @@ void draw_linear_arr_2() {
Arrangement arr;
auto& traits = *arr.traits();
// Insert a n*n grid, each cell is a square of size 5
int n = 1;
// for(int i = 0; i < n; ++i) {
// Point_2 p1(i * 5, 0);
// Point_2 p2(i * 5, 1);
// CGAL::insert(arr, Curve_2(Line_2(p1, p2)));
// }
// for(int i = 0; i < n; ++i) {
// Point_2 p1(0, i * 5);
// Point_2 p2(1, i * 5);
// CGAL::insert(arr, Curve_2(Line_2(p1, p2)));
// }
int n = 5;
for(int i = 0; i < n; ++i) {
Point_2 p1(i * 5, 0);
Point_2 p2(i * 5, 1);
CGAL::insert(arr, Curve_2(Line_2(p1, p2)));
}
for(int i = 0; i < n; ++i) {
Point_2 p1(0, i * 5);
Point_2 p2(1, i * 5);
CGAL::insert(arr, Curve_2(Line_2(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) {
@ -183,12 +278,49 @@ void draw_linear_arr_2() {
CGAL::insert(arr, Curve_2(Segment_2(tri_p1, top)));
}
}
auto arr2 = arr;
std::cout << "Arrangement has " << arr.number_of_faces() << " faces." << std::endl;
CGAL::draw_viewer(arr);
}
// void draw_linear_arr_3() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
// using Traits = CGAL::Arr_linear_traits_2<Exact_kernel>;
// using Point_2 = Traits::Point_2;
// using Line_2 = Traits::Line_2;
// using Segment_2 = Traits::Segment_2;
// using Ray_2 = Traits::Ray_2;
// using Curve_2 = Traits::Curve_2;
// using Arrangement = CGAL::Arrangement_2<Traits>;
// using Face_const_handle = Arrangement::Face_const_handle;
// using Halfedge_const_handle = Arrangement::Halfedge_const_iterator;
// using X_monotone_curve_2 = Traits::X_monotone_curve_2;
// std::vector<Point_2> points{
// {11, 23}, {12, -13}, {17, -13}, {18, 1}, {18, 16}, {18, 23}, {15, 35}, {15, 44}, {18, 47}, {19, 42},
// {19, 37}, {20, 28}, {23, 21}, {23, 9}, {24, -7}, {22, -15}, {20, -19}, {15, -19}, {10, -16}, {7, -14},
// {7, -3}, {5, 11}, {1, 20}, {0, 35}, {1, 45}, {3, 51}, {5, 53}, {11, 53}, {19, 53}, {23, 49},
// {25, 40}, {28, 31}, {34, 16}, {35, 7}, {38, -11}, {44, -11}, {43, -7}, {43, -5}, {42, 7}, {40, 23},
// {38, 30}, {35, 49}, {19, 57}, {8, 58}, {-2, 55}, {-2, 30}, {-3, 15}, {-3, -16}, {1, -20}, {12, -22},
// {20, -22}, {25, -21}, {26, -14}, {28, -3}, {27, 15}, {23, 31}, {22, 44}, {20, 49}, {15, 49}, {10, 46},
// {8, 39}, {8, 32}, {8, 28}, {11, 23},
// };
// Arrangement arr;
// auto& traits = *arr.traits();
// std::vector<X_monotone_curve_2> segments;
// for(size_t i = 0; i < points.size() - 1; ++i) {
// Point_2 p1 = points[i];
// Point_2 p2 = points[i + 1];
// // create a segment
// X_monotone_curve_2 seg = traits.construct_x_monotone_curve_2_object()(p1, p2);
// segments.push_back(seg);
// }
// // insert segments into the arrangement
// CGAL::insert(arr, segments.begin(), segments.end());
// CGAL::draw_viewer(arr);
// }
// // supports segments
// void draw_circle_segs_arr() {
// using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
@ -311,7 +443,7 @@ using Point = Kernel::Point_2;
// }
int main() {
// draw_segments_arr_2();
// draw_segments_arr_6();
draw_linear_arr_2();
// test_zone();
// draw_conic_arcs_arr();

View File

@ -52,11 +52,30 @@ int main() {
using Point = K::Point_2;
Cdt cdt;
std::vector<Point> outer = {Point(0, 0), Point(5, 0), Point(5, 5), Point(0, 5), Point(0, 0)};
std::vector<Point> outer = {{4, 1}, {4, 4}, {2.5, 4}, {2.32026, 3.28104}, {2.32026, 2.64052},
{2.5, 3}, {3, 2}, {2.32026, 2}, {2.32026, 3.28104}, {2.5, 4},
{2.32026, 4}, {2.32026, 1}, {4, 1}};
std::vector<Point> inner = {Point(0, 0), Point(0.5, 0), Point(0.5, 0.5), Point(0.5, 0),
Point(1, 0), Point(1, 1), Point(0, 1)};
std::vector<Point> outer_constraint = {Point(0, 0), Point(-1, -1), Point(5, 0), Point(6, -1),
Point(5, 5), Point(6, 6), Point(0, 5), Point(-1, 6)};
std::vector<Point> outer_constraint = {{4, 1},
{4, 4},
{2.5, 4},
{2.32026, 3.28104},
{1.32026, 3.28104},
{1.32026, 2.64052},
{2.32026, 2.64052},
{2.5, 3},
{3, 2},
{2.32026, 2},
{1.32026, 2},
{1.32026, 3.28104},
{2.32026, 3.28104},
{2.5, 4},
{2.32026, 4},
{1.32026, 4},
{1.32026, 1},
{2.32026, 1},
{4, 1}};
auto add_info = [](const Point& p) { return std::make_pair(p, 1); };

View File

@ -1,71 +0,0 @@
#ifndef ZONE_EXP_H
#define ZONE_EXP_H
#include "CGAL/Arr_trapezoid_ric_point_location.h"
#include <CGAL/Arr_algebraic_segment_traits_2.h>
#include <CGAL/Arr_linear_traits_2.h>
#include <CGAL/Arr_segment_traits_2.h>
#include <CGAL/Arrangement_2.h>
#include <CGAL/Arrangement_2/Arr_compute_zone_visitor.h>
#include <CGAL/Arrangement_zone_2.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/draw_arrangement_2.h>
void test_zone() {
using Exact_kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Segment_traits = CGAL::Arr_segment_traits_2<Exact_kernel>;
using Point_2 = Segment_traits::Point_2;
using Arrangement = CGAL::Arrangement_2<Segment_traits>;
using X_monotone_curve_2 = Segment_traits::X_monotone_curve_2;
using Traits = CGAL::Arr_linear_traits_2<Exact_kernel>;
using Face_const_handle = Arrangement::Face_const_handle;
using Halfedge_const_handle = Arrangement::Halfedge_const_handle;
using Vertex_const_handle = Arrangement::Vertex_const_handle;
using Face_handle = Arrangement::Face_handle;
using Halfedge_handle = Arrangement::Halfedge_handle;
using Vertex_handle = Arrangement::Vertex_handle;
using Halfedge_const_handle = Arrangement::Halfedge_const_handle;
Arrangement arr;
auto traits = arr.traits();
auto cst_x_curve = traits->construct_x_monotone_curve_2_object();
auto tri = {
cst_x_curve({-1, 0}, {1, 0}),
cst_x_curve({1, 0}, {0, 3}),
cst_x_curve({0, 3}, {-1, 0}),
};
CGAL::insert(arr, tri.begin(), tri.end());
// point location
auto pl = CGAL::Arr_trapezoid_ric_point_location<Arrangement>(arr);
// test zone construction
using Feature = std::variant<Vertex_handle, Halfedge_handle, Face_handle>;
using Feature_vector = std::vector<Feature>;
using Feature_vector_out_iter = std::back_insert_iterator<Feature_vector>;
using Zone_visitor = CGAL::Arr_compute_zone_visitor<Arrangement, Feature_vector_out_iter>;
Feature_vector zone_features;
Feature_vector_out_iter out_iter(zone_features);
Zone_visitor zone_visitor(out_iter);
auto cv = cst_x_curve({-1, 0}, {1, 0});
CGAL::Arrangement_zone_2<Arrangement, Zone_visitor> zone(arr, &zone_visitor);
zone.init(cv, pl);
zone.compute_zone();
for(const auto& feature : zone_features) {
if(auto* hf = std::get_if<Halfedge_handle>(&feature)) {
std::cout << "Halfedge: " << (*hf)->curve() << std::endl;
Halfedge_const_handle che(*hf);
std::cout << " const curve" << che->curve() << std::endl;
} else if(auto* vh = std::get_if<Vertex_handle>(&feature)) {
std::cout << "Vertex: " << (*vh)->point() << std::endl;
} else if(auto* fh = std::get_if<Face_handle>(&feature)) {
std::cout << "Face: " << std::endl;
}
}
CGAL::draw(arr);
}
#endif // ZONE_EXP_H

View File

@ -13,8 +13,8 @@
#ifndef CGAL_BASIC_VIEWER_H
#define CGAL_BASIC_VIEWER_H
#include <CGAL/Graphics_scene.h>
#include <CGAL/license/GraphicsView.h>
#include <CGAL/Graphics_scene.h>
// compatibility
#if defined(CGAL_USE_BASIC_VIEWER) && !defined(CGAL_USE_BASIC_VIEWER_QT)
@ -30,10 +30,14 @@
// #elif defined(CGAL_USE_BASIC_VIEWER_GLFW)
// #include <CGAL/GLFW/Basic_viewer.h>
#else
namespace CGAL {
inline void draw_graphics_scene(const Graphics_scene&, const char* = "CGAL Basic Viewer") {
std::cerr << "Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined." << std::endl;
}
namespace CGAL
{
inline
void draw_graphics_scene(const Graphics_scene&,
const char* ="CGAL Basic Viewer")
{
std::cerr<<"Impossible to draw, CGAL_USE_BASIC_VIEWER is not defined."<<std::endl;
}
} // End namespace CGAL
#endif

File diff suppressed because it is too large Load Diff