49namespace BezierFitter{
55 Point const &tHat1,
Point const &tHat2,
double tolerance_sq);
69 unsigned *splitPoint);
72 double const tolerance);
75 double const tolerance) {
76 CubicBezier cb(bezCurve[0], bezCurve[1], bezCurve[2], bezCurve[3]);
83 CubicBezier cb(bezCurve[0], bezCurve[1], bezCurve[2], bezCurve[3]);
96#define B0(u) ( ( 1.0 - u ) * ( 1.0 - u ) * ( 1.0 - u ) )
97#define B1(u) ( 3 * u * ( 1.0 - u ) * ( 1.0 - u ) )
98#define B2(u) ( 3 * u * u * ( 1.0 - u ) )
99#define B3(u) ( u * u * u )
102# define DOUBLE_ASSERT(x) assert( ( (x) > -SP_HUGE ) && ( (x) < SP_HUGE ) )
103# define BEZIER_ASSERT(b) do { \
104 DOUBLE_ASSERT((b)[0][X]); DOUBLE_ASSERT((b)[0][Y]); \
105 DOUBLE_ASSERT((b)[1][X]); DOUBLE_ASSERT((b)[1][Y]); \
106 DOUBLE_ASSERT((b)[2][X]); DOUBLE_ASSERT((b)[2][Y]); \
107 DOUBLE_ASSERT((b)[3][X]); DOUBLE_ASSERT((b)[3][Y]); \
110# define DOUBLE_ASSERT(x) do { } while(0)
111# define BEZIER_ASSERT(b) do { } while(0)
138 assert( d[0] != d[1] );
156 unsigned const last =
len - 1;
157 unsigned const prev = last - 1;
158 assert( d[last] != d[prev] );
177 assert( 0 <= tolerance_sq );
178 for (
unsigned i = 1;;) {
179 Point const pi(d[i]);
180 Point const t(pi - d[0]);
181 double const distsq =
dot(t, t);
182 if ( tolerance_sq < distsq ) {
208 assert( 0 <= tolerance_sq );
209 unsigned const last =
len - 1;
210 for (
unsigned i = last - 1;; i--) {
211 Point const pi(d[i]);
212 Point const t(pi - d[last]);
213 double const distsq =
dot(t, t);
214 if ( tolerance_sq < distsq ) {
237 unsigned const center,
240 assert( center != 0 );
241 assert( center <
len - 1 );
244 if ( d[center + 1] == d[center - 1] ) {
246 Point const diff = d[center] - d[center - 1];
249 ret = d[center - 1] - d[center + 1];
263 double const error,
unsigned const max_beziers);
292 max_beziers >= (1ul << (31 - 2 - 1 - 3)))
298 if ( uniqued_len < 2 ) {
299 delete[] uniqued_data;
307 delete[] uniqued_data;
323 if ( si == src_len ) {
326 if (!std::isnan(src[si][
X]) &&
327 !std::isnan(src[si][
Y])) {
328 dest[0] =
Point(src[si]);
335 for (; si < src_len; ++si) {
337 if ( src_pt != dest[di]
338 && !std::isnan(src_pt[
X])
339 && !std::isnan(src_pt[
Y])) {
343 unsigned dest_len = di + 1;
344 assert( dest_len <= src_len );
360 double const error,
unsigned const max_beziers)
362 int const maxIterations = 4;
364 if(!(bezier != NULL) ||
367 !(max_beziers >= 1) ||
371 if (
len < 2 )
return 0;
377 double const dist =
distance(bezier[0], bezier[3]) / 3.0;
378 if (std::isnan(dist)) {
380 bezier[1] = bezier[0];
381 bezier[2] = bezier[3];
384 ? ( 2 * bezier[0] + bezier[3] ) / 3.
385 : bezier[0] + dist * tHat1 );
387 ? ( bezier[0] + 2 * bezier[3] ) / 3.
388 : bezier[3] + dist * tHat2 );
390 BEZIER_ASSERT(bezier);
398 double *u =
new double[
len];
400 if ( u[
len - 1] == 0.0 ) {
414 double const tolerance = std::sqrt(error + 1e-9);
417 if ( fabs(maxErrorRatio) <= 1.0 ) {
418 BEZIER_ASSERT(bezier);
424 if ( 0.0 <= maxErrorRatio && maxErrorRatio <= 3.0 ) {
425 for (
int i = 0; i < maxIterations; i++) {
429 if ( fabs(maxErrorRatio) <= 1.0 ) {
430 BEZIER_ASSERT(bezier);
437 is_corner = (maxErrorRatio < 0);
441 assert(splitPoint <
unsigned(
len));
442 if (splitPoint == 0) {
450 }
else if (splitPoint ==
unsigned(
len - 1)) {
461 if ( 1 < max_beziers ) {
465 unsigned const rec_max_beziers1 = max_beziers - 1;
467 Point recTHat2, recTHat1;
469 if(!(0 < splitPoint && splitPoint <
unsigned(
len - 1)))
475 recTHat1 = -recTHat2;
478 tHat1, recTHat2, error, rec_max_beziers1);
481 g_print(
"fit_cubic[1]: recursive call failed\n");
485 assert( nsegs1 != 0 );
486 if (split_points != NULL) {
487 split_points[nsegs1 - 1] = splitPoint;
489 unsigned const rec_max_beziers2 = max_beziers - nsegs1;
491 ( split_points == NULL
493 : split_points + nsegs1 ),
494 data + splitPoint,
len - splitPoint,
495 recTHat1, tHat2, error, rec_max_beziers2);
498 g_print(
"fit_cubic[2]: recursive call failed\n");
504 g_print(
"fit_cubic: success[nsegs: %d+%d=%d] on max_beziers:%u\n",
505 nsegs1, nsegs2, nsegs1 + nsegs2, max_beziers);
507 return nsegs1 + nsegs2;
527 Point const data[],
double const u[],
unsigned const len,
529 double const tolerance_sq)
531 bool const est1 =
is_zero(tHat1);
532 bool const est2 =
is_zero(tHat2);
533 Point est_tHat1( est1
536 Point est_tHat2( est2
544 if (bezier[1] != bezier[0]) {
554 Point const data[],
double const uPrime[],
unsigned const len,
573 for (
unsigned i = 0; i <
len; i++) {
575 double const b0 = B0(uPrime[i]);
576 double const b1 = B1(uPrime[i]);
577 double const b2 = B2(uPrime[i]);
578 double const b3 = B3(uPrime[i]);
581 Point const a1 = b1 * tHat1;
582 Point const a2 = b2 * tHat2;
584 C[0][0] +=
dot(a1, a1);
585 C[0][1] +=
dot(a1, a2);
587 C[1][1] +=
dot(a2, a2);
591 Point const shortfall
593 - ( ( b0 + b1 ) * bezier[0] )
594 - ( ( b2 + b3 ) * bezier[3] ) );
595 X[0] +=
dot(a1, shortfall);
596 X[1] +=
dot(a2, shortfall);
601 double alpha_l, alpha_r;
604 double const det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
605 if ( det_C0_C1 != 0 ) {
607 double const det_C0_X = C[0][0] *
X[1] - C[0][1] *
X[0];
608 double const det_X_C1 =
X[0] * C[1][1] -
X[1] * C[0][1];
609 alpha_l = det_X_C1 / det_C0_C1;
610 alpha_r = det_C0_X / det_C0_C1;
620 double const c0 = C[0][0] + C[0][1];
622 alpha_l = alpha_r =
X[0] / c0;
624 double const c1 = C[1][0] + C[1][1];
626 alpha_l = alpha_r =
X[1] / c1;
629 alpha_l = alpha_r = 0.;
639 if ( alpha_l < 1.0e-6 ||
647 bezier[1] = alpha_l * tHat1 + bezier[0];
648 bezier[2] = alpha_r * tHat2 + bezier[3];
659 Point const data[],
double const u[],
unsigned const len)
661 if(!(1 <= ei && ei <= 2))
663 unsigned const oi = 3 - ei;
664 double num[2] = {0., 0.};
666 for (
unsigned i = 0; i <
len; ++i) {
667 double const ui = u[i];
668 double const b[4] = {
675 for (
unsigned d = 0; d < 2; ++d) {
676 num[d] += b[ei] * (b[0] * bezier[0][d] +
677 b[oi] * bezier[oi][d] +
678 b[3] * bezier[3][d] +
681 den -= b[ei] * b[ei];
685 for (
unsigned d = 0; d < 2; ++d) {
686 bezier[ei][d] =
num[d] / den;
689 bezier[ei] = ( oi * bezier[0] + ei * bezier[3] ) / 3.;
711 unsigned const last =
len - 1;
712 assert( bezCurve[0] == d[0] );
713 assert( bezCurve[3] == d[last] );
714 assert( u[0] == 0.0 );
715 assert( u[last] == 1.0 );
718 for (
unsigned i = 1; i < last; i++) {
744 Point const diff = Q_u[0] - P;
745 double numerator =
dot(diff, Q_u[1]);
746 double denominator =
dot(Q_u[1], Q_u[1]) +
dot(diff, Q_u[2]);
749 if ( denominator > 0. ) {
752 improved_u = u - ( numerator / denominator );
756 if ( numerator > 0. ) {
757 improved_u = u * .98 - .01;
758 }
else if ( numerator < 0. ) {
760 improved_u = .031 + u * .98;
766 if (!std::isfinite(improved_u)) {
768 }
else if ( improved_u < 0.0 ) {
770 }
else if ( improved_u > 1.0 ) {
776 double const diff_lensq =
lensq(diff);
777 for (
double proportion = .125; ; proportion += .125) {
778 if (
lensq( Q.
pointAt(improved_u) - P ) > diff_lensq ) {
779 if ( proportion > 1.0 ) {
784 improved_u = ( ( 1 - proportion ) * improved_u +
792 DOUBLE_ASSERT(improved_u);
810 for (
unsigned i = 1; i <
len; i++) {
811 double const dist =
distance(d[i], d[i-1]);
812 u[i] = u[i-1] + dist;
816 double tot_len = u[
len - 1];
817 if(!( tot_len != 0 ))
819 if (std::isfinite(tot_len)) {
820 for (
unsigned i = 1; i <
len; ++i) {
825 for (
unsigned i = 1; i <
len; ++i) {
826 u[i] = i / (double) (
len - 1 );
835 if (u[
len - 1] != 1) {
836 double const diff = u[
len - 1] - 1;
837 if (fabs(diff) > 1e-13) {
846 assert( u[0] == 0.0 && u[
len - 1] == 1.0 );
847 for (
unsigned i = 1; i <
len; i++) {
848 assert( u[i] >= u[i-1] );
869 BezierCurve const bezCurve,
double const tolerance,
870 unsigned *
const splitPoint)
873 unsigned const last =
len - 1;
874 assert( bezCurve[0] == d[0] );
875 assert( bezCurve[3] == d[last] );
876 assert( u[0] == 0.0 );
877 assert( u[last] == 1.0 );
883 double maxDistsq = 0.0;
884 double max_hook_ratio = 0.0;
885 unsigned snap_end = 0;
886 Point prev = bezCurve[0];
887 for (
unsigned i = 1; i <= last; i++) {
889 double const distsq =
lensq( curr - d[i] );
890 if ( distsq > maxDistsq ) {
894 double const hook_ratio =
compute_hook(prev, curr, .5 * (u[i - 1] + u[i]), bezCurve, tolerance);
895 if (max_hook_ratio < hook_ratio) {
896 max_hook_ratio = hook_ratio;
902 double const dist_ratio = std::sqrt(maxDistsq) / tolerance;
904 if (max_hook_ratio <= dist_ratio) {
907 assert(0 < snap_end);
908 ret = -max_hook_ratio;
909 *splitPoint = snap_end - 1;
912 || ( ( *splitPoint < last )
913 && ( *splitPoint != 0 || ret < 0. ) ) );
940 double const tolerance)
943 double const dist =
distance((a+b)*.5, P);
944 if (dist < tolerance) {
947 double const allowed =
distance(a, b) + tolerance;
948 return dist / allowed;
967class PointToBezierTester:
public Toy {
970 std::vector<Toggle> toggles;
973 void draw(
cairo_t *cr, std::ostringstream *notify,
int width,
int height,
bool save, std::ostringstream *timer_stream)
override {
977 cairo_set_line_width (cr, 0.5);
978 adjuster2.
pos[0]=150;
979 adjuster2.
pos[1]=std::min(std::max(adjuster2.
pos[1],150.),450.);
984 double scale0=(450-adjuster2.
pos[1])/300;
985 double curve_precision =
pow(10, scale0*5-2);
986 val_s << curve_precision;
993 cairo_set_line_width (cr, 0.5);
995 if(!mouses.empty()) {
997 for(
auto & mouse : mouses) {
1003 if(!mouses.empty()) {
1012 mouses.size(), curve_precision, 240);
1015 *notify <<
"original time = " << als_time << std::endl;
1023 for(
int i = 0; i < segsgenerated; i ++) {
1024 cairo_curve_to(cr, bezier[bezi], bezier[bezi+1], bezier[bezi+2]);
1037 mouses.size(), curve_precision, 240);
1040 *notify <<
"experimental version time = " << als_time << std::endl;
1048 for(
int i = 0; i < segsgenerated; i ++) {
1049 cairo_curve_to(cr, bezier[bezi], bezier[bezi+1], bezier[bezi+2]);
1055 *notify <<
"segments : "<< segsgenerated <<
"\n";
1070 void key_hit(
unsigned keyval,
unsigned modifiers)
override {
1071 if(keyval ==
's') toggles[0].toggle();
1074 vector<Point> mouses;
1089 mouses.emplace_back(pos);
1101 for(
unsigned i = 2; i < mouses.size(); i+=2) {
1108 PointToBezierTester() {
1110 handles.push_back(&adjuster2);
1111 toggles.emplace_back(
"Seq",
false);
1112 toggles.emplace_back(
"Linfty",
true);
1120 init(argc, argv,
new PointToBezierTester);
Basic intersection routines.
Bezier fitting algorithms.
Bezier curve with compile-time specified order.
Two-dimensional Bezier curve of arbitrary order.
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
std::vector< Point > pointAndDerivatives(Coord t, unsigned n) const override
Evaluate the curve and its derivatives.
Sequence of contiguous curves, aka spline.
Piecewise< D2< SBasis > > toPwSb() const
void append(Curve *curve)
Add a new curve to the end of the path.
Function defined as discrete pieces.
Two-dimensional point that doubles as a vector.
void normalize()
Normalize the vector representing the point.
void ask_for_timeslice()
Ask the OS nicely for a big time slice.
virtual void mouse_pressed(Geom::Point const &pos, unsigned button, unsigned modifiers)
virtual void mouse_moved(Geom::Point const &pos, unsigned modifiers)
vector< Handle * > handles
virtual void mouse_released(Geom::Point const &pos, unsigned button, unsigned modifiers)
virtual void save(FILE *f)
virtual void key_hit(unsigned keyval, unsigned modifiers)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
Lifts one dimensional objects into 2D.
BezierCurveN< 2 > QuadraticBezier
Quadratic (order 2) Bezier curve.
Low level math functions and compatibility wrappers.
static void reparameterize(Point const d[], unsigned len, double u[], CubicBezier const &bezCurve)
Given set of points and their parameterization, try to find a better assignment of parameter values f...
static Point const unconstrained_tangent(0, 0)
int bezier_fit_cubic_full(Point bezier[], int split_points[], Point const data[], int const len, Point const &tHat1, Point const &tHat2, double const error, unsigned const max_beziers)
Fit a multi-segment Bezier curve to a set of digitized points, without possible weedout of identical ...
static void generate_bezier(Point b[], Point const d[], double const u[], unsigned len, Point const &tHat1, Point const &tHat2, double tolerance_sq)
Fill in bezier[] based on the given data and tangent requirements, using a least-squares fit.
Point bezier_pt(unsigned const degree, Point const V[], double const t)
int bezier_fit_cubic(Point *bezier, Point const *data, int len, double error)
Fit a single-segment Bezier curve to a set of digitized points.
int bezier_fit_cubic_r(Point bezier[], Point const data[], int const len, double const error, unsigned const max_beziers)
Fit a multi-segment Bezier curve to a set of digitized points, with possible weedout of identical poi...
static Point darray_right_tangent(Point const d[], unsigned const len)
Estimates the (backward) tangent at d[last - 0.5].
static void estimate_lengths(Point bezier[], Point const data[], double const u[], unsigned len, Point const &tHat1, Point const &tHat2)
static void estimate_bi(Point b[4], unsigned ei, Point const data[], double const u[], unsigned len)
static void reparameterize_pts(Point const d[], unsigned len, double u[], BezierCurve const bezCurve)
static double lensq(Point const p)
static double NewtonRaphsonRootFind(CubicBezier const &Q, Point const &P, double u)
Use Newton-Raphson iteration to find better root.
static double compute_hook(Point const &a, Point const &b, double const u, CubicBezier const &bezCurve, double const tolerance)
Whereas compute_max_error_ratio() checks for itself that each data point is near some point on the cu...
static void chord_length_parameterize(Point const d[], double u[], unsigned len)
Assign parameter values to digitized points using relative distances between points.
static double compute_max_error_ratio(Point const d[], double const u[], unsigned len, BezierCurve const bezCurve, double tolerance, unsigned *splitPoint)
Find the maximum squared distance of digitized points to fitted curve, and (if this maximum error is ...
Point darray_left_tangent(Point const d[], unsigned const len)
Estimate the (forward) tangent at point d[first + 0.5].
static Point darray_center_tangent(Point const d[], unsigned center, unsigned length)
Estimates the (backward) tangent at d[center], by averaging the two segments connected to d[center] (...
static unsigned copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[])
Copy points from src to dest, filter out points containing NaN and adjacent points with equal x and y...
Various utility functions.
Coord length(LineSegment const &seg)
Angle distance(Angle const &a, Angle const &b)
bool is_zero(Point const &p)
int bezier_fit_cubic_r(Point bezier[], Point const data[], int len, double error, unsigned max_beziers)
Fit a multi-segment Bezier curve to a set of digitized points, with possible weedout of identical poi...
T bernstein_value_at(double t, T const *c_, unsigned n)
Compute the value of a Bernstein-Bezier polynomial.
T dot(D2< T > const &a, D2< T > const &b)
Point unit_vector(Point const &a)
D2< T > rot90(D2< T > const &a)
void cairo_line_to(cairo_t *cr, Geom::Point p1)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
void cairo_curve_to(cairo_t *cr, Geom::Point p1, Geom::Point p2, Geom::Point p3)
two-dimensional geometric operators.
Polynomial in symmetric power basis (S-basis)
void draw_text(cairo_t *cr, Geom::Point pos, const char *txt, bool bottom=false, const char *fontdesc="Sans")
void toggle_events(std::vector< Toggle > &ts, Geom::Point const &pos, unsigned button)
void cairo_set_source_rgba(cairo_t *cr, colour c)
void init(int argc, char **argv, Toy *t, int width=600, int height=600)