Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <basegfx/curve/b2dcubicbezier.hxx>
21 : #include <basegfx/vector/b2dvector.hxx>
22 : #include <basegfx/polygon/b2dpolygon.hxx>
23 : #include <basegfx/numeric/ftools.hxx>
24 :
25 : #include <osl/diagnose.h>
26 :
27 : #include <limits>
28 :
29 : // #i37443#
30 : #define FACTOR_FOR_UNSHARPEN (1.6)
31 : #ifdef DBG_UTIL
32 : static double fMultFactUnsharpen = FACTOR_FOR_UNSHARPEN;
33 : #endif
34 :
35 : namespace basegfx
36 : {
37 : namespace
38 : {
39 139823 : void ImpSubDivAngle(
40 : const B2DPoint& rfPA, // start point
41 : const B2DPoint& rfEA, // edge on A
42 : const B2DPoint& rfEB, // edge on B
43 : const B2DPoint& rfPB, // end point
44 : B2DPolygon& rTarget, // target polygon
45 : double fAngleBound, // angle bound in [0.0 .. 2PI]
46 : bool bAllowUnsharpen, // #i37443# allow the criteria to get unsharp in recursions
47 : sal_uInt16 nMaxRecursionDepth) // endless loop protection
48 : {
49 139823 : if(nMaxRecursionDepth)
50 : {
51 : // do angle test
52 139823 : B2DVector aLeft(rfEA - rfPA);
53 279646 : B2DVector aRight(rfEB - rfPB);
54 :
55 : // #i72104#
56 139823 : if(aLeft.equalZero())
57 : {
58 42 : aLeft = rfEB - rfPA;
59 : }
60 :
61 139823 : if(aRight.equalZero())
62 : {
63 12 : aRight = rfEA - rfPB;
64 : }
65 :
66 139823 : const double fCurrentAngle(aLeft.angle(aRight));
67 :
68 139823 : if(fabs(fCurrentAngle) > (F_PI - fAngleBound))
69 : {
70 : // end recursion
71 78828 : nMaxRecursionDepth = 0;
72 : }
73 : else
74 : {
75 60995 : if(bAllowUnsharpen)
76 : {
77 : // #i37443# unsharpen criteria
78 : #ifdef DBG_UTIL
79 : fAngleBound *= fMultFactUnsharpen;
80 : #else
81 60995 : fAngleBound *= FACTOR_FOR_UNSHARPEN;
82 : #endif
83 : }
84 139823 : }
85 : }
86 :
87 139823 : if(nMaxRecursionDepth)
88 : {
89 : // divide at 0.5
90 60995 : const B2DPoint aS1L(average(rfPA, rfEA));
91 121990 : const B2DPoint aS1C(average(rfEA, rfEB));
92 121990 : const B2DPoint aS1R(average(rfEB, rfPB));
93 121990 : const B2DPoint aS2L(average(aS1L, aS1C));
94 121990 : const B2DPoint aS2R(average(aS1C, aS1R));
95 121990 : const B2DPoint aS3C(average(aS2L, aS2R));
96 :
97 : // left recursion
98 60995 : ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
99 :
100 : // right recursion
101 121990 : ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, fAngleBound, bAllowUnsharpen, nMaxRecursionDepth - 1);
102 : }
103 : else
104 : {
105 78828 : rTarget.append(rfPB);
106 : }
107 139823 : }
108 :
109 9320 : void ImpSubDivAngleStart(
110 : const B2DPoint& rfPA, // start point
111 : const B2DPoint& rfEA, // edge on A
112 : const B2DPoint& rfEB, // edge on B
113 : const B2DPoint& rfPB, // end point
114 : B2DPolygon& rTarget, // target polygon
115 : const double& rfAngleBound, // angle bound in [0.0 .. 2PI]
116 : bool bAllowUnsharpen) // #i37443# allow the criteria to get unsharp in recursions
117 : {
118 9320 : sal_uInt16 nMaxRecursionDepth(8);
119 9320 : const B2DVector aLeft(rfEA - rfPA);
120 18640 : const B2DVector aRight(rfEB - rfPB);
121 9320 : bool bLeftEqualZero(aLeft.equalZero());
122 9320 : bool bRightEqualZero(aRight.equalZero());
123 9320 : bool bAllParallel(false);
124 :
125 9320 : if(bLeftEqualZero && bRightEqualZero)
126 : {
127 0 : nMaxRecursionDepth = 0;
128 : }
129 : else
130 : {
131 9320 : const B2DVector aBase(rfPB - rfPA);
132 9320 : const bool bBaseEqualZero(aBase.equalZero()); // #i72104#
133 :
134 9320 : if(!bBaseEqualZero)
135 : {
136 9320 : const bool bLeftParallel(bLeftEqualZero || areParallel(aLeft, aBase));
137 9320 : const bool bRightParallel(bRightEqualZero || areParallel(aRight, aBase));
138 :
139 9320 : if(bLeftParallel && bRightParallel)
140 : {
141 0 : bAllParallel = true;
142 :
143 0 : if(!bLeftEqualZero)
144 : {
145 : double fFactor;
146 :
147 0 : if(fabs(aBase.getX()) > fabs(aBase.getY()))
148 : {
149 0 : fFactor = aLeft.getX() / aBase.getX();
150 : }
151 : else
152 : {
153 0 : fFactor = aLeft.getY() / aBase.getY();
154 : }
155 :
156 0 : if(fFactor >= 0.0 && fFactor <= 1.0)
157 : {
158 0 : bLeftEqualZero = true;
159 : }
160 : }
161 :
162 0 : if(!bRightEqualZero)
163 : {
164 : double fFactor;
165 :
166 0 : if(fabs(aBase.getX()) > fabs(aBase.getY()))
167 : {
168 0 : fFactor = aRight.getX() / -aBase.getX();
169 : }
170 : else
171 : {
172 0 : fFactor = aRight.getY() / -aBase.getY();
173 : }
174 :
175 0 : if(fFactor >= 0.0 && fFactor <= 1.0)
176 : {
177 0 : bRightEqualZero = true;
178 : }
179 : }
180 :
181 0 : if(bLeftEqualZero && bRightEqualZero)
182 : {
183 0 : nMaxRecursionDepth = 0;
184 : }
185 : }
186 9320 : }
187 : }
188 :
189 9320 : if(nMaxRecursionDepth)
190 : {
191 : // divide at 0.5 ad test both edges for angle criteria
192 9320 : const B2DPoint aS1L(average(rfPA, rfEA));
193 18640 : const B2DPoint aS1C(average(rfEA, rfEB));
194 18640 : const B2DPoint aS1R(average(rfEB, rfPB));
195 18640 : const B2DPoint aS2L(average(aS1L, aS1C));
196 18640 : const B2DPoint aS2R(average(aS1C, aS1R));
197 18640 : const B2DPoint aS3C(average(aS2L, aS2R));
198 :
199 : // test left
200 9320 : bool bAngleIsSmallerLeft(bAllParallel && bLeftEqualZero);
201 9320 : if(!bAngleIsSmallerLeft)
202 : {
203 9320 : const B2DVector aLeftLeft(bLeftEqualZero ? aS2L - aS1L : aS1L - rfPA); // #i72104#
204 18640 : const B2DVector aRightLeft(aS2L - aS3C);
205 9320 : const double fCurrentAngleLeft(aLeftLeft.angle(aRightLeft));
206 18640 : bAngleIsSmallerLeft = (fabs(fCurrentAngleLeft) > (F_PI - rfAngleBound));
207 : }
208 :
209 : // test right
210 9320 : bool bAngleIsSmallerRight(bAllParallel && bRightEqualZero);
211 9320 : if(!bAngleIsSmallerRight)
212 : {
213 9320 : const B2DVector aLeftRight(aS2R - aS3C);
214 18640 : const B2DVector aRightRight(bRightEqualZero ? aS2R - aS1R : aS1R - rfPB); // #i72104#
215 9320 : const double fCurrentAngleRight(aLeftRight.angle(aRightRight));
216 18640 : bAngleIsSmallerRight = (fabs(fCurrentAngleRight) > (F_PI - rfAngleBound));
217 : }
218 :
219 9320 : if(bAngleIsSmallerLeft && bAngleIsSmallerRight)
220 : {
221 : // no recursion necessary at all
222 73 : nMaxRecursionDepth = 0;
223 : }
224 : else
225 : {
226 : // left
227 9247 : if(bAngleIsSmallerLeft)
228 : {
229 303 : rTarget.append(aS3C);
230 : }
231 : else
232 : {
233 8944 : ImpSubDivAngle(rfPA, aS1L, aS2L, aS3C, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
234 : }
235 :
236 : // right
237 9247 : if(bAngleIsSmallerRight)
238 : {
239 358 : rTarget.append(rfPB);
240 : }
241 : else
242 : {
243 8889 : ImpSubDivAngle(aS3C, aS2R, aS1R, rfPB, rTarget, rfAngleBound, bAllowUnsharpen, nMaxRecursionDepth);
244 : }
245 9320 : }
246 : }
247 :
248 9320 : if(!nMaxRecursionDepth)
249 : {
250 73 : rTarget.append(rfPB);
251 9320 : }
252 9320 : }
253 :
254 0 : void ImpSubDivDistance(
255 : const B2DPoint& rfPA, // start point
256 : const B2DPoint& rfEA, // edge on A
257 : const B2DPoint& rfEB, // edge on B
258 : const B2DPoint& rfPB, // end point
259 : B2DPolygon& rTarget, // target polygon
260 : double fDistanceBound2, // quadratic distance criteria
261 : double fLastDistanceError2, // the last quadratic distance error
262 : sal_uInt16 nMaxRecursionDepth) // endless loop protection
263 : {
264 0 : if(nMaxRecursionDepth)
265 : {
266 : // decide if another recursion is needed. If not, set
267 : // nMaxRecursionDepth to zero
268 :
269 : // Perform bezier flatness test (lecture notes from R. Schaback,
270 : // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
271 :
272 : // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
273 : // 0<=j<=n
274 :
275 : // What is calculated here is an upper bound to the distance from
276 : // a line through b_0 and b_3 (rfPA and P4 in our notation) and the
277 : // curve. We can drop 0 and n from the running indices, since the
278 : // argument of max becomes zero for those cases.
279 0 : const double fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX()));
280 0 : const double fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY()));
281 0 : const double fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX()));
282 0 : const double fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY()));
283 0 : const double fDistanceError2(::std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y));
284 :
285 : // stop if error measure does not improve anymore. This is a
286 : // safety guard against floating point inaccuracies.
287 : // stop if distance from line is guaranteed to be bounded by d
288 0 : const bool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2);
289 :
290 0 : if(bFurtherDivision)
291 : {
292 : // remember last error value
293 0 : fLastDistanceError2 = fDistanceError2;
294 : }
295 : else
296 : {
297 : // stop recustion
298 0 : nMaxRecursionDepth = 0;
299 : }
300 : }
301 :
302 0 : if(nMaxRecursionDepth)
303 : {
304 : // divide at 0.5
305 0 : const B2DPoint aS1L(average(rfPA, rfEA));
306 0 : const B2DPoint aS1C(average(rfEA, rfEB));
307 0 : const B2DPoint aS1R(average(rfEB, rfPB));
308 0 : const B2DPoint aS2L(average(aS1L, aS1C));
309 0 : const B2DPoint aS2R(average(aS1C, aS1R));
310 0 : const B2DPoint aS3C(average(aS2L, aS2R));
311 :
312 : // left recursion
313 0 : ImpSubDivDistance(rfPA, aS1L, aS2L, aS3C, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
314 :
315 : // right recursion
316 0 : ImpSubDivDistance(aS3C, aS2R, aS1R, rfPB, rTarget, fDistanceBound2, fLastDistanceError2, nMaxRecursionDepth - 1);
317 : }
318 : else
319 : {
320 0 : rTarget.append(rfPB);
321 : }
322 0 : }
323 : } // end of anonymous namespace
324 : } // end of namespace basegfx
325 :
326 : namespace basegfx
327 : {
328 0 : B2DCubicBezier::B2DCubicBezier(const B2DCubicBezier& rBezier)
329 : : maStartPoint(rBezier.maStartPoint),
330 : maEndPoint(rBezier.maEndPoint),
331 : maControlPointA(rBezier.maControlPointA),
332 0 : maControlPointB(rBezier.maControlPointB)
333 : {
334 0 : }
335 :
336 114365 : B2DCubicBezier::B2DCubicBezier()
337 : {
338 114365 : }
339 :
340 30722 : B2DCubicBezier::B2DCubicBezier(const B2DPoint& rStart, const B2DPoint& rControlPointA, const B2DPoint& rControlPointB, const B2DPoint& rEnd)
341 : : maStartPoint(rStart),
342 : maEndPoint(rEnd),
343 : maControlPointA(rControlPointA),
344 30722 : maControlPointB(rControlPointB)
345 : {
346 30722 : }
347 :
348 145087 : B2DCubicBezier::~B2DCubicBezier()
349 : {
350 145087 : }
351 :
352 : // assignment operator
353 15478 : B2DCubicBezier& B2DCubicBezier::operator=(const B2DCubicBezier& rBezier)
354 : {
355 15478 : maStartPoint = rBezier.maStartPoint;
356 15478 : maEndPoint = rBezier.maEndPoint;
357 15478 : maControlPointA = rBezier.maControlPointA;
358 15478 : maControlPointB = rBezier.maControlPointB;
359 :
360 15478 : return *this;
361 : }
362 :
363 : // compare operators
364 0 : bool B2DCubicBezier::operator==(const B2DCubicBezier& rBezier) const
365 : {
366 : return (
367 0 : maStartPoint == rBezier.maStartPoint
368 0 : && maEndPoint == rBezier.maEndPoint
369 0 : && maControlPointA == rBezier.maControlPointA
370 0 : && maControlPointB == rBezier.maControlPointB
371 0 : );
372 : }
373 :
374 0 : bool B2DCubicBezier::operator!=(const B2DCubicBezier& rBezier) const
375 : {
376 : return (
377 0 : maStartPoint != rBezier.maStartPoint
378 0 : || maEndPoint != rBezier.maEndPoint
379 0 : || maControlPointA != rBezier.maControlPointA
380 0 : || maControlPointB != rBezier.maControlPointB
381 0 : );
382 : }
383 :
384 2655 : bool B2DCubicBezier::equal(const B2DCubicBezier& rBezier) const
385 : {
386 : return (
387 2655 : maStartPoint.equal(rBezier.maStartPoint)
388 2655 : && maEndPoint.equal(rBezier.maEndPoint)
389 441 : && maControlPointA.equal(rBezier.maControlPointA)
390 3030 : && maControlPointB.equal(rBezier.maControlPointB)
391 2655 : );
392 : }
393 :
394 : // test if vectors are used
395 3471989 : bool B2DCubicBezier::isBezier() const
396 : {
397 3471989 : if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
398 : {
399 3134723 : return true;
400 : }
401 :
402 337266 : return false;
403 : }
404 :
405 839925 : void B2DCubicBezier::testAndSolveTrivialBezier()
406 : {
407 839925 : if(maControlPointA != maStartPoint || maControlPointB != maEndPoint)
408 : {
409 382918 : const B2DVector aEdge(maEndPoint - maStartPoint);
410 :
411 : // controls parallel to edge can be trivial. No edge -> not parallel -> control can
412 : // still not be trivial (e.g. ballon loop)
413 382918 : if(!aEdge.equalZero())
414 : {
415 : // get control vectors
416 382742 : const B2DVector aVecA(maControlPointA - maStartPoint);
417 765484 : const B2DVector aVecB(maControlPointB - maEndPoint);
418 :
419 : // check if trivial per se
420 382742 : bool bAIsTrivial(aVecA.equalZero());
421 382742 : bool bBIsTrivial(aVecB.equalZero());
422 :
423 : // #i102241# prepare inverse edge length to normalize cross values;
424 : // else the small compare value used in fTools::equalZero
425 : // will be length dependent and this detection will work as less
426 : // precise as longer the edge is. In principle, the length of the control
427 : // vector would need to be used too, but to be trivial it is assumed to
428 : // be of roughly equal length to the edge, so edge length can be used
429 : // for both. Only needed when one of both is not trivial per se.
430 396697 : const double fInverseEdgeLength(bAIsTrivial && bBIsTrivial
431 : ? 1.0
432 765484 : : 1.0 / aEdge.getLength());
433 :
434 : // if A is not zero, check if it could be
435 382742 : if(!bAIsTrivial)
436 : {
437 : // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what
438 : // we need here with the precision we need
439 368787 : const double fCross(aVecA.cross(aEdge) * fInverseEdgeLength);
440 :
441 368787 : if(fTools::equalZero(fCross))
442 : {
443 : // get scale to edge. Use bigger distance for numeric quality
444 110452 : const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
445 2398 : ? aVecA.getX() / aEdge.getX()
446 112850 : : aVecA.getY() / aEdge.getY());
447 :
448 : // relative end point of vector in edge range?
449 110452 : if(fTools::moreOrEqual(fScale, 0.0) && fTools::lessOrEqual(fScale, 1.0))
450 : {
451 110420 : bAIsTrivial = true;
452 : }
453 : }
454 : }
455 :
456 : // if B is not zero, check if it could be, but only if A is already trivial;
457 : // else solve to trivial will not be possible for whole edge
458 382742 : if(bAIsTrivial && !bBIsTrivial)
459 : {
460 : // parallel to edge? Check aVecB, aEdge
461 122784 : const double fCross(aVecB.cross(aEdge) * fInverseEdgeLength);
462 :
463 122784 : if(fTools::equalZero(fCross))
464 : {
465 : // get scale to edge. Use bigger distance for numeric quality
466 110571 : const double fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
467 2497 : ? aVecB.getX() / aEdge.getX()
468 113068 : : aVecB.getY() / aEdge.getY());
469 :
470 : // end point of vector in edge range? Caution: controlB is directed AGAINST edge
471 110571 : if(fTools::lessOrEqual(fScale, 0.0) && fTools::moreOrEqual(fScale, -1.0))
472 : {
473 110571 : bBIsTrivial = true;
474 : }
475 : }
476 : }
477 :
478 : // if both are/can be reduced, do it.
479 : // Not possible if only one is/can be reduced (!)
480 382742 : if(bAIsTrivial && bBIsTrivial)
481 : {
482 112162 : maControlPointA = maStartPoint;
483 112162 : maControlPointB = maEndPoint;
484 382742 : }
485 382918 : }
486 : }
487 839925 : }
488 :
489 : namespace {
490 667 : double impGetLength(const B2DCubicBezier& rEdge, double fDeviation, sal_uInt32 nRecursionWatch)
491 : {
492 667 : const double fEdgeLength(rEdge.getEdgeLength());
493 667 : const double fControlPolygonLength(rEdge.getControlPolygonLength());
494 667 : const double fCurrentDeviation(fTools::equalZero(fControlPolygonLength) ? 0.0 : 1.0 - (fEdgeLength / fControlPolygonLength));
495 :
496 667 : if(!nRecursionWatch || fTools:: lessOrEqual(fCurrentDeviation, fDeviation))
497 : {
498 348 : return (fEdgeLength + fControlPolygonLength) * 0.5;
499 : }
500 : else
501 : {
502 638 : B2DCubicBezier aLeft, aRight;
503 319 : const double fNewDeviation(fDeviation * 0.5);
504 319 : const sal_uInt32 nNewRecursionWatch(nRecursionWatch - 1);
505 :
506 319 : rEdge.split(0.5, &aLeft, &aRight);
507 :
508 319 : return impGetLength(aLeft, fNewDeviation, nNewRecursionWatch)
509 638 : + impGetLength(aRight, fNewDeviation, nNewRecursionWatch);
510 : }
511 : }
512 : }
513 :
514 29 : double B2DCubicBezier::getLength(double fDeviation) const
515 : {
516 29 : if(isBezier())
517 : {
518 29 : if(fDeviation < 0.00000001)
519 : {
520 0 : fDeviation = 0.00000001;
521 : }
522 :
523 29 : return impGetLength(*this, fDeviation, 6);
524 : }
525 : else
526 : {
527 0 : return B2DVector(getEndPoint() - getStartPoint()).getLength();
528 : }
529 : }
530 :
531 4489 : double B2DCubicBezier::getEdgeLength() const
532 : {
533 4489 : const B2DVector aEdge(maEndPoint - maStartPoint);
534 4489 : return aEdge.getLength();
535 : }
536 :
537 667 : double B2DCubicBezier::getControlPolygonLength() const
538 : {
539 667 : const B2DVector aVectorA(maControlPointA - maStartPoint);
540 1334 : const B2DVector aVectorB(maEndPoint - maControlPointB);
541 :
542 667 : if(!aVectorA.equalZero() || !aVectorB.equalZero())
543 : {
544 667 : const B2DVector aTop(maControlPointB - maControlPointA);
545 667 : return (aVectorA.getLength() + aVectorB.getLength() + aTop.getLength());
546 : }
547 : else
548 : {
549 0 : return getEdgeLength();
550 667 : }
551 : }
552 :
553 9320 : void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound, bool bAllowUnsharpen) const
554 : {
555 9320 : if(isBezier())
556 : {
557 : // use support method #i37443# and allow unsharpen the criteria
558 9320 : ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget, fAngleBound * F_PI180, bAllowUnsharpen);
559 : }
560 : else
561 : {
562 0 : rTarget.append(getEndPoint());
563 : }
564 9320 : }
565 :
566 35507 : B2DVector B2DCubicBezier::getTangent(double t) const
567 : {
568 35507 : if(fTools::lessOrEqual(t, 0.0))
569 : {
570 : // tangent in start point
571 17991 : B2DVector aTangent(getControlPointA() - getStartPoint());
572 :
573 17991 : if(!aTangent.equalZero())
574 : {
575 3605 : return aTangent;
576 : }
577 :
578 : // start point and control vector are the same, fallback
579 : // to implicit start vector to control point B
580 14386 : aTangent = (getControlPointB() - getStartPoint()) * 0.3;
581 :
582 14386 : if(!aTangent.equalZero())
583 : {
584 13975 : return aTangent;
585 : }
586 :
587 : // not a bezier at all, return edge vector
588 411 : return (getEndPoint() - getStartPoint()) * 0.3;
589 : }
590 17516 : else if(fTools::moreOrEqual(t, 1.0))
591 : {
592 : // tangent in end point
593 17516 : B2DVector aTangent(getEndPoint() - getControlPointB());
594 :
595 17516 : if(!aTangent.equalZero())
596 : {
597 3299 : return aTangent;
598 : }
599 :
600 : // end point and control vector are the same, fallback
601 : // to implicit start vector from control point A
602 14217 : aTangent = (getEndPoint() - getControlPointA()) * 0.3;
603 :
604 14217 : if(!aTangent.equalZero())
605 : {
606 13806 : return aTangent;
607 : }
608 :
609 : // not a bezier at all, return edge vector
610 411 : return (getEndPoint() - getStartPoint()) * 0.3;
611 : }
612 : else
613 : {
614 : // t is in ]0.0 .. 1.0[. Split and extract
615 0 : B2DCubicBezier aRight;
616 0 : split(t, 0, &aRight);
617 :
618 0 : return aRight.getControlPointA() - aRight.getStartPoint();
619 : }
620 : }
621 :
622 : // #i37443# adaptive subdivide by nCount subdivisions
623 64840 : void B2DCubicBezier::adaptiveSubdivideByCount(B2DPolygon& rTarget, sal_uInt32 nCount) const
624 : {
625 64840 : const double fLenFact(1.0 / static_cast< double >(nCount + 1));
626 :
627 2767690 : for(sal_uInt32 a(1); a <= nCount; a++)
628 : {
629 2702850 : const double fPos(static_cast< double >(a) * fLenFact);
630 2702850 : rTarget.append(interpolatePoint(fPos));
631 : }
632 :
633 64840 : rTarget.append(getEndPoint());
634 64840 : }
635 :
636 : // adaptive subdivide by distance
637 0 : void B2DCubicBezier::adaptiveSubdivideByDistance(B2DPolygon& rTarget, double fDistanceBound) const
638 : {
639 0 : if(isBezier())
640 : {
641 : ImpSubDivDistance(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget,
642 0 : fDistanceBound * fDistanceBound, ::std::numeric_limits<double>::max(), 30);
643 : }
644 : else
645 : {
646 0 : rTarget.append(getEndPoint());
647 : }
648 0 : }
649 :
650 2744810 : B2DPoint B2DCubicBezier::interpolatePoint(double t) const
651 : {
652 : OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)");
653 :
654 2744810 : if(isBezier())
655 : {
656 2735632 : const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
657 5471264 : const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
658 5471264 : const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
659 5471264 : const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
660 5471264 : const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
661 :
662 5471264 : return interpolate(aS2L, aS2R, t);
663 : }
664 : else
665 : {
666 9178 : return interpolate(maStartPoint, maEndPoint, t);
667 : }
668 : }
669 :
670 0 : double B2DCubicBezier::getSmallestDistancePointToBezierSegment(const B2DPoint& rTestPoint, double& rCut) const
671 : {
672 0 : const sal_uInt32 nInitialDivisions(3L);
673 0 : B2DPolygon aInitialPolygon;
674 :
675 : // as start make a fix division, creates nInitialDivisions + 2L points
676 0 : aInitialPolygon.append(getStartPoint());
677 0 : adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions);
678 :
679 : // now look for the closest point
680 0 : const sal_uInt32 nPointCount(aInitialPolygon.count());
681 0 : B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0L));
682 0 : double fQuadDist(aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY());
683 : double fNewQuadDist;
684 0 : sal_uInt32 nSmallestIndex(0L);
685 :
686 0 : for(sal_uInt32 a(1L); a < nPointCount; a++)
687 : {
688 0 : aVector = B2DVector(rTestPoint - aInitialPolygon.getB2DPoint(a));
689 0 : fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
690 :
691 0 : if(fNewQuadDist < fQuadDist)
692 : {
693 0 : fQuadDist = fNewQuadDist;
694 0 : nSmallestIndex = a;
695 : }
696 : }
697 :
698 : // look right and left for even smaller distances
699 0 : double fStepValue(1.0 / (double)((nPointCount - 1L) * 2L)); // half the edge step width
700 0 : double fPosition((double)nSmallestIndex / (double)(nPointCount - 1L));
701 0 : bool bDone(false);
702 :
703 0 : while(!bDone)
704 : {
705 0 : if(!bDone)
706 : {
707 : // test left
708 0 : double fPosLeft(fPosition - fStepValue);
709 :
710 0 : if(fPosLeft < 0.0)
711 : {
712 0 : fPosLeft = 0.0;
713 0 : aVector = B2DVector(rTestPoint - maStartPoint);
714 : }
715 : else
716 : {
717 0 : aVector = B2DVector(rTestPoint - interpolatePoint(fPosLeft));
718 : }
719 :
720 0 : fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
721 :
722 0 : if(fTools::less(fNewQuadDist, fQuadDist))
723 : {
724 0 : fQuadDist = fNewQuadDist;
725 0 : fPosition = fPosLeft;
726 : }
727 : else
728 : {
729 : // test right
730 0 : double fPosRight(fPosition + fStepValue);
731 :
732 0 : if(fPosRight > 1.0)
733 : {
734 0 : fPosRight = 1.0;
735 0 : aVector = B2DVector(rTestPoint - maEndPoint);
736 : }
737 : else
738 : {
739 0 : aVector = B2DVector(rTestPoint - interpolatePoint(fPosRight));
740 : }
741 :
742 0 : fNewQuadDist = aVector.getX() * aVector.getX() + aVector.getY() * aVector.getY();
743 :
744 0 : if(fTools::less(fNewQuadDist, fQuadDist))
745 : {
746 0 : fQuadDist = fNewQuadDist;
747 0 : fPosition = fPosRight;
748 : }
749 : else
750 : {
751 : // not less left or right, done
752 0 : bDone = true;
753 : }
754 : }
755 : }
756 :
757 0 : if(0.0 == fPosition || 1.0 == fPosition)
758 : {
759 : // if we are completely left or right, we are done
760 0 : bDone = true;
761 : }
762 :
763 0 : if(!bDone)
764 : {
765 : // prepare next step value
766 0 : fStepValue /= 2.0;
767 : }
768 : }
769 :
770 0 : rCut = fPosition;
771 0 : return sqrt(fQuadDist);
772 : }
773 :
774 3704 : void B2DCubicBezier::split(double t, B2DCubicBezier* pBezierA, B2DCubicBezier* pBezierB) const
775 : {
776 : OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::split: Access out of range (!)");
777 :
778 3704 : if(!pBezierA && !pBezierB)
779 : {
780 3704 : return;
781 : }
782 :
783 3704 : if(isBezier())
784 : {
785 3704 : const B2DPoint aS1L(interpolate(maStartPoint, maControlPointA, t));
786 7408 : const B2DPoint aS1C(interpolate(maControlPointA, maControlPointB, t));
787 7408 : const B2DPoint aS1R(interpolate(maControlPointB, maEndPoint, t));
788 7408 : const B2DPoint aS2L(interpolate(aS1L, aS1C, t));
789 7408 : const B2DPoint aS2R(interpolate(aS1C, aS1R, t));
790 7408 : const B2DPoint aS3C(interpolate(aS2L, aS2R, t));
791 :
792 3704 : if(pBezierA)
793 : {
794 2557 : pBezierA->setStartPoint(maStartPoint);
795 2557 : pBezierA->setEndPoint(aS3C);
796 2557 : pBezierA->setControlPointA(aS1L);
797 2557 : pBezierA->setControlPointB(aS2L);
798 : }
799 :
800 3704 : if(pBezierB)
801 : {
802 2561 : pBezierB->setStartPoint(aS3C);
803 2561 : pBezierB->setEndPoint(maEndPoint);
804 2561 : pBezierB->setControlPointA(aS2R);
805 2561 : pBezierB->setControlPointB(aS1R);
806 3704 : }
807 : }
808 : else
809 : {
810 0 : const B2DPoint aSplit(interpolate(maStartPoint, maEndPoint, t));
811 :
812 0 : if(pBezierA)
813 : {
814 0 : pBezierA->setStartPoint(maStartPoint);
815 0 : pBezierA->setEndPoint(aSplit);
816 0 : pBezierA->setControlPointA(maStartPoint);
817 0 : pBezierA->setControlPointB(aSplit);
818 : }
819 :
820 0 : if(pBezierB)
821 : {
822 0 : pBezierB->setStartPoint(aSplit);
823 0 : pBezierB->setEndPoint(maEndPoint);
824 0 : pBezierB->setControlPointA(aSplit);
825 0 : pBezierB->setControlPointB(maEndPoint);
826 0 : }
827 : }
828 : }
829 :
830 1141 : B2DCubicBezier B2DCubicBezier::snippet(double fStart, double fEnd) const
831 : {
832 1141 : B2DCubicBezier aRetval;
833 :
834 1141 : if(fTools::more(fStart, 1.0))
835 : {
836 0 : fStart = 1.0;
837 : }
838 1141 : else if(fTools::less(fStart, 0.0))
839 : {
840 0 : fStart = 0.0;
841 : }
842 :
843 1141 : if(fTools::more(fEnd, 1.0))
844 : {
845 0 : fEnd = 1.0;
846 : }
847 1141 : else if(fTools::less(fEnd, 0.0))
848 : {
849 0 : fEnd = 0.0;
850 : }
851 :
852 1141 : if(fEnd <= fStart)
853 : {
854 : // empty or NULL, create single point at center
855 0 : const double fSplit((fEnd + fStart) * 0.5);
856 0 : const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit));
857 0 : aRetval.setStartPoint(aPoint);
858 0 : aRetval.setEndPoint(aPoint);
859 0 : aRetval.setControlPointA(aPoint);
860 0 : aRetval.setControlPointB(aPoint);
861 : }
862 : else
863 : {
864 1141 : if(isBezier())
865 : {
866 : // copy bezier; cut off right, then cut off left. Do not forget to
867 : // adapt cut value when both cuts happen
868 1141 : const bool bEndIsOne(fTools::equal(fEnd, 1.0));
869 1141 : const bool bStartIsZero(fTools::equalZero(fStart));
870 1141 : aRetval = *this;
871 :
872 1141 : if(!bEndIsOne)
873 : {
874 1141 : aRetval.split(fEnd, &aRetval, 0);
875 :
876 1141 : if(!bStartIsZero)
877 : {
878 955 : fStart /= fEnd;
879 : }
880 : }
881 :
882 1141 : if(!bStartIsZero)
883 : {
884 955 : aRetval.split(fStart, 0, &aRetval);
885 : }
886 : }
887 : else
888 : {
889 : // no bezier, create simple edge
890 0 : const B2DPoint aPointA(interpolate(getStartPoint(), getEndPoint(), fStart));
891 0 : const B2DPoint aPointB(interpolate(getStartPoint(), getEndPoint(), fEnd));
892 0 : aRetval.setStartPoint(aPointA);
893 0 : aRetval.setEndPoint(aPointB);
894 0 : aRetval.setControlPointA(aPointA);
895 0 : aRetval.setControlPointB(aPointB);
896 : }
897 : }
898 :
899 1141 : return aRetval;
900 : }
901 :
902 907039 : B2DRange B2DCubicBezier::getRange() const
903 : {
904 907039 : B2DRange aRetval(maStartPoint, maEndPoint);
905 :
906 907039 : aRetval.expand(maControlPointA);
907 907039 : aRetval.expand(maControlPointB);
908 :
909 907039 : return aRetval;
910 : }
911 :
912 13115 : bool B2DCubicBezier::getMinimumExtremumPosition(double& rfResult) const
913 : {
914 13115 : ::std::vector< double > aAllResults;
915 :
916 13115 : aAllResults.reserve(4);
917 13115 : getAllExtremumPositions(aAllResults);
918 :
919 13115 : const sal_uInt32 nCount(aAllResults.size());
920 :
921 13115 : if(!nCount)
922 : {
923 10865 : return false;
924 : }
925 2250 : else if(1 == nCount)
926 : {
927 2197 : rfResult = aAllResults[0];
928 2197 : return true;
929 : }
930 : else
931 : {
932 53 : rfResult = *(::std::min_element(aAllResults.begin(), aAllResults.end()));
933 53 : return true;
934 13115 : }
935 : }
936 :
937 : namespace
938 : {
939 157959 : inline void impCheckExtremumResult(double fCandidate, ::std::vector< double >& rResult)
940 : {
941 : // check for range ]0.0 .. 1.0[ with excluding 1.0 and 0.0 clearly
942 : // by using the equalZero test, NOT ::more or ::less which will use the
943 : // ApproxEqual() which is too exact here
944 157959 : if(fCandidate > 0.0 && !fTools::equalZero(fCandidate))
945 : {
946 94301 : if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0))
947 : {
948 32299 : rResult.push_back(fCandidate);
949 : }
950 : }
951 157959 : }
952 : }
953 :
954 43892 : void B2DCubicBezier::getAllExtremumPositions(::std::vector< double >& rResults) const
955 : {
956 43892 : rResults.clear();
957 :
958 : // calculate the x-extrema parameters by zeroing first x-derivative
959 : // of the cubic bezier's parametric formula, which results in a
960 : // quadratic equation: dBezier/dt = t*t*fAX - 2*t*fBX + fCX
961 43892 : const B2DPoint aControlDiff( maControlPointA - maControlPointB );
962 43892 : double fCX = maControlPointA.getX() - maStartPoint.getX();
963 43892 : const double fBX = fCX + aControlDiff.getX();
964 43892 : const double fAX = 3 * aControlDiff.getX() + (maEndPoint.getX() - maStartPoint.getX());
965 :
966 43892 : if(fTools::equalZero(fCX))
967 : {
968 : // detect fCX equal zero and truncate to real zero value in that case
969 3828 : fCX = 0.0;
970 : }
971 :
972 43892 : if( !fTools::equalZero(fAX) )
973 : {
974 : // derivative is polynomial of order 2 => use binomial formula
975 39776 : const double fD = fBX*fBX - fAX*fCX;
976 39776 : if( fD >= 0.0 )
977 : {
978 37560 : const double fS = sqrt(fD);
979 : // calculate both roots (avoiding a numerically unstable subtraction)
980 37560 : const double fQ = fBX + ((fBX >= 0) ? +fS : -fS);
981 37560 : impCheckExtremumResult(fQ / fAX, rResults);
982 37560 : if( !fTools::equalZero(fS) ) // ignore root multiplicity
983 37431 : impCheckExtremumResult(fCX / fQ, rResults);
984 : }
985 : }
986 4116 : else if( !fTools::equalZero(fBX) )
987 : {
988 : // derivative is polynomial of order 1 => one extrema
989 3472 : impCheckExtremumResult(fCX / (2 * fBX), rResults);
990 : }
991 :
992 : // calculate the y-extrema parameters by zeroing first y-derivative
993 43892 : double fCY = maControlPointA.getY() - maStartPoint.getY();
994 43892 : const double fBY = fCY + aControlDiff.getY();
995 43892 : const double fAY = 3 * aControlDiff.getY() + (maEndPoint.getY() - maStartPoint.getY());
996 :
997 43892 : if(fTools::equalZero(fCY))
998 : {
999 : // detect fCY equal zero and truncate to real zero value in that case
1000 4852 : fCY = 0.0;
1001 : }
1002 :
1003 43892 : if( !fTools::equalZero(fAY) )
1004 : {
1005 : // derivative is polynomial of order 2 => use binomial formula
1006 39850 : const double fD = fBY*fBY - fAY*fCY;
1007 39850 : if( fD >= 0.0 )
1008 : {
1009 38173 : const double fS = sqrt(fD);
1010 : // calculate both roots (avoiding a numerically unstable subtraction)
1011 38173 : const double fQ = fBY + ((fBY >= 0) ? +fS : -fS);
1012 38173 : impCheckExtremumResult(fQ / fAY, rResults);
1013 38173 : if( !fTools::equalZero(fS) ) // ignore root multiplicity
1014 38053 : impCheckExtremumResult(fCY / fQ, rResults);
1015 : }
1016 : }
1017 4042 : else if( !fTools::equalZero(fBY) )
1018 : {
1019 : // derivative is polynomial of order 1 => one extrema
1020 3270 : impCheckExtremumResult(fCY / (2 * fBY), rResults);
1021 43892 : }
1022 43892 : }
1023 :
1024 : } // end of namespace basegfx
1025 :
1026 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|