bezier-clipping.cpp revision ea8dd7683dd12883474f6cf9b5f424f8ed831166
/*
* Implement the Bezier clipping algorithm for finding
* Bezier curve intersection points and collinear normals
*
* Authors:
* Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2008 authors
*
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
//#include <2geom/convex-cover.h>
#include <cassert>
#include <vector>
#include <algorithm>
#include <utility>
//#include <iomanip>
#define VERBOSE 0
#define CHECK 0
namespace Geom {
namespace detail { namespace bezier_clipping {
////////////////////////////////////////////////////////////////////////////////
// for debugging
//
inline
{
}
template< class charT >
inline
{
return os;
}
inline
{
return (180 * a / M_PI);
}
inline
{
double d = I.extent();
double e = 0.1, p = 10;
int n = 0;
while (n < 16 && d < e)
{
p *= 10;
e = 1/p;
++n;
}
return n;
}
inline
void range_assertion(int k, int m, int n, const char* msg)
{
if ( k < m || k > n)
{
<< "value: " << k
assert (k >= m && k <= n);
}
}
////////////////////////////////////////////////////////////////////////////////
// convex hull
/*
* return true in case the oriented polyline p0, p1, p2 is a right turn
*/
inline
{
}
/*
* return true if p < q wrt the lexicographyc order induced by the coordinates
*/
struct lex_less
{
{
return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));
}
};
/*
* return true if p > q wrt the lexicographyc order induced by the coordinates
*/
struct lex_greater
{
{
return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));
}
};
/*
* Compute the convex hull of a set of points.
* The implementation is based on the Andrew's scan algorithm
* note: in the Bezier clipping for collinear normals it seems
* to be more stable wrt the Graham's scan algorithm and in general
* a bit quikier
*/
{
if (n < 2) return;
if (n < 4) return;
// upper hull
size_t u = 2;
for (size_t i = 2; i < n; ++i)
{
{
--u;
}
++u;
}
// lower hull
size_t l = u;
size_t k = u - 1;
for (size_t i = l; i < n; ++i)
{
{
--l;
}
++l;
}
P.resize(l);
}
////////////////////////////////////////////////////////////////////////////////
// numerical routines
/*
* Compute the binomial coefficient (n, k)
*/
inline
double binomial(unsigned int n, unsigned int k)
{
}
/*
* Compute the determinant of the 2x2 matrix with column the point P1, P2
*/
inline
{
}
/*
* Solve the linear system [P1,P2] * P = Q
* in case there isn't exactly one solution the routine returns false
*/
inline
{
if (d == 0) return false;
d = 1 / d;
return true;
}
////////////////////////////////////////////////////////////////////////////////
// interval routines
/*
* Map the sub-interval I in [0,1] into the interval J and assign it to J
*/
inline
{
}
/*
* The interval [1,0] is used to represent the empty interval, this routine
* is just an helper function for creating such an interval
*/
inline
{
Interval I(0);
I[0] = 1;
return I;
}
////////////////////////////////////////////////////////////////////////////////
// bezier curve routines
/*
* Return true if all the Bezier curve control points are near,
* false otherwise
*/
inline
{
for (unsigned int i = 1; i < A.size(); ++i)
{
return false;
}
return true;
}
/*
* Compute the hodograph of the bezier curve B and return it in D
*/
inline
{
D.clear();
if (sz == 0) return;
if (sz == 1)
{
return;
}
D.reserve(n);
for (size_t i = 0; i < n; ++i)
{
D.push_back(n*(B[i+1] - B[i]));
}
}
/*
* Compute the hodograph of the Bezier curve B rotated of 90 degree
* and return it in D; we have N(t) orthogonal to B(t) for any t
*/
inline
{
derivative(N,B);
{
N[i] = rot90(N[i]);
}
}
/*
* Compute the portion of the Bezier curve "B" wrt the interval [0,t]
*/
inline
{
for (size_t i = 1; i < n; ++i)
{
{
B[j] = lerp(t, B[j-1], B[j]);
}
}
}
/*
* Compute the portion of the Bezier curve "B" wrt the interval [t,1]
*/
inline
{
for (size_t i = 1; i < n; ++i)
{
for (size_t j = 0; j < n-i; ++j)
{
B[j] = lerp(t, B[j], B[j+1]);
}
}
}
/*
* Compute the portion of the Bezier curve "B" wrt the interval "I"
*/
inline
{
if (I.min() == 0)
{
if (I.max() == 1) return;
left_portion(I.max(), B);
return;
}
right_portion(I.min(), B);
if (I.max() == 1) return;
left_portion(t, B);
}
////////////////////////////////////////////////////////////////////////////////
// tags
struct intersection_point_tag;
struct collinear_normal_tag;
template <typename Tag>
template <typename Tag>
double precision );
////////////////////////////////////////////////////////////////////////////////
// intersection
/*
* Make up an orientation line using the control points c[i] and c[j]
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
inline
{
l[0] = c[j][Y] - c[i][Y];
l[1] = c[i][X] - c[j][X];
l[2] = cross(c[i], c[j]);
l[0] /= length;
l[1] /= length;
l[2] /= length;
}
/*
* Pick up an orientation line for the Bezier curve "c" and return it in
* the output parameter "l"
*/
inline
{
while (--i > 0 && are_near(c[0], c[i]))
{}
if (i == 0)
{
// this should never happen because when a new curve portion is created
// we check that it is not constant;
// however this requires that the precision used in the is_constant
// routine has to be the same used here in the are_near test
assert(i != 0);
}
orientation_line(l, c, 0, i);
//std::cerr << "i = " << i << std::endl;
}
/*
* Make up an orientation line for constant bezier curve;
* the orientation line is made up orthogonal to the other curve base line;
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
inline
Point const& p)
{
if (is_constant(c))
{
// this should never happen
assert(!is_constant(c));
}
ol[0] = p;
}
/*
* Compute the signed distance of the point "P" from the normalized line l
*/
inline
{
return l[X] * P[X] + l[Y] * P[Y] + l[2];
}
/*
* Compute the min and max distance of the control points of the Bezier
* curve "c" from the normalized orientation line "l".
* This bounds are returned through the output Interval parameter"bound".
*/
inline
{
bound[0] = 0;
bound[1] = 0;
double d;
{
d = distance(c[i], l);
}
}
/*
* return the x component of the intersection point between the line
* passing through points p1, p2 and the line Y = "y"
*/
inline
{
// we are sure that p2[Y] != p1[Y] because this routine is called
// only when the lower or the upper bound is crossed
}
/*
* Clip the Bezier curve "B" wrt the fat line defined by the orientation
* line "l" and the interval range "bound", the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
{
double d;
{
d = distance (B[i], l);
}
//print(D);
convex_hull(D);
//print(p);
// std::cerr << "bound : " << bound << std::endl;
{
// std::cerr << "0 : inside " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
{
{
// std::cerr << i << " : inside " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
{
// std::cerr << i << " : lower " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
{
// std::cerr << i << " : higher " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
}
// we have to test the closing segment for intersection
{
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
{
// std::cerr << "0 : higher " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
}
/*
* Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
* intersection points the new parameter interval for the clipped curve
* is returned through the output parameter "dom"
*/
template <>
inline
{
if (is_constant(A))
{
orthogonal_orientation_line(bl, B, M);
}
else
{
pick_orientation_line(bl, A);
}
}
///////////////////////////////////////////////////////////////////////////////
// collinear normal
/*
* Compute a closed focus for the Bezier curve B and return it in F
* A focus is any curve through which all lines perpendicular to B(t) pass.
*/
inline
{
normal(F, B);
#if VERBOSE
if (!solve(c, F[0], -F[n-1], B[n]-B[0]))
{
}
#else
solve(c, F[0], -F[n-1], B[n]-B[0]);
#endif
// std::cerr << "c = " << c << std::endl;
// B(t) + c(t) * N(t)
double n_inv = 1 / (double)(n);
F[n] += B[n];
for (size_t i = n-1; i > 0; --i)
{
F[i] *= -c[0];
c0ni = F[i];
F[i] += (c[1] * F[i-1]);
F[i] *= (i * n_inv);
F[i] -= c0ni;
F[i] += B[i];
}
F[0] *= c[0];
F[0] += B[0];
}
/*
* Compute the projection on the plane (t, d) of the control points
* (t, u, D(t,u)) where D(t,u) = <(B(t) - F(u)), B'(t)> with 0 <= t, u <= 1
* B is a Bezier curve and F is a focus of another Bezier curve.
* See Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping.
*/
{
const double r_inv = 1 / (double)(r);
D.clear();
for (size_t k = 0; k < n; ++k)
{
}
for (size_t i = 0; i < n; ++i)
for (size_t i = 0; i < n; ++i)
for (size_t i = 0; i <= r; ++i)
{
for (size_t j = 0; j <= m; ++j)
{
d[j] = 0;
}
{
//if (k > i || (i-k) > n) continue;
l = i - k;
#if CHECK
assert (l <= n);
#endif
for (size_t j = 0; j <= m; ++j)
{
//d[j] += bc * dot(dB[k], B[l] - F[j]);
}
}
for (size_t j = 0; j < m; ++j)
{
}
}
}
/*
* Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
{
distance_control_points(D, B, F);
//print(D, "D");
// ConvexHull chD(D);
// std::vector<Point>& p = chD.boundary; // convex hull vertices
convex_hull(D);
//print(p, "CH(D)");
plower = (p[0][Y] < 0);
if (p[0][Y] == 0) // on the x axis
{
// std::cerr << "0 : on x axis " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
{
clower = (p[i][Y] < 0);
if (p[i][Y] == 0) // on x axis
{
// std::cerr << i << " : on x axis " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
{
t = intersect(p[i-1], p[i], 0);
// std::cerr << i << " : lower " << p[i]
// << " : tmin = " << tmin << ", tmax = " << tmax
// << std::endl;
}
}
// we have to test the closing segment for intersection
clower = (p[0][Y] < 0);
{
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
}
/*
* Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
* points which have collinear normals; the new parameter interval
* for the clipped curve is returned through the output parameter "dom"
*/
template <>
inline
{
make_focus(F, A);
clip_interval(dom, B, F);
}
const double MAX_PRECISION = 1e-8;
const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
/*
* iterate
*
* input:
* A, B: control point sets of two bezier curves
* domA, domB: real parameter intervals of the two curves
* precision: required computational precision of the returned parameter ranges
* output:
* domsA, domsB: sets of parameter intervals
*
* The parameter intervals are computed by using a Bezier clipping algorithm,
* in case the clipping doesn't shrink the initial interval more than 20%,
* a subdivision step is performed.
* If during the computation both curves collapse to a single point
* the routine exits indipendently by the precision reached in the computation
* of the curve intervals.
*/
template <>
double precision )
{
// in order to limit recursion
if (++counter > 100) return;
#if VERBOSE
// std::cerr << "angle(A) : " << angle(A) << std::endl;
// std::cerr << "angle(B) : " << angle(B) << std::endl;
#endif
if (precision < MAX_PRECISION)
if ( is_constant(A) && is_constant(B) ){
}
return;
}
while (++iter < 100
{
#if VERBOSE
#endif
// [1,0] is utilized to represent an empty interval
if (dom == EMPTY_INTERVAL)
{
#if VERBOSE
#endif
return;
}
#if VERBOSE
#endif
// all other cases where dom[0] > dom[1] are invalid
{
}
{
#if VERBOSE
#endif
break; // append the new interval
else
return; // exit without appending any new interval
}
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
{
#if VERBOSE
#endif
{
}
else
{
}
return;
}
#if VERBOSE
#endif
}
}
/*
* iterate
*
* input:
* A, B: control point sets of two bezier curves
* domA, domB: real parameter intervals of the two curves
* precision: required computational precision of the returned parameter ranges
* output:
* domsA, domsB: sets of parameter intervals
*
* The parameter intervals are computed by using a Bezier clipping algorithm,
* in case the clipping doesn't shrink the initial interval more than 20%,
* a subdivision step is performed.
* If during the computation one of the two curve interval length becomes less
* than MAX_PRECISION the routine exits indipendently by the precision reached
* in the computation of the other curve interval.
*/
template <>
double precision)
{
// in order to limit recursion
if (++counter > 100) return;
#if VERBOSE
// std::cerr << "angle(A) : " << angle(A) << std::endl;
// std::cerr << "angle(B) : " << angle(B) << std::endl;
#endif
if (precision < MAX_PRECISION)
while (++iter < 100
{
#if VERBOSE
#endif
// [1,0] is utilized to represent an empty interval
if (dom == EMPTY_INTERVAL)
{
#if VERBOSE
#endif
return;
}
#if VERBOSE
#endif
// all other cases where dom[0] > dom[1] are invalid
{
}
// it's better to stop before losing computational precision
{
#if VERBOSE
#endif
break;
}
{
#if VERBOSE
#endif
break;
}
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
{
#if VERBOSE
#endif
{
{
break;
}
if (false && is_constant(pC1))
{
#if VERBOSE
#endif
break;
}
if (is_constant(pC2))
{
#if VERBOSE
#endif
break;
}
}
else
{
{
break;
}
if (is_constant(pC1))
{
#if VERBOSE
#endif
break;
}
if (is_constant(pC2))
{
#if VERBOSE
#endif
break;
}
}
return;
}
#if VERBOSE
#endif
}
}
/*
* get_solutions
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* input: clip - the routine used for clipping
* output: xs - set of pairs of parameter values
* at which the clipping algorithm converges
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg - Computer Aided Geometric Design
*/
template <typename Tag>
double precision)
{
{
}
{
#if VERBOSE
#endif
}
}
} /* end namespace bezier_clipping */ } /* end namespace detail */
/*
* find_collinear_normal
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* output: xs - set of pairs of parameter values
* at which there are collinear normals
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
*/
double precision)
{
}
/*
* find_intersections_bezier_clipping
*
* input: A, B - set of control points of two Bezier curve
* input: precision - required precision of computation
* output: xs - set of pairs of parameter values
* at which crossing happens
*
* This routine is based on the Bezier Clipping Algorithm,
* see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
*/
double precision)
{
}
} // end namespace Geom
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :