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