mirror of https://github.com/CGAL/cgal
approximated offset fixes
This commit is contained in:
parent
6723c8d7c2
commit
c4e38a9096
|
|
@ -523,3 +523,12 @@ $P$ and computed its offset. This ofset is obtain by taking the outer offset
|
||||||
of $P$'s outer boundary, and computing the inner offsets of $P$'s holes.
|
of $P$'s outer boundary, and computing the inner offsets of $P$'s holes.
|
||||||
The former polygon defines the output boundary of $P \oplus B_r$, and the latter
|
The former polygon defines the output boundary of $P \oplus B_r$, and the latter
|
||||||
define the holes within the result.
|
define the holes within the result.
|
||||||
|
|
||||||
|
\section*{Acknowledgements}
|
||||||
|
%==========================
|
||||||
|
|
||||||
|
Andreas Fabri and Laurent Rineau helped tracing and solving several bugs in
|
||||||
|
the approximated offset function. They have also suggested a few algorithmic
|
||||||
|
improvements that made their way into version 3.4, yielding a faster approximation
|
||||||
|
scheme.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2006 Tel-Aviv University (Israel).
|
// Copyright (c) 2006-2008 Tel-Aviv University (Israel).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// This file is part of CGAL (www.cgal.org); you may redistribute it under
|
// This file is part of CGAL (www.cgal.org); you may redistribute it under
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
// $Id$
|
// $Id$
|
||||||
//
|
//
|
||||||
// Author(s) : Ron Wein <wein@post.tau.ac.il>
|
// Author(s) : Ron Wein <wein@post.tau.ac.il>
|
||||||
|
// Andreas Fabri <Andreas.Fabri@geometryfactory.com>
|
||||||
|
// Laurent Rineau <Laurent.Rineau@geometryfactory.com>
|
||||||
|
|
||||||
#ifndef CGAL_APPROXIMATED_OFFSET_BASE_H
|
#ifndef CGAL_APPROXIMATED_OFFSET_BASE_H
|
||||||
#define CGAL_APPROXIMATED_OFFSET_BASE_H
|
#define CGAL_APPROXIMATED_OFFSET_BASE_H
|
||||||
|
|
@ -111,11 +113,17 @@ protected:
|
||||||
{
|
{
|
||||||
// Prepare circulators over the polygon vertices.
|
// Prepare circulators over the polygon vertices.
|
||||||
const bool forward = (pgn.orientation() == orient);
|
const bool forward = (pgn.orientation() == orient);
|
||||||
Vertex_circulator first, curr, next;
|
Vertex_circulator first, curr, next, prev;
|
||||||
|
|
||||||
first = pgn.vertices_circulator();
|
first = pgn.vertices_circulator();
|
||||||
curr = first;
|
curr = first;
|
||||||
next = first;
|
next = first;
|
||||||
|
prev = first;
|
||||||
|
|
||||||
|
if (forward)
|
||||||
|
--prev;
|
||||||
|
else
|
||||||
|
++prev;
|
||||||
|
|
||||||
// Traverse the polygon vertices and edges and approximate the arcs that
|
// Traverse the polygon vertices and edges and approximate the arcs that
|
||||||
// constitute the single convolution cycle.
|
// constitute the single convolution cycle.
|
||||||
|
|
@ -149,6 +157,8 @@ protected:
|
||||||
unsigned int curve_index = 0;
|
unsigned int curve_index = 0;
|
||||||
X_monotone_curve_2 seg1, seg2;
|
X_monotone_curve_2 seg1, seg2;
|
||||||
bool dir_right1 = false, dir_right2 = false;
|
bool dir_right1 = false, dir_right2 = false;
|
||||||
|
X_monotone_curve_2 seg_short;
|
||||||
|
bool dir_right_short;
|
||||||
int n_segments;
|
int n_segments;
|
||||||
|
|
||||||
Kernel ker;
|
Kernel ker;
|
||||||
|
|
@ -157,6 +167,7 @@ protected:
|
||||||
typename Kernel::Construct_perpendicular_line_2
|
typename Kernel::Construct_perpendicular_line_2
|
||||||
f_perp_line = ker.construct_perpendicular_line_2_object();
|
f_perp_line = ker.construct_perpendicular_line_2_object();
|
||||||
typename Kernel::Compare_xy_2 f_comp_xy = ker.compare_xy_2_object();
|
typename Kernel::Compare_xy_2 f_comp_xy = ker.compare_xy_2_object();
|
||||||
|
typename Kernel::Orientation_2 f_orient = ker.orientation_2_object();
|
||||||
|
|
||||||
Traits_2 traits;
|
Traits_2 traits;
|
||||||
std::list<Object> xobjs;
|
std::list<Object> xobjs;
|
||||||
|
|
@ -242,28 +253,58 @@ protected:
|
||||||
// Compute the upper bound for the approximation error for d.
|
// Compute the upper bound for the approximation error for d.
|
||||||
// This bound is given by:
|
// This bound is given by:
|
||||||
//
|
//
|
||||||
// | (d - delta_y) |
|
// d - |delta_y|
|
||||||
// bound = 2 * d * eps * | --------------- |
|
// bound = 2 * d * eps * ---------------
|
||||||
// |delta_x|
|
// |delta_x|
|
||||||
//
|
//
|
||||||
|
// As we use floating-point arithmetic, if |delta_x| is small, then
|
||||||
|
// it might be that to_double(|delta_y|) == to_double(d), hence we
|
||||||
|
// have a 0 tolerance in the approximation bound. Luckily, because
|
||||||
|
// of symmetry, we can rotate the scene by pi/2, and swap roles of
|
||||||
|
// x and y. In fact, we do that in order to get a larger approximation
|
||||||
|
// bound if possible.
|
||||||
const double dd = CGAL::sqrt (CGAL::to_double (sqr_d));
|
const double dd = CGAL::sqrt (CGAL::to_double (sqr_d));
|
||||||
const double derr_bound =
|
const double dabs_dx = CGAL::to_double (abs_delta_x);
|
||||||
2 * dd * _eps * ::fabs ((dd - CGAL::to_double (delta_y)) /
|
const double dabs_dy = CGAL::to_double (abs_delta_y);
|
||||||
CGAL::to_double (delta_x));
|
double derr_bound;
|
||||||
|
|
||||||
|
if (dabs_dy < dabs_dx)
|
||||||
|
{
|
||||||
|
derr_bound = 2 * dd * _eps * (dd - dabs_dy) / dabs_dx;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
derr_bound = 2 * dd * _eps * (dd - dabs_dx) / dabs_dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGAL_assertion (derr_bound > 0);
|
||||||
err_bound = NT (derr_bound);
|
err_bound = NT (derr_bound);
|
||||||
|
|
||||||
// Compute an approximation for d (the squared root of sqr_d).
|
// Compute an approximation for d (the squared root of sqr_d).
|
||||||
|
int numer;
|
||||||
int denom = _inv_sqrt_eps;
|
int denom = _inv_sqrt_eps;
|
||||||
const int max_int = (1 << (8*sizeof(int) - 2));
|
const int max_int = (1 << (8*sizeof(int) - 2));
|
||||||
|
|
||||||
while (static_cast<double>(max_int) / denom < dd)
|
numer = static_cast<int> (dd * denom + 0.5);
|
||||||
|
if (numer > 0)
|
||||||
{
|
{
|
||||||
denom /= 2;
|
while (static_cast<double>(max_int) / denom < dd &&
|
||||||
|
numer > 0)
|
||||||
|
{
|
||||||
|
denom >>= 1;
|
||||||
|
numer = static_cast<int> (dd * denom + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (numer == 0)
|
||||||
|
{
|
||||||
|
denom <<= 1;
|
||||||
|
numer = static_cast<int> (dd * denom + 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app_d = NT (static_cast<int> (dd * denom + 0.5)) /
|
app_d = NT (numer) / NT (denom);
|
||||||
NT (denom);
|
|
||||||
app_err = sqr_d - CGAL::square (app_d);
|
app_err = sqr_d - CGAL::square (app_d);
|
||||||
|
|
||||||
while (CGAL::compare (CGAL::abs (app_err),
|
while (CGAL::compare (CGAL::abs (app_err),
|
||||||
|
|
@ -295,6 +336,45 @@ protected:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// In case |x2 - x1| < |y2 - y1| (and phi is small) it is possible
|
||||||
|
// that the approximation t' of t = tan(phi/2) is of opposite sign.
|
||||||
|
// To avoid this problem, we symbolically rotate the scene by pi/2,
|
||||||
|
// swapping roles between x and y. Thus, t is not close to zero, and
|
||||||
|
// we are guaranteed to have: phi- < phi < phi+ .
|
||||||
|
bool rotate_pi2 = false;
|
||||||
|
|
||||||
|
if (CGAL::compare (CGAL::abs(delta_x),
|
||||||
|
CGAL::abs(delta_y)) == SMALLER)
|
||||||
|
{
|
||||||
|
rotate_pi2 = true;
|
||||||
|
|
||||||
|
// We use the rotation matrix by pi/2:
|
||||||
|
//
|
||||||
|
// +- -+
|
||||||
|
// | 0 -1 |
|
||||||
|
// | 1 0 |
|
||||||
|
// +- -+
|
||||||
|
//
|
||||||
|
// Thus, the point (x, y) is converted to (-y, x):
|
||||||
|
NT tmp = x1;
|
||||||
|
|
||||||
|
x1 = -y1;
|
||||||
|
y1 = tmp;
|
||||||
|
|
||||||
|
tmp = x2;
|
||||||
|
x2 = -y2;
|
||||||
|
y2 = tmp;
|
||||||
|
|
||||||
|
// Swap the delta_x and delta_y values.
|
||||||
|
tmp = delta_x;
|
||||||
|
delta_x = -delta_y;
|
||||||
|
delta_y = tmp;
|
||||||
|
|
||||||
|
CGAL::Sign tmp_sign = sign_delta_x;
|
||||||
|
sign_delta_x = CGAL::opposite (sign_delta_y);
|
||||||
|
sign_delta_y = tmp_sign;
|
||||||
|
}
|
||||||
|
|
||||||
// Act according to the sign of delta_x.
|
// Act according to the sign of delta_x.
|
||||||
if (sign_delta_x == CGAL::NEGATIVE)
|
if (sign_delta_x == CGAL::NEGATIVE)
|
||||||
{
|
{
|
||||||
|
|
@ -324,7 +404,16 @@ protected:
|
||||||
sin_phi = 2 * lower_tan_half_phi / (1 + sqr_tan_half_phi);
|
sin_phi = 2 * lower_tan_half_phi / (1 + sqr_tan_half_phi);
|
||||||
cos_phi = (1 - sqr_tan_half_phi) / (1 + sqr_tan_half_phi);
|
cos_phi = (1 - sqr_tan_half_phi) / (1 + sqr_tan_half_phi);
|
||||||
|
|
||||||
|
if (! rotate_pi2)
|
||||||
|
{
|
||||||
op1 = Point_2 (x1 + r*cos_phi, y1 + r*sin_phi);
|
op1 = Point_2 (x1 + r*cos_phi, y1 + r*sin_phi);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In case of symbolic rotation by pi/2, we have to rotate the
|
||||||
|
// translated point by -(pi/2), transforming (x, y) to (y, -x).
|
||||||
|
op1 = Point_2 (y1 + r*sin_phi, -(x1 + r*cos_phi));
|
||||||
|
}
|
||||||
|
|
||||||
// Translate (x2, y2) by (r*cos(phi+), r*sin(phi+)) and create the
|
// Translate (x2, y2) by (r*cos(phi+), r*sin(phi+)) and create the
|
||||||
// second offset point.
|
// second offset point.
|
||||||
|
|
@ -332,7 +421,16 @@ protected:
|
||||||
sin_phi = 2 * upper_tan_half_phi / (1 + sqr_tan_half_phi);
|
sin_phi = 2 * upper_tan_half_phi / (1 + sqr_tan_half_phi);
|
||||||
cos_phi = (1 - sqr_tan_half_phi) / (1 + sqr_tan_half_phi);
|
cos_phi = (1 - sqr_tan_half_phi) / (1 + sqr_tan_half_phi);
|
||||||
|
|
||||||
|
if (! rotate_pi2)
|
||||||
|
{
|
||||||
op2 = Point_2 (x2 + r*cos_phi, y2 + r*sin_phi);
|
op2 = Point_2 (x2 + r*cos_phi, y2 + r*sin_phi);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In case of symbolic rotation by pi/2, we have to rotate the
|
||||||
|
// translated point by -(pi/2), transforming (x, y) to (y, -x).
|
||||||
|
op2 = Point_2 (y2 + r*sin_phi, -(x2 + r*cos_phi));
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the line l1 tangent to the circle centered at (x1, y1)
|
// Compute the line l1 tangent to the circle centered at (x1, y1)
|
||||||
// with radius r at the approximated point op1.
|
// with radius r at the approximated point op1.
|
||||||
|
|
@ -349,6 +447,12 @@ protected:
|
||||||
assign_success = CGAL::assign (mid_p, obj);
|
assign_success = CGAL::assign (mid_p, obj);
|
||||||
CGAL_assertion (assign_success);
|
CGAL_assertion (assign_success);
|
||||||
|
|
||||||
|
// Andreas's assertions:
|
||||||
|
CGAL_assertion( right_turn(*curr, *next, op2) );
|
||||||
|
CGAL_assertion( angle(*curr, *next, op2) != ACUTE);
|
||||||
|
CGAL_assertion( angle(op1, *curr, *next) != ACUTE);
|
||||||
|
CGAL_assertion( right_turn(op1, *curr, *next) );
|
||||||
|
|
||||||
// Create the two segments [op1 -> p_mid] and [p_min -> op2].
|
// Create the two segments [op1 -> p_mid] and [p_min -> op2].
|
||||||
seg1 = X_monotone_curve_2 (op1, mid_p);
|
seg1 = X_monotone_curve_2 (op1, mid_p);
|
||||||
dir_right1 = (f_comp_xy (op1, mid_p) == CGAL::SMALLER);
|
dir_right1 = (f_comp_xy (op1, mid_p) == CGAL::SMALLER);
|
||||||
|
|
@ -367,8 +471,14 @@ protected:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Connect prev_op and op1 with a circular arc, whose supporting circle
|
// Connect the offset target point of the previous edge to the
|
||||||
// is (x1, x2) with radius r.
|
// offset source of the current edge.
|
||||||
|
CGAL::Orientation orient = f_orient (*prev, *curr, *next);
|
||||||
|
|
||||||
|
if (orient == CGAL::LEFT_TURN)
|
||||||
|
{
|
||||||
|
// Connect prev_op and op1 with a circular arc, whose supporting
|
||||||
|
// circle is (x1, x2) with radius r.
|
||||||
arc = Curve_2 (*curr, r, CGAL::COUNTERCLOCKWISE,
|
arc = Curve_2 (*curr, r, CGAL::COUNTERCLOCKWISE,
|
||||||
Tr_point_2 (prev_op.x(), prev_op.y()),
|
Tr_point_2 (prev_op.x(), prev_op.y()),
|
||||||
Tr_point_2 (op1.x(), op1.y()));
|
Tr_point_2 (op1.x(), op1.y()));
|
||||||
|
|
@ -391,6 +501,25 @@ protected:
|
||||||
curve_index++;
|
curve_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (orient == CGAL::RIGHT_TURN)
|
||||||
|
{
|
||||||
|
// In case the current angle between the previous and the current
|
||||||
|
// edge is larger than pi/2, it not necessary to connect prev_op
|
||||||
|
// and op1 by a circular arc (as the case above): it is sufficient
|
||||||
|
// to shortcut the circular arc using a segment, whose sole purpose
|
||||||
|
// is to guarantee the continuity of the convolution cycle (we know
|
||||||
|
// this segment will not be part of the output offset or inet).
|
||||||
|
seg_short = X_monotone_curve_2(prev_op, op1);
|
||||||
|
|
||||||
|
dir_right_short = (f_comp_xy (prev_op, op1) == CGAL::SMALLER);
|
||||||
|
*oi = Labeled_curve_2 (seg_short,
|
||||||
|
X_curve_label (dir_right_short,
|
||||||
|
cycle_id,
|
||||||
|
curve_index));
|
||||||
|
oi++;
|
||||||
|
curve_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Append the offset segment(s) to the convolution cycle.
|
// Append the offset segment(s) to the convolution cycle.
|
||||||
CGAL_assertion (n_segments == 1 || n_segments == 2);
|
CGAL_assertion (n_segments == 1 || n_segments == 2);
|
||||||
|
|
@ -414,6 +543,7 @@ protected:
|
||||||
|
|
||||||
// Proceed to the next polygon vertex.
|
// Proceed to the next polygon vertex.
|
||||||
prev_op = op2;
|
prev_op = op2;
|
||||||
|
prev = curr;
|
||||||
curr = next;
|
curr = next;
|
||||||
|
|
||||||
} while (curr != first);
|
} while (curr != first);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue