Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / basegfx / source / workbench / bezierclip.cxx
blobd7162afaa1ac2cb9d6b8190ec0e1f8c067587777
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"
30 // what to test
31 #define WITH_ASSERTIONS
32 //#define WITH_CONVEXHULL_TEST
33 //#define WITH_MULTISUBDIVIDE_TEST
34 //#define WITH_FATLINE_TEST
35 //#define WITH_CALCFOCUS_TEST
36 //#define WITH_SAFEPARAMBASE_TEST
37 //#define WITH_SAFEPARAMS_TEST
38 //#define WITH_SAFEPARAM_DETAILED_TEST
39 //#define WITH_SAFEFOCUSPARAM_CALCFOCUS
40 //#define WITH_SAFEFOCUSPARAM_TEST
41 //#define WITH_SAFEFOCUSPARAM_DETAILED_TEST
42 #define WITH_BEZIERCLIP_TEST
44 /* Implementation of the so-called 'Fat-Line Bezier Clipping Algorithm' by Sederberg et al.
46 * Actual reference is: T. W. Sederberg and T Nishita: Curve
47 * intersection using Bezier clipping. In Computer Aided Design, 22
48 * (9), 1990, pp. 538--549
51 /* Misc helper
52 * ===========
54 int fallFac( int n, int k )
56 #ifdef WITH_ASSERTIONS
57 assert(n>=k); // "For factorials, n must be greater or equal k"
58 assert(n>=0); // "For factorials, n must be positive"
59 assert(k>=0); // "For factorials, k must be positive"
60 #endif
62 int res( 1 );
64 while( k-- && n ) res *= n--;
66 return res;
69 int fac( int n )
71 return fallFac(n, n);
74 /* Bezier fat line clipping part
75 * =============================
78 void Impl_calcFatLine( FatLine& line, const Bezier& c )
80 // Prepare normalized implicit line
81 // ================================
83 // calculate vector orthogonal to p1-p4:
84 line.a = -(c.p0.y - c.p3.y);
85 line.b = (c.p0.x - c.p3.x);
87 // normalize
88 const double len( sqrt( line.a*line.a + line.b*line.b ) );
89 if( !tolZero(len) )
91 line.a /= len;
92 line.b /= len;
95 line.c = -(line.a*c.p0.x + line.b*c.p0.y);
97 // Determine bounding fat line from it
98 // ===================================
100 // calc control point distances
101 const double dP2( calcLineDistance(line.a, line.b, line.c, c.p1.x, c.p1.y ) );
102 const double dP3( calcLineDistance(line.a, line.b, line.c, c.p2.x, c.p2.y ) );
104 // calc approximate bounding lines to curve (tight bounds are
105 // possible here, but more expensive to calculate and thus not
106 // worth the overhead)
107 if( dP2 * dP3 > 0.0 )
109 line.dMin = 3.0/4.0 * ::std::min(0.0, ::std::min(dP2, dP3));
110 line.dMax = 3.0/4.0 * ::std::max(0.0, ::std::max(dP2, dP3));
112 else
114 line.dMin = 4.0/9.0 * ::std::min(0.0, ::std::min(dP2, dP3));
115 line.dMax = 4.0/9.0 * ::std::max(0.0, ::std::max(dP2, dP3));
119 void Impl_calcBounds( Point2D& leftTop,
120 Point2D& rightBottom,
121 const Bezier& c1 )
123 leftTop.x = ::std::min( c1.p0.x, ::std::min( c1.p1.x, ::std::min( c1.p2.x, c1.p3.x ) ) );
124 leftTop.y = ::std::min( c1.p0.y, ::std::min( c1.p1.y, ::std::min( c1.p2.y, c1.p3.y ) ) );
125 rightBottom.x = ::std::max( c1.p0.x, ::std::max( c1.p1.x, ::std::max( c1.p2.x, c1.p3.x ) ) );
126 rightBottom.y = ::std::max( c1.p0.y, ::std::max( c1.p1.y, ::std::max( c1.p2.y, c1.p3.y ) ) );
129 bool Impl_doBBoxIntersect( const Bezier& c1,
130 const Bezier& c2 )
132 // calc rectangular boxes from c1 and c2
133 Point2D lt1;
134 Point2D rb1;
135 Point2D lt2;
136 Point2D rb2;
138 Impl_calcBounds( lt1, rb1, c1 );
139 Impl_calcBounds( lt2, rb2, c2 );
141 if( ::std::min(rb1.x, rb2.x) < ::std::max(lt1.x, lt2.x) ||
142 ::std::min(rb1.y, rb2.y) < ::std::max(lt1.y, lt2.y) )
144 return false;
146 else
148 return true;
152 /* calculates two t's for the given bernstein control polygon: the first is
153 * the intersection of the min value line with the convex hull from
154 * the left, the second is the intersection of the max value line with
155 * the convex hull from the right.
157 bool Impl_calcSafeParams( double& t1,
158 double& t2,
159 const Polygon2D& rPoly,
160 double lowerYBound,
161 double upperYBound )
163 // need the convex hull of the control polygon, as this is
164 // guaranteed to completely bound the curve
165 Polygon2D convHull( convexHull(rPoly) );
167 // init min and max buffers
168 t1 = 0.0 ;
169 double currLowerT( 1.0 );
171 t2 = 1.0;
172 double currHigherT( 0.0 );
174 if( convHull.size() <= 1 )
175 return false; // only one point? Then we're done with clipping
177 /* now, clip against lower and higher bounds */
178 Point2D p0;
179 Point2D p1;
181 bool bIntersection( false );
183 for( Polygon2D::size_type i=0; i<convHull.size(); ++i )
185 // have to check against convHull.size() segments, as the
186 // convex hull is, by definition, closed. Thus, for the
187 // last point, we take the first point as partner.
188 if( i+1 == convHull.size() )
190 // close the polygon
191 p0 = convHull[i];
192 p1 = convHull[0];
194 else
196 p0 = convHull[i];
197 p1 = convHull[i+1];
200 // is the segment in question within or crossing the
201 // horizontal band spanned by lowerYBound and upperYBound? If
202 // not, we've got no intersection. If yes, we maybe don't have
203 // an intersection, but we've got to update the permissible
204 // range, nevertheless. This is because inside lying segments
205 // leads to full range forbidden.
206 if( (tolLessEqual(p0.y, upperYBound) || tolLessEqual(p1.y, upperYBound)) &&
207 (tolGreaterEqual(p0.y, lowerYBound) || tolGreaterEqual(p1.y, lowerYBound)) )
209 // calc intersection of convex hull segment with
210 // one of the horizontal bounds lines
211 // to optimize a bit, r_x is calculated only in else case
212 const double r_y( p1.y - p0.y );
214 if( tolZero(r_y) )
216 // r_y is virtually zero, thus we've got a horizontal
217 // line. Now check whether we maybe coincide with lower or
218 // upper horizonal bound line.
219 if( tolEqual(p0.y, lowerYBound) ||
220 tolEqual(p0.y, upperYBound) )
222 // yes, simulate intersection then
223 currLowerT = ::std::min(currLowerT, ::std::min(p0.x, p1.x));
224 currHigherT = ::std::max(currHigherT, ::std::max(p0.x, p1.x));
227 else
229 // check against lower and higher bounds
230 // =====================================
231 const double r_x( p1.x - p0.x );
233 // calc intersection with horizontal dMin line
234 const double currTLow( (lowerYBound - p0.y) * r_x / r_y + p0.x );
236 // calc intersection with horizontal dMax line
237 const double currTHigh( (upperYBound - p0.y) * r_x / r_y + p0.x );
239 currLowerT = ::std::min(currLowerT, ::std::min(currTLow, currTHigh));
240 currHigherT = ::std::max(currHigherT, ::std::max(currTLow, currTHigh));
243 // set flag that at least one segment is contained or
244 // intersects given horizontal band.
245 bIntersection = true;
249 #ifndef WITH_SAFEPARAMBASE_TEST
250 // limit intersections found to permissible t parameter range
251 t1 = ::std::max(0.0, currLowerT);
252 t2 = ::std::min(1.0, currHigherT);
253 #endif
255 return bIntersection;
258 /* calculates two t's for the given bernstein polynomial: the first is
259 * the intersection of the min value line with the convex hull from
260 * the left, the second is the intersection of the max value line with
261 * the convex hull from the right.
263 * The polynomial coefficients c0 to c3 given to this method
264 * must correspond to t values of 0, 1/3, 2/3 and 1, respectively.
266 bool Impl_calcSafeParams_clip( double& t1,
267 double& t2,
268 const FatLine& bounds,
269 double c0,
270 double c1,
271 double c2,
272 double c3 )
274 /* first of all, determine convex hull of c0-c3 */
275 Polygon2D poly(4);
276 poly[0] = Point2D(0, c0);
277 poly[1] = Point2D(1.0/3.0, c1);
278 poly[2] = Point2D(2.0/3.0, c2);
279 poly[3] = Point2D(1, c3);
281 #ifndef WITH_SAFEPARAM_DETAILED_TEST
283 return Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax );
285 #else
286 bool bRet( Impl_calcSafeParams( t1, t2, poly, bounds.dMin, bounds.dMax ) );
288 Polygon2D convHull( convexHull( poly ) );
290 cout << "# convex hull testing" << endl
291 << "plot [t=0:1] ";
292 cout << " bez("
293 << poly[0].x << ","
294 << poly[1].x << ","
295 << poly[2].x << ","
296 << poly[3].x << ",t),bez("
297 << poly[0].y << ","
298 << poly[1].y << ","
299 << poly[2].y << ","
300 << poly[3].y << ",t), "
301 << "t, " << bounds.dMin << ", "
302 << "t, " << bounds.dMax << ", "
303 << t1 << ", t, "
304 << t2 << ", t, "
305 << "'-' using ($1):($2) title \"control polygon\" with lp, "
306 << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
308 unsigned int k;
309 for( k=0; k<poly.size(); ++k )
311 cout << poly[k].x << " " << poly[k].y << endl;
313 cout << poly[0].x << " " << poly[0].y << endl;
314 cout << "e" << endl;
316 for( k=0; k<convHull.size(); ++k )
318 cout << convHull[k].x << " " << convHull[k].y << endl;
320 cout << convHull[0].x << " " << convHull[0].y << endl;
321 cout << "e" << endl;
323 return bRet;
324 #endif
327 void Impl_deCasteljauAt( Bezier& part1,
328 Bezier& part2,
329 const Bezier& input,
330 double t )
332 // deCasteljau bezier arc, scheme is:
334 // First row is C_0^n,C_1^n,...,C_n^n
335 // Second row is P_1^n,...,P_n^n
336 // etc.
337 // with P_k^r = (1 - x_s)P_{k-1}^{r-1} + x_s P_k{r-1}
339 // this results in:
341 // P1 P2 P3 P4
342 // L1 P2 P3 R4
343 // L2 H R3
344 // L3 R2
345 // L4/R1
346 if( tolZero(t) )
348 // t is zero -> part2 is input curve, part1 is empty (input.p0, that is)
349 part1.p0.x = part1.p1.x = part1.p2.x = part1.p3.x = input.p0.x;
350 part1.p0.y = part1.p1.y = part1.p2.y = part1.p3.y = input.p0.y;
351 part2 = input;
353 else if( tolEqual(t, 1.0) )
355 // t is one -> part1 is input curve, part2 is empty (input.p3, that is)
356 part1 = input;
357 part2.p0.x = part2.p1.x = part2.p2.x = part2.p3.x = input.p3.x;
358 part2.p0.y = part2.p1.y = part2.p2.y = part2.p3.y = input.p3.y;
360 else
362 part1.p0.x = input.p0.x; part1.p0.y = input.p0.y;
363 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;
364 const double Hx ( (1.0 - t)*input.p1.x + t*input.p2.x ), Hy ( (1.0 - t)*input.p1.y + t*input.p2.y );
365 part1.p2.x = (1.0 - t)*part1.p1.x + t*Hx; part1.p2.y = (1.0 - t)*part1.p1.y + t*Hy;
366 part2.p3.x = input.p3.x; part2.p3.y = input.p3.y;
367 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;
368 part2.p1.x = (1.0 - t)*Hx + t*part2.p2.x; part2.p1.y = (1.0 - t)*Hy + t*part2.p2.y;
369 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;
370 part1.p3.x = part2.p0.x; part1.p3.y = part2.p0.y;
374 void printCurvesWithSafeRange( const Bezier& c1, const Bezier& c2, double t1_c1, double t2_c1,
375 const Bezier& c2_part, const FatLine& bounds_c2 )
377 static int offset = 0;
379 cout << "# safe param range testing" << endl
380 << "plot [t=0.0:1.0] ";
382 // clip safe ranges off c1
383 Bezier c1_part1;
384 Bezier c1_part2;
385 Bezier c1_part3;
387 // subdivide at t1_c1
388 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1_c1 );
389 // subdivide at t2_c1
390 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, t2_c1 );
392 // output remaining segment (c1_part1)
394 cout << "bez("
395 << c1.p0.x+offset << ","
396 << c1.p1.x+offset << ","
397 << c1.p2.x+offset << ","
398 << c1.p3.x+offset << ",t),bez("
399 << c1.p0.y << ","
400 << c1.p1.y << ","
401 << c1.p2.y << ","
402 << c1.p3.y << ",t), bez("
403 << c2.p0.x+offset << ","
404 << c2.p1.x+offset << ","
405 << c2.p2.x+offset << ","
406 << c2.p3.x+offset << ",t),bez("
407 << c2.p0.y << ","
408 << c2.p1.y << ","
409 << c2.p2.y << ","
410 << c2.p3.y << ",t), "
411 #if 1
412 << "bez("
413 << c1_part1.p0.x+offset << ","
414 << c1_part1.p1.x+offset << ","
415 << c1_part1.p2.x+offset << ","
416 << c1_part1.p3.x+offset << ",t),bez("
417 << c1_part1.p0.y << ","
418 << c1_part1.p1.y << ","
419 << c1_part1.p2.y << ","
420 << c1_part1.p3.y << ",t), "
421 #endif
422 #if 1
423 << "bez("
424 << c2_part.p0.x+offset << ","
425 << c2_part.p1.x+offset << ","
426 << c2_part.p2.x+offset << ","
427 << c2_part.p3.x+offset << ",t),bez("
428 << c2_part.p0.y << ","
429 << c2_part.p1.y << ","
430 << c2_part.p2.y << ","
431 << c2_part.p3.y << ",t), "
432 #endif
433 << "linex("
434 << bounds_c2.a << ","
435 << bounds_c2.b << ","
436 << bounds_c2.c << ",t)+" << offset << ", liney("
437 << bounds_c2.a << ","
438 << bounds_c2.b << ","
439 << bounds_c2.c << ",t) title \"fat line (center)\", linex("
440 << bounds_c2.a << ","
441 << bounds_c2.b << ","
442 << bounds_c2.c-bounds_c2.dMin << ",t)+" << offset << ", liney("
443 << bounds_c2.a << ","
444 << bounds_c2.b << ","
445 << bounds_c2.c-bounds_c2.dMin << ",t) title \"fat line (min) \", linex("
446 << bounds_c2.a << ","
447 << bounds_c2.b << ","
448 << bounds_c2.c-bounds_c2.dMax << ",t)+" << offset << ", liney("
449 << bounds_c2.a << ","
450 << bounds_c2.b << ","
451 << bounds_c2.c-bounds_c2.dMax << ",t) title \"fat line (max) \"" << endl;
453 offset += 1;
456 void printResultWithFinalCurves( const Bezier& c1, const Bezier& c1_part,
457 const Bezier& c2, const Bezier& c2_part,
458 double t1_c1, double t2_c1 )
460 static int offset = 0;
462 cout << "# final result" << endl
463 << "plot [t=0.0:1.0] ";
465 cout << "bez("
466 << c1.p0.x+offset << ","
467 << c1.p1.x+offset << ","
468 << c1.p2.x+offset << ","
469 << c1.p3.x+offset << ",t),bez("
470 << c1.p0.y << ","
471 << c1.p1.y << ","
472 << c1.p2.y << ","
473 << c1.p3.y << ",t), bez("
474 << c1_part.p0.x+offset << ","
475 << c1_part.p1.x+offset << ","
476 << c1_part.p2.x+offset << ","
477 << c1_part.p3.x+offset << ",t),bez("
478 << c1_part.p0.y << ","
479 << c1_part.p1.y << ","
480 << c1_part.p2.y << ","
481 << c1_part.p3.y << ",t), "
482 << " pointmarkx(bez("
483 << c1.p0.x+offset << ","
484 << c1.p1.x+offset << ","
485 << c1.p2.x+offset << ","
486 << c1.p3.x+offset << ","
487 << t1_c1 << "),t), "
488 << " pointmarky(bez("
489 << c1.p0.y << ","
490 << c1.p1.y << ","
491 << c1.p2.y << ","
492 << c1.p3.y << ","
493 << t1_c1 << "),t), "
494 << " pointmarkx(bez("
495 << c1.p0.x+offset << ","
496 << c1.p1.x+offset << ","
497 << c1.p2.x+offset << ","
498 << c1.p3.x+offset << ","
499 << t2_c1 << "),t), "
500 << " pointmarky(bez("
501 << c1.p0.y << ","
502 << c1.p1.y << ","
503 << c1.p2.y << ","
504 << c1.p3.y << ","
505 << t2_c1 << "),t), "
507 << "bez("
508 << c2.p0.x+offset << ","
509 << c2.p1.x+offset << ","
510 << c2.p2.x+offset << ","
511 << c2.p3.x+offset << ",t),bez("
512 << c2.p0.y << ","
513 << c2.p1.y << ","
514 << c2.p2.y << ","
515 << c2.p3.y << ",t), "
516 << "bez("
517 << c2_part.p0.x+offset << ","
518 << c2_part.p1.x+offset << ","
519 << c2_part.p2.x+offset << ","
520 << c2_part.p3.x+offset << ",t),bez("
521 << c2_part.p0.y << ","
522 << c2_part.p1.y << ","
523 << c2_part.p2.y << ","
524 << c2_part.p3.y << ",t)" << endl;
526 offset += 1;
529 /** determine parameter ranges [0,t1) and (t2,1] on c1, where c1 is guaranteed to lie outside c2.
530 Returns false, if the two curves don't even intersect.
532 @param t1
533 Range [0,t1) on c1 is guaranteed to lie outside c2
535 @param t2
536 Range (t2,1] on c1 is guaranteed to lie outside c2
538 @param c1_orig
539 Original curve c1
541 @param c1_part
542 Subdivided current part of c1
544 @param c2_orig
545 Original curve c2
547 @param c2_part
548 Subdivided current part of c2
550 bool Impl_calcClipRange( double& t1,
551 double& t2,
552 const Bezier& c1_orig,
553 const Bezier& c1_part,
554 const Bezier& c2_orig,
555 const Bezier& c2_part )
557 // TODO: Maybe also check fat line orthogonal to P0P3, having P0
558 // and P3 as the extremal points
560 if( Impl_doBBoxIntersect(c1_part, c2_part) )
562 // Calculate fat lines around c1
563 FatLine bounds_c2;
565 // must use the subdivided version of c2, since the fat line
566 // algorithm works implicitly with the convex hull bounding
567 // box.
568 Impl_calcFatLine(bounds_c2, c2_part);
570 // determine clip positions on c2. Can use original c1 (which
571 // is necessary anyway, to get the t's on the original curve),
572 // since the distance calculations work directly in the
573 // Bernstein polynom parameter domain.
574 if( Impl_calcSafeParams_clip( t1, t2, bounds_c2,
575 calcLineDistance( bounds_c2.a,
576 bounds_c2.b,
577 bounds_c2.c,
578 c1_orig.p0.x,
579 c1_orig.p0.y ),
580 calcLineDistance( bounds_c2.a,
581 bounds_c2.b,
582 bounds_c2.c,
583 c1_orig.p1.x,
584 c1_orig.p1.y ),
585 calcLineDistance( bounds_c2.a,
586 bounds_c2.b,
587 bounds_c2.c,
588 c1_orig.p2.x,
589 c1_orig.p2.y ),
590 calcLineDistance( bounds_c2.a,
591 bounds_c2.b,
592 bounds_c2.c,
593 c1_orig.p3.x,
594 c1_orig.p3.y ) ) )
596 //printCurvesWithSafeRange(c1_orig, c2_orig, t1, t2, c2_part, bounds_c2);
598 // they do intersect
599 return true;
603 // they don't intersect: nothing to do
604 return false;
607 /* Tangent intersection part
608 * =========================
611 void Impl_calcFocus( Bezier& res, const Bezier& c )
613 // arbitrary small value, for now
614 // TODO: find meaningful value
615 const double minPivotValue( 1.0e-20 );
617 Point2D::value_type fMatrix[6];
618 Point2D::value_type fRes[2];
620 // calc new curve from hodograph, c and linear blend
622 // Coefficients for derivative of c are (C_i=n(C_{i+1} - C_i)):
624 // 3(P1 - P0), 3(P2 - P1), 3(P3 - P2) (bezier curve of degree 2)
626 // The hodograph is then (bezier curve of 2nd degree is P0(1-t)^2 + 2P1(1-t)t + P2t^2):
628 // 3(P1 - P0)(1-t)^2 + 6(P2 - P1)(1-t)t + 3(P3 - P2)t^2
630 // rotate by 90 degrees: x=-y, y=x and you get the normal vector function N(t):
632 // 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)
633 // 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
635 // Now, the focus curve is defined to be F(t)=P(t) + c(t)N(t),
636 // where P(t) is the original curve, and c(t)=c0(1-t) + c1 t
638 // This results in the following expression for F(t):
640 // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
641 // (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)
643 // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1.t)t^2 + P3.y t^3 +
644 // (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)
646 // As a heuristic, we set F(0)=F(1) (thus, the curve is closed and _tends_ to be small):
648 // For F(0), the following results:
650 // x(0) = P0.x - c0 3(P1.y - P0.y)
651 // y(0) = P0.y + c0 3(P1.x - P0.x)
653 // For F(1), the following results:
655 // x(1) = P3.x - c1 3(P3.y - P2.y)
656 // y(1) = P3.y + c1 3(P3.x - P2.x)
658 // Reorder, collect and substitute into F(0)=F(1):
660 // P0.x - c0 3(P1.y - P0.y) = P3.x - c1 3(P3.y - P2.y)
661 // P0.y + c0 3(P1.x - P0.x) = P3.y + c1 3(P3.x - P2.x)
663 // which yields
665 // (P0.y - P1.y)c0 + (P3.y - P2.y)c1 = (P3.x - P0.x)/3
666 // (P1.x - P0.x)c0 + (P2.x - P3.x)c1 = (P3.y - P0.y)/3
668 // so, this is what we calculate here (determine c0 and c1):
669 fMatrix[0] = c.p1.x - c.p0.x;
670 fMatrix[1] = c.p2.x - c.p3.x;
671 fMatrix[2] = (c.p3.y - c.p0.y)/3.0;
672 fMatrix[3] = c.p0.y - c.p1.y;
673 fMatrix[4] = c.p3.y - c.p2.y;
674 fMatrix[5] = (c.p3.x - c.p0.x)/3.0;
676 // TODO: determine meaningful value for
677 if( !solve(fMatrix, 2, 3, fRes, minPivotValue) )
679 // TODO: generate meaningful values here
680 // singular or nearly singular system -- use arbitrary
681 // values for res
682 fRes[0] = 0.0;
683 fRes[1] = 1.0;
685 cerr << "Matrix singular!" << endl;
688 // now, the reordered and per-coefficient collected focus curve is
689 // the following third degree bezier curve F(t):
691 // x(t) = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
692 // (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)
693 // = P0.x (1-t)^3 + 3 P1.x (1-t)^2t + 3 P2.x (1.t)t^2 + P3.x t^3 -
694 // (3c0P1.y(1-t)^3 - 3c0P0.y(1-t)^3 + 6c0P2.y(1-t)^2t - 6c0P1.y(1-t)^2t +
695 // 3c0P3.y(1-t)t^2 - 3c0P2.y(1-t)t^2 +
696 // 3c1P1.y(1-t)^2t - 3c1P0.y(1-t)^2t + 6c1P2.y(1-t)t^2 - 6c1P1.y(1-t)t^2 +
697 // 3c1P3.yt^3 - 3c1P2.yt^3)
698 // = (P0.x - 3 c0 P1.y + 3 c0 P0.y)(1-t)^3 +
699 // 3(P1.x - c1 P1.y + c1 P0.y - 2 c0 P2.y + 2 c0 P1.y)(1-t)^2t +
700 // 3(P2.x - 2 c1 P2.y + 2 c1 P1.y - c0 P3.y + c0 P2.y)(1-t)t^2 +
701 // (P3.x - 3 c1 P3.y + 3 c1 P2.y)t^3
702 // = (P0.x - 3 c0(P1.y - P0.y))(1-t)^3 +
703 // 3(P1.x - c1(P1.y - P0.y) - 2c0(P2.y - P1.y))(1-t)^2t +
704 // 3(P2.x - 2 c1(P2.y - P1.y) - c0(P3.y - P2.y))(1-t)t^2 +
705 // (P3.x - 3 c1(P3.y - P2.y))t^3
707 // y(t) = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
708 // (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)
709 // = P0.y (1-t)^3 + 3 P1.y (1-t)^2t + 3 P2.y (1-t)t^2 + P3.y t^3 +
710 // 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 +
711 // 3c1(P1.x - P0.x)(1-t)^2t + 6c1(P2.x - P1.x)(1-t)t^2 + 3c1(P3.x - P2.x)t^3
712 // = (P0.y + 3 c0 (P1.x - P0.x))(1-t)^3 +
713 // 3(P1.y + 2 c0 (P2.x - P1.x) + c1 (P1.x - P0.x))(1-t)^2t +
714 // 3(P2.y + c0 (P3.x - P2.x) + 2 c1 (P2.x - P1.x))(1-t)t^2 +
715 // (P3.y + 3 c1 (P3.x - P2.x))t^3
717 // Therefore, the coefficients F0 to F3 of the focus curve are:
719 // F0.x = (P0.x - 3 c0(P1.y - P0.y)) F0.y = (P0.y + 3 c0 (P1.x - P0.x))
720 // 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))
721 // 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))
722 // F3.x = (P3.x - 3 c1(P3.y - P2.y)) F3.y = (P3.y + 3 c1 (P3.x - P2.x))
724 res.p0.x = c.p0.x - 3*fRes[0]*(c.p1.y - c.p0.y);
725 res.p1.x = c.p1.x - fRes[1]*(c.p1.y - c.p0.y) - 2*fRes[0]*(c.p2.y - c.p1.y);
726 res.p2.x = c.p2.x - 2*fRes[1]*(c.p2.y - c.p1.y) - fRes[0]*(c.p3.y - c.p2.y);
727 res.p3.x = c.p3.x - 3*fRes[1]*(c.p3.y - c.p2.y);
729 res.p0.y = c.p0.y + 3*fRes[0]*(c.p1.x - c.p0.x);
730 res.p1.y = c.p1.y + 2*fRes[0]*(c.p2.x - c.p1.x) + fRes[1]*(c.p1.x - c.p0.x);
731 res.p2.y = c.p2.y + fRes[0]*(c.p3.x - c.p2.x) + 2*fRes[1]*(c.p2.x - c.p1.x);
732 res.p3.y = c.p3.y + 3*fRes[1]*(c.p3.x - c.p2.x);
735 bool Impl_calcSafeParams_focus( double& t1,
736 double& t2,
737 const Bezier& curve,
738 const Bezier& focus )
740 // now, we want to determine which normals of the original curve
741 // P(t) intersect with the focus curve F(t). The condition for
742 // this statement is P'(t)(P(t) - F) = 0, i.e. hodograph P'(t) and
743 // line through P(t) and F are perpendicular.
744 // If you expand this equation, you end up with something like
746 // (\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))
748 // Multiplying that out (as the scalar product is linear, we can
749 // extract some terms) yields:
751 // (P_i - F)^T n(P_{j+1} - P_j) B_i^n(t)B_j^{n-1}(t) + ...
753 // If we combine the B_i^n(t)B_j^{n-1}(t) product, we arrive at a
754 // Bernstein polynomial of degree 2n-1, as
756 // \binom{n}{i}(1-t)^{n-i}t^i) \binom{n-1}{j}(1-t)^{n-1-j}t^j) =
757 // \binom{n}{i}\binom{n-1}{j}(1-t)^{2n-1-i-j}t^{i+j}
759 // Thus, with the defining equation for a 2n-1 degree Bernstein
760 // polynomial
762 // \sum_{i=0}^{2n-1} d_i B_i^{2n-1}(t)
764 // the d_i are calculated as follows:
766 // 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)
768 // Okay, but F is now not a single point, but itself a curve
769 // F(u). Thus, for every value of u, we get a different 2n-1
770 // bezier curve from the above equation. Therefore, we have a
771 // tensor product bezier patch, with the following defining
772 // equation:
774 // d(t,u) = \sum_{i=0}^{2n-1} \sum_{j=0}^m B_i^{2n-1}(t) B_j^{m}(u) d_{ij}, where
775 // 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)
777 // as above, only that now F is one of the focus' control points.
779 // Note the difference in the binomial coefficients to the
780 // reference paper, these formulas most probably contained a typo.
782 // To determine, where D(t,u) is _not_ zero (these are the parts
783 // of the curve that don't share normals with the focus and can
784 // thus be safely clipped away), we project D(u,t) onto the
785 // (d(t,u), t) plane, determine the convex hull there and proceed
786 // as for the curve intersection part (projection is orthogonal to
787 // u axis, thus simply throw away u coordinate).
789 // \fallfac are so-called falling factorials (see Concrete
790 // Mathematics, p. 47 for a definition).
792 // now, for tensor product bezier curves, the convex hull property
793 // holds, too. Thus, we simply project the control points (t_{ij},
794 // u_{ij}, d_{ij}) onto the (t,d) plane and calculate the
795 // intersections of the convex hull with the t axis, as for the
796 // bezier clipping case.
798 // calc polygon of control points (t_{ij}, d_{ij}):
800 const int n( 3 ); // cubic bezier curves, as a matter of fact
801 const int i_card( 2*n );
802 const int j_card( n + 1 );
803 const int k_max( n-1 );
804 Polygon2D controlPolygon( i_card*j_card ); // vector of (t_{ij}, d_{ij}) in row-major order
806 int i, j, k, l; // variable notation from formulas above and Sederberg article
807 Point2D::value_type d;
808 for( i=0; i<i_card; ++i )
810 for( j=0; j<j_card; ++j )
812 // calc single d_{ij} sum:
813 for( d=0.0, k=::std::max(0,i-n); k<=k_max && k<=i; ++k )
815 l = i - k; // invariant: k + l = i
816 assert(k>=0 && k<=n-1); // k \in {0,...,n-1}
817 assert(l>=0 && l<=n); // l \in {0,...,n}
819 // TODO: find, document and assert proper limits for n and int's max_val.
820 // This becomes important should anybody wants to use
821 // this code for higher-than-cubic beziers
822 d += static_cast<double>(fallFac(n,l)*fallFac(n-1,k)*fac(i)) /
823 static_cast<double>(fac(l)*fac(k) * fallFac(2*n-1,i)) * n *
824 ( (curve[k+1].x - curve[k].x)*(curve[l].x - focus[j].x) + // dot product here
825 (curve[k+1].y - curve[k].y)*(curve[l].y - focus[j].y) );
828 // Note that the t_{ij} values are evenly spaced on the
829 // [0,1] interval, thus t_{ij}=i/(2n-1)
830 controlPolygon[ i*j_card + j ] = Point2D( i/(2.0*n-1.0), d );
834 #ifndef WITH_SAFEFOCUSPARAM_DETAILED_TEST
836 // calc safe parameter range, to determine [0,t1] and [t2,1] where
837 // no zero crossing is guaranteed.
838 return Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 );
840 #else
841 bool bRet( Impl_calcSafeParams( t1, t2, controlPolygon, 0.0, 0.0 ) );
843 Polygon2D convHull( convexHull( controlPolygon ) );
845 cout << "# convex hull testing (focus)" << endl
846 << "plot [t=0:1] ";
847 cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
848 << "'-' using ($1):($2) title \"convex hull\" with lp" << endl;
850 unsigned int count;
851 for( count=0; count<controlPolygon.size(); ++count )
853 cout << controlPolygon[count].x << " " << controlPolygon[count].y << endl;
855 cout << controlPolygon[0].x << " " << controlPolygon[0].y << endl;
856 cout << "e" << endl;
858 for( count=0; count<convHull.size(); ++count )
860 cout << convHull[count].x << " " << convHull[count].y << endl;
862 cout << convHull[0].x << " " << convHull[0].y << endl;
863 cout << "e" << endl;
865 return bRet;
866 #endif
869 /** Calc all values t_i on c1, for which safeRanges functor does not
870 give a safe range on c1 and c2.
872 This method is the workhorse of the bezier clipping. Because c1
873 and c2 must be alternatingly tested against each other (first
874 determine safe parameter interval on c1 with regard to c2, then
875 the other way around), we call this method recursively with c1 and
876 c2 swapped.
878 @param result
879 Output iterator where the final t values are added to. If curves
880 don't intersect, nothing is added.
882 @param delta
883 Maximal allowed distance to true critical point (measured in the
884 original curve's coordinate system)
886 @param safeRangeFunctor
887 Functor object, that must provide the following operator():
888 bool safeRangeFunctor( double& t1,
889 double& t2,
890 const Bezier& c1_orig,
891 const Bezier& c1_part,
892 const Bezier& c2_orig,
893 const Bezier& c2_part );
894 This functor must calculate the safe ranges [0,t1] and [t2,1] on
895 c1_orig, where c1_orig is 'safe' from c2_part. If the whole
896 c1_orig is safe, false must be returned, true otherwise.
898 template <class Functor> void Impl_applySafeRanges_rec( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
899 double delta,
900 const Functor& safeRangeFunctor,
901 int recursionLevel,
902 const Bezier& c1_orig,
903 const Bezier& c1_part,
904 double last_t1_c1,
905 double last_t2_c1,
906 const Bezier& c2_orig,
907 const Bezier& c2_part,
908 double last_t1_c2,
909 double last_t2_c2 )
911 // check end condition
912 // ===================
914 // TODO: tidy up recursion handling. maybe put everything in a
915 // struct and swap that here at method entry
917 // TODO: Implement limit on recursion depth. Should that limit be
918 // reached, chances are that we're on a higher-order tangency. For
919 // this case, AW proposed to take the middle of the current
920 // interval, and to correct both curve's tangents at that new
921 // endpoint to be equal. That virtually generates a first-order
922 // tangency, and justifies to return a single intersection
923 // point. Otherwise, inside/outside test might fail here.
925 for( int i=0; i<recursionLevel; ++i ) cerr << " ";
926 if( recursionLevel % 2 )
928 cerr << "level: " << recursionLevel
929 << " t: "
930 << last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0
931 << ", c1: " << last_t1_c2 << " " << last_t2_c2
932 << ", c2: " << last_t1_c1 << " " << last_t2_c1
933 << endl;
935 else
937 cerr << "level: " << recursionLevel
938 << " t: "
939 << last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0
940 << ", c1: " << last_t1_c1 << " " << last_t2_c1
941 << ", c2: " << last_t1_c2 << " " << last_t2_c2
942 << endl;
945 // refine solution
946 // ===============
948 double t1_c1, t2_c1;
950 // Note: we first perform the clipping and only test for precision
951 // sufficiency afterwards, since we want to exploit the fact that
952 // Impl_calcClipRange returns false if the curves don't
953 // intersect. We would have to check that separately for the end
954 // condition, otherwise.
956 // determine safe range on c1_orig
957 if( safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part ) )
959 // now, t1 and t2 are calculated on the original curve
960 // (but against a fat line calculated from the subdivided
961 // c2, namely c2_part). If the [t1,t2] range is outside
962 // our current [last_t1,last_t2] range, we're done in this
963 // branch - the curves no longer intersect.
964 if( tolLessEqual(t1_c1, last_t2_c1) && tolGreaterEqual(t2_c1, last_t1_c1) )
966 // As noted above, t1 and t2 are calculated on the
967 // original curve, but against a fat line
968 // calculated from the subdivided c2, namely
969 // c2_part. Our domain to work on is
970 // [last_t1,last_t2], on the other hand, so values
971 // of [t1,t2] outside that range are irrelevant
972 // here. Clip range appropriately.
973 t1_c1 = ::std::max(t1_c1, last_t1_c1);
974 t2_c1 = ::std::min(t2_c1, last_t2_c1);
976 // TODO: respect delta
977 // for now, end condition is just a fixed threshold on the t's
979 // check end condition
980 // ===================
982 #if 1
983 if( fabs(last_t2_c1 - last_t1_c1) < 0.0001 &&
984 fabs(last_t2_c2 - last_t1_c2) < 0.0001 )
985 #else
986 if( fabs(last_t2_c1 - last_t1_c1) < 0.01 &&
987 fabs(last_t2_c2 - last_t1_c2) < 0.01 )
988 #endif
990 // done. Add to result
991 if( recursionLevel % 2 )
993 // uneven level: have to swap the t's, since curves are swapped, too
994 *result++ = ::std::make_pair( last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0,
995 last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0 );
997 else
999 *result++ = ::std::make_pair( last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0,
1000 last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0 );
1003 #if 0
1004 //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, last_t1_c1, last_t2_c1 );
1005 printResultWithFinalCurves( c1_orig, c1_part, c2_orig, c2_part, t1_c1, t2_c1 );
1006 #else
1007 // calc focus curve of c2
1008 Bezier focus;
1009 Impl_calcFocus(focus, c2_part); // need to use subdivided c2
1011 safeRangeFunctor( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
1013 //printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, t1_c1, t2_c1 );
1014 printResultWithFinalCurves( c1_orig, c1_part, c2_orig, focus, last_t1_c1, last_t2_c1 );
1015 #endif
1017 else
1019 // heuristic: if parameter range is not reduced by at least
1020 // 20%, subdivide longest curve, and clip shortest against
1021 // both parts of longest
1022 // if( (last_t2_c1 - last_t1_c1 - t2_c1 + t1_c1) / (last_t2_c1 - last_t1_c1) < 0.2 )
1023 if( false )
1025 // subdivide and descend
1026 // =====================
1028 Bezier part1;
1029 Bezier part2;
1031 double intervalMiddle;
1033 if( last_t2_c1 - last_t1_c1 > last_t2_c2 - last_t1_c2 )
1035 // subdivide c1
1036 // ============
1038 intervalMiddle = last_t1_c1 + (last_t2_c1 - last_t1_c1)/2.0;
1040 // subdivide at the middle of the interval (as
1041 // we're not subdividing on the original
1042 // curve, this simply amounts to subdivision
1043 // at 0.5)
1044 Impl_deCasteljauAt( part1, part2, c1_part, 0.5 );
1046 // and descend recursively with swapped curves
1047 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1048 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1049 c1_orig, part1, last_t1_c1, intervalMiddle );
1051 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1052 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1053 c1_orig, part2, intervalMiddle, last_t2_c1 );
1055 else
1057 // subdivide c2
1058 // ============
1060 intervalMiddle = last_t1_c2 + (last_t2_c2 - last_t1_c2)/2.0;
1062 // subdivide at the middle of the interval (as
1063 // we're not subdividing on the original
1064 // curve, this simply amounts to subdivision
1065 // at 0.5)
1066 Impl_deCasteljauAt( part1, part2, c2_part, 0.5 );
1068 // and descend recursively with swapped curves
1069 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1070 c2_orig, part1, last_t1_c2, intervalMiddle,
1071 c1_orig, c1_part, last_t1_c1, last_t2_c1 );
1073 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1074 c2_orig, part2, intervalMiddle, last_t2_c2,
1075 c1_orig, c1_part, last_t1_c1, last_t2_c1 );
1078 else
1080 // apply calculated clip
1081 // =====================
1083 // clip safe ranges off c1_orig
1084 Bezier c1_part1;
1085 Bezier c1_part2;
1086 Bezier c1_part3;
1088 // subdivide at t1_c1
1089 Impl_deCasteljauAt( c1_part1, c1_part2, c1_orig, t1_c1 );
1091 // subdivide at t2_c1. As we're working on
1092 // c1_part2 now, we have to adapt t2_c1 since
1093 // we're no longer in the original parameter
1094 // interval. This is based on the following
1095 // assumption: t2_new = (t2-t1)/(1-t1), which
1096 // relates the t2 value into the new parameter
1097 // range [0,1] of c1_part2.
1098 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2_c1-t1_c1)/(1.0-t1_c1) );
1100 // descend with swapped curves and c1_part1 as the
1101 // remaining (middle) segment
1102 Impl_applySafeRanges_rec( result, delta, safeRangeFunctor, recursionLevel+1,
1103 c2_orig, c2_part, last_t1_c2, last_t2_c2,
1104 c1_orig, c1_part1, t1_c1, t2_c1 );
1111 struct ClipBezierFunctor
1113 bool operator()( double& t1_c1,
1114 double& t2_c1,
1115 const Bezier& c1_orig,
1116 const Bezier& c1_part,
1117 const Bezier& c2_orig,
1118 const Bezier& c2_part ) const
1120 return Impl_calcClipRange( t1_c1, t2_c1, c1_orig, c1_part, c2_orig, c2_part );
1124 struct BezierTangencyFunctor
1126 bool operator()( double& t1_c1,
1127 double& t2_c1,
1128 const Bezier& c1_orig,
1129 const Bezier& c1_part,
1130 const Bezier& c2_orig,
1131 const Bezier& c2_part ) const
1133 // calc focus curve of c2
1134 Bezier focus;
1135 Impl_calcFocus(focus, c2_part); // need to use subdivided c2
1136 // here, as the whole curve is
1137 // used for focus calculation
1139 // determine safe range on c1_orig
1140 bool bRet( Impl_calcSafeParams_focus( t1_c1, t2_c1,
1141 c1_orig, // use orig curve here, need t's on original curve
1142 focus ) );
1144 cerr << "range: " << t2_c1 - t1_c1 << ", ret: " << bRet << endl;
1146 return bRet;
1150 /** Perform a bezier clip (curve against curve)
1152 @param result
1153 Output iterator where the final t values are added to. This
1154 iterator will remain empty, if there are no intersections.
1156 @param delta
1157 Maximal allowed distance to true intersection (measured in the
1158 original curve's coordinate system)
1160 void clipBezier( ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > >& result,
1161 double delta,
1162 const Bezier& c1,
1163 const Bezier& c2 )
1165 #if 0
1166 // first of all, determine list of collinear normals. Collinear
1167 // normals typically separate two intersections, thus, subdivide
1168 // at all collinear normal's t values beforehand. This will cater
1169 // for tangent intersections, where two or more intersections are
1170 // infinitesimally close together.
1172 // TODO: evaluate effects of higher-than-second-order
1173 // tangencies. Sederberg et al. state that collinear normal
1174 // algorithm then degrades quickly.
1176 ::std::vector< ::std::pair<double,double> > results;
1177 ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(results);
1179 Impl_calcCollinearNormals( ii, delta, 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1181 // As Sederberg's collinear normal theorem is only sufficient, not
1182 // necessary for two intersections left and right, we've to test
1183 // all segments generated by the collinear normal algorithm
1184 // against each other. In other words, if the two curves are both
1185 // divided in a left and a right part, the collinear normal
1186 // theorem does _not_ state that the left part of curve 1 does not
1187 // e.g. intersect with the right part of curve 2.
1189 // divide c1 and c2 at collinear normal intersection points
1190 ::std::vector< Bezier > c1_segments( results.size()+1 );
1191 ::std::vector< Bezier > c2_segments( results.size()+1 );
1192 Bezier c1_remainder( c1 );
1193 Bezier c2_remainder( c2 );
1194 unsigned int i;
1195 for( i=0; i<results.size(); ++i )
1197 Bezier c1_part2;
1198 Impl_deCasteljauAt( c1_segments[i], c1_part2, c1_remainder, results[i].first );
1199 c1_remainder = c1_part2;
1201 Bezier c2_part2;
1202 Impl_deCasteljauAt( c2_segments[i], c2_part2, c2_remainder, results[i].second );
1203 c2_remainder = c2_part2;
1205 c1_segments[i] = c1_remainder;
1206 c2_segments[i] = c2_remainder;
1208 // now, c1/c2_segments contain all segments, then
1209 // clip every resulting segment against every other
1210 unsigned int c1_curr, c2_curr;
1211 for( c1_curr=0; c1_curr<c1_segments.size(); ++c1_curr )
1213 for( c2_curr=0; c2_curr<c2_segments.size(); ++c2_curr )
1215 if( c1_curr != c2_curr )
1217 Impl_clipBezier_rec(result, delta, 0,
1218 c1_segments[c1_curr], c1_segments[c1_curr],
1219 0.0, 1.0,
1220 c2_segments[c2_curr], c2_segments[c2_curr],
1221 0.0, 1.0);
1225 #else
1226 Impl_applySafeRanges_rec( result, delta, BezierTangencyFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1227 //Impl_applySafeRanges_rec( result, delta, ClipBezierFunctor(), 0, c1, c1, 0.0, 1.0, c2, c2, 0.0, 1.0 );
1228 #endif
1229 // that's it, boys'n'girls!
1232 int main(int argc, const char *argv[])
1234 double curr_Offset( 0 );
1235 unsigned int i,j,k;
1237 Bezier someCurves[] =
1239 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.0)},
1240 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
1241 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)}
1242 // {Point2D(0.25+1,0.5),Point2D(0.25+1,0.708333),Point2D(0.423611+1,0.916667),Point2D(0.770833+1,0.980324)},
1243 // {Point2D(0.0+1,0.0),Point2D(0.0+1,1.0),Point2D(1.0+1,1.0),Point2D(1.0+1,0.5)}
1245 // tangency1
1246 // {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)},
1247 // {Point2D(0.0,1.0),Point2D(0.1,1.0),Point2D(0.4,1.0),Point2D(0.5,1.0)}
1249 // {Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0),Point2D(1.0,0.5)},
1250 // {Point2D(0.60114,0.933091),Point2D(0.69461,0.969419),Point2D(0.80676,0.992976),Point2D(0.93756,0.998663)}
1251 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
1252 // {Point2D(0.62712,0.828427),Point2D(0.76305,0.828507),Point2D(0.88555,0.77312),Point2D(0.95069,0.67325)}
1254 // clipping1
1255 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1256 // {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)}
1258 // tangency2
1259 // {Point2D(0.0,1.0),Point2D(3.5,1.0),Point2D(-2.5,0.0),Point2D(1.0,0.0)},
1260 // {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
1262 // tangency3
1263 // {Point2D(1.0,0.0),Point2D(0.0,0.0),Point2D(0.0,1.0),Point2D(1.0,1.0)},
1264 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)}
1266 // tangency4
1267 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
1268 // {Point2D(0.26,0.4),Point2D(0.25,0.5),Point2D(0.25,0.5),Point2D(0.26,0.6)}
1270 // tangency5
1271 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1272 // {Point2D(15.3621,0.00986464),Point2D(15.3683,0.0109389),Point2D(15.3682,0.0109315),Point2D(15.3621,0.00986464)}
1274 // tangency6
1275 // {Point2D(0.0,0.0),Point2D(0.0,3.5),Point2D(1.0,-2.5),Point2D(1.0,1.0)},
1276 // {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
1278 // tangency7
1279 // {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)},
1280 // {Point2D(15.3621,10.00986464),Point2D(15.3683,10.0109389),Point2D(15.3682,10.0109315),Point2D(15.3621,10.00986464)}
1282 // tangency Sederberg example
1283 {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)},
1284 {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)}
1286 // clipping2
1287 // {Point2D(-0.5,0.0),Point2D(0.5,0.0),Point2D(0.5,1.0),Point2D(-0.5,1.0)},
1288 // {Point2D(0.2575,0.4),Point2D(0.2475,0.5),Point2D(0.2475,0.5),Point2D(0.2575,0.6)}
1290 // {Point2D(0.0,0.1),Point2D(0.2,3.5),Point2D(1.0,-2.5),Point2D(1.1,1.2)},
1291 // {Point2D(0.0,1.0),Point2D(3.5,0.9),Point2D(-2.5,0.1),Point2D(1.1,0.2)}
1292 // {Point2D(0.0,0.1),Point2D(0.2,3.0),Point2D(1.0,-2.0),Point2D(1.1,1.2)},
1293 // {Point2D(0.627124+1,0.828427),Point2D(0.763048+1,0.828507),Point2D(0.885547+1,0.77312),Point2D(0.950692+1,0.67325)}
1294 // {Point2D(0.0,1.0),Point2D(3.0,0.9),Point2D(-2.0,0.1),Point2D(1.1,0.2)}
1295 // {Point2D(0.0,4.0),Point2D(0.1,5.0),Point2D(0.9,5.0),Point2D(1.0,4.0)},
1296 // {Point2D(0.0,0.0),Point2D(0.1,0.5),Point2D(0.9,0.5),Point2D(1.0,0.0)},
1297 // {Point2D(0.0,0.1),Point2D(0.1,1.5),Point2D(0.9,1.5),Point2D(1.0,0.1)},
1298 // {Point2D(0.0,-4.0),Point2D(0.1,-5.0),Point2D(0.9,-5.0),Point2D(1.0,-4.0)}
1301 // output gnuplot setup
1302 cout << "#!/usr/bin/gnuplot -persist" << endl
1303 << "#" << endl
1304 << "# automatically generated by bezierclip, don't change!" << endl
1305 << "#" << endl
1306 << "set parametric" << endl
1307 << "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
1308 << "bezd(p,q,r,s,t) = 3*(q-p)*(1-t)**2+6*(r-q)*(1-t)*t+3*(s-r)*t**2" << endl
1309 << "pointmarkx(c,t) = c-0.03*t" << endl
1310 << "pointmarky(c,t) = c+0.03*t" << endl
1311 << "linex(a,b,c,t) = a*-c + t*-b" << endl
1312 << "liney(a,b,c,t) = b*-c + t*a" << endl << endl
1313 << "# end of setup" << endl << endl;
1315 #ifdef WITH_CONVEXHULL_TEST
1316 // test convex hull algorithm
1317 const double convHull_xOffset( curr_Offset );
1318 curr_Offset += 20;
1319 cout << "# convex hull testing" << endl
1320 << "plot [t=0:1] ";
1321 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1323 Polygon2D aTestPoly(4);
1324 aTestPoly[0] = someCurves[i].p0;
1325 aTestPoly[1] = someCurves[i].p1;
1326 aTestPoly[2] = someCurves[i].p2;
1327 aTestPoly[3] = someCurves[i].p3;
1329 aTestPoly[0].x += convHull_xOffset;
1330 aTestPoly[1].x += convHull_xOffset;
1331 aTestPoly[2].x += convHull_xOffset;
1332 aTestPoly[3].x += convHull_xOffset;
1334 cout << " bez("
1335 << aTestPoly[0].x << ","
1336 << aTestPoly[1].x << ","
1337 << aTestPoly[2].x << ","
1338 << aTestPoly[3].x << ",t),bez("
1339 << aTestPoly[0].y << ","
1340 << aTestPoly[1].y << ","
1341 << aTestPoly[2].y << ","
1342 << aTestPoly[3].y << ",t), '-' using ($1):($2) title \"convex hull " << i << "\" with lp";
1344 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1345 cout << ",\\" << endl;
1346 else
1347 cout << endl;
1349 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1351 Polygon2D aTestPoly(4);
1352 aTestPoly[0] = someCurves[i].p0;
1353 aTestPoly[1] = someCurves[i].p1;
1354 aTestPoly[2] = someCurves[i].p2;
1355 aTestPoly[3] = someCurves[i].p3;
1357 aTestPoly[0].x += convHull_xOffset;
1358 aTestPoly[1].x += convHull_xOffset;
1359 aTestPoly[2].x += convHull_xOffset;
1360 aTestPoly[3].x += convHull_xOffset;
1362 Polygon2D convHull( convexHull(aTestPoly) );
1364 for( k=0; k<convHull.size(); ++k )
1366 cout << convHull[k].x << " " << convHull[k].y << endl;
1368 cout << convHull[0].x << " " << convHull[0].y << endl;
1369 cout << "e" << endl;
1371 #endif
1373 #ifdef WITH_MULTISUBDIVIDE_TEST
1374 // test convex hull algorithm
1375 const double multiSubdivide_xOffset( curr_Offset );
1376 curr_Offset += 20;
1377 cout << "# multi subdivide testing" << endl
1378 << "plot [t=0:1] ";
1379 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1381 Bezier c( someCurves[i] );
1382 Bezier c1_part1;
1383 Bezier c1_part2;
1384 Bezier c1_part3;
1386 c.p0.x += multiSubdivide_xOffset;
1387 c.p1.x += multiSubdivide_xOffset;
1388 c.p2.x += multiSubdivide_xOffset;
1389 c.p3.x += multiSubdivide_xOffset;
1391 const double t1( 0.1+i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
1392 const double t2( 0.9-i/(3.0*sizeof(someCurves)/sizeof(Bezier)) );
1394 // subdivide at t1
1395 Impl_deCasteljauAt( c1_part1, c1_part2, c, t1 );
1397 // subdivide at t2_c1. As we're working on
1398 // c1_part2 now, we have to adapt t2_c1 since
1399 // we're no longer in the original parameter
1400 // interval. This is based on the following
1401 // assumption: t2_new = (t2-t1)/(1-t1), which
1402 // relates the t2 value into the new parameter
1403 // range [0,1] of c1_part2.
1404 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1406 // subdivide at t2
1407 Impl_deCasteljauAt( c1_part3, c1_part2, c, t2 );
1409 cout << " bez("
1410 << c1_part1.p0.x << ","
1411 << c1_part1.p1.x << ","
1412 << c1_part1.p2.x << ","
1413 << c1_part1.p3.x << ",t), bez("
1414 << c1_part1.p0.y+0.01 << ","
1415 << c1_part1.p1.y+0.01 << ","
1416 << c1_part1.p2.y+0.01 << ","
1417 << c1_part1.p3.y+0.01 << ",t) title \"middle " << i << "\", "
1418 << " bez("
1419 << c1_part2.p0.x << ","
1420 << c1_part2.p1.x << ","
1421 << c1_part2.p2.x << ","
1422 << c1_part2.p3.x << ",t), bez("
1423 << c1_part2.p0.y << ","
1424 << c1_part2.p1.y << ","
1425 << c1_part2.p2.y << ","
1426 << c1_part2.p3.y << ",t) title \"right " << i << "\", "
1427 << " bez("
1428 << c1_part3.p0.x << ","
1429 << c1_part3.p1.x << ","
1430 << c1_part3.p2.x << ","
1431 << c1_part3.p3.x << ",t), bez("
1432 << c1_part3.p0.y << ","
1433 << c1_part3.p1.y << ","
1434 << c1_part3.p2.y << ","
1435 << c1_part3.p3.y << ",t) title \"left " << i << "\"";
1437 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1438 cout << ",\\" << endl;
1439 else
1440 cout << endl;
1442 #endif
1444 #ifdef WITH_FATLINE_TEST
1445 // test fatline algorithm
1446 const double fatLine_xOffset( curr_Offset );
1447 curr_Offset += 20;
1448 cout << "# fat line testing" << endl
1449 << "plot [t=0:1] ";
1450 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1452 Bezier c( someCurves[i] );
1454 c.p0.x += fatLine_xOffset;
1455 c.p1.x += fatLine_xOffset;
1456 c.p2.x += fatLine_xOffset;
1457 c.p3.x += fatLine_xOffset;
1459 FatLine line;
1461 Impl_calcFatLine(line, c);
1463 cout << " bez("
1464 << c.p0.x << ","
1465 << c.p1.x << ","
1466 << c.p2.x << ","
1467 << c.p3.x << ",t), bez("
1468 << c.p0.y << ","
1469 << c.p1.y << ","
1470 << c.p2.y << ","
1471 << c.p3.y << ",t) title \"bezier " << i << "\", linex("
1472 << line.a << ","
1473 << line.b << ","
1474 << line.c << ",t), liney("
1475 << line.a << ","
1476 << line.b << ","
1477 << line.c << ",t) title \"fat line (center) on " << i << "\", linex("
1478 << line.a << ","
1479 << line.b << ","
1480 << line.c-line.dMin << ",t), liney("
1481 << line.a << ","
1482 << line.b << ","
1483 << line.c-line.dMin << ",t) title \"fat line (min) on " << i << "\", linex("
1484 << line.a << ","
1485 << line.b << ","
1486 << line.c-line.dMax << ",t), liney("
1487 << line.a << ","
1488 << line.b << ","
1489 << line.c-line.dMax << ",t) title \"fat line (max) on " << i << "\"";
1491 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1492 cout << ",\\" << endl;
1493 else
1494 cout << endl;
1496 #endif
1498 #ifdef WITH_CALCFOCUS_TEST
1499 // test focus curve algorithm
1500 const double focus_xOffset( curr_Offset );
1501 curr_Offset += 20;
1502 cout << "# focus line testing" << endl
1503 << "plot [t=0:1] ";
1504 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1506 Bezier c( someCurves[i] );
1508 c.p0.x += focus_xOffset;
1509 c.p1.x += focus_xOffset;
1510 c.p2.x += focus_xOffset;
1511 c.p3.x += focus_xOffset;
1513 // calc focus curve
1514 Bezier focus;
1515 Impl_calcFocus(focus, c);
1517 cout << " bez("
1518 << c.p0.x << ","
1519 << c.p1.x << ","
1520 << c.p2.x << ","
1521 << c.p3.x << ",t), bez("
1522 << c.p0.y << ","
1523 << c.p1.y << ","
1524 << c.p2.y << ","
1525 << c.p3.y << ",t) title \"bezier " << i << "\", bez("
1526 << focus.p0.x << ","
1527 << focus.p1.x << ","
1528 << focus.p2.x << ","
1529 << focus.p3.x << ",t), bez("
1530 << focus.p0.y << ","
1531 << focus.p1.y << ","
1532 << focus.p2.y << ","
1533 << focus.p3.y << ",t) title \"focus " << i << "\"";
1535 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1536 cout << ",\\" << endl;
1537 else
1538 cout << endl;
1540 #endif
1542 #ifdef WITH_SAFEPARAMBASE_TEST
1543 // test safe params base method
1544 double safeParamsBase_xOffset( curr_Offset );
1545 cout << "# safe param base method 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 += safeParamsBase_xOffset;
1552 c.p1.x += safeParamsBase_xOffset;
1553 c.p2.x += safeParamsBase_xOffset;
1554 c.p3.x += safeParamsBase_xOffset;
1556 Polygon2D poly(4);
1557 poly[0] = c.p0;
1558 poly[1] = c.p1;
1559 poly[2] = c.p2;
1560 poly[3] = c.p3;
1562 double t1, t2;
1564 bool bRet( Impl_calcSafeParams( t1, t2, poly, 0, 1 ) );
1566 Polygon2D convHull( convexHull( poly ) );
1568 cout << " bez("
1569 << poly[0].x << ","
1570 << poly[1].x << ","
1571 << poly[2].x << ","
1572 << poly[3].x << ",t),bez("
1573 << poly[0].y << ","
1574 << poly[1].y << ","
1575 << poly[2].y << ","
1576 << poly[3].y << ",t), "
1577 << "t+" << safeParamsBase_xOffset << ", 0, "
1578 << "t+" << safeParamsBase_xOffset << ", 1, ";
1579 if( bRet )
1581 cout << t1+safeParamsBase_xOffset << ", t, "
1582 << t2+safeParamsBase_xOffset << ", t, ";
1584 cout << "'-' using ($1):($2) title \"control polygon\" with lp, "
1585 << "'-' using ($1):($2) title \"convex hull\" with lp";
1587 if( i+1<sizeof(someCurves)/sizeof(Bezier) )
1588 cout << ",\\" << endl;
1589 else
1590 cout << endl;
1592 safeParamsBase_xOffset += 2;
1595 safeParamsBase_xOffset = curr_Offset;
1596 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1598 Bezier c( someCurves[i] );
1600 c.p0.x += safeParamsBase_xOffset;
1601 c.p1.x += safeParamsBase_xOffset;
1602 c.p2.x += safeParamsBase_xOffset;
1603 c.p3.x += safeParamsBase_xOffset;
1605 Polygon2D poly(4);
1606 poly[0] = c.p0;
1607 poly[1] = c.p1;
1608 poly[2] = c.p2;
1609 poly[3] = c.p3;
1611 double t1, t2;
1613 Impl_calcSafeParams( t1, t2, poly, 0, 1 );
1615 Polygon2D convHull( convexHull( poly ) );
1617 unsigned int k;
1618 for( k=0; k<poly.size(); ++k )
1620 cout << poly[k].x << " " << poly[k].y << endl;
1622 cout << poly[0].x << " " << poly[0].y << endl;
1623 cout << "e" << endl;
1625 for( k=0; k<convHull.size(); ++k )
1627 cout << convHull[k].x << " " << convHull[k].y << endl;
1629 cout << convHull[0].x << " " << convHull[0].y << endl;
1630 cout << "e" << endl;
1632 safeParamsBase_xOffset += 2;
1634 curr_Offset += 20;
1635 #endif
1637 #ifdef WITH_SAFEPARAMS_TEST
1638 // test safe parameter range algorithm
1639 const double safeParams_xOffset( curr_Offset );
1640 curr_Offset += 20;
1641 cout << "# safe param range testing" << endl
1642 << "plot [t=0.0:1.0] ";
1643 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1645 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1647 Bezier c1( someCurves[i] );
1648 Bezier c2( someCurves[j] );
1650 c1.p0.x += safeParams_xOffset;
1651 c1.p1.x += safeParams_xOffset;
1652 c1.p2.x += safeParams_xOffset;
1653 c1.p3.x += safeParams_xOffset;
1654 c2.p0.x += safeParams_xOffset;
1655 c2.p1.x += safeParams_xOffset;
1656 c2.p2.x += safeParams_xOffset;
1657 c2.p3.x += safeParams_xOffset;
1659 double t1, t2;
1661 if( Impl_calcClipRange(t1, t2, c1, c1, c2, c2) )
1663 // clip safe ranges off c1
1664 Bezier c1_part1;
1665 Bezier c1_part2;
1666 Bezier c1_part3;
1668 // subdivide at t1_c1
1669 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
1670 // subdivide at t2_c1
1671 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1673 // output remaining segment (c1_part1)
1675 cout << " bez("
1676 << c1.p0.x << ","
1677 << c1.p1.x << ","
1678 << c1.p2.x << ","
1679 << c1.p3.x << ",t),bez("
1680 << c1.p0.y << ","
1681 << c1.p1.y << ","
1682 << c1.p2.y << ","
1683 << c1.p3.y << ",t), bez("
1684 << c2.p0.x << ","
1685 << c2.p1.x << ","
1686 << c2.p2.x << ","
1687 << c2.p3.x << ",t),bez("
1688 << c2.p0.y << ","
1689 << c2.p1.y << ","
1690 << c2.p2.y << ","
1691 << c2.p3.y << ",t), bez("
1692 << c1_part1.p0.x << ","
1693 << c1_part1.p1.x << ","
1694 << c1_part1.p2.x << ","
1695 << c1_part1.p3.x << ",t),bez("
1696 << c1_part1.p0.y << ","
1697 << c1_part1.p1.y << ","
1698 << c1_part1.p2.y << ","
1699 << c1_part1.p3.y << ",t)";
1701 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
1702 cout << ",\\" << endl;
1703 else
1704 cout << endl;
1708 #endif
1710 #ifdef WITH_SAFEPARAM_DETAILED_TEST
1711 // test safe parameter range algorithm
1712 const double safeParams2_xOffset( curr_Offset );
1713 curr_Offset += 20;
1714 if( sizeof(someCurves)/sizeof(Bezier) > 1 )
1716 Bezier c1( someCurves[0] );
1717 Bezier c2( someCurves[1] );
1719 c1.p0.x += safeParams2_xOffset;
1720 c1.p1.x += safeParams2_xOffset;
1721 c1.p2.x += safeParams2_xOffset;
1722 c1.p3.x += safeParams2_xOffset;
1723 c2.p0.x += safeParams2_xOffset;
1724 c2.p1.x += safeParams2_xOffset;
1725 c2.p2.x += safeParams2_xOffset;
1726 c2.p3.x += safeParams2_xOffset;
1728 double t1, t2;
1730 // output happens here
1731 Impl_calcClipRange(t1, t2, c1, c1, c2, c2);
1733 #endif
1735 #ifdef WITH_SAFEFOCUSPARAM_TEST
1736 // test safe parameter range from focus algorithm
1737 const double safeParamsFocus_xOffset( curr_Offset );
1738 curr_Offset += 20;
1739 cout << "# safe param range from focus testing" << endl
1740 << "plot [t=0.0:1.0] ";
1741 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1743 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1745 Bezier c1( someCurves[i] );
1746 Bezier c2( someCurves[j] );
1748 c1.p0.x += safeParamsFocus_xOffset;
1749 c1.p1.x += safeParamsFocus_xOffset;
1750 c1.p2.x += safeParamsFocus_xOffset;
1751 c1.p3.x += safeParamsFocus_xOffset;
1752 c2.p0.x += safeParamsFocus_xOffset;
1753 c2.p1.x += safeParamsFocus_xOffset;
1754 c2.p2.x += safeParamsFocus_xOffset;
1755 c2.p3.x += safeParamsFocus_xOffset;
1757 double t1, t2;
1759 Bezier focus;
1760 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1761 #if 0
1763 // clip safe ranges off c1_orig
1764 Bezier c1_part1;
1765 Bezier c1_part2;
1766 Bezier c1_part3;
1768 // subdivide at t1_c1
1769 Impl_deCasteljauAt( c1_part1, c1_part2, c2, 0.30204 );
1771 // subdivide at t2_c1. As we're working on
1772 // c1_part2 now, we have to adapt t2_c1 since
1773 // we're no longer in the original parameter
1774 // interval. This is based on the following
1775 // assumption: t2_new = (t2-t1)/(1-t1), which
1776 // relates the t2 value into the new parameter
1777 // range [0,1] of c1_part2.
1778 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (0.57151-0.30204)/(1.0-0.30204) );
1780 c2 = c1_part1;
1781 Impl_calcFocus( focus, c2 );
1783 #else
1784 Impl_calcFocus( focus, c2 );
1785 #endif
1786 #else
1787 focus = c2;
1788 #endif
1789 // determine safe range on c1
1790 bool bRet( Impl_calcSafeParams_focus( t1, t2,
1791 c1, focus ) );
1793 cerr << "t1: " << t1 << ", t2: " << t2 << endl;
1795 // clip safe ranges off c1
1796 Bezier c1_part1;
1797 Bezier c1_part2;
1798 Bezier c1_part3;
1800 // subdivide at t1_c1
1801 Impl_deCasteljauAt( c1_part1, c1_part2, c1, t1 );
1802 // subdivide at t2_c1
1803 Impl_deCasteljauAt( c1_part1, c1_part3, c1_part2, (t2-t1)/(1.0-t1) );
1805 // output remaining segment (c1_part1)
1807 cout << " bez("
1808 << c1.p0.x << ","
1809 << c1.p1.x << ","
1810 << c1.p2.x << ","
1811 << c1.p3.x << ",t),bez("
1812 << c1.p0.y << ","
1813 << c1.p1.y << ","
1814 << c1.p2.y << ","
1815 << c1.p3.y << ",t) title \"c1\", "
1816 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1817 << "bez("
1818 << c2.p0.x << ","
1819 << c2.p1.x << ","
1820 << c2.p2.x << ","
1821 << c2.p3.x << ",t),bez("
1822 << c2.p0.y << ","
1823 << c2.p1.y << ","
1824 << c2.p2.y << ","
1825 << c2.p3.y << ",t) title \"c2\", "
1826 << "bez("
1827 << focus.p0.x << ","
1828 << focus.p1.x << ","
1829 << focus.p2.x << ","
1830 << focus.p3.x << ",t),bez("
1831 << focus.p0.y << ","
1832 << focus.p1.y << ","
1833 << focus.p2.y << ","
1834 << focus.p3.y << ",t) title \"focus\"";
1835 #else
1836 << "bez("
1837 << c2.p0.x << ","
1838 << c2.p1.x << ","
1839 << c2.p2.x << ","
1840 << c2.p3.x << ",t),bez("
1841 << c2.p0.y << ","
1842 << c2.p1.y << ","
1843 << c2.p2.y << ","
1844 << c2.p3.y << ",t) title \"focus\"";
1845 #endif
1846 if( bRet )
1848 cout << ", bez("
1849 << c1_part1.p0.x << ","
1850 << c1_part1.p1.x << ","
1851 << c1_part1.p2.x << ","
1852 << c1_part1.p3.x << ",t),bez("
1853 << c1_part1.p0.y+0.01 << ","
1854 << c1_part1.p1.y+0.01 << ","
1855 << c1_part1.p2.y+0.01 << ","
1856 << c1_part1.p3.y+0.01 << ",t) title \"part\"";
1859 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
1860 cout << ",\\" << endl;
1861 else
1862 cout << endl;
1865 #endif
1867 #ifdef WITH_SAFEFOCUSPARAM_DETAILED_TEST
1868 // test safe parameter range algorithm
1869 const double safeParams3_xOffset( curr_Offset );
1870 curr_Offset += 20;
1871 if( sizeof(someCurves)/sizeof(Bezier) > 1 )
1873 Bezier c1( someCurves[0] );
1874 Bezier c2( someCurves[1] );
1876 c1.p0.x += safeParams3_xOffset;
1877 c1.p1.x += safeParams3_xOffset;
1878 c1.p2.x += safeParams3_xOffset;
1879 c1.p3.x += safeParams3_xOffset;
1880 c2.p0.x += safeParams3_xOffset;
1881 c2.p1.x += safeParams3_xOffset;
1882 c2.p2.x += safeParams3_xOffset;
1883 c2.p3.x += safeParams3_xOffset;
1885 double t1, t2;
1887 Bezier focus;
1888 #ifdef WITH_SAFEFOCUSPARAM_CALCFOCUS
1889 Impl_calcFocus( focus, c2 );
1890 #else
1891 focus = c2;
1892 #endif
1894 // determine safe range on c1, output happens here
1895 Impl_calcSafeParams_focus( t1, t2,
1896 c1, focus );
1898 #endif
1900 #ifdef WITH_BEZIERCLIP_TEST
1901 ::std::vector< ::std::pair<double, double> > result;
1902 ::std::back_insert_iterator< ::std::vector< ::std::pair<double, double> > > ii(result);
1904 // test full bezier clipping
1905 const double bezierClip_xOffset( curr_Offset );
1906 curr_Offset += 20;
1907 cout << endl << endl << "# bezier clip testing" << endl
1908 << "plot [t=0:1] ";
1909 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1911 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1913 Bezier c1( someCurves[i] );
1914 Bezier c2( someCurves[j] );
1916 c1.p0.x += bezierClip_xOffset;
1917 c1.p1.x += bezierClip_xOffset;
1918 c1.p2.x += bezierClip_xOffset;
1919 c1.p3.x += bezierClip_xOffset;
1920 c2.p0.x += bezierClip_xOffset;
1921 c2.p1.x += bezierClip_xOffset;
1922 c2.p2.x += bezierClip_xOffset;
1923 c2.p3.x += bezierClip_xOffset;
1925 cout << " bez("
1926 << c1.p0.x << ","
1927 << c1.p1.x << ","
1928 << c1.p2.x << ","
1929 << c1.p3.x << ",t),bez("
1930 << c1.p0.y << ","
1931 << c1.p1.y << ","
1932 << c1.p2.y << ","
1933 << c1.p3.y << ",t), bez("
1934 << c2.p0.x << ","
1935 << c2.p1.x << ","
1936 << c2.p2.x << ","
1937 << c2.p3.x << ",t),bez("
1938 << c2.p0.y << ","
1939 << c2.p1.y << ","
1940 << c2.p2.y << ","
1941 << c2.p3.y << ",t), '-' using (bez("
1942 << c1.p0.x << ","
1943 << c1.p1.x << ","
1944 << c1.p2.x << ","
1945 << c1.p3.x
1946 << ",$1)):(bez("
1947 << c1.p0.y << ","
1948 << c1.p1.y << ","
1949 << c1.p2.y << ","
1950 << c1.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << i << ")\", "
1951 << " '-' using (bez("
1952 << c2.p0.x << ","
1953 << c2.p1.x << ","
1954 << c2.p2.x << ","
1955 << c2.p3.x
1956 << ",$1)):(bez("
1957 << c2.p0.y << ","
1958 << c2.p1.y << ","
1959 << c2.p2.y << ","
1960 << c2.p3.y << ",$1)) title \"bezier " << i << " clipped against " << j << " (t on " << j << ")\"";
1962 if( i+2<sizeof(someCurves)/sizeof(Bezier) )
1963 cout << ",\\" << endl;
1964 else
1965 cout << endl;
1968 for( i=0; i<sizeof(someCurves)/sizeof(Bezier); ++i )
1970 for( j=i+1; j<sizeof(someCurves)/sizeof(Bezier); ++j )
1972 result.clear();
1973 Bezier c1( someCurves[i] );
1974 Bezier c2( someCurves[j] );
1976 c1.p0.x += bezierClip_xOffset;
1977 c1.p1.x += bezierClip_xOffset;
1978 c1.p2.x += bezierClip_xOffset;
1979 c1.p3.x += bezierClip_xOffset;
1980 c2.p0.x += bezierClip_xOffset;
1981 c2.p1.x += bezierClip_xOffset;
1982 c2.p2.x += bezierClip_xOffset;
1983 c2.p3.x += bezierClip_xOffset;
1985 clipBezier( ii, 0.00001, c1, c2 );
1987 for( k=0; k<result.size(); ++k )
1989 cout << result[k].first << endl;
1991 cout << "e" << endl;
1993 for( k=0; k<result.size(); ++k )
1995 cout << result[k].second << endl;
1997 cout << "e" << endl;
2000 #endif
2002 return 0;
2005 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */