Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / basegfx / source / workbench / bezierclip.cxx
blob7d99dfafc032010ac35c35bdff8deca59fe455b7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
21 #include <iterator>
22 #include <vector>
23 #include <utility>
25 #include <math.h>
27 #include "bezierclip.hxx"
28 #include "gauss.hxx"
32 // what to test
33 #define WITH_ASSERTIONS
34 //#define WITH_CONVEXHULL_TEST
35 //#define WITH_MULTISUBDIVIDE_TEST
36 //#define WITH_FATLINE_TEST
37 //#define WITH_CALCFOCUS_TEST
38 //#define WITH_SAFEPARAMBASE_TEST
39 //#define WITH_SAFEPARAMS_TEST
40 //#define WITH_SAFEPARAM_DETAILED_TEST
41 //#define WITH_SAFEFOCUSPARAM_CALCFOCUS
42 //#define WITH_SAFEFOCUSPARAM_TEST
43 //#define WITH_SAFEFOCUSPARAM_DETAILED_TEST
44 #define WITH_BEZIERCLIP_TEST
48 // -----------------------------------------------------------------------------
50 /* Implementation of the so-called 'Fat-Line Bezier Clipping Algorithm' by Sederberg et al.
52 * Actual reference is: T. W. Sederberg and T Nishita: Curve
53 * intersection using Bezier clipping. In Computer Aided Design, 22
54 * (9), 1990, pp. 538--549
57 // -----------------------------------------------------------------------------
59 /* Misc helper
60 * ===========
62 int fallFac( int n, int k )
64 #ifdef WITH_ASSERTIONS
65 assert(n>=k); // "For factorials, n must be greater or equal k"
66 assert(n>=0); // "For factorials, n must be positive"
67 assert(k>=0); // "For factorials, k must be positive"
68 #endif
70 int res( 1 );
72 while( k-- && n ) res *= n--;
74 return res;
77 // -----------------------------------------------------------------------------
79 int fac( int n )
81 return fallFac(n, n);
84 // -----------------------------------------------------------------------------
86 /* Bezier fat line clipping part
87 * =============================
90 // -----------------------------------------------------------------------------
92 void Impl_calcFatLine( FatLine& line, const Bezier& c )
94 // Prepare normalized implicit line
95 // ================================
97 // calculate vector orthogonal to p1-p4:
98 line.a = -(c.p0.y - c.p3.y);
99 line.b = (c.p0.x - c.p3.x);
101 // normalize
102 const double len( sqrt( line.a*line.a + line.b*line.b ) );
103 if( !tolZero(len) )
105 line.a /= len;
106 line.b /= len;
109 line.c = -(line.a*c.p0.x + line.b*c.p0.y);
112 // Determine bounding fat line from it
113 // ===================================
115 // calc control point distances
116 const double dP2( calcLineDistance(line.a, line.b, line.c, c.p1.x, c.p1.y ) );
117 const double dP3( calcLineDistance(line.a, line.b, line.c, c.p2.x, c.p2.y ) );
119 // calc approximate bounding lines to curve (tight bounds are
120 // possible here, but more expensive to calculate and thus not
121 // worth the overhead)
122 if( dP2 * dP3 > 0.0 )
124 line.dMin = 3.0/4.0 * ::std::min(0.0, ::std::min(dP2, dP3));
125 line.dMax = 3.0/4.0 * ::std::max(0.0, ::std::max(dP2, dP3));
127 else
129 line.dMin = 4.0/9.0 * ::std::min(0.0, ::std::min(dP2, dP3));
130 line.dMax = 4.0/9.0 * ::std::max(0.0, ::std::max(dP2, dP3));
134 void Impl_calcBounds( Point2D& leftTop,
135 Point2D& rightBottom,
136 const Bezier& c1 )
138 leftTop.x = ::std::min( c1.p0.x, ::std::min( c1.p1.x, ::std::min( c1.p2.x, c1.p3.x ) ) );
139 leftTop.y = ::std::min( c1.p0.y, ::std::min( c1.p1.y, ::std::min( c1.p2.y, c1.p3.y ) ) );
140 rightBottom.x = ::std::max( c1.p0.x, ::std::max( c1.p1.x, ::std::max( c1.p2.x, c1.p3.x ) ) );
141 rightBottom.y = ::std::max( c1.p0.y, ::std::max( c1.p1.y, ::std::max( c1.p2.y, c1.p3.y ) ) );
144 bool Impl_doBBoxIntersect( const Bezier& c1,
145 const Bezier& c2 )
147 // calc rectangular boxes from c1 and c2
148 Point2D lt1;
149 Point2D rb1;
150 Point2D lt2;
151 Point2D rb2;
153 Impl_calcBounds( lt1, rb1, c1 );
154 Impl_calcBounds( lt2, rb2, c2 );
156 if( ::std::min(rb1.x, rb2.x) < ::std::max(lt1.x, lt2.x) ||
157 ::std::min(rb1.y, rb2.y) < ::std::max(lt1.y, lt2.y) )
159 return false;
161 else
163 return true;
167 /* calculates two t's for the given bernstein control polygon: the first is
168 * the intersection of the min value line with the convex hull from
169 * the left, the second is the intersection of the max value line with
170 * the convex hull from the right.
172 bool Impl_calcSafeParams( double& t1,
173 double& t2,
174 const Polygon2D& rPoly,
175 double lowerYBound,
176 double upperYBound )
178 // need the convex hull of the control polygon, as this is
179 // guaranteed to completely bound the curve
180 Polygon2D convHull( convexHull(rPoly) );
182 // init min and max buffers
183 t1 = 0.0 ;
184 double currLowerT( 1.0 );
186 t2 = 1.0;
187 double currHigherT( 0.0 );
189 if( convHull.size() <= 1 )
190 return false; // only one point? Then we're done with clipping
192 /* now, clip against lower and higher bounds */
193 Point2D p0;
194 Point2D p1;
196 bool bIntersection( false );
198 for( Polygon2D::size_type i=0; i<convHull.size(); ++i )
200 // have to check against convHull.size() segments, as the
201 // convex hull is, by definition, closed. Thus, for the
202 // last point, we take the first point as partner.
203 if( i+1 == convHull.size() )
205 // close the polygon
206 p0 = convHull[i];
207 p1 = convHull[0];
209 else
211 p0 = convHull[i];
212 p1 = convHull[i+1];
215 // is the segment in question within or crossing the
216 // horizontal band spanned by lowerYBound and upperYBound? If
217 // not, we've got no intersection. If yes, we maybe don't have
218 // an intersection, but we've got to update the permissible
219 // range, nevertheless. This is because inside lying segments
220 // leads to full range forbidden.
221 if( (tolLessEqual(p0.y, upperYBound) || tolLessEqual(p1.y, upperYBound)) &&
222 (tolGreaterEqual(p0.y, lowerYBound) || tolGreaterEqual(p1.y, lowerYBound)) )
224 // calc intersection of convex hull segment with
225 // one of the horizontal bounds lines
226 // to optimize a bit, r_x is calculated only in else case
227 const double r_y( p1.y - p0.y );
229 if( tolZero(r_y) )
231 // r_y is virtually zero, thus we've got a horizontal
232 // line. Now check whether we maybe coincide with lower or
233 // upper horizonal bound line.
234 if( tolEqual(p0.y, lowerYBound) ||
235 tolEqual(p0.y, upperYBound) )
237 // yes, simulate intersection then
238 currLowerT = ::std::min(currLowerT, ::std::min(p0.x, p1.x));
239 currHigherT = ::std::max(currHigherT, ::std::max(p0.x, p1.x));
242 else
244 // check against lower and higher bounds
245 // =====================================
246 const double r_x( p1.x - p0.x );
248 // calc intersection with horizontal dMin line
249 const double currTLow( (lowerYBound - p0.y) * r_x / r_y + p0.x );
251 // calc intersection with horizontal dMax line
252 const double currTHigh( (upperYBound - p0.y) * r_x / r_y + p0.x );
254 currLowerT = ::std::min(currLowerT, ::std::min(currTLow, currTHigh));
255 currHigherT = ::std::max(currHigherT, ::std::max(currTLow, currTHigh));
258 // set flag that at least one segment is contained or
259 // intersects given horizontal band.
260 bIntersection = true;
264 #ifndef WITH_SAFEPARAMBASE_TEST
265 // limit intersections found to permissible t parameter range
266 t1 = ::std::max(0.0, currLowerT);
267 t2 = ::std::min(1.0, currHigherT);
268 #endif
270 return bIntersection;
274 /* calculates two t's for the given bernstein polynomial: the first is
275 * the intersection of the min value line with the convex hull from
276 * the left, the second is the intersection of the max value line with
277 * the convex hull from the right.
279 * The polynomial coefficients c0 to c3 given to this method
280 * must correspond to t values of 0, 1/3, 2/3 and 1, respectively.
282 bool Impl_calcSafeParams_clip( double& t1,
283 double& t2,
284 const FatLine& bounds,
285 double c0,
286 double c1,
287 double c2,
288 double c3 )
290 /* first of all, determine convex hull of c0-c3 */
291 Polygon2D poly(4);
292 poly[0] = Point2D(0, c0);
293 poly[1] = Point2D(1.0/3.0, c1);
294 poly[2] = Point2D(2.0/3.0, c2);
295 poly[3] = Point2D(1, c3);
297 #ifndef WITH_SAFEPARAM_DETAILED_TEST
299 return Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax );
301 #else
302 bool bRet( Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax ) );
304 Polygon2D convHull( convexHull( poly ) );
306 cout << "# convex hull testing" << endl
307 << "plot [t=0:1] ";
308 cout << " bez("
309 << poly[0].x << ","
310 << poly[1].x << ","
311 << poly[2].x << ","
312 << poly[3].x << ",t),bez("
313 << poly[0].y << ","
314 << poly[1].y << ","
315 << poly[2].y << ","
316 << poly[3].y << ",t), "
317 << "t, " << bounds.dMin << ", "
318 << "t, " << bounds.dMax << ", "
319 << t1 << ", t, "
320 << t2 << ", t, "
321 << "'-' using ($1):($2) title \"control polygon\" with lp, "
322 << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
324 unsigned int k;
325 for( k=0; k<poly.size(); ++k )
327 cout << poly[k].x << " " << poly[k].y << endl;
329 cout << poly[0].x << " " << poly[0].y << endl;
330 cout << "e" << endl;
332 for( k=0; k<convHull.size(); ++k )
334 cout << convHull[k].x << " " << convHull[k].y << endl;
336 cout << convHull[0].x << " " << convHull[0].y << endl;
337 cout << "e" << endl;
339 return bRet;
340 #endif
343 // -----------------------------------------------------------------------------
345 void Impl_deCasteljauAt( Bezier& part1,
346 Bezier& part2,
347 const Bezier& input,
348 double t )
350 // deCasteljau bezier arc, scheme is:
352 // First row is C_0^n,C_1^n,...,C_n^n
353 // Second row is P_1^n,...,P_n^n
354 // etc.
355 // with P_k^r = (1 - x_s)P_{k-1}^{r-1} + x_s P_k{r-1}
357 // this results in:
359 // P1 P2 P3 P4
360 // L1 P2 P3 R4
361 // L2 H R3
362 // L3 R2
363 // L4/R1
364 if( tolZero(t) )
366 // t is zero -> part2 is input curve, part1 is empty (input.p0, that is)
367 part1.p0.x = part1.p1.x = part1.p2.x = part1.p3.x = input.p0.x;
368 part1.p0.y = part1.p1.y = part1.p2.y = part1.p3.y = input.p0.y;
369 part2 = input;
371 else if( tolEqual(t, 1.0) )
373 // t is one -> part1 is input curve, part2 is empty (input.p3, that is)
374 part1 = input;
375 part2.p0.x = part2.p1.x = part2.p2.x = part2.p3.x = input.p3.x;
376 part2.p0.y = part2.p1.y = part2.p2.y = part2.p3.y = input.p3.y;
378 else
380 part1.p0.x = input.p0.x; part1.p0.y = input.p0.y;
381 part1.p1.x = (1.0 - t)*part1.p0.x + t*input.p1.x; part1.p1.y = (1.0 - t)*part1.p0.y + t*input.p1.y;
382 const double Hx ( (1.0 - t)*input.p1.x + t*input.p2.x ), Hy ( (1.0 - t)*input.p1.y + t*input.p2.y );
383 part1.p2.x = (1.0 - t)*part1.p1.x + t*Hx; part1.p2.y = (1.0 - t)*part1.p1.y + t*Hy;
384 part2.p3.x = input.p3.x; part2.p3.y = input.p3.y;
385 part2.p2.x = (1.0 - t)*input.p2.x + t*input.p3.x; part2.p2.y = (1.0 - t)*input.p2.y + t*input.p3.y;
386 part2.p1.x = (1.0 - t)*Hx + t*part2.p2.x; part2.p1.y = (1.0 - t)*Hy + t*part2.p2.y;
387 part2.p0.x = (1.0 - t)*part1.p2.x + t*part2.p1.x; part2.p0.y = (1.0 - t)*part1.p2.y + t*part2.p1.y;
388 part1.p3.x = part2.p0.x; part1.p3.y = part2.p0.y;
392 // -----------------------------------------------------------------------------
394 void printCurvesWithSafeRange( const Bezier& c1, const Bezier& c2, double t1_c1, double t2_c1,
395 const Bezier& c2_part, const FatLine& bounds_c2 )
397 static int offset = 0;
399 cout << "# safe param range testing" << endl
400 << "plot [t=0.0:1.0] ";
402 // clip safe ranges off c1
403 Bezier c1_part1;
404 Bezier c1_part2;
405 Bezier c1_part3;
407 // subdivide at t1_c1
408 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1_c1 );
409 // subdivide at t2_c1
410 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, t2_c1 );
412 // output remaining segment (c1_part1)
414 cout << "bez("
415 << c1.p0.x+offset << ","
416 << c1.p1.x+offset << ","
417 << c1.p2.x+offset << ","
418 << c1.p3.x+offset << ",t),bez("
419 << c1.p0.y << ","
420 << c1.p1.y << ","
421 << c1.p2.y << ","
422 << c1.p3.y << ",t), bez("
423 << c2.p0.x+offset << ","
424 << c2.p1.x+offset << ","
425 << c2.p2.x+offset << ","
426 << c2.p3.x+offset << ",t),bez("
427 << c2.p0.y << ","
428 << c2.p1.y << ","
429 << c2.p2.y << ","
430 << c2.p3.y << ",t), "
431 #if 1
432 << "bez("
433 << c1_part1.p0.x+offset << ","
434 << c1_part1.p1.x+offset << ","
435 << c1_part1.p2.x+offset << ","
436 << c1_part1.p3.x+offset << ",t),bez("
437 << c1_part1.p0.y << ","
438 << c1_part1.p1.y << ","
439 << c1_part1.p2.y << ","
440 << c1_part1.p3.y << ",t), "
441 #endif
442 #if 1
443 << "bez("
444 << c2_part.p0.x+offset << ","
445 << c2_part.p1.x+offset << ","
446 << c2_part.p2.x+offset << ","
447 << c2_part.p3.x+offset << ",t),bez("
448 << c2_part.p0.y << ","
449 << c2_part.p1.y << ","
450 << c2_part.p2.y << ","
451 << c2_part.p3.y << ",t), "
452 #endif
453 << "linex("
454 << bounds_c2.a << ","
455 << bounds_c2.b << ","
456 << bounds_c2.c << ",t)+" << offset << ", liney("
457 << bounds_c2.a << ","
458 << bounds_c2.b << ","
459 << bounds_c2.c << ",t) title \"fat line (center)\", linex("
460 << bounds_c2.a << ","
461 << bounds_c2.b << ","
462 << bounds_c2.c-bounds_c2.dMin << ",t)+" << offset << ", liney("
463 << bounds_c2.a << ","
464 << bounds_c2.b << ","
465 << bounds_c2.c-bounds_c2.dMin << ",t) title \"fat line (min) \", linex("
466 << bounds_c2.a << ","
467 << bounds_c2.b << ","
468 << bounds_c2.c-bounds_c2.dMax << ",t)+" << offset << ", liney("
469 << bounds_c2.a << ","
470 << bounds_c2.b << ","
471 << bounds_c2.c-bounds_c2.dMax << ",t) title \"fat line (max) \"" << endl;
473 offset += 1;
476 // -----------------------------------------------------------------------------
478 void printResultWithFinalCurves( const Bezier& c1, const Bezier& c1_part,
479 const Bezier& c2, const Bezier& c2_part,
480 double t1_c1, double t2_c1 )
482 static int offset = 0;
484 cout << "# final result" << endl
485 << "plot [t=0.0:1.0] ";
487 cout << "bez("
488 << c1.p0.x+offset << ","
489 << c1.p1.x+offset << ","
490 << c1.p2.x+offset << ","
491 << c1.p3.x+offset << ",t),bez("
492 << c1.p0.y << ","
493 << c1.p1.y << ","
494 << c1.p2.y << ","
495 << c1.p3.y << ",t), bez("
496 << c1_part.p0.x+offset << ","
497 << c1_part.p1.x+offset << ","
498 << c1_part.p2.x+offset << ","
499 << c1_part.p3.x+offset << ",t),bez("
500 << c1_part.p0.y << ","
501 << c1_part.p1.y << ","
502 << c1_part.p2.y << ","
503 << c1_part.p3.y << ",t), "
504 << " pointmarkx(bez("
505 << c1.p0.x+offset << ","
506 << c1.p1.x+offset << ","
507 << c1.p2.x+offset << ","
508 << c1.p3.x+offset << ","
509 << t1_c1 << "),t), "
510 << " pointmarky(bez("
511 << c1.p0.y << ","
512 << c1.p1.y << ","
513 << c1.p2.y << ","
514 << c1.p3.y << ","
515 << t1_c1 << "),t), "
516 << " pointmarkx(bez("
517 << c1.p0.x+offset << ","
518 << c1.p1.x+offset << ","
519 << c1.p2.x+offset << ","
520 << c1.p3.x+offset << ","
521 << t2_c1 << "),t), "
522 << " pointmarky(bez("
523 << c1.p0.y << ","
524 << c1.p1.y << ","
525 << c1.p2.y << ","
526 << c1.p3.y << ","
527 << t2_c1 << "),t), "
529 << "bez("
530 << c2.p0.x+offset << ","
531 << c2.p1.x+offset << ","
532 << c2.p2.x+offset << ","
533 << c2.p3.x+offset << ",t),bez("
534 << c2.p0.y << ","
535 << c2.p1.y << ","
536 << c2.p2.y << ","
537 << c2.p3.y << ",t), "
538 << "bez("
539 << c2_part.p0.x+offset << ","
540 << c2_part.p1.x+offset << ","
541 << c2_part.p2.x+offset << ","
542 << c2_part.p3.x+offset << ",t),bez("
543 << c2_part.p0.y << ","
544 << c2_part.p1.y << ","
545 << c2_part.p2.y << ","
546 << c2_part.p3.y << ",t)" << endl;
548 offset += 1;
551 // -----------------------------------------------------------------------------
553 /** determine parameter ranges [0,t1) and (t2,1] on c1, where c1 is guaranteed to lie outside c2.
554 Returns false, if the two curves don't even intersect.
556 @param t1
557 Range [0,t1) on c1 is guaranteed to lie outside c2
559 @param t2
560 Range (t2,1] on c1 is guaranteed to lie outside c2
562 @param c1_orig
563 Original curve c1
565 @param c1_part
566 Subdivided current part of c1
568 @param c2_orig
569 Original curve c2
571 @param c2_part
572 Subdivided current part of c2
574 bool Impl_calcClipRange( double& t1,
575 double& t2,
576 const Bezier& c1_orig,
577 const Bezier& c1_part,
578 const Bezier& c2_orig,
579 const Bezier& c2_part )
581 // TODO: Maybe also check fat line orthogonal to P0P3, having P0
582 // and P3 as the extremal points
584 if( Impl_doBBoxIntersect(c1_part, c2_part) )
586 // Calculate fat lines around c1
587 FatLine bounds_c2;
589 // must use the subdivided version of c2, since the fat line
590 // algorithm works implicitely with the convex hull bounding
591 // box.
592 Impl_calcFatLine(bounds_c2, c2_part);
594 // determine clip positions on c2. Can use original c1 (which
595 // is necessary anyway, to get the t's on the original curve),
596 // since the distance calculations work directly in the
597 // Bernstein polynom parameter domain.
598 if( Impl_calcSafeParams_clip( t1, t2, bounds_c2,
599 calcLineDistance( bounds_c2.a,
600 bounds_c2.b,
601 bounds_c2.c,
602 c1_orig.p0.x,
603 c1_orig.p0.y ),
604 calcLineDistance( bounds_c2.a,
605 bounds_c2.b,
606 bounds_c2.c,
607 c1_orig.p1.x,
608 c1_orig.p1.y ),
609 calcLineDistance( bounds_c2.a,
610 bounds_c2.b,
611 bounds_c2.c,
612 c1_orig.p2.x,
613 c1_orig.p2.y ),
614 calcLineDistance( bounds_c2.a,
615 bounds_c2.b,
616 bounds_c2.c,
617 c1_orig.p3.x,
618 c1_orig.p3.y ) ) )
620 //printCurvesWithSafeRange(c1_orig, c2_orig, t1, t2, c2_part, bounds_c2);
622 // they do intersect
623 return true;
627 // they don't intersect: nothing to do
628 return false;
631 // -----------------------------------------------------------------------------
633 /* Tangent intersection part
634 * =========================
637 // -----------------------------------------------------------------------------
639 void Impl_calcFocus( Bezier& res, const Bezier& c )
641 // arbitrary small value, for now
642 // TODO: find meaningful value
643 const double minPivotValue( 1.0e-20 );
645 Point2D::value_type fMatrix[6];
646 Point2D::value_type fRes[2];
648 // calc new curve from hodograph, c and linear blend
650 // Coefficients for derivative of c are (C_i=n(C_{i+1} - C_i)):
652 // 3(P1 - P0), 3(P2 - P1), 3(P3 - P2) (bezier curve of degree 2)
654 // The hodograph is then (bezier curve of 2nd degree is P0(1-t)^2 + 2P1(1-t)t + P2t^2):
656 // 3(P1 - P0)(1-t)^2 + 6(P2 - P1)(1-t)t + 3(P3 - P2)t^2
658 // rotate by 90 degrees: x=-y, y=x and you get the normal vector function N(t):
660 // x(t) = -(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
661 // y(t) = 3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2
663 // Now, the focus curve is defined to be F(t)=P(t) + c(t)N(t),
664 // where P(t) is the original curve, and c(t)=c0(1-t) + c1 t
666 // This results in the following expression for F(t):
668 // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
669 // (c0(1-t) + c1 t)(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
671 // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1.t)t^2 + P3.y t^3 +
672 // (c0(1-t) + c1 t)(3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2)
674 // As a heuristic, we set F(0)=F(1) (thus, the curve is closed and _tends_ to be small):
676 // For F(0), the following results:
678 // x(0) = P0.x - c0 3(P1.y - P0.y)
679 // y(0) = P0.y + c0 3(P1.x - P0.x)
681 // For F(1), the following results:
683 // x(1) = P3.x - c1 3(P3.y - P2.y)
684 // y(1) = P3.y + c1 3(P3.x - P2.x)
686 // Reorder, collect and substitute into F(0)=F(1):
688 // P0.x - c0 3(P1.y - P0.y) = P3.x - c1 3(P3.y - P2.y)
689 // P0.y + c0 3(P1.x - P0.x) = P3.y + c1 3(P3.x - P2.x)
691 // which yields
693 // (P0.y - P1.y)c0 + (P3.y - P2.y)c1 = (P3.x - P0.x)/3
694 // (P1.x - P0.x)c0 + (P2.x - P3.x)c1 = (P3.y - P0.y)/3
697 // so, this is what we calculate here (determine c0 and c1):
698 fMatrix[0] = c.p1.x - c.p0.x;
699 fMatrix[1] = c.p2.x - c.p3.x;
700 fMatrix[2] = (c.p3.y - c.p0.y)/3.0;
701 fMatrix[3] = c.p0.y - c.p1.y;
702 fMatrix[4] = c.p3.y - c.p2.y;
703 fMatrix[5] = (c.p3.x - c.p0.x)/3.0;
705 // TODO: determine meaningful value for
706 if( !solve(fMatrix, 2, 3, fRes, minPivotValue) )
708 // TODO: generate meaningful values here
709 // singular or nearly singular system -- use arbitrary
710 // values for res
711 fRes[0] = 0.0;
712 fRes[1] = 1.0;
714 cerr << "Matrix singular!" << endl;
717 // now, the reordered and per-coefficient collected focus curve is
718 // the following third degree bezier curve F(t):
720 // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
721 // (c0(1-t) + c1 t)(3(P1.y - P0.y)(1-t)^2 + 6(P2.y - P1.y)(1-t)t + 3(P3.y - P2.y)t^2)
722 // = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
723 // (3c0P1.y(1-t)^3 - 3c0P0.y(1-t)^3 + 6c0P2.y(1-t)^2t - 6c0P1.y(1-t)^2t +
724 // 3c0P3.y(1-t)t^2 - 3c0P2.y(1-t)t^2 +
725 // 3c1P1.y(1-t)^2t - 3c1P0.y(1-t)^2t + 6c1P2.y(1-t)t^2 - 6c1P1.y(1-t)t^2 +
726 // 3c1P3.yt^3 - 3c1P2.yt^3)
727 // = (P0.x - 3 c0 P1.y + 3 c0 P0.y)(1-t)^3 +
728 // 3(P1.x - c1 P1.y + c1 P0.y - 2 c0 P2.y + 2 c0 P1.y)(1-t)^2t +
729 // 3(P2.x - 2 c1 P2.y + 2 c1 P1.y - c0 P3.y + c0 P2.y)(1-t)t^2 +
730 // (P3.x - 3 c1 P3.y + 3 c1 P2.y)t^3
731 // = (P0.x - 3 c0(P1.y - P0.y))(1-t)^3 +
732 // 3(P1.x - c1(P1.y - P0.y) - 2c0(P2.y - P1.y))(1-t)^2t +
733 // 3(P2.x - 2 c1(P2.y - P1.y) - c0(P3.y - P2.y))(1-t)t^2 +
734 // (P3.x - 3 c1(P3.y - P2.y))t^3
736 // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
737 // (c0(1-t) + c1 t)(3(P1.x - P0.x)(1-t)^2 + 6(P2.x - P1.x)(1-t)t + 3(P3.x - P2.x)t^2)
738 // = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
739 // 3c0(P1.x - P0.x)(1-t)^3 + 6c0(P2.x - P1.x)(1-t)^2t + 3c0(P3.x - P2.x)(1-t)t^2 +
740 // 3c1(P1.x - P0.x)(1-t)^2t + 6c1(P2.x - P1.x)(1-t)t^2 + 3c1(P3.x - P2.x)t^3
741 // = (P0.y + 3 c0 (P1.x - P0.x))(1-t)^3 +
742 // 3(P1.y + 2 c0 (P2.x - P1.x) + c1 (P1.x - P0.x))(1-t)^2t +
743 // 3(P2.y + c0 (P3.x - P2.x) + 2 c1 (P2.x - P1.x))(1-t)t^2 +
744 // (P3.y + 3 c1 (P3.x - P2.x))t^3
746 // Therefore, the coefficients F0 to F3 of the focus curve are:
748 // F0.x = (P0.x - 3 c0(P1.y - P0.y)) F0.y = (P0.y + 3 c0 (P1.x - P0.x))
749 // F1.x = (P1.x - c1(P1.y - P0.y) - 2c0(P2.y - P1.y)) F1.y = (P1.y + 2 c0 (P2.x - P1.x) + c1 (P1.x - P0.x))
750 // F2.x = (P2.x - 2 c1(P2.y - P1.y) - c0(P3.y - P2.y)) F2.y = (P2.y + c0 (P3.x - P2.x) + 2 c1 (P2.x - P1.x))
751 // F3.x = (P3.x - 3 c1(P3.y - P2.y)) F3.y = (P3.y + 3 c1 (P3.x - P2.x))
753 res.p0.x = c.p0.x - 3*fRes[0]*(c.p1.y - c.p0.y);
754 res.p1.x = c.p1.x - fRes[1]*(c.p1.y - c.p0.y) - 2*fRes[0]*(c.p2.y - c.p1.y);
755 res.p2.x = c.p2.x - 2*fRes[1]*(c.p2.y - c.p1.y) - fRes[0]*(c.p3.y - c.p2.y);
756 res.p3.x = c.p3.x - 3*fRes[1]*(c.p3.y - c.p2.y);
758 res.p0.y = c.p0.y + 3*fRes[0]*(c.p1.x - c.p0.x);
759 res.p1.y = c.p1.y + 2*fRes[0]*(c.p2.x - c.p1.x) + fRes[1]*(c.p1.x - c.p0.x);
760 res.p2.y = c.p2.y + fRes[0]*(c.p3.x - c.p2.x) + 2*fRes[1]*(c.p2.x - c.p1.x);
761 res.p3.y = c.p3.y + 3*fRes[1]*(c.p3.x - c.p2.x);
764 // -----------------------------------------------------------------------------
766 bool Impl_calcSafeParams_focus( double& t1,
767 double& t2,
768 const Bezier& curve,
769 const Bezier& focus )
771 // now, we want to determine which normals of the original curve
772 // P(t) intersect with the focus curve F(t). The condition for
773 // this statement is P'(t)(P(t) - F) = 0, i.e. hodograph P'(t) and
774 // line through P(t) and F are perpendicular.
775 // If you expand this equation, you end up with something like
777 // (\sum_{i=0}^n (P_i - F)B_i^n(t))^T (\sum_{j=0}^{n-1} n(P_{j+1} - P_j)B_j^{n-1}(t))
779 // Multiplying that out (as the scalar product is linear, we can
780 // extract some terms) yields:
782 // (P_i - F)^T n(P_{j+1} - P_j) B_i^n(t)B_j^{n-1}(t) + ...
784 // If we combine the B_i^n(t)B_j^{n-1}(t) product, we arrive at a
785 // Bernstein polynomial of degree 2n-1, as
787 // \binom{n}{i}(1-t)^{n-i}t^i) \binom{n-1}{j}(1-t)^{n-1-j}t^j) =
788 // \binom{n}{i}\binom{n-1}{j}(1-t)^{2n-1-i-j}t^{i+j}
790 // Thus, with the defining equation for a 2n-1 degree Bernstein
791 // polynomial
793 // \sum_{i=0}^{2n-1} d_i B_i^{2n-1}(t)
795 // the d_i are calculated as follows:
797 // d_i = \sum_{j+k=i, j\in\{0,...,n\}, k\in\{0,...,n-1\}} \frac{\binom{n}{j}\binom{n-1}{k}}{\binom{2n-1}{i}} n (P_{k+1} - P_k)^T(P_j - F)
800 // Okay, but F is now not a single point, but itself a curve
801 // F(u). Thus, for every value of u, we get a different 2n-1
802 // bezier curve from the above equation. Therefore, we have a
803 // tensor product bezier patch, with the following defining
804 // equation:
806 // d(t,u) = \sum_{i=0}^{2n-1} \sum_{j=0}^m B_i^{2n-1}(t) B_j^{m}(u) d_{ij}, where
807 // d_{ij} = \sum_{k+l=i, l\in\{0,...,n\}, k\in\{0,...,n-1\}} \frac{\binom{n}{l}\binom{n-1}{k}}{\binom{2n-1}{i}} n (P_{k+1} - P_k)^T(P_l - F_j)
809 // as above, only that now F is one of the focus' control points.
811 // Note the difference in the binomial coefficients to the
812 // reference paper, these formulas most probably contained a typo.
814 // To determine, where D(t,u) is _not_ zero (these are the parts
815 // of the curve that don't share normals with the focus and can
816 // thus be safely clipped away), we project D(u,t) onto the
817 // (d(t,u), t) plane, determine the convex hull there and proceed
818 // as for the curve intersection part (projection is orthogonal to
819 // u axis, thus simply throw away u coordinate).
821 // \fallfac are so-called falling factorials (see Concrete
822 // Mathematics, p. 47 for a definition).
825 // now, for tensor product bezier curves, the convex hull property
826 // holds, too. Thus, we simply project the control points (t_{ij},
827 // u_{ij}, d_{ij}) onto the (t,d) plane and calculate the
828 // intersections of the convex hull with the t axis, as for the
829 // bezier clipping case.
832 // calc polygon of control points (t_{ij}, d_{ij}):
834 const int n( 3 ); // cubic bezier curves, as a matter of fact
835 const int i_card( 2*n );
836 const int j_card( n + 1 );
837 const int k_max( n-1 );
838 Polygon2D controlPolygon( i_card*j_card ); // vector of (t_{ij}, d_{ij}) in row-major order
840 int i, j, k, l; // variable notation from formulas above and Sederberg article
841 Point2D::value_type d;
842 for( i=0; i<i_card; ++i )
844 for( j=0; j<j_card; ++j )
846 // calc single d_{ij} sum:
847 for( d=0.0, k=::std::max(0,i-n); k<=k_max && k<=i; ++k )
849 l = i - k; // invariant: k + l = i
850 assert(k>=0 && k<=n-1); // k \in {0,...,n-1}
851 assert(l>=0 && l<=n); // l \in {0,...,n}
853 // TODO: find, document and assert proper limits for n and int's max_val.
854 // This becomes important should anybody wants to use
855 // this code for higher-than-cubic beziers
856 d += static_cast<double>(fallFac(n,l)*fallFac(n-1,k)*fac(i)) /
857 static_cast<double>(fac(l)*fac(k) * fallFac(2*n-1,i)) * n *
858 ( (curve[k+1].x - curve[k].x)*(curve[l].x - focus[j].x) + // dot product here
859 (curve[k+1].y - curve[k].y)*(curve[l].y - focus[j].y) );
862 // Note that the t_{ij} values are evenly spaced on the
863 // [0,1] interval, thus t_{ij}=i/(2n-1)
864 controlPolygon[ i*j_card + j ] = Point2D( i/(2.0*n-1.0), d );
868 #ifndef WITH_SAFEFOCUSPARAM_DETAILED_TEST
870 // calc safe parameter range, to determine [0,t1] and [t2,1] where
871 // no zero crossing is guaranteed.
872 return Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 );
874 #else
875 bool bRet( Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 ) );
877 Polygon2D convHull( convexHull( controlPolygon ) );
879 cout << "# convex hull testing (focus)" << endl
880 << "plot [t=0:1] ";
881 cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
882 << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
884 unsigned int count;
885 for( count=0; count<controlPolygon.size(); ++count )
887 cout << controlPolygon[count].x << " " << controlPolygon[count].y << endl;
889 cout << controlPolygon[0].x << " " << controlPolygon[0].y << endl;
890 cout << "e" << endl;
892 for( count=0; count<convHull.size(); ++count )
894 cout << convHull[count].x << " " << convHull[count].y << endl;
896 cout << convHull[0].x << " " << convHull[0].y << endl;
897 cout << "e" << endl;
899 return bRet;
900 #endif
903 // -----------------------------------------------------------------------------
905 /** Calc all values t_i on c1, for which safeRanges functor does not
906 give a safe range on c1 and c2.
908 This method is the workhorse of the bezier clipping. Because c1
909 and c2 must be alternatingly tested against each other (first
910 determine safe parameter interval on c1 with regard to c2, then
911 the other way around), we call this method recursively with c1 and
912 c2 swapped.
914 @param result
915 Output iterator where the final t values are added to. If curves
916 don't intersect, nothing is added.
918 @param delta
919 Maximal allowed distance to true critical point (measured in the
920 original curve's coordinate system)
922 @param safeRangeFunctor
923 Functor object, that must provide the following operator():
924 bool safeRangeFunctor( double& t1,
925 double& t2,
926 const Bezier& c1_orig,
927 const Bezier& c1_part,
928 const Bezier& c2_orig,
929 const Bezier& c2_part );
930 This functor must calculate the safe ranges [0,t1] and [t2,1] on
931 c1_orig, where c1_orig is 'safe' from c2_part. If the whole
932 c1_orig is safe, false must be returned, true otherwise.
934 template <class Functor> void Impl_applySafeRanges_rec( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
935 double delta,
936 const Functor& safeRangeFunctor,
937 int recursionLevel,
938 const Bezier& c1_orig,
939 const Bezier& c1_part,
940 double last_t1_c1,
941 double last_t2_c1,
942 const Bezier& c2_orig,
943 const Bezier& c2_part,
944 double last_t1_c2,
945 double last_t2_c2 )
947 // check end condition
948 // ===================
950 // TODO: tidy up recursion handling. maybe put everything in a
951 // struct and swap that here at method entry
953 // TODO: Implement limit on recursion depth. Should that limit be
954 // reached, chances are that we're on a higher-order tangency. For
955 // this case, AW proposed to take the middle of the current
956 // interval, and to correct both curve's tangents at that new
957 // endpoint to be equal. That virtually generates a first-order
958 // tangency, and justifies to return a single intersection
959 // point. Otherwise, inside/outside test might fail here.
961 for( int i=0; i<recursionLevel; ++i ) cerr << " ";
962 if( recursionLevel % 2 )
964 cerr << "level: " << recursionLevel
965 << " t: "
966 << last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0
967 << ", c1: " << last_t1_c2 << " " << last_t2_c2
968 << ", c2: " << last_t1_c1 << " " << last_t2_c1
969 << endl;
971 else
973 cerr << "level: " << recursionLevel
974 << " t: "
975 << last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0
976 << ", c1: " << last_t1_c1 << " " << last_t2_c1
977 << ", c2: " << last_t1_c2 << " " << last_t2_c2
978 << endl;
981 // refine solution
982 // ===============
984 double t1_c1, t2_c1;
986 // Note: we first perform the clipping and only test for precision
987 // sufficiency afterwards, since we want to exploit the fact that
988 // Impl_calcClipRange returns false if the curves don't
989 // intersect. We would have to check that separately for the end
990 // condition, otherwise.
992 // determine safe range on c1_orig
993 if( safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part ) )
995 // now, t1 and t2 are calculated on the original curve
996 // (but against a fat line calculated from the subdivided
997 // c2, namely c2_part). If the [t1,t2] range is outside
998 // our current [last_t1,last_t2] range, we're done in this
999 // branch - the curves no longer intersect.
1000 if( tolLessEqual(t1_c1, last_t2_c1) && tolGreaterEqual(t2_c1, last_t1_c1) )
1002 // As noted above, t1 and t2 are calculated on the
1003 // original curve, but against a fat line
1004 // calculated from the subdivided c2, namely
1005 // c2_part. Our domain to work on is
1006 // [last_t1,last_t2], on the other hand, so values
1007 // of [t1,t2] outside that range are irrelevant
1008 // here. Clip range appropriately.
1009 t1_c1 = ::std::max(t1_c1, last_t1_c1);
1010 t2_c1 = ::std::min(t2_c1, last_t2_c1);
1012 // TODO: respect delta
1013 // for now, end condition is just a fixed threshold on the t's
1015 // check end condition
1016 // ===================
1018 #if 1
1019 if( fabs(last_t2_c1 - last_t1_c1) < 0.0001 &&
1020 fabs(last_t2_c2 - last_t1_c2) < 0.0001 )
1021 #else
1022 if( fabs(last_t2_c1 - last_t1_c1) < 0.01 &&
1023 fabs(last_t2_c2 - last_t1_c2) < 0.01 )
1024 #endif
1026 // done. Add to result
1027 if( recursionLevel % 2 )
1029 // uneven level: have to swap the t's, since curves are swapped, too
1030 *result++ = ::std::make_pair( last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0,
1031 last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0 );
1033 else
1035 *result++ = ::std::make_pair( last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0,
1036 last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0 );
1039 #if 0
1040 //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, last_t1_c1, last_t2_c1 );
1041 printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, t1_c1, t2_c1 );
1042 #else
1043 // calc focus curve of c2
1044 Bezier focus;
1045 Impl_calcFocus(focus, c2_part); // need to use subdivided c2
1047 safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
1049 //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, t1_c1, t2_c1 );
1050 printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, last_t1_c1, last_t2_c1 );
1051 #endif
1053 else
1055 // heuristic: if parameter range is not reduced by at least
1056 // 20%, subdivide longest curve, and clip shortest against
1057 // both parts of longest
1058 // if( (last_t2_c1 - last_t1_c1 - t2_c1 + t1_c1) / (last_t2_c1 - last_t1_c1) < 0.2 )
1059 if( false )
1061 // subdivide and descend
1062 // =====================
1064 Bezier part1;
1065 Bezier part2;
1067 double intervalMiddle;
1069 if( last_t2_c1 - last_t1_c1 > last_t2_c2 - last_t1_c2 )
1071 // subdivide c1
1072 // ============
1074 intervalMiddle = last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0;
1076 // subdivide at the middle of the interval (as
1077 // we're not subdividing on the original
1078 // curve, this simply amounts to subdivision
1079 // at 0.5)
1080 Impl_deCasteljauAt( part1, part2, c1_part, 0.5 );
1082 // and descend recursively with swapped curves
1083 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1084 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1085 c1_orig, part1, last_t1_c1, intervalMiddle );
1087 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1088 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1089 c1_orig, part2, intervalMiddle, last_t2_c1 );
1091 else
1093 // subdivide c2
1094 // ============
1096 intervalMiddle = last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0;
1098 // subdivide at the middle of the interval (as
1099 // we're not subdividing on the original
1100 // curve, this simply amounts to subdivision
1101 // at 0.5)
1102 Impl_deCasteljauAt( part1, part2, c2_part, 0.5 );
1104 // and descend recursively with swapped curves
1105 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1106 c2_orig, part1, last_t1_c2, intervalMiddle,
1107 c1_orig, c1_part, last_t1_c1, last_t2_c1 );
1109 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1110 c2_orig, part2, intervalMiddle, last_t2_c2,
1111 c1_orig, c1_part, last_t1_c1, last_t2_c1 );
1114 else
1116 // apply calculated clip
1117 // =====================
1119 // clip safe ranges off c1_orig
1120 Bezier c1_part1;
1121 Bezier c1_part2;
1122 Bezier c1_part3;
1124 // subdivide at t1_c1
1125 Impl_deCasteljauAt( c1_part1, c1_part2, c1_orig, t1_c1 );
1127 // subdivide at t2_c1. As we're working on
1128 // c1_part2 now, we have to adapt t2_c1 since
1129 // we're no longer in the original parameter
1130 // interval. This is based on the following
1131 // assumption: t2_new = (t2-t1)/(1-t1), which
1132 // relates the t2 value into the new parameter
1133 // range [0,1] of c1_part2.
1134 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2_c1-t1_c1)/(1.0-t1_c1) );
1136 // descend with swapped curves and c1_part1 as the
1137 // remaining (middle) segment
1138 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1139 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1140 c1_orig, c1_part1, t1_c1, t2_c1 );
1147 // -----------------------------------------------------------------------------
1149 struct ClipBezierFunctor
1151 bool operator()( double& t1_c1,
1152 double& t2_c1,
1153 const Bezier& c1_orig,
1154 const Bezier& c1_part,
1155 const Bezier& c2_orig,
1156 const Bezier& c2_part ) const
1158 return Impl_calcClipRange( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
1162 // -----------------------------------------------------------------------------
1164 struct BezierTangencyFunctor
1166 bool operator()( double& t1_c1,
1167 double& t2_c1,
1168 const Bezier& c1_orig,
1169 const Bezier& c1_part,
1170 const Bezier& c2_orig,
1171 const Bezier& c2_part ) const
1173 // calc focus curve of c2
1174 Bezier focus;
1175 Impl_calcFocus(focus, c2_part); // need to use subdivided c2
1176 // here, as the whole curve is
1177 // used for focus calculation
1179 // determine safe range on c1_orig
1180 bool bRet( Impl_calcSafeParams_focus( t1_c1, t2_c1,
1181 c1_orig, // use orig curve here, need t's on original curve
1182 focus ) );
1184 cerr << "range: " << t2_c1 - t1_c1 << ", ret: " << bRet << endl;
1186 return bRet;
1190 // -----------------------------------------------------------------------------
1192 /** Perform a bezier clip (curve against curve)
1194 @param result
1195 Output iterator where the final t values are added to. This
1196 iterator will remain empty, if there are no intersections.
1198 @param delta
1199 Maximal allowed distance to true intersection (measured in the
1200 original curve's coordinate system)
1202 void clipBezier( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
1203 double delta,
1204 const Bezier& c1,
1205 const Bezier& c2 )
1207 #if 0
1208 // first of all, determine list of collinear normals. Collinear
1209 // normals typically separate two intersections, thus, subdivide
1210 // at all collinear normal's t values beforehand. This will cater
1211 // for tangent intersections, where two or more intersections are
1212 // infinitesimally close together.
1214 // TODO: evaluate effects of higher-than-second-order
1215 // tangencies. Sederberg et al. state that collinear normal
1216 // algorithm then degrades quickly.
1218 ::std::vector< ::std::pair<double,double> > results;
1219 ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(results);
1221 Impl_calcCollinearNormals( ii, delta, 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1223 // As Sederberg's collinear normal theorem is only sufficient, not
1224 // necessary for two intersections left and right, we've to test
1225 // all segments generated by the collinear normal algorithm
1226 // against each other. In other words, if the two curves are both
1227 // divided in a left and a right part, the collinear normal
1228 // theorem does _not_ state that the left part of curve 1 does not
1229 // e.g. intersect with the right part of curve 2.
1231 // divide c1 and c2 at collinear normal intersection points
1232 ::std::vector< Bezier > c1_segments( results.size()+1 );
1233 ::std::vector< Bezier > c2_segments( results.size()+1 );
1234 Bezier c1_remainder( c1 );
1235 Bezier c2_remainder( c2 );
1236 unsigned int i;
1237 for( i=0; i<results.size(); ++i )
1239 Bezier c1_part2;
1240 Impl_deCasteljauAt( c1_segments[i], c1_part2, c1_remainder, results[i].first );
1241 c1_remainder = c1_part2;
1243 Bezier c2_part2;
1244 Impl_deCasteljauAt( c2_segments[i], c2_part2, c2_remainder, results[i].second );
1245 c2_remainder = c2_part2;
1247 c1_segments[i] = c1_remainder;
1248 c2_segments[i] = c2_remainder;
1250 // now, c1/c2_segments contain all segments, then
1251 // clip every resulting segment against every other
1252 unsigned int c1_curr, c2_curr;
1253 for( c1_curr=0; c1_curr<c1_segments.size(); ++c1_curr )
1255 for( c2_curr=0; c2_curr<c2_segments.size(); ++c2_curr )
1257 if( c1_curr != c2_curr )
1259 Impl_clipBezier_rec(result, delta, 0,
1260 c1_segments[c1_curr], c1_segments[c1_curr],
1261 0.0, 1.0,
1262 c2_segments[c2_curr], c2_segments[c2_curr],
1263 0.0, 1.0);
1267 #else
1268 Impl_applySafeRanges_rec( result, delta, BezierTangencyFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1269 //Impl_applySafeRanges_rec( result, delta, ClipBezierFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1270 #endif
1271 // that's it, boys'n'girls!
1274 int main(int argc, const char *argv[])
1276 double curr_Offset( 0 );
1277 unsigned int i,j,k;
1279 Bezier someCurves[] =
1281 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.0)},
1282 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
1283 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)}
1284 // {Point2D(0.25+1,0.5),Point2D(0.25+1,0.708333),Point2D(0.423611+1,0.916667),Point2D(0.770833+1,0.980324)},
1285 // {Point2D(0.0+1,0.0),Point2D(0.0+1,1.0),Point2D(1.0+1,1.0),Point2D(1.0+1,0.5)}
1287 // tangency1
1288 // {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)},
1289 // {Point2D(0.0,1.0),Point2D(0.1,1.0),Point2D(0.4,1.0),Point2D(0.5,1.0)}
1291 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
1292 // {Point2D(0.60114,0.933091),Point2D(0.69461,0.969419),Point2D(0.80676,0.992976),Point2D(0.93756,0.998663)}
1293 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
1294 // {Point2D(0.62712,0.828427),Point2D(0.76305,0.828507),Point2D(0.88555,0.77312),Point2D(0.95069,0.67325)}
1296 // clipping1
1297 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1298 // {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)}
1300 // tangency2
1301 // {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)},
1302 // {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
1304 // tangency3
1305 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
1306 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)}
1308 // tangency4
1309 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
1310 // {Point2D(0.26,0.4),Point2D(0.25,0.5),Point2D(0.25,0.5),Point2D(0.26,0.6)}
1312 // tangency5
1313 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1314 // {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
1316 // tangency6
1317 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1318 // {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
1320 // tangency7
1321 // {Point2D(2.505,0.0),Point2D(2.505+4.915,4.300),Point2D(2.505+3.213,10.019),Point2D(2.505-2.505,10.255)},
1322 // {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
1324 // tangency Sederberg example
1325 {Point2D(2.505,0.0),Point2D(2.505+4.915,4.300),Point2D(2.505+3.213,10.019),Point2D(2.505-2.505,10.255)},
1326 {Point2D(5.33+9.311,0.0),Point2D(5.33+9.311-13.279,4.205),Point2D(5.33+9.311-10.681,9.119),Point2D(5.33+9.311-2.603,10.254)}
1328 // clipping2
1329 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
1330 // {Point2D(0.2575,0.4),Point2D(0.2475,0.5),Point2D(0.2475,0.5),Point2D(0.2575,0.6)}
1332 // {Point2D(0.0,0.1),Point2D(0.2,3.5),Point2D(1.0,-2.5),Point2D(1.1,1.2)},
1333 // {Point2D(0.0,1.0),Point2D(3.5,0.9),Point2D(-2.5,0.1),Point2D(1.1,0.2)}
1334 // {Point2D(0.0,0.1),Point2D(0.2,3.0),Point2D(1.0,-2.0),Point2D(1.1,1.2)},
1335 // {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)}
1336 // {Point2D(0.0,1.0),Point2D(3.0,0.9),Point2D(-2.0,0.1),Point2D(1.1,0.2)}
1337 // {Point2D(0.0,4.0),Point2D(0.1,5.0),Point2D(0.9,5.0),Point2D(1.0,4.0)},
1338 // {Point2D(0.0,0.0),Point2D(0.1,0.5),Point2D(0.9,0.5),Point2D(1.0,0.0)},
1339 // {Point2D(0.0,0.1),Point2D(0.1,1.5),Point2D(0.9,1.5),Point2D(1.0,0.1)},
1340 // {Point2D(0.0,-4.0),Point2D(0.1,-5.0),Point2D(0.9,-5.0),Point2D(1.0,-4.0)}
1343 // output gnuplot setup
1344 cout << "#!/usr/bin/gnuplot -persist" << endl
1345 << "#" << endl
1346 << "# automatically generated by bezierclip, don't change!" << endl
1347 << "#" << endl
1348 << "set parametric" << endl
1349 << "bez(p,q,r,s,t) = p*(1-t)**3+q*3*(1-t)**2*t+r*3*(1-t)*t**2+s*t**3" << endl
1350 << "bezd(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2" << endl
1351 << "pointmarkx(c,t) = c-0.03*t" << endl
1352 << "pointmarky(c,t) = c+0.03*t" << endl
1353 << "linex(a,b,c,t) = a*-c + t*-b" << endl
1354 << "liney(a,b,c,t) = b*-c + t*a" << endl << endl
1355 << "# end of setup" << endl << endl;
1357 #ifdef WITH_CONVEXHULL_TEST
1358 // test convex hull algorithm
1359 const double convHull_xOffset( curr_Offset );
1360 curr_Offset += 20;
1361 cout << "# convex hull testing" << endl
1362 << "plot [t=0:1] ";
1363 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1365 Polygon2D aTestPoly(4);
1366 aTestPoly[0] = someCurves[i].p0;
1367 aTestPoly[1] = someCurves[i].p1;
1368 aTestPoly[2] = someCurves[i].p2;
1369 aTestPoly[3] = someCurves[i].p3;
1371 aTestPoly[0].x += convHull_xOffset;
1372 aTestPoly[1].x += convHull_xOffset;
1373 aTestPoly[2].x += convHull_xOffset;
1374 aTestPoly[3].x += convHull_xOffset;
1376 cout << " bez("
1377 << aTestPoly[0].x << ","
1378 << aTestPoly[1].x << ","
1379 << aTestPoly[2].x << ","
1380 << aTestPoly[3].x << ",t),bez("
1381 << aTestPoly[0].y << ","
1382 << aTestPoly[1].y << ","
1383 << aTestPoly[2].y << ","
1384 << aTestPoly[3].y << ",t), '-' using ($1):($2) title \"convex hull " << i << "\" with lp";
1386 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1387 cout << ",\\" << endl;
1388 else
1389 cout << endl;
1391 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1393 Polygon2D aTestPoly(4);
1394 aTestPoly[0] = someCurves[i].p0;
1395 aTestPoly[1] = someCurves[i].p1;
1396 aTestPoly[2] = someCurves[i].p2;
1397 aTestPoly[3] = someCurves[i].p3;
1399 aTestPoly[0].x += convHull_xOffset;
1400 aTestPoly[1].x += convHull_xOffset;
1401 aTestPoly[2].x += convHull_xOffset;
1402 aTestPoly[3].x += convHull_xOffset;
1404 Polygon2D convHull( convexHull(aTestPoly) );
1406 for( k=0; k<convHull.size(); ++k )
1408 cout << convHull[k].x << " " << convHull[k].y << endl;
1410 cout << convHull[0].x << " " << convHull[0].y << endl;
1411 cout << "e" << endl;
1413 #endif
1415 #ifdef WITH_MULTISUBDIVIDE_TEST
1416 // test convex hull algorithm
1417 const double multiSubdivide_xOffset( curr_Offset );
1418 curr_Offset += 20;
1419 cout << "# multi subdivide testing" << endl
1420 << "plot [t=0:1] ";
1421 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1423 Bezier c( someCurves[i] );
1424 Bezier c1_part1;
1425 Bezier c1_part2;
1426 Bezier c1_part3;
1428 c.p0.x += multiSubdivide_xOffset;
1429 c.p1.x += multiSubdivide_xOffset;
1430 c.p2.x += multiSubdivide_xOffset;
1431 c.p3.x += multiSubdivide_xOffset;
1433 const double t1( 0.1+i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
1434 const double t2( 0.9-i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
1436 // subdivide at t1
1437 Impl_deCasteljauAt( c1_part1, c1_part2, c, t1 );
1439 // subdivide at t2_c1. As we're working on
1440 // c1_part2 now, we have to adapt t2_c1 since
1441 // we're no longer in the original parameter
1442 // interval. This is based on the following
1443 // assumption: t2_new = (t2-t1)/(1-t1), which
1444 // relates the t2 value into the new parameter
1445 // range [0,1] of c1_part2.
1446 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1448 // subdivide at t2
1449 Impl_deCasteljauAt( c1_part3, c1_part2, c, t2 );
1451 cout << " bez("
1452 << c1_part1.p0.x << ","
1453 << c1_part1.p1.x << ","
1454 << c1_part1.p2.x << ","
1455 << c1_part1.p3.x << ",t), bez("
1456 << c1_part1.p0.y+0.01 << ","
1457 << c1_part1.p1.y+0.01 << ","
1458 << c1_part1.p2.y+0.01 << ","
1459 << c1_part1.p3.y+0.01 << ",t) title \"middle " << i << "\", "
1460 << " bez("
1461 << c1_part2.p0.x << ","
1462 << c1_part2.p1.x << ","
1463 << c1_part2.p2.x << ","
1464 << c1_part2.p3.x << ",t), bez("
1465 << c1_part2.p0.y << ","
1466 << c1_part2.p1.y << ","
1467 << c1_part2.p2.y << ","
1468 << c1_part2.p3.y << ",t) title \"right " << i << "\", "
1469 << " bez("
1470 << c1_part3.p0.x << ","
1471 << c1_part3.p1.x << ","
1472 << c1_part3.p2.x << ","
1473 << c1_part3.p3.x << ",t), bez("
1474 << c1_part3.p0.y << ","
1475 << c1_part3.p1.y << ","
1476 << c1_part3.p2.y << ","
1477 << c1_part3.p3.y << ",t) title \"left " << i << "\"";
1480 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1481 cout << ",\\" << endl;
1482 else
1483 cout << endl;
1485 #endif
1487 #ifdef WITH_FATLINE_TEST
1488 // test fatline algorithm
1489 const double fatLine_xOffset( curr_Offset );
1490 curr_Offset += 20;
1491 cout << "# fat line testing" << endl
1492 << "plot [t=0:1] ";
1493 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1495 Bezier c( someCurves[i] );
1497 c.p0.x += fatLine_xOffset;
1498 c.p1.x += fatLine_xOffset;
1499 c.p2.x += fatLine_xOffset;
1500 c.p3.x += fatLine_xOffset;
1502 FatLine line;
1504 Impl_calcFatLine(line, c);
1506 cout << " bez("
1507 << c.p0.x << ","
1508 << c.p1.x << ","
1509 << c.p2.x << ","
1510 << c.p3.x << ",t), bez("
1511 << c.p0.y << ","
1512 << c.p1.y << ","
1513 << c.p2.y << ","
1514 << c.p3.y << ",t) title \"bezier " << i << "\", linex("
1515 << line.a << ","
1516 << line.b << ","
1517 << line.c << ",t), liney("
1518 << line.a << ","
1519 << line.b << ","
1520 << line.c << ",t) title \"fat line (center) on " << i << "\", linex("
1521 << line.a << ","
1522 << line.b << ","
1523 << line.c-line.dMin << ",t), liney("
1524 << line.a << ","
1525 << line.b << ","
1526 << line.c-line.dMin << ",t) title \"fat line (min) on " << i << "\", linex("
1527 << line.a << ","
1528 << line.b << ","
1529 << line.c-line.dMax << ",t), liney("
1530 << line.a << ","
1531 << line.b << ","
1532 << line.c-line.dMax << ",t) title \"fat line (max) on " << i << "\"";
1534 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1535 cout << ",\\" << endl;
1536 else
1537 cout << endl;
1539 #endif
1541 #ifdef WITH_CALCFOCUS_TEST
1542 // test focus curve algorithm
1543 const double focus_xOffset( curr_Offset );
1544 curr_Offset += 20;
1545 cout << "# focus line testing" << endl
1546 << "plot [t=0:1] ";
1547 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1549 Bezier c( someCurves[i] );
1551 c.p0.x += focus_xOffset;
1552 c.p1.x += focus_xOffset;
1553 c.p2.x += focus_xOffset;
1554 c.p3.x += focus_xOffset;
1556 // calc focus curve
1557 Bezier focus;
1558 Impl_calcFocus(focus, c);
1560 cout << " bez("
1561 << c.p0.x << ","
1562 << c.p1.x << ","
1563 << c.p2.x << ","
1564 << c.p3.x << ",t), bez("
1565 << c.p0.y << ","
1566 << c.p1.y << ","
1567 << c.p2.y << ","
1568 << c.p3.y << ",t) title \"bezier " << i << "\", bez("
1569 << focus.p0.x << ","
1570 << focus.p1.x << ","
1571 << focus.p2.x << ","
1572 << focus.p3.x << ",t), bez("
1573 << focus.p0.y << ","
1574 << focus.p1.y << ","
1575 << focus.p2.y << ","
1576 << focus.p3.y << ",t) title \"focus " << i << "\"";
1579 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1580 cout << ",\\" << endl;
1581 else
1582 cout << endl;
1584 #endif
1586 #ifdef WITH_SAFEPARAMBASE_TEST
1587 // test safe params base method
1588 double safeParamsBase_xOffset( curr_Offset );
1589 cout << "# safe param base method testing" << endl
1590 << "plot [t=0:1] ";
1591 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1593 Bezier c( someCurves[i] );
1595 c.p0.x += safeParamsBase_xOffset;
1596 c.p1.x += safeParamsBase_xOffset;
1597 c.p2.x += safeParamsBase_xOffset;
1598 c.p3.x += safeParamsBase_xOffset;
1600 Polygon2D poly(4);
1601 poly[0] = c.p0;
1602 poly[1] = c.p1;
1603 poly[2] = c.p2;
1604 poly[3] = c.p3;
1606 double t1, t2;
1608 bool bRet( Impl_calcSafeParams( t1, t2, poly, 0, 1 ) );
1610 Polygon2D convHull( convexHull( poly ) );
1612 cout << " bez("
1613 << poly[0].x << ","
1614 << poly[1].x << ","
1615 << poly[2].x << ","
1616 << poly[3].x << ",t),bez("
1617 << poly[0].y << ","
1618 << poly[1].y << ","
1619 << poly[2].y << ","
1620 << poly[3].y << ",t), "
1621 << "t+" << safeParamsBase_xOffset << ", 0, "
1622 << "t+" << safeParamsBase_xOffset << ", 1, ";
1623 if( bRet )
1625 cout << t1+safeParamsBase_xOffset << ", t, "
1626 << t2+safeParamsBase_xOffset << ", t, ";
1628 cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
1629 << "'-' using ($1):($2) title \"convex hull\" with lp";
1631 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1632 cout << ",\\" << endl;
1633 else
1634 cout << endl;
1636 safeParamsBase_xOffset += 2;
1639 safeParamsBase_xOffset = curr_Offset;
1640 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1642 Bezier c( someCurves[i] );
1644 c.p0.x += safeParamsBase_xOffset;
1645 c.p1.x += safeParamsBase_xOffset;
1646 c.p2.x += safeParamsBase_xOffset;
1647 c.p3.x += safeParamsBase_xOffset;
1649 Polygon2D poly(4);
1650 poly[0] = c.p0;
1651 poly[1] = c.p1;
1652 poly[2] = c.p2;
1653 poly[3] = c.p3;
1655 double t1, t2;
1657 Impl_calcSafeParams( t1, t2, poly, 0, 1 );
1659 Polygon2D convHull( convexHull( poly ) );
1661 unsigned int k;
1662 for( k=0; k<poly.size(); ++k )
1664 cout << poly[k].x << " " << poly[k].y << endl;
1666 cout << poly[0].x << " " << poly[0].y << endl;
1667 cout << "e" << endl;
1669 for( k=0; k<convHull.size(); ++k )
1671 cout << convHull[k].x << " " << convHull[k].y << endl;
1673 cout << convHull[0].x << " " << convHull[0].y << endl;
1674 cout << "e" << endl;
1676 safeParamsBase_xOffset += 2;
1678 curr_Offset += 20;
1679 #endif
1681 #ifdef WITH_SAFEPARAMS_TEST
1682 // test safe parameter range algorithm
1683 const double safeParams_xOffset( curr_Offset );
1684 curr_Offset += 20;
1685 cout << "# safe param range testing" << endl
1686 << "plot [t=0.0:1.0] ";
1687 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1689 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1691 Bezier c1( someCurves[i] );
1692 Bezier c2( someCurves[j] );
1694 c1.p0.x += safeParams_xOffset;
1695 c1.p1.x += safeParams_xOffset;
1696 c1.p2.x += safeParams_xOffset;
1697 c1.p3.x += safeParams_xOffset;
1698 c2.p0.x += safeParams_xOffset;
1699 c2.p1.x += safeParams_xOffset;
1700 c2.p2.x += safeParams_xOffset;
1701 c2.p3.x += safeParams_xOffset;
1703 double t1, t2;
1705 if( Impl_calcClipRange(t1, t2, c1, c1, c2, c2) )
1707 // clip safe ranges off c1
1708 Bezier c1_part1;
1709 Bezier c1_part2;
1710 Bezier c1_part3;
1712 // subdivide at t1_c1
1713 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
1714 // subdivide at t2_c1
1715 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1717 // output remaining segment (c1_part1)
1719 cout << " bez("
1720 << c1.p0.x << ","
1721 << c1.p1.x << ","
1722 << c1.p2.x << ","
1723 << c1.p3.x << ",t),bez("
1724 << c1.p0.y << ","
1725 << c1.p1.y << ","
1726 << c1.p2.y << ","
1727 << c1.p3.y << ",t), bez("
1728 << c2.p0.x << ","
1729 << c2.p1.x << ","
1730 << c2.p2.x << ","
1731 << c2.p3.x << ",t),bez("
1732 << c2.p0.y << ","
1733 << c2.p1.y << ","
1734 << c2.p2.y << ","
1735 << c2.p3.y << ",t), bez("
1736 << c1_part1.p0.x << ","
1737 << c1_part1.p1.x << ","
1738 << c1_part1.p2.x << ","
1739 << c1_part1.p3.x << ",t),bez("
1740 << c1_part1.p0.y << ","
1741 << c1_part1.p1.y << ","
1742 << c1_part1.p2.y << ","
1743 << c1_part1.p3.y << ",t)";
1745 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
1746 cout << ",\\" << endl;
1747 else
1748 cout << endl;
1752 #endif
1754 #ifdef WITH_SAFEPARAM_DETAILED_TEST
1755 // test safe parameter range algorithm
1756 const double safeParams2_xOffset( curr_Offset );
1757 curr_Offset += 20;
1758 if( sizeof(someCurves)/sizeof(Bezier) > 1 )
1760 Bezier c1( someCurves[0] );
1761 Bezier c2( someCurves[1] );
1763 c1.p0.x += safeParams2_xOffset;
1764 c1.p1.x += safeParams2_xOffset;
1765 c1.p2.x += safeParams2_xOffset;
1766 c1.p3.x += safeParams2_xOffset;
1767 c2.p0.x += safeParams2_xOffset;
1768 c2.p1.x += safeParams2_xOffset;
1769 c2.p2.x += safeParams2_xOffset;
1770 c2.p3.x += safeParams2_xOffset;
1772 double t1, t2;
1774 // output happens here
1775 Impl_calcClipRange(t1, t2, c1, c1, c2, c2);
1777 #endif
1779 #ifdef WITH_SAFEFOCUSPARAM_TEST
1780 // test safe parameter range from focus algorithm
1781 const double safeParamsFocus_xOffset( curr_Offset );
1782 curr_Offset += 20;
1783 cout << "# safe param range from focus testing" << endl
1784 << "plot [t=0.0:1.0] ";
1785 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1787 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1789 Bezier c1( someCurves[i] );
1790 Bezier c2( someCurves[j] );
1792 c1.p0.x += safeParamsFocus_xOffset;
1793 c1.p1.x += safeParamsFocus_xOffset;
1794 c1.p2.x += safeParamsFocus_xOffset;
1795 c1.p3.x += safeParamsFocus_xOffset;
1796 c2.p0.x += safeParamsFocus_xOffset;
1797 c2.p1.x += safeParamsFocus_xOffset;
1798 c2.p2.x += safeParamsFocus_xOffset;
1799 c2.p3.x += safeParamsFocus_xOffset;
1801 double t1, t2;
1803 Bezier focus;
1804 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1805 #if 0
1807 // clip safe ranges off c1_orig
1808 Bezier c1_part1;
1809 Bezier c1_part2;
1810 Bezier c1_part3;
1812 // subdivide at t1_c1
1813 Impl_deCasteljauAt( c1_part1, c1_part2, c2, 0.30204 );
1815 // subdivide at t2_c1. As we're working on
1816 // c1_part2 now, we have to adapt t2_c1 since
1817 // we're no longer in the original parameter
1818 // interval. This is based on the following
1819 // assumption: t2_new = (t2-t1)/(1-t1), which
1820 // relates the t2 value into the new parameter
1821 // range [0,1] of c1_part2.
1822 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (0.57151-0.30204)/(1.0-0.30204) );
1824 c2 = c1_part1;
1825 Impl_calcFocus( focus, c2 );
1827 #else
1828 Impl_calcFocus( focus, c2 );
1829 #endif
1830 #else
1831 focus = c2;
1832 #endif
1833 // determine safe range on c1
1834 bool bRet( Impl_calcSafeParams_focus( t1, t2,
1835 c1, focus ) );
1837 cerr << "t1: " << t1 << ", t2: " << t2 << endl;
1839 // clip safe ranges off c1
1840 Bezier c1_part1;
1841 Bezier c1_part2;
1842 Bezier c1_part3;
1844 // subdivide at t1_c1
1845 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
1846 // subdivide at t2_c1
1847 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1849 // output remaining segment (c1_part1)
1851 cout << " bez("
1852 << c1.p0.x << ","
1853 << c1.p1.x << ","
1854 << c1.p2.x << ","
1855 << c1.p3.x << ",t),bez("
1856 << c1.p0.y << ","
1857 << c1.p1.y << ","
1858 << c1.p2.y << ","
1859 << c1.p3.y << ",t) title \"c1\", "
1860 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1861 << "bez("
1862 << c2.p0.x << ","
1863 << c2.p1.x << ","
1864 << c2.p2.x << ","
1865 << c2.p3.x << ",t),bez("
1866 << c2.p0.y << ","
1867 << c2.p1.y << ","
1868 << c2.p2.y << ","
1869 << c2.p3.y << ",t) title \"c2\", "
1870 << "bez("
1871 << focus.p0.x << ","
1872 << focus.p1.x << ","
1873 << focus.p2.x << ","
1874 << focus.p3.x << ",t),bez("
1875 << focus.p0.y << ","
1876 << focus.p1.y << ","
1877 << focus.p2.y << ","
1878 << focus.p3.y << ",t) title \"focus\"";
1879 #else
1880 << "bez("
1881 << c2.p0.x << ","
1882 << c2.p1.x << ","
1883 << c2.p2.x << ","
1884 << c2.p3.x << ",t),bez("
1885 << c2.p0.y << ","
1886 << c2.p1.y << ","
1887 << c2.p2.y << ","
1888 << c2.p3.y << ",t) title \"focus\"";
1889 #endif
1890 if( bRet )
1892 cout << ", bez("
1893 << c1_part1.p0.x << ","
1894 << c1_part1.p1.x << ","
1895 << c1_part1.p2.x << ","
1896 << c1_part1.p3.x << ",t),bez("
1897 << c1_part1.p0.y+0.01 << ","
1898 << c1_part1.p1.y+0.01 << ","
1899 << c1_part1.p2.y+0.01 << ","
1900 << c1_part1.p3.y+0.01 << ",t) title \"part\"";
1903 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
1904 cout << ",\\" << endl;
1905 else
1906 cout << endl;
1909 #endif
1911 #ifdef WITH_SAFEFOCUSPARAM_DETAILED_TEST
1912 // test safe parameter range algorithm
1913 const double safeParams3_xOffset( curr_Offset );
1914 curr_Offset += 20;
1915 if( sizeof(someCurves)/sizeof(Bezier) > 1 )
1917 Bezier c1( someCurves[0] );
1918 Bezier c2( someCurves[1] );
1920 c1.p0.x += safeParams3_xOffset;
1921 c1.p1.x += safeParams3_xOffset;
1922 c1.p2.x += safeParams3_xOffset;
1923 c1.p3.x += safeParams3_xOffset;
1924 c2.p0.x += safeParams3_xOffset;
1925 c2.p1.x += safeParams3_xOffset;
1926 c2.p2.x += safeParams3_xOffset;
1927 c2.p3.x += safeParams3_xOffset;
1929 double t1, t2;
1931 Bezier focus;
1932 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1933 Impl_calcFocus( focus, c2 );
1934 #else
1935 focus = c2;
1936 #endif
1938 // determine safe range on c1, output happens here
1939 Impl_calcSafeParams_focus( t1, t2,
1940 c1, focus );
1942 #endif
1944 #ifdef WITH_BEZIERCLIP_TEST
1945 ::std::vector< ::std::pair<double, double> > result;
1946 ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(result);
1948 // test full bezier clipping
1949 const double bezierClip_xOffset( curr_Offset );
1950 curr_Offset += 20;
1951 cout << endl << endl << "# bezier clip testing" << endl
1952 << "plot [t=0:1] ";
1953 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1955 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1957 Bezier c1( someCurves[i] );
1958 Bezier c2( someCurves[j] );
1960 c1.p0.x += bezierClip_xOffset;
1961 c1.p1.x += bezierClip_xOffset;
1962 c1.p2.x += bezierClip_xOffset;
1963 c1.p3.x += bezierClip_xOffset;
1964 c2.p0.x += bezierClip_xOffset;
1965 c2.p1.x += bezierClip_xOffset;
1966 c2.p2.x += bezierClip_xOffset;
1967 c2.p3.x += bezierClip_xOffset;
1969 cout << " bez("
1970 << c1.p0.x << ","
1971 << c1.p1.x << ","
1972 << c1.p2.x << ","
1973 << c1.p3.x << ",t),bez("
1974 << c1.p0.y << ","
1975 << c1.p1.y << ","
1976 << c1.p2.y << ","
1977 << c1.p3.y << ",t), bez("
1978 << c2.p0.x << ","
1979 << c2.p1.x << ","
1980 << c2.p2.x << ","
1981 << c2.p3.x << ",t),bez("
1982 << c2.p0.y << ","
1983 << c2.p1.y << ","
1984 << c2.p2.y << ","
1985 << c2.p3.y << ",t), '-' using (bez("
1986 << c1.p0.x << ","
1987 << c1.p1.x << ","
1988 << c1.p2.x << ","
1989 << c1.p3.x
1990 << ",$1)):(bez("
1991 << c1.p0.y << ","
1992 << c1.p1.y << ","
1993 << c1.p2.y << ","
1994 << c1.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << i << ")\", "
1995 << " '-' using (bez("
1996 << c2.p0.x << ","
1997 << c2.p1.x << ","
1998 << c2.p2.x << ","
1999 << c2.p3.x
2000 << ",$1)):(bez("
2001 << c2.p0.y << ","
2002 << c2.p1.y << ","
2003 << c2.p2.y << ","
2004 << c2.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << j << ")\"";
2006 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
2007 cout << ",\\" << endl;
2008 else
2009 cout << endl;
2012 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
2014 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
2016 result.clear();
2017 Bezier c1( someCurves[i] );
2018 Bezier c2( someCurves[j] );
2020 c1.p0.x += bezierClip_xOffset;
2021 c1.p1.x += bezierClip_xOffset;
2022 c1.p2.x += bezierClip_xOffset;
2023 c1.p3.x += bezierClip_xOffset;
2024 c2.p0.x += bezierClip_xOffset;
2025 c2.p1.x += bezierClip_xOffset;
2026 c2.p2.x += bezierClip_xOffset;
2027 c2.p3.x += bezierClip_xOffset;
2029 clipBezier( ii, 0.00001, c1, c2 );
2031 for( k=0; k<result.size(); ++k )
2033 cout << result[k].first << endl;
2035 cout << "e" << endl;
2037 for( k=0; k<result.size(); ++k )
2039 cout << result[k].second << endl;
2041 cout << "e" << endl;
2044 #endif
2046 return 0;
2049 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */