/*! ========================================================================
** Extended Template Library
** Bezier Template Class Implementation
** $Id$
**
** Copyright (c) 2002 Robert B. Quattlebaum Jr.
** Copyright (c) 2007 Chris Moore
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** === N O T E S ===========================================================
**
** This is an internal header file, included by other ETL headers.
** You should not attempt to use it directly.
**
** ========================================================================= */
/* === S T A R T =========================================================== */
#ifndef __ETL__BEZIER_H
#define __ETL__BEZIER_H
/* === H E A D E R S ======================================================= */
#include "_curve_func.h"
#include <cmath> // for ldexp
// #include <ETL/fixed> // not used
/* === M A C R O S ========================================================= */
#define MAXDEPTH 64 /* Maximum depth for recursion */
/* take binary sign of a, either -1, or 1 if >= 0 */
#define SGN(a) (((a)<0) ? -1 : 1)
/* find minimum of a and b */
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
/* find maximum of a and b */
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
#define BEZIER_EPSILON (ldexp(1.0,-MAXDEPTH-1)) /*Flatness control value */
//#define BEZIER_EPSILON 0.00005 /*Flatness control value */
#define DEGREE 3 /* Cubic Bezier curve */
#define W_DEGREE 5 /* Degree of eqn to find roots of */
/* === T Y P E D E F S ===================================================== */
/* === C L A S S E S & S T R U C T S ======================================= */
namespace etl {
template<typename V,typename T> class bezier;
//! Cubic Bezier Curve Base Class
// This generic implementation uses the DeCasteljau algorithm.
// Works for just about anything that has an affine combination function
template <typename V,typename T=float>
class bezier_base : public std::unary_function<T,V>
{
public:
typedef V value_type;
typedef T time_type;
private:
value_type a,b,c,d;
time_type r,s;
protected:
affine_combo<value_type,time_type> affine_func;
public:
bezier_base(): r(0.0), s(1.0) { }
bezier_base(
const value_type &a, const value_type &b, const value_type &c, const value_type &d,
const time_type &r=0.0, const time_type &s=1.0):
a(a),b(b),c(c),d(d),r(r),s(s) { sync(); }
void sync()
{
}
value_type
operator()(time_type t)const
{
t=(t-r)/(s-r);
return
affine_func(
affine_func(
affine_func(a,b,t),
affine_func(b,c,t)
,t),
affine_func(
affine_func(b,c,t),
affine_func(c,d,t)
,t)
,t);
}
/*
void evaluate(time_type t, value_type &f, value_type &df) const
{
t=(t-r)/(s-r);
value_type p1 = affine_func(
affine_func(a,b,t),
affine_func(b,c,t)
,t);
value_type p2 = affine_func(
affine_func(b,c,t),
affine_func(c,d,t)
,t);
f = affine_func(p1,p2,t);
df = (p2-p1)*3;
}
*/
void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; }
void set_r(time_type new_r) { r=new_r; }
void set_s(time_type new_s) { s=new_s; }
const time_type &get_r()const { return r; }
const time_type &get_s()const { return s; }
time_type get_dt()const { return s-r; }
bool intersect_hull(const bezier_base<value_type,time_type> &/*x*/)const
{
return 0;
}
//! Bezier curve intersection function
/*! Calculates the time of intersection
** for the calling curve.
**
** I still have not figured out a good generic
** method of doing this for a bi-infinite
** cubic bezier curve calculated with the DeCasteljau
** algorithm.
**
** One method, although it does not work for the
** entire bi-infinite curve, is to iteratively
** intersect the hulls. However, we would only detect
** intersections that occur between R and S.
**
** It is entirely possible that a new construct similar
** to the affine combination function will be necessary
** for this to work properly.
**
** For now, this function is BROKEN. (although it works
** for the floating-point specializations, using newton's method)
*/
time_type intersect(const bezier_base<value_type,time_type> &/*x*/, time_type /*near=0.0*/)const
{
return 0;
}
/* subdivide at some time t into 2 separate curves left and right
b0 l1
* 0+1 l2
b1 * 1+2*1+2 l3
* 1+2 * 0+3*1+3*2+3 l4,r1
b2 * 1+2*2+2 r2 *
* 2+3 r3 *
b3 r4 *
*
0.1 2.3 -> 0.1 2 3 4 5.6
*/
/* void subdivide(bezier_base *left, bezier_base *right, const time_type &time = (time_type)0.5) const
{
time_type t = (time-r)/(s-r);
bezier_base lt,rt;
value_type temp;
//1st stage points to keep
lt.a = a;
rt.d = d;
//2nd stage calc
lt.b = affine_func(a,b,t);
temp = affine_func(b,c,t);
rt.c = affine_func(c,d,t);
//3rd stage calc
lt.c = affine_func(lt.b,temp,t);
rt.b = affine_func(temp,rt.c,t);
//last stage calc
lt.d = rt.a = affine_func(lt.c,rt.b,t);
//set the time range for l,r (the inside values should be 1, 0 respectively)
lt.r = r;
rt.s = s;
//give back the curves
if(left) *left = lt;
if(right) *right = rt;
}
*/
value_type &
operator[](int i)
{ return (&a)[i]; }
const value_type &
operator[](int i) const
{ return (&a)[i]; }
};
#if 1
// Fast float implementation of a cubic bezier curve
template <>
class bezier_base<float,float> : public std::unary_function<float,float>
{
public:
typedef float value_type;
typedef float time_type;
private:
// affine_combo<value_type,time_type> affine_func;
value_type a,b,c,d;
time_type r,s;
value_type _coeff[4];
time_type drs; // reciprocal of (s-r)
public:
bezier_base():r(0.0),s(1.0),drs(1.0) { }
bezier_base(
const value_type &a, const value_type &b, const value_type &c, const value_type &d,
const time_type &r=0.0, const time_type &s=1.0):
a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); }
void sync()
{
// drs=1.0/(s-r);
_coeff[0]= a;
_coeff[1]= b*3 - a*3;
_coeff[2]= c*3 - b*6 + a*3;
_coeff[3]= d - c*3 + b*3 - a;
}
// Cost Summary: 4 products, 3 sums, and 1 difference.
inline value_type
operator()(time_type t)const
{ t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; }
void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=1.0/(s-r); }
void set_r(time_type new_r) { r=new_r; drs=1.0/(s-r); }
void set_s(time_type new_s) { s=new_s; drs=1.0/(s-r); }
const time_type &get_r()const { return r; }
const time_type &get_s()const { return s; }
time_type get_dt()const { return s-r; }
//! Bezier curve intersection function
/*! Calculates the time of intersection
** for the calling curve.
*/
time_type intersect(const bezier_base<value_type,time_type> &x, time_type t=0.0,int i=15)const
{
//BROKEN - the time values of the 2 curves should be independent
value_type system[4];
system[0]=_coeff[0]-x._coeff[0];
system[1]=_coeff[1]-x._coeff[1];
system[2]=_coeff[2]-x._coeff[2];
system[3]=_coeff[3]-x._coeff[3];
t-=r;
t*=drs;
// Newton's method
// Inner loop cost summary: 7 products, 5 sums, 1 difference
for(;i;i--)
t-= (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/
(system[1]+(system[2]*2+(system[3]*3)*t)*t);
t*=(s-r);
t+=r;
return t;
}
value_type &
operator[](int i)
{ return (&a)[i]; }
const value_type &
operator[](int i) const
{ return (&a)[i]; }
};
// Fast double implementation of a cubic bezier curve
template <>
class bezier_base<double,float> : public std::unary_function<float,double>
{
public:
typedef double value_type;
typedef float time_type;
private:
// affine_combo<value_type,time_type> affine_func;
value_type a,b,c,d;
time_type r,s;
value_type _coeff[4];
time_type drs; // reciprocal of (s-r)
public:
bezier_base():r(0.0),s(1.0),drs(1.0) { }
bezier_base(
const value_type &a, const value_type &b, const value_type &c, const value_type &d,
const time_type &r=0.0, const time_type &s=1.0):
a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); }
void sync()
{
// drs=1.0/(s-r);
_coeff[0]= a;
_coeff[1]= b*3 - a*3;
_coeff[2]= c*3 - b*6 + a*3;
_coeff[3]= d - c*3 + b*3 - a;
}
// 4 products, 3 sums, and 1 difference.
inline value_type
operator()(time_type t)const
{ t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; }
void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=1.0/(s-r); }
void set_r(time_type new_r) { r=new_r; drs=1.0/(s-r); }
void set_s(time_type new_s) { s=new_s; drs=1.0/(s-r); }
const time_type &get_r()const { return r; }
const time_type &get_s()const { return s; }
time_type get_dt()const { return s-r; }
//! Bezier curve intersection function
/*! Calculates the time of intersection
** for the calling curve.
*/
time_type intersect(const bezier_base<value_type,time_type> &x, time_type t=0.0,int i=15)const
{
//BROKEN - the time values of the 2 curves should be independent
value_type system[4];
system[0]=_coeff[0]-x._coeff[0];
system[1]=_coeff[1]-x._coeff[1];
system[2]=_coeff[2]-x._coeff[2];
system[3]=_coeff[3]-x._coeff[3];
t-=r;
t*=drs;
// Newton's method
// Inner loop: 7 products, 5 sums, 1 difference
for(;i;i--)
t-= (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/
(system[1]+(system[2]*2+(system[3]*3)*t)*t);
t*=(s-r);
t+=r;
return t;
}
value_type &
operator[](int i)
{ return (&a)[i]; }
const value_type &
operator[](int i) const
{ return (&a)[i]; }
};
//#ifdef __FIXED__
// Fast double implementation of a cubic bezier curve
/*
template <>
template <class T,unsigned int FIXED_BITS>
class bezier_base<fixed_base<T,FIXED_BITS> > : std::unary_function<fixed_base<T,FIXED_BITS>,fixed_base<T,FIXED_BITS> >
{
public:
typedef fixed_base<T,FIXED_BITS> value_type;
typedef fixed_base<T,FIXED_BITS> time_type;
private:
affine_combo<value_type,time_type> affine_func;
value_type a,b,c,d;
time_type r,s;
value_type _coeff[4];
time_type drs; // reciprocal of (s-r)
public:
bezier_base():r(0.0),s(1.0),drs(1.0) { }
bezier_base(
const value_type &a, const value_type &b, const value_type &c, const value_type &d,
const time_type &r=0, const time_type &s=1):
a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); }
void sync()
{
drs=time_type(1)/(s-r);
_coeff[0]= a;
_coeff[1]= b*3 - a*3;
_coeff[2]= c*3 - b*6 + a*3;
_coeff[3]= d - c*3 + b*3 - a;
}
// 4 products, 3 sums, and 1 difference.
inline value_type
operator()(time_type t)const
{ t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; }
void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=time_type(1)/(s-r); }
void set_r(time_type new_r) { r=new_r; drs=time_type(1)/(s-r); }
void set_s(time_type new_s) { s=new_s; drs=time_type(1)/(s-r); }
const time_type &get_r()const { return r; }
const time_type &get_s()const { return s; }
time_type get_dt()const { return s-r; }
//! Bezier curve intersection function
//! Calculates the time of intersection
// for the calling curve.
//
time_type intersect(const bezier_base<value_type,time_type> &x, time_type t=0,int i=15)const
{
value_type system[4];
system[0]=_coeff[0]-x._coeff[0];
system[1]=_coeff[1]-x._coeff[1];
system[2]=_coeff[2]-x._coeff[2];
system[3]=_coeff[3]-x._coeff[3];
t-=r;
t*=drs;
// Newton's method
// Inner loop: 7 products, 5 sums, 1 difference
for(;i;i--)
t-=(time_type) ( (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/
(system[1]+(system[2]*2+(system[3]*3)*t)*t) );
t*=(s-r);
t+=r;
return t;
}
value_type &
operator[](int i)
{ return (&a)[i]; }
const value_type &
operator[](int i) const
{ return (&a)[i]; }
};
*/
//#endif
#endif
template <typename V, typename T>
class bezier_iterator
{
public:
struct iterator_category {};
typedef V value_type;
typedef T difference_type;
typedef V reference;
private:
difference_type t;
difference_type dt;
bezier_base<V,T> curve;
public:
/*
reference
operator*(void)const { return curve(t); }
const surface_iterator&
operator++(void)
{ t+=dt; return &this; }
const surface_iterator&
operator++(int)
{ hermite_iterator _tmp=*this; t+=dt; return _tmp; }
const surface_iterator&
operator--(void)
{ t-=dt; return &this; }
const surface_iterator&
operator--(int)
{ hermite_iterator _tmp=*this; t-=dt; return _tmp; }
surface_iterator
operator+(difference_type __n) const
{ return surface_iterator(data+__n[0]+__n[1]*pitch,pitch); }
surface_iterator
operator-(difference_type __n) const
{ return surface_iterator(data-__n[0]-__n[1]*pitch,pitch); }
*/
};
template <typename V,typename T=float>
class bezier : public bezier_base<V,T>
{
public:
typedef V value_type;
typedef T time_type;
typedef float distance_type;
typedef bezier_iterator<V,T> iterator;
typedef bezier_iterator<V,T> const_iterator;
distance_func<value_type> dist;
using bezier_base<V,T>::get_r;
using bezier_base<V,T>::get_s;
using bezier_base<V,T>::get_dt;
public:
bezier() { }
bezier(const value_type &a, const value_type &b, const value_type &c, const value_type &d):
bezier_base<V,T>(a,b,c,d) { }
const_iterator begin()const;
const_iterator end()const;
time_type find_closest(bool fast, const value_type& x, int i=7)const
{
if (!fast)
{
value_type array[4] = {
bezier<V,T>::operator[](0),
bezier<V,T>::operator[](1),
bezier<V,T>::operator[](2),
bezier<V,T>::operator[](3)};
return NearestPointOnCurve(x, array);
}
else
{
time_type r(0), s(1);
float t((r+s)*0.5); /* half way between r and s */
for(;i;i--)
{
// compare 33% of the way between r and s with 67% of the way between r and s
if(dist(this->operator()((s-r)*(1.0/3.0)+r), x) <
dist(this->operator()((s-r)*(2.0/3.0)+r), x))
s=t;
else
r=t;
t=((r+s)*0.5);
}
return t;
}
}
distance_type find_distance(time_type r, time_type s, int steps=7)const
{
const time_type inc((s-r)/steps);
if (!inc) return 0;
distance_type ret(0);
value_type last(this->operator()(r));
for(r+=inc;r<s;r+=inc)
{
const value_type n(this->operator()(r));
ret+=dist.uncook(dist(last,n));
last=n;
}
ret+=dist.uncook(dist(last,this->operator()(r)))*(s-(r-inc))/inc;
return ret;
}
distance_type length()const { return find_distance(get_r(),get_s()); }
/* subdivide at some time t into 2 separate curves left and right
b0 l1
* 0+1 l2
b1 * 1+2*1+2 l3
* 1+2 * 0+3*1+3*2+3 l4,r1
b2 * 1+2*2+2 r2 *
* 2+3 r3 *
b3 r4 *
*
0.1 2.3 -> 0.1 2 3 4 5.6
*/
void subdivide(bezier *left, bezier *right, const time_type &time = (time_type)0.5) const
{
time_type t=(time-get_r())/get_dt();
bezier lt,rt;
value_type temp;
const value_type& a((*this)[0]);
const value_type& b((*this)[1]);
const value_type& c((*this)[2]);
const value_type& d((*this)[3]);
//1st stage points to keep
lt[0] = a;
rt[3] = d;
//2nd stage calc
lt[1] = this->affine_func(a,b,t);
temp = this->affine_func(b,c,t);
rt[2] = this->affine_func(c,d,t);
//3rd stage calc
lt[2] = this->affine_func(lt[1],temp,t);
rt[1] = this->affine_func(temp,rt[2],t);
//last stage calc
lt[3] = rt[0] = this->affine_func(lt[2],rt[1],t);
//set the time range for l,r (the inside values should be 1, 0 respectively)
lt.set_r(get_r());
rt.set_s(get_s());
lt.sync();
rt.sync();
//give back the curves
if(left) *left = lt;
if(right) *right = rt;
}
void evaluate(time_type t, value_type &f, value_type &df) const
{
t=(t-get_r())/get_dt();
const value_type& a((*this)[0]);
const value_type& b((*this)[1]);
const value_type& c((*this)[2]);
const value_type& d((*this)[3]);
const value_type p1 = affine_func(
affine_func(a,b,t),
affine_func(b,c,t)
,t);
const value_type p2 = affine_func(
affine_func(b,c,t),
affine_func(c,d,t)
,t);
f = affine_func(p1,p2,t);
df = (p2-p1)*3;
}
private:
/*
* Bezier :
* Evaluate a Bezier curve at a particular parameter value
* Fill in control points for resulting sub-curves if "Left" and
* "Right" are non-null.
*
* int degree; Degree of bezier curve
* value_type *VT; Control pts
* time_type t; Parameter value
* value_type *Left; RETURN left half ctl pts
* value_type *Right; RETURN right half ctl pts
*/
static value_type Bezier(value_type *VT, int degree, time_type t, value_type *Left, value_type *Right)
{
int i, j; /* Index variables */
value_type Vtemp[W_DEGREE+1][W_DEGREE+1];
/* Copy control points */
for (j = 0; j <= degree; j++)
Vtemp[0][j] = VT[j];
/* Triangle computation */
for (i = 1; i <= degree; i++)
for (j =0 ; j <= degree - i; j++)
{
Vtemp[i][j][0] = (1.0 - t) * Vtemp[i-1][j][0] + t * Vtemp[i-1][j+1][0];
Vtemp[i][j][1] = (1.0 - t) * Vtemp[i-1][j][1] + t * Vtemp[i-1][j+1][1];
}
if (Left != NULL)
for (j = 0; j <= degree; j++)
Left[j] = Vtemp[j][0];
if (Right != NULL)
for (j = 0; j <= degree; j++)
Right[j] = Vtemp[degree-j][j];
return (Vtemp[degree][0]);
}
/*
* CrossingCount :
* Count the number of times a Bezier control polygon
* crosses the 0-axis. This number is >= the number of roots.
*
* value_type *VT; Control pts of Bezier curve
*/
static int CrossingCount(value_type *VT)
{
int i;
int n_crossings = 0; /* Number of zero-crossings */
int sign, old_sign; /* Sign of coefficients */
sign = old_sign = SGN(VT[0][1]);
for (i = 1; i <= W_DEGREE; i++)
{
sign = SGN(VT[i][1]);
if (sign != old_sign) n_crossings++;
old_sign = sign;
}
return n_crossings;
}
/*
* ControlPolygonFlatEnough :
* Check if the control polygon of a Bezier curve is flat enough
* for recursive subdivision to bottom out.
*
* value_type *VT; Control points
*/
static int ControlPolygonFlatEnough(value_type *VT)
{
int i; /* Index variable */
distance_type distance[W_DEGREE] = {}; /* Distances from pts to line */
distance_type max_distance_above; /* maximum of these */
distance_type max_distance_below;
time_type intercept_1, intercept_2, left_intercept, right_intercept;
distance_type a, b, c; /* Coefficients of implicit */
/* eqn for line from VT[0]-VT[deg] */
/* Find the perpendicular distance */
/* from each interior control point to */
/* line connecting VT[0] and VT[W_DEGREE] */
{
distance_type abSquared;
/* Derive the implicit equation for line connecting first *
* and last control points */
a = VT[0][1] - VT[W_DEGREE][1];
b = VT[W_DEGREE][0] - VT[0][0];
c = VT[0][0] * VT[W_DEGREE][1] - VT[W_DEGREE][0] * VT[0][1];
abSquared = (a * a) + (b * b);
for (i = 1; i < W_DEGREE; i++)
{
/* Compute distance from each of the points to that line */
distance[i] = a * VT[i][0] + b * VT[i][1] + c;
if (distance[i] > 0.0) distance[i] = (distance[i] * distance[i]) / abSquared;
if (distance[i] < 0.0) distance[i] = -(distance[i] * distance[i]) / abSquared;
}
}
/* Find the largest distance */
max_distance_above = max_distance_below = 0.0;
for (i = 1; i < W_DEGREE; i++)
{
if (distance[i] < 0.0) max_distance_below = MIN(max_distance_below, distance[i]);
if (distance[i] > 0.0) max_distance_above = MAX(max_distance_above, distance[i]);
}
/* Implicit equation for "above" line */
intercept_1 = -(c + max_distance_above)/a;
/* Implicit equation for "below" line */
intercept_2 = -(c + max_distance_below)/a;
/* Compute intercepts of bounding box */
left_intercept = MIN(intercept_1, intercept_2);
right_intercept = MAX(intercept_1, intercept_2);
return 0.5 * (right_intercept-left_intercept) < BEZIER_EPSILON ? 1 : 0;
}
/*
* ComputeXIntercept :
* Compute intersection of chord from first control point to last
* with 0-axis.
*
* value_type *VT; Control points
*/
static time_type ComputeXIntercept(value_type *VT)
{
distance_type YNM = VT[W_DEGREE][1] - VT[0][1];
return (YNM*VT[0][0] - (VT[W_DEGREE][0] - VT[0][0])*VT[0][1]) / YNM;
}
/*
* FindRoots :
* Given a 5th-degree equation in Bernstein-Bezier form, find
* all of the roots in the interval [0, 1]. Return the number
* of roots found.
*
* value_type *w; The control points
* time_type *t; RETURN candidate t-values
* int depth; The depth of the recursion
*/
static int FindRoots(value_type *w, time_type *t, int depth)
{
int i;
value_type Left[W_DEGREE+1]; /* New left and right */
value_type Right[W_DEGREE+1]; /* control polygons */
int left_count; /* Solution count from */
int right_count; /* children */
time_type left_t[W_DEGREE+1]; /* Solutions from kids */
time_type right_t[W_DEGREE+1];
switch (CrossingCount(w))
{
case 0 :
{ /* No solutions here */
return 0;
}
case 1 :
{ /* Unique solution */
/* Stop recursion when the tree is deep enough */
/* if deep enough, return 1 solution at midpoint */
if (depth >= MAXDEPTH)
{
t[0] = (w[0][0] + w[W_DEGREE][0]) / 2.0;
return 1;
}
if (ControlPolygonFlatEnough(w))
{
t[0] = ComputeXIntercept(w);
return 1;
}
break;
}
}
/* Otherwise, solve recursively after */
/* subdividing control polygon */
Bezier(w, W_DEGREE, 0.5, Left, Right);
left_count = FindRoots(Left, left_t, depth+1);
right_count = FindRoots(Right, right_t, depth+1);
/* Gather solutions together */
for (i = 0; i < left_count; i++) t[i] = left_t[i];
for (i = 0; i < right_count; i++) t[i+left_count] = right_t[i];
/* Send back total number of solutions */
return (left_count+right_count);
}
/*
* ConvertToBezierForm :
* Given a point and a Bezier curve, generate a 5th-degree
* Bezier-format equation whose solution finds the point on the
* curve nearest the user-defined point.
*
* value_type& P; The point to find t for
* value_type *VT; The control points
*/
static void ConvertToBezierForm(const value_type& P, value_type *VT, value_type w[W_DEGREE+1])
{
int i, j, k, m, n, ub, lb;
int row, column; /* Table indices */
value_type c[DEGREE+1]; /* VT(i)'s - P */
value_type d[DEGREE]; /* VT(i+1) - VT(i) */
distance_type cdTable[3][4]; /* Dot product of c, d */
static distance_type z[3][4] = { /* Precomputed "z" for cubics */
{1.0, 0.6, 0.3, 0.1},
{0.4, 0.6, 0.6, 0.4},
{0.1, 0.3, 0.6, 1.0}};
/* Determine the c's -- these are vectors created by subtracting */
/* point P from each of the control points */
for (i = 0; i <= DEGREE; i++)
c[i] = VT[i] - P;
/* Determine the d's -- these are vectors created by subtracting */
/* each control point from the next */
for (i = 0; i <= DEGREE - 1; i++)
d[i] = (VT[i+1] - VT[i]) * 3.0;
/* Create the c,d table -- this is a table of dot products of the */
/* c's and d's */
for (row = 0; row <= DEGREE - 1; row++)
for (column = 0; column <= DEGREE; column++)
cdTable[row][column] = d[row] * c[column];
/* Now, apply the z's to the dot products, on the skew diagonal */
/* Also, set up the x-values, making these "points" */
for (i = 0; i <= W_DEGREE; i++)
{
w[i][0] = (distance_type)(i) / W_DEGREE;
w[i][1] = 0.0;
}
n = DEGREE;
m = DEGREE-1;
for (k = 0; k <= n + m; k++)
{
lb = MAX(0, k - m);
ub = MIN(k, n);
for (i = lb; i <= ub; i++)
{
j = k - i;
w[k][1] += cdTable[j][i] * z[j][i];
//w[i+j][1] += cdTable[j][i] * z[j][i];
}
}
}
/*
* NearestPointOnCurve :
* Compute the parameter value of the point on a Bezier
* curve segment closest to some arbitrary, user-input point.
* Return the point on the curve at that parameter value.
*
* value_type& P; The user-supplied point
* value_type *VT; Control points of cubic Bezier
*/
static time_type NearestPointOnCurve(const value_type& P, value_type VT[4])
{
value_type w[W_DEGREE+1]; /* Ctl pts of 5th-degree curve */
time_type t_candidate[W_DEGREE]; /* Possible roots */
int n_solutions; /* Number of roots found */
time_type t; /* Parameter value of closest pt */
/* Convert problem to 5th-degree Bezier form */
ConvertToBezierForm(P, VT, w);
/* Find all possible roots of 5th-degree equation */
n_solutions = FindRoots(w, t_candidate, 0);
/* Compare distances of P to all candidates, and to t=0, and t=1 */
{
distance_type dist, new_dist;
value_type p, v;
int i;
/* Check distance to beginning of curve, where t = 0 */
dist = (P - VT[0]).mag_squared();
t = 0.0;
/* Find distances for candidate points */
for (i = 0; i < n_solutions; i++)
{
p = Bezier(VT, DEGREE, t_candidate[i], (value_type *)NULL, (value_type *)NULL);
new_dist = (P - p).mag_squared();
if (new_dist < dist)
{
dist = new_dist;
t = t_candidate[i];
}
}
/* Finally, look at distance to end point, where t = 1.0 */
new_dist = (P - VT[DEGREE]).mag_squared();
if (new_dist < dist)
{
dist = new_dist;
t = 1.0;
}
}
/* Return the point on the curve at parameter value t */
return t;
}
};
};
/* === E X T E R N S ======================================================= */
/* === E N D =============================================================== */
#endif