Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <basegfx/numeric/ftools.hxx>
30 : : #include <basegfx/polygon/b2dpolygontools.hxx>
31 : : #include <osl/diagnose.h>
32 : : #include <rtl/math.hxx>
33 : : #include <rtl/instance.hxx>
34 : : #include <basegfx/polygon/b2dpolygon.hxx>
35 : : #include <basegfx/polygon/b2dpolypolygon.hxx>
36 : : #include <basegfx/range/b2drange.hxx>
37 : : #include <basegfx/curve/b2dcubicbezier.hxx>
38 : : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
39 : : #include <basegfx/point/b3dpoint.hxx>
40 : : #include <basegfx/matrix/b3dhommatrix.hxx>
41 : : #include <basegfx/matrix/b2dhommatrix.hxx>
42 : : #include <basegfx/curve/b2dbeziertools.hxx>
43 : : #include <basegfx/matrix/b2dhommatrixtools.hxx>
44 : : #include <osl/mutex.hxx>
45 : :
46 : : #include <numeric>
47 : : #include <limits>
48 : :
49 : : // #i37443#
50 : : #define ANGLE_BOUND_START_VALUE (2.25)
51 : : #define ANGLE_BOUND_MINIMUM_VALUE (0.1)
52 : : #define COUNT_SUBDIVIDE_DEFAULT (4L)
53 : : #ifdef DBG_UTIL
54 : : static double fAngleBoundStartValue = ANGLE_BOUND_START_VALUE;
55 : : #endif
56 : : #define STEPSPERQUARTER (3)
57 : :
58 : : //////////////////////////////////////////////////////////////////////////////
59 : :
60 : : namespace basegfx
61 : : {
62 : : namespace tools
63 : : {
64 : 0 : void openWithGeometryChange(B2DPolygon& rCandidate)
65 : : {
66 [ # # ]: 0 : if(rCandidate.isClosed())
67 : : {
68 [ # # ]: 0 : if(rCandidate.count())
69 : : {
70 [ # # ]: 0 : rCandidate.append(rCandidate.getB2DPoint(0));
71 : :
72 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0))
[ # # ]
73 : : {
74 [ # # ][ # # ]: 0 : rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0));
75 : 0 : rCandidate.resetPrevControlPoint(0);
76 : : }
77 : : }
78 : :
79 : 0 : rCandidate.setClosed(false);
80 : : }
81 : 0 : }
82 : :
83 : 21168 : void closeWithGeometryChange(B2DPolygon& rCandidate)
84 : : {
85 [ + - ]: 21168 : if(!rCandidate.isClosed())
86 : : {
87 [ + - ][ + + ]: 40006 : while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ + + ]
[ + + # #
# # ]
88 : : {
89 [ + + ][ + + ]: 18838 : if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1))
[ + + ]
90 : : {
91 [ + - ]: 2113 : rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1));
92 : : }
93 : :
94 : 18838 : rCandidate.remove(rCandidate.count() - 1);
95 : : }
96 : :
97 : 21168 : rCandidate.setClosed(true);
98 : : }
99 : 21168 : }
100 : :
101 : 51131 : void checkClosed(B2DPolygon& rCandidate)
102 : : {
103 : : // #i80172# Removed unnecessary assertion
104 : : // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");
105 : :
106 [ + - ][ + - ]: 51131 : if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + + # #
# # ]
107 : : {
108 : 17232 : closeWithGeometryChange(rCandidate);
109 : : }
110 : 51131 : }
111 : :
112 : : // Get successor and predecessor indices. Returning the same index means there
113 : : // is none. Same for successor.
114 : 175 : sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
115 : : {
116 : : OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
117 : :
118 [ - + ]: 175 : if(nIndex)
119 : : {
120 : 0 : return nIndex - 1L;
121 : : }
122 [ + - ]: 175 : else if(rCandidate.count())
123 : : {
124 : 175 : return rCandidate.count() - 1L;
125 : : }
126 : : else
127 : : {
128 : 175 : return nIndex;
129 : : }
130 : : }
131 : :
132 : 175 : sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
133 : : {
134 : : OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
135 : :
136 [ + + ]: 175 : if(nIndex + 1L < rCandidate.count())
137 : : {
138 : 155 : return nIndex + 1L;
139 : : }
140 [ + - ]: 20 : else if(nIndex + 1L == rCandidate.count())
141 : : {
142 : 20 : return 0L;
143 : : }
144 : : else
145 : : {
146 : 175 : return nIndex;
147 : : }
148 : : }
149 : :
150 : 13792 : B2VectorOrientation getOrientation(const B2DPolygon& rCandidate)
151 : : {
152 : 13792 : B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
153 : :
154 [ + + ][ - + ]: 13792 : if(rCandidate.count() > 2L || rCandidate.areControlPointsUsed())
[ + + ]
155 : : {
156 [ + - ]: 13687 : const double fSignedArea(getSignedArea(rCandidate));
157 : :
158 : 13687 : if(fTools::equalZero(fSignedArea))
159 : : {
160 : : // ORIENTATION_NEUTRAL, already set
161 : : }
162 [ + + ]: 13687 : if(fSignedArea > 0.0)
163 : : {
164 : 7791 : eRetval = ORIENTATION_POSITIVE;
165 : : }
166 [ + + ]: 5896 : else if(fSignedArea < 0.0)
167 : : {
168 : 13687 : eRetval = ORIENTATION_NEGATIVE;
169 : : }
170 : : }
171 : :
172 : 13792 : return eRetval;
173 : : }
174 : :
175 : 0 : B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
176 : : {
177 : 0 : return rCandidate.getContinuityInPoint(nIndex);
178 : : }
179 : :
180 : 0 : B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound)
181 : : {
182 [ # # ]: 0 : if(rCandidate.areControlPointsUsed())
183 : : {
184 [ # # ]: 0 : const sal_uInt32 nPointCount(rCandidate.count());
185 [ # # ]: 0 : B2DPolygon aRetval;
186 : :
187 [ # # ]: 0 : if(nPointCount)
188 : : {
189 : : // prepare edge-oriented loop
190 [ # # ][ # # ]: 0 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
191 [ # # ]: 0 : B2DCubicBezier aBezier;
192 [ # # ]: 0 : aBezier.setStartPoint(rCandidate.getB2DPoint(0));
193 : :
194 : : // perf: try to avoid too many realloctions by guessing the result's pointcount
195 [ # # ]: 0 : aRetval.reserve(nPointCount*4);
196 : :
197 : : // add start point (always)
198 [ # # ]: 0 : aRetval.append(aBezier.getStartPoint());
199 : :
200 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nEdgeCount; a++)
201 : : {
202 : : // get next and control points
203 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
204 [ # # ]: 0 : aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
205 [ # # ]: 0 : aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
206 [ # # ]: 0 : aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
207 [ # # ]: 0 : aBezier.testAndSolveTrivialBezier();
208 : :
209 [ # # ][ # # ]: 0 : if(aBezier.isBezier())
210 : : {
211 : : // add curved edge and generate DistanceBound
212 : 0 : double fBound(0.0);
213 : :
214 [ # # ]: 0 : if(0.0 == fDistanceBound)
215 : : {
216 : : // If not set, use B2DCubicBezier functionality to guess a rough value
217 [ # # ][ # # ]: 0 : const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0);
218 : :
219 : : // take 1/100th of the rough curve length
220 : 0 : fBound = fRoughLength * 0.01;
221 : : }
222 : : else
223 : : {
224 : : // use given bound value
225 : 0 : fBound = fDistanceBound;
226 : : }
227 : :
228 : : // make sure bound value is not too small. The base units are 1/100th mm, thus
229 : : // just make sure it's not smaller then 1/100th of that
230 [ # # ]: 0 : if(fBound < 0.01)
231 : : {
232 : 0 : fBound = 0.01;
233 : : }
234 : :
235 : : // call adaptive subdivide which adds edges to aRetval accordingly
236 [ # # ]: 0 : aBezier.adaptiveSubdivideByDistance(aRetval, fBound);
237 : : }
238 : : else
239 : : {
240 : : // add non-curved edge
241 [ # # ]: 0 : aRetval.append(aBezier.getEndPoint());
242 : : }
243 : :
244 : : // prepare next step
245 : 0 : aBezier.setStartPoint(aBezier.getEndPoint());
246 : : }
247 : :
248 [ # # ][ # # ]: 0 : if(rCandidate.isClosed())
249 : : {
250 : : // set closed flag and correct last point (which is added double now).
251 [ # # ]: 0 : closeWithGeometryChange(aRetval);
252 [ # # ]: 0 : }
253 : : }
254 : :
255 [ # # ][ # # ]: 0 : return aRetval;
256 : : }
257 : : else
258 : : {
259 : 0 : return rCandidate;
260 : : }
261 : : }
262 : :
263 : 25 : B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound)
264 : : {
265 [ + - ]: 25 : if(rCandidate.areControlPointsUsed())
266 : : {
267 [ + - ]: 25 : const sal_uInt32 nPointCount(rCandidate.count());
268 [ + - ]: 25 : B2DPolygon aRetval;
269 : :
270 [ + - ]: 25 : if(nPointCount)
271 : : {
272 : : // prepare edge-oriented loop
273 [ + - ][ + - ]: 25 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
274 [ + - ]: 25 : B2DCubicBezier aBezier;
275 [ + - ]: 25 : aBezier.setStartPoint(rCandidate.getB2DPoint(0));
276 : :
277 : : // perf: try to avoid too many realloctions by guessing the result's pointcount
278 [ + - ]: 25 : aRetval.reserve(nPointCount*4);
279 : :
280 : : // add start point (always)
281 [ + - ]: 25 : aRetval.append(aBezier.getStartPoint());
282 : :
283 : : // #i37443# prepare convenient AngleBound if none was given
284 [ + - ]: 25 : if(0.0 == fAngleBound)
285 : : {
286 : : #ifdef DBG_UTIL
287 : : fAngleBound = fAngleBoundStartValue;
288 : : #else
289 : 25 : fAngleBound = ANGLE_BOUND_START_VALUE;
290 : : #endif
291 : : }
292 [ # # ]: 0 : else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE))
293 : : {
294 : 0 : fAngleBound = 0.1;
295 : : }
296 : :
297 [ + + ]: 150 : for(sal_uInt32 a(0L); a < nEdgeCount; a++)
298 : : {
299 : : // get next and control points
300 : 125 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
301 [ + - ]: 125 : aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
302 [ + - ]: 125 : aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
303 [ + - ]: 125 : aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
304 [ + - ]: 125 : aBezier.testAndSolveTrivialBezier();
305 : :
306 [ + - ][ + + ]: 125 : if(aBezier.isBezier())
307 : : {
308 : : // call adaptive subdivide
309 [ + - ]: 100 : aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound, true);
310 : : }
311 : : else
312 : : {
313 : : // add non-curved edge
314 [ + - ]: 25 : aRetval.append(aBezier.getEndPoint());
315 : : }
316 : :
317 : : // prepare next step
318 : 125 : aBezier.setStartPoint(aBezier.getEndPoint());
319 : : }
320 : :
321 [ + - ][ + - ]: 25 : if(rCandidate.isClosed())
322 : : {
323 : : // set closed flag and correct last point (which is added double now).
324 [ + - ]: 25 : closeWithGeometryChange(aRetval);
325 [ + - ]: 25 : }
326 : : }
327 : :
328 [ + - ][ + - ]: 25 : return aRetval;
329 : : }
330 : : else
331 : : {
332 : 25 : return rCandidate;
333 : : }
334 : : }
335 : :
336 : 0 : B2DPolygon adaptiveSubdivideByCount(const B2DPolygon& rCandidate, sal_uInt32 nCount)
337 : : {
338 [ # # ]: 0 : if(rCandidate.areControlPointsUsed())
339 : : {
340 [ # # ]: 0 : const sal_uInt32 nPointCount(rCandidate.count());
341 [ # # ]: 0 : B2DPolygon aRetval;
342 : :
343 [ # # ]: 0 : if(nPointCount)
344 : : {
345 : : // prepare edge-oriented loop
346 [ # # ][ # # ]: 0 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
347 [ # # ]: 0 : B2DCubicBezier aBezier;
348 [ # # ]: 0 : aBezier.setStartPoint(rCandidate.getB2DPoint(0));
349 : :
350 : : // perf: try to avoid too many realloctions by guessing the result's pointcount
351 [ # # ]: 0 : aRetval.reserve(nPointCount*4);
352 : :
353 : : // add start point (always)
354 [ # # ]: 0 : aRetval.append(aBezier.getStartPoint());
355 : :
356 : : // #i37443# prepare convenient count if none was given
357 [ # # ]: 0 : if(0L == nCount)
358 : : {
359 : 0 : nCount = COUNT_SUBDIVIDE_DEFAULT;
360 : : }
361 : :
362 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nEdgeCount; a++)
363 : : {
364 : : // get next and control points
365 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
366 [ # # ]: 0 : aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
367 [ # # ]: 0 : aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
368 [ # # ]: 0 : aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
369 [ # # ]: 0 : aBezier.testAndSolveTrivialBezier();
370 : :
371 [ # # ][ # # ]: 0 : if(aBezier.isBezier())
372 : : {
373 : : // call adaptive subdivide
374 [ # # ]: 0 : aBezier.adaptiveSubdivideByCount(aRetval, nCount);
375 : : }
376 : : else
377 : : {
378 : : // add non-curved edge
379 [ # # ]: 0 : aRetval.append(aBezier.getEndPoint());
380 : : }
381 : :
382 : : // prepare next step
383 : 0 : aBezier.setStartPoint(aBezier.getEndPoint());
384 : : }
385 : :
386 [ # # ][ # # ]: 0 : if(rCandidate.isClosed())
387 : : {
388 : : // set closed flag and correct last point (which is added double now).
389 [ # # ]: 0 : closeWithGeometryChange(aRetval);
390 [ # # ]: 0 : }
391 : : }
392 : :
393 [ # # ][ # # ]: 0 : return aRetval;
394 : : }
395 : : else
396 : : {
397 : 0 : return rCandidate;
398 : : }
399 : : }
400 : :
401 : 7116 : bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
402 : : {
403 [ + - ][ - + ]: 7116 : const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
[ # # ][ + - ]
404 : :
405 [ + + ][ + - ]: 7116 : if(bWithBorder && isPointOnPolygon(aCandidate, rPoint, true))
[ + + ][ + + ]
406 : : {
407 : 3713 : return true;
408 : : }
409 : : else
410 : : {
411 : 3403 : bool bRetval(false);
412 [ + - ]: 3403 : const sal_uInt32 nPointCount(aCandidate.count());
413 : :
414 [ + - ]: 3403 : if(nPointCount)
415 : : {
416 [ + - ]: 3403 : B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1L));
417 : :
418 [ + + ]: 29201 : for(sal_uInt32 a(0L); a < nPointCount; a++)
419 : : {
420 : 25798 : const B2DPoint aPreviousPoint(aCurrentPoint);
421 [ + - ]: 25798 : aCurrentPoint = aCandidate.getB2DPoint(a);
422 : :
423 : : // cross-over in Y?
424 : 25798 : const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
425 : 25798 : const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
426 : :
427 [ + + ]: 25798 : if(bCompYA != bCompYB)
428 : : {
429 : : // cross-over in X?
430 : 6324 : const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
431 : 6324 : const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
432 : :
433 [ + + ]: 6324 : if(bCompXA == bCompXB)
434 : : {
435 [ + + ]: 6250 : if(bCompXA)
436 : : {
437 : 3083 : bRetval = !bRetval;
438 : : }
439 : : }
440 : : else
441 : : {
442 : : const double fCompare(
443 : 74 : aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
444 : 74 : (aPreviousPoint.getX() - aCurrentPoint.getX()) /
445 : 74 : (aPreviousPoint.getY() - aCurrentPoint.getY()));
446 : :
447 [ + + ]: 74 : if(fTools::more(fCompare, rPoint.getX()))
448 : : {
449 : 74 : bRetval = !bRetval;
450 : : }
451 : : }
452 : : }
453 : 29201 : }
454 : : }
455 : :
456 : 3403 : return bRetval;
457 [ + - ]: 7116 : }
458 : : }
459 : :
460 : 1475 : bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder)
461 : : {
462 [ + - ][ - + ]: 1475 : const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
[ # # ][ + - ]
463 [ + - ][ - + ]: 1475 : const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon);
[ # # ][ + - ]
464 [ + - ]: 1475 : const sal_uInt32 nPointCount(aPolygon.count());
465 : :
466 [ + + ]: 7649 : for(sal_uInt32 a(0L); a < nPointCount; a++)
467 : : {
468 [ + - ]: 6174 : const B2DPoint aTestPoint(aPolygon.getB2DPoint(a));
469 : :
470 [ + - ][ + + ]: 6174 : if(!isInside(aCandidate, aTestPoint, bWithBorder))
471 : : {
472 : 6174 : return false;
473 : : }
474 [ + + ]: 6174 : }
475 : :
476 [ + - ][ + - ]: 1475 : return true;
477 : : }
478 : :
479 : 4365804 : B2DRange getRange(const B2DPolygon& rCandidate)
480 : : {
481 : : // changed to use internally buffered version at B2DPolygon
482 : 4365804 : return rCandidate.getB2DRange();
483 : : }
484 : :
485 : 13687 : double getSignedArea(const B2DPolygon& rCandidate)
486 : : {
487 [ + - ][ - + ]: 13687 : const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
[ # # ][ + - ]
488 : 13687 : double fRetval(0.0);
489 [ + - ]: 13687 : const sal_uInt32 nPointCount(aCandidate.count());
490 : :
491 [ + - ]: 13687 : if(nPointCount > 2)
492 : : {
493 [ + + ]: 86438 : for(sal_uInt32 a(0L); a < nPointCount; a++)
494 : : {
495 [ + + ][ + - ]: 72751 : const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1L : a - 1L));
496 [ + - ]: 72751 : const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a));
497 : :
498 : 72751 : fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
499 : 72751 : fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
500 : 72751 : }
501 : :
502 : 13687 : fRetval /= 2.0;
503 : :
504 : : // correct to zero if small enough. Also test the quadratic
505 : : // of the result since the precision is near quadratic due to
506 : : // the algorithm
507 [ + + ][ - + ]: 13687 : if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval))
[ + + ][ + + ]
508 : : {
509 : 29 : fRetval = 0.0;
510 : : }
511 : : }
512 : :
513 [ + - ]: 13687 : return fRetval;
514 : : }
515 : :
516 : 0 : double getArea(const B2DPolygon& rCandidate)
517 : : {
518 : 0 : double fRetval(0.0);
519 : :
520 [ # # ][ # # ]: 0 : if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
[ # # ][ # # ]
[ # # ]
521 : : {
522 [ # # ]: 0 : fRetval = getSignedArea(rCandidate);
523 : 0 : const double fZero(0.0);
524 : :
525 [ # # ]: 0 : if(fTools::less(fRetval, fZero))
526 : : {
527 : 0 : fRetval = -fRetval;
528 : : }
529 : : }
530 : :
531 : 0 : return fRetval;
532 : : }
533 : :
534 : 30 : double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
535 : : {
536 : 30 : const sal_uInt32 nPointCount(rCandidate.count());
537 : : OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)");
538 : 30 : double fRetval(0.0);
539 : :
540 [ + - ]: 30 : if(nPointCount)
541 : : {
542 : 30 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
543 : :
544 [ - + ]: 30 : if(rCandidate.areControlPointsUsed())
545 : : {
546 [ # # ]: 0 : B2DCubicBezier aEdge;
547 : :
548 [ # # ]: 0 : aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex));
549 [ # # ]: 0 : aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex));
550 [ # # ]: 0 : aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
551 [ # # ]: 0 : aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
552 : :
553 [ # # ][ # # ]: 0 : fRetval = aEdge.getLength();
554 : : }
555 : : else
556 : : {
557 [ + - ]: 30 : const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex));
558 [ + - ]: 30 : const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
559 : :
560 [ + - ]: 30 : fRetval = B2DVector(aNext - aCurrent).getLength();
561 : : }
562 : : }
563 : :
564 : 30 : return fRetval;
565 : : }
566 : :
567 : 15 : double getLength(const B2DPolygon& rCandidate)
568 : : {
569 : 15 : double fRetval(0.0);
570 : 15 : const sal_uInt32 nPointCount(rCandidate.count());
571 : :
572 [ + - ]: 15 : if(nPointCount)
573 : : {
574 [ - + ]: 15 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
575 : :
576 [ - + ]: 15 : if(rCandidate.areControlPointsUsed())
577 : : {
578 [ # # ]: 0 : B2DCubicBezier aEdge;
579 [ # # ]: 0 : aEdge.setStartPoint(rCandidate.getB2DPoint(0));
580 : :
581 [ # # ]: 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
582 : : {
583 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
584 [ # # ]: 0 : aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
585 [ # # ]: 0 : aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
586 [ # # ]: 0 : aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
587 : :
588 [ # # ]: 0 : fRetval += aEdge.getLength();
589 : 0 : aEdge.setStartPoint(aEdge.getEndPoint());
590 [ # # ]: 0 : }
591 : : }
592 : : else
593 : : {
594 [ + - ]: 15 : B2DPoint aCurrent(rCandidate.getB2DPoint(0));
595 : :
596 [ + + ]: 35 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
597 : : {
598 : 20 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
599 [ + - ]: 20 : const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));
600 : :
601 [ + - ]: 20 : fRetval += B2DVector(aNext - aCurrent).getLength();
602 : 20 : aCurrent = aNext;
603 : 35 : }
604 : : }
605 : : }
606 : :
607 : 15 : return fRetval;
608 : : }
609 : :
610 : 15 : B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength)
611 : : {
612 : 15 : B2DPoint aRetval;
613 [ + - ]: 15 : const sal_uInt32 nPointCount(rCandidate.count());
614 : :
615 [ - + ]: 15 : if( 1L == nPointCount )
616 : : {
617 : : // only one point (i.e. no edge) - simply take that point
618 [ # # ]: 0 : aRetval = rCandidate.getB2DPoint(0);
619 : : }
620 [ + - ]: 15 : else if(nPointCount > 1L)
621 : : {
622 [ + - ][ - + ]: 15 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
623 : 15 : sal_uInt32 nIndex(0L);
624 : 15 : bool bIndexDone(false);
625 : :
626 : : // get length if not given
627 [ - + ]: 15 : if(fTools::equalZero(fLength))
628 : : {
629 [ # # ]: 0 : fLength = getLength(rCandidate);
630 : : }
631 : :
632 [ - + ]: 15 : if(fTools::less(fDistance, 0.0))
633 : : {
634 : : // handle fDistance < 0.0
635 [ # # ][ # # ]: 0 : if(rCandidate.isClosed())
636 : : {
637 : : // if fDistance < 0.0 increment with multiple of fLength
638 : 0 : sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
639 : 0 : fDistance += double(nCount + 1L) * fLength;
640 : : }
641 : : else
642 : : {
643 : : // crop to polygon start
644 : 0 : fDistance = 0.0;
645 : 0 : bIndexDone = true;
646 : : }
647 : : }
648 [ - + ]: 15 : else if(fTools::moreOrEqual(fDistance, fLength))
649 : : {
650 : : // handle fDistance >= fLength
651 [ # # ][ # # ]: 0 : if(rCandidate.isClosed())
652 : : {
653 : : // if fDistance >= fLength decrement with multiple of fLength
654 : 0 : sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
655 : 0 : fDistance -= (double)(nCount) * fLength;
656 : : }
657 : : else
658 : : {
659 : : // crop to polygon end
660 : 0 : fDistance = 0.0;
661 : 0 : nIndex = nEdgeCount;
662 : 0 : bIndexDone = true;
663 : : }
664 : : }
665 : :
666 : : // look for correct index. fDistance is now [0.0 .. fLength[
667 [ + - ]: 15 : double fEdgeLength(getEdgeLength(rCandidate, nIndex));
668 : :
669 [ + + ]: 30 : while(!bIndexDone)
670 : : {
671 : : // edge found must be on the half-open range
672 : : // [0,fEdgeLength).
673 : : // Note that in theory, we cannot move beyond
674 : : // the last polygon point, since fDistance>=fLength
675 : : // is checked above. Unfortunately, with floating-
676 : : // point calculations, this case might happen.
677 : : // Handled by nIndex check below
678 [ + - ][ - + ]: 15 : if(nIndex < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength))
[ - + ]
679 : : {
680 : : // go to next edge
681 : 0 : fDistance -= fEdgeLength;
682 [ # # ]: 0 : fEdgeLength = getEdgeLength(rCandidate, ++nIndex);
683 : : }
684 : : else
685 : : {
686 : : // it's on this edge, stop
687 : 15 : bIndexDone = true;
688 : : }
689 : : }
690 : :
691 : : // get the point using nIndex
692 [ + - ]: 15 : aRetval = rCandidate.getB2DPoint(nIndex);
693 : :
694 : : // if fDistance != 0.0, move that length on the edge. The edge
695 : : // length is in fEdgeLength.
696 [ + - ]: 15 : if(!fTools::equalZero(fDistance))
697 : : {
698 [ - + ]: 15 : if(fTools::moreOrEqual(fDistance, fEdgeLength))
699 : : {
700 : : // end point of choosen edge
701 : 0 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
702 [ # # ]: 0 : aRetval = rCandidate.getB2DPoint(nNextIndex);
703 : : }
704 [ - + ]: 15 : else if(fTools::equalZero(fDistance))
705 : : {
706 : : // start point of choosen edge
707 : 0 : aRetval = aRetval;
708 : : }
709 : : else
710 : : {
711 : : // inside edge
712 : 15 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
713 [ + - ]: 15 : const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
714 : 15 : bool bDone(false);
715 : :
716 : : // add calculated average value to the return value
717 [ + - ][ - + ]: 15 : if(rCandidate.areControlPointsUsed())
718 : : {
719 : : // get as bezier segment
720 : : const B2DCubicBezier aBezierSegment(
721 : : aRetval, rCandidate.getNextControlPoint(nIndex),
722 [ # # ][ # # ]: 0 : rCandidate.getPrevControlPoint(nNextIndex), aNextPoint);
[ # # ]
723 : :
724 [ # # ][ # # ]: 0 : if(aBezierSegment.isBezier())
725 : : {
726 : : // use B2DCubicBezierHelper to bridge the non-linear gap between
727 : : // length and bezier distances
728 [ # # ]: 0 : const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
729 [ # # ]: 0 : const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance));
730 : :
731 [ # # ]: 0 : aRetval = aBezierSegment.interpolatePoint(fBezierDistance);
732 : 0 : bDone = true;
733 [ # # ]: 0 : }
734 : : }
735 : :
736 [ + - ]: 15 : if(!bDone)
737 : : {
738 : 15 : const double fRelativeInEdge(fDistance / fEdgeLength);
739 [ + - ]: 15 : aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge);
740 : 15 : }
741 : : }
742 : : }
743 : : }
744 : :
745 : 15 : return aRetval;
746 : : }
747 : :
748 : 0 : B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength)
749 : : {
750 : : // get length if not given
751 [ # # ]: 0 : if(fTools::equalZero(fLength))
752 : : {
753 : 0 : fLength = getLength(rCandidate);
754 : : }
755 : :
756 : : // multiply fDistance with real length to get absolute position and
757 : : // use getPositionAbsolute
758 : 0 : return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
759 : : }
760 : :
761 : 10 : B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
762 : : {
763 : 10 : const sal_uInt32 nPointCount(rCandidate.count());
764 : :
765 [ + - ]: 10 : if(nPointCount)
766 : : {
767 : : // get length if not given
768 [ - + ]: 10 : if(fTools::equalZero(fLength))
769 : : {
770 : 0 : fLength = getLength(rCandidate);
771 : : }
772 : :
773 : : // test and correct fFrom
774 [ - + ]: 10 : if(fTools::less(fFrom, 0.0))
775 : : {
776 : 0 : fFrom = 0.0;
777 : : }
778 : :
779 : : // test and correct fTo
780 [ - + ]: 10 : if(fTools::more(fTo, fLength))
781 : : {
782 : 0 : fTo = fLength;
783 : : }
784 : :
785 : : // test and correct relationship of fFrom, fTo
786 [ - + ]: 10 : if(fTools::more(fFrom, fTo))
787 : : {
788 : 0 : fFrom = fTo = (fFrom + fTo) / 2.0;
789 : : }
790 : :
791 [ + + ][ - + ]: 10 : if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength))
[ - + ]
792 : : {
793 : : // no change, result is the whole polygon
794 : 0 : return rCandidate;
795 : : }
796 : : else
797 : : {
798 [ + - ]: 10 : B2DPolygon aRetval;
799 [ + - ][ - + ]: 10 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
800 : 10 : double fPositionOfStart(0.0);
801 : 10 : bool bStartDone(false);
802 : 10 : bool bEndDone(false);
803 : :
804 [ + + ][ + + ]: 25 : for(sal_uInt32 a(0L); !(bStartDone && bEndDone) && a < nEdgeCount; a++)
[ + + ][ + + ]
805 : : {
806 [ + - ]: 15 : const double fEdgeLength(getEdgeLength(rCandidate, a));
807 : :
808 [ + + ]: 15 : if(!bStartDone)
809 : : {
810 [ + + ]: 10 : if(fTools::equalZero(fFrom))
811 : : {
812 [ + - ][ + - ]: 5 : aRetval.append(rCandidate.getB2DPoint(a));
813 : :
814 [ - + ][ + - ]: 5 : if(rCandidate.areControlPointsUsed())
815 : : {
816 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
[ # # ]
817 : : }
818 : :
819 : 5 : bStartDone = true;
820 : : }
821 [ + - ][ + - ]: 5 : else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength))
[ + - ][ + - ]
822 : : {
823 : : // calculate and add start point
824 [ - + ]: 5 : if(fTools::equalZero(fEdgeLength))
825 : : {
826 [ # # ][ # # ]: 0 : aRetval.append(rCandidate.getB2DPoint(a));
827 : :
828 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed())
829 : : {
830 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
[ # # ]
831 : : }
832 : : }
833 : : else
834 : : {
835 : 5 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
836 [ + - ]: 5 : const B2DPoint aStart(rCandidate.getB2DPoint(a));
837 [ + - ]: 5 : const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
838 : 5 : bool bDone(false);
839 : :
840 [ + - ][ - + ]: 5 : if(rCandidate.areControlPointsUsed())
841 : : {
842 : : const B2DCubicBezier aBezierSegment(
843 : : aStart, rCandidate.getNextControlPoint(a),
844 [ # # ][ # # ]: 0 : rCandidate.getPrevControlPoint(nNextIndex), aEnd);
[ # # ]
845 : :
846 [ # # ][ # # ]: 0 : if(aBezierSegment.isBezier())
847 : : {
848 : : // use B2DCubicBezierHelper to bridge the non-linear gap between
849 : : // length and bezier distances
850 [ # # ]: 0 : const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
851 [ # # ]: 0 : const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart));
852 [ # # ]: 0 : B2DCubicBezier aRight;
853 : :
854 [ # # ]: 0 : aBezierSegment.split(fBezierDistance, 0, &aRight);
855 [ # # ]: 0 : aRetval.append(aRight.getStartPoint());
856 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA());
857 [ # # ]: 0 : bDone = true;
858 [ # # ]: 0 : }
859 : : }
860 : :
861 [ + - ]: 5 : if(!bDone)
862 : : {
863 : 5 : const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength);
864 [ + - ]: 5 : aRetval.append(interpolate(aStart, aEnd, fRelValue));
865 : 5 : }
866 : : }
867 : :
868 : 5 : bStartDone = true;
869 : :
870 : : // if same point, end is done, too.
871 [ - + ]: 5 : if(fFrom == fTo)
872 : : {
873 : 0 : bEndDone = true;
874 : : }
875 : : }
876 : : }
877 : :
878 [ + - ][ + - ]: 15 : if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength))
[ + + ][ + - ]
[ + + ]
879 : : {
880 : : // calculate and add end point
881 [ - + ]: 5 : if(fTools::equalZero(fEdgeLength))
882 : : {
883 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
884 [ # # ][ # # ]: 0 : aRetval.append(rCandidate.getB2DPoint(nNextIndex));
885 : :
886 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed())
887 : : {
888 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
[ # # ]
889 : : }
890 : : }
891 : : else
892 : : {
893 : 5 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
894 [ + - ]: 5 : const B2DPoint aStart(rCandidate.getB2DPoint(a));
895 [ + - ]: 5 : const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
896 : 5 : bool bDone(false);
897 : :
898 [ + - ][ - + ]: 5 : if(rCandidate.areControlPointsUsed())
899 : : {
900 : : const B2DCubicBezier aBezierSegment(
901 : : aStart, rCandidate.getNextControlPoint(a),
902 [ # # ][ # # ]: 0 : rCandidate.getPrevControlPoint(nNextIndex), aEnd);
[ # # ]
903 : :
904 [ # # ][ # # ]: 0 : if(aBezierSegment.isBezier())
905 : : {
906 : : // use B2DCubicBezierHelper to bridge the non-linear gap between
907 : : // length and bezier distances
908 [ # # ]: 0 : const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
909 [ # # ]: 0 : const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart));
910 [ # # ]: 0 : B2DCubicBezier aLeft;
911 : :
912 [ # # ]: 0 : aBezierSegment.split(fBezierDistance, &aLeft, 0);
913 [ # # ]: 0 : aRetval.append(aLeft.getEndPoint());
914 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB());
915 [ # # ]: 0 : bDone = true;
916 [ # # ]: 0 : }
917 : : }
918 : :
919 [ + - ]: 5 : if(!bDone)
920 : : {
921 : 5 : const double fRelValue((fTo - fPositionOfStart) / fEdgeLength);
922 [ + - ]: 5 : aRetval.append(interpolate(aStart, aEnd, fRelValue));
923 : 5 : }
924 : : }
925 : :
926 : 5 : bEndDone = true;
927 : : }
928 : :
929 [ + + ]: 15 : if(!bEndDone)
930 : : {
931 [ + - ]: 10 : if(bStartDone)
932 : : {
933 : : // add segments end point
934 : 10 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
935 [ + - ][ + - ]: 10 : aRetval.append(rCandidate.getB2DPoint(nNextIndex));
936 : :
937 [ - + ][ + - ]: 10 : if(rCandidate.areControlPointsUsed())
938 : : {
939 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
[ # # ]
940 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex));
[ # # ]
941 : : }
942 : : }
943 : :
944 : : // increment fPositionOfStart
945 : 10 : fPositionOfStart += fEdgeLength;
946 : : }
947 : : }
948 [ + - ][ + - ]: 10 : return aRetval;
949 : : }
950 : : }
951 : : else
952 : : {
953 : 10 : return rCandidate;
954 : : }
955 : : }
956 : :
957 : 39008 : CutFlagValue findCut(
958 : : const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
959 : : const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
960 : : CutFlagValue aCutFlags,
961 : : double* pCut1, double* pCut2)
962 : : {
963 : 39008 : CutFlagValue aRetval(CUTFLAG_NONE);
964 : 39008 : double fCut1(0.0);
965 : 39008 : double fCut2(0.0);
966 : 39008 : bool bFinished(!((bool)(aCutFlags & CUTFLAG_ALL)));
967 : :
968 : : // test for same points?
969 [ + - ][ + - ]: 39008 : if(!bFinished
[ + - ]
970 : : && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END1))
971 : : && (aCutFlags & (CUTFLAG_START2|CUTFLAG_END2)))
972 : : {
973 : : // same startpoint?
974 [ + - ][ + - ]: 39008 : if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_START2)) == (CUTFLAG_START1|CUTFLAG_START2))
975 : : {
976 [ - + ]: 39008 : if(rEdge1Start.equal(rEdge2Start))
977 : : {
978 : 0 : bFinished = true;
979 : 0 : aRetval = (CUTFLAG_START1|CUTFLAG_START2);
980 : : }
981 : : }
982 : :
983 : : // same endpoint?
984 [ + - ][ + - ]: 39008 : if(!bFinished && (aCutFlags & (CUTFLAG_END1|CUTFLAG_END2)) == (CUTFLAG_END1|CUTFLAG_END2))
985 : : {
986 : 39008 : const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
987 : 39008 : const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
988 : :
989 [ - + ]: 39008 : if(aEnd1.equal(aEnd2))
990 : : {
991 : 0 : bFinished = true;
992 : 0 : aRetval = (CUTFLAG_END1|CUTFLAG_END2);
993 : 0 : fCut1 = fCut2 = 1.0;
994 : 39008 : }
995 : : }
996 : :
997 : : // startpoint1 == endpoint2?
998 [ + - ][ + - ]: 39008 : if(!bFinished && (aCutFlags & (CUTFLAG_START1|CUTFLAG_END2)) == (CUTFLAG_START1|CUTFLAG_END2))
999 : : {
1000 : 39008 : const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
1001 : :
1002 [ - + ]: 39008 : if(rEdge1Start.equal(aEnd2))
1003 : : {
1004 : 0 : bFinished = true;
1005 : 0 : aRetval = (CUTFLAG_START1|CUTFLAG_END2);
1006 : 0 : fCut1 = 0.0;
1007 : 0 : fCut2 = 1.0;
1008 : 39008 : }
1009 : : }
1010 : :
1011 : : // startpoint2 == endpoint1?
1012 [ + - ][ + - ]: 39008 : if(!bFinished&& (aCutFlags & (CUTFLAG_START2|CUTFLAG_END1)) == (CUTFLAG_START2|CUTFLAG_END1))
1013 : : {
1014 : 39008 : const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
1015 : :
1016 [ - + ]: 39008 : if(rEdge2Start.equal(aEnd1))
1017 : : {
1018 : 0 : bFinished = true;
1019 : 0 : aRetval = (CUTFLAG_START2|CUTFLAG_END1);
1020 : 0 : fCut1 = 1.0;
1021 : 0 : fCut2 = 0.0;
1022 : 39008 : }
1023 : : }
1024 : : }
1025 : :
1026 [ + - ][ + - ]: 39008 : if(!bFinished && (aCutFlags & CUTFLAG_LINE))
1027 : : {
1028 [ + - ][ + - ]: 39008 : if(!bFinished && (aCutFlags & CUTFLAG_START1))
1029 : : {
1030 : : // start1 on line 2 ?
1031 [ + - ][ + + ]: 39008 : if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2))
1032 : : {
1033 : 296 : bFinished = true;
1034 : 296 : aRetval = (CUTFLAG_LINE|CUTFLAG_START1);
1035 : : }
1036 : : }
1037 : :
1038 [ + + ][ + - ]: 39008 : if(!bFinished && (aCutFlags & CUTFLAG_START2))
1039 : : {
1040 : : // start2 on line 1 ?
1041 [ + - ][ - + ]: 38712 : if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1))
1042 : : {
1043 : 0 : bFinished = true;
1044 : 0 : aRetval = (CUTFLAG_LINE|CUTFLAG_START2);
1045 : : }
1046 : : }
1047 : :
1048 [ + + ][ + - ]: 39008 : if(!bFinished && (aCutFlags & CUTFLAG_END1))
1049 : : {
1050 : : // end1 on line 2 ?
1051 : 38712 : const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
1052 : :
1053 [ - + ][ + - ]: 38712 : if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2))
1054 : : {
1055 : 0 : bFinished = true;
1056 : 0 : aRetval = (CUTFLAG_LINE|CUTFLAG_END1);
1057 : 38712 : }
1058 : : }
1059 : :
1060 [ + + ][ + - ]: 39008 : if(!bFinished && (aCutFlags & CUTFLAG_END2))
1061 : : {
1062 : : // end2 on line 1 ?
1063 : 38712 : const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);
1064 : :
1065 [ - + ][ + - ]: 38712 : if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1))
1066 : : {
1067 : 0 : bFinished = true;
1068 : 0 : aRetval = (CUTFLAG_LINE|CUTFLAG_END2);
1069 : 38712 : }
1070 : : }
1071 : :
1072 [ + + ]: 39008 : if(!bFinished)
1073 : : {
1074 : : // cut in line1, line2 ?
1075 : 38712 : fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX());
1076 : :
1077 [ + - ]: 38712 : if(!fTools::equalZero(fCut1))
1078 : : {
1079 : 38712 : fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX())
1080 : 38712 : + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1;
1081 : :
1082 : 38712 : const double fZero(0.0);
1083 : 38712 : const double fOne(1.0);
1084 : :
1085 : : // inside parameter range edge1 AND fCut2 is calcable
1086 [ + + + - : 87728 : if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne)
+ - ][ + + ]
[ + + ]
1087 [ + + ][ + + ]: 49016 : && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY())))
1088 : : {
1089 : : // take the mopre precise calculation of the two possible
1090 [ - + ]: 5152 : if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY()))
1091 : : {
1092 : 0 : fCut2 = (rEdge1Start.getX() + fCut1
1093 : 0 : * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX();
1094 : : }
1095 : : else
1096 : : {
1097 : 5152 : fCut2 = (rEdge1Start.getY() + fCut1
1098 : 5152 : * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY();
1099 : : }
1100 : :
1101 : : // inside parameter range edge2, too
1102 [ - + ][ # # ]: 5152 : if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
[ - + ]
1103 : : {
1104 : 0 : bFinished = true;
1105 : 38712 : aRetval = CUTFLAG_LINE;
1106 : : }
1107 : : }
1108 : : }
1109 : : }
1110 : : }
1111 : :
1112 : : // copy values if wanted
1113 [ + - ]: 39008 : if(pCut1)
1114 : : {
1115 : 39008 : *pCut1 = fCut1;
1116 : : }
1117 : :
1118 [ - + ]: 39008 : if(pCut2)
1119 : : {
1120 : 0 : *pCut2 = fCut2;
1121 : : }
1122 : :
1123 : 39008 : return aRetval;
1124 : : }
1125 : :
1126 : 155144 : bool isPointOnEdge(
1127 : : const B2DPoint& rPoint,
1128 : : const B2DPoint& rEdgeStart,
1129 : : const B2DVector& rEdgeDelta,
1130 : : double* pCut)
1131 : : {
1132 : 155144 : bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX()));
1133 : 155144 : bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY()));
1134 : 155144 : const double fZero(0.0);
1135 : 155144 : const double fOne(1.0);
1136 : :
1137 [ - + ][ + + ]: 155144 : if(bDeltaXIsZero && bDeltaYIsZero)
1138 : : {
1139 : : // no line, just a point
1140 : 0 : return false;
1141 : : }
1142 [ + + ]: 155144 : else if(bDeltaXIsZero)
1143 : : {
1144 : : // vertical line
1145 [ + + ]: 77720 : if(fTools::equal(rPoint.getX(), rEdgeStart.getX()))
1146 : : {
1147 : 296 : double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
1148 : :
1149 [ + - ][ + - ]: 296 : if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
[ + - ]
1150 : : {
1151 [ + - ]: 296 : if(pCut)
1152 : : {
1153 : 296 : *pCut = fValue;
1154 : : }
1155 : :
1156 : 296 : return true;
1157 : : }
1158 : : }
1159 : : }
1160 [ + - ]: 77424 : else if(bDeltaYIsZero)
1161 : : {
1162 : : // horizontal line
1163 [ - + ]: 77424 : if(fTools::equal(rPoint.getY(), rEdgeStart.getY()))
1164 : : {
1165 : 0 : double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
1166 : :
1167 [ # # ][ # # ]: 0 : if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
[ # # ]
1168 : : {
1169 [ # # ]: 0 : if(pCut)
1170 : : {
1171 : 0 : *pCut = fValue;
1172 : : }
1173 : :
1174 : 0 : return true;
1175 : : }
1176 : : }
1177 : : }
1178 : : else
1179 : : {
1180 : : // any angle line
1181 : 0 : double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
1182 : 0 : double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();
1183 : :
1184 [ # # ]: 0 : if(fTools::equal(fTOne, fTTwo))
1185 : : {
1186 : : // same parameter representation, point is on line. Take
1187 : : // middle value for better results
1188 : 0 : double fValue = (fTOne + fTTwo) / 2.0;
1189 : :
1190 [ # # ][ # # ]: 0 : if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
[ # # ]
1191 : : {
1192 : : // point is inside line bounds, too
1193 [ # # ]: 0 : if(pCut)
1194 : : {
1195 : 0 : *pCut = fValue;
1196 : : }
1197 : :
1198 : 0 : return true;
1199 : : }
1200 : : }
1201 : : }
1202 : :
1203 : 155144 : return false;
1204 : : }
1205 : :
1206 : 3297 : void applyLineDashing(const B2DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fDotDashLength)
1207 : : {
1208 : 3297 : const sal_uInt32 nPointCount(rCandidate.count());
1209 : 3297 : const sal_uInt32 nDotDashCount(rDotDashArray.size());
1210 : :
1211 [ + + ]: 3297 : if(fTools::lessOrEqual(fDotDashLength, 0.0))
1212 : : {
1213 : 43 : fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
1214 : : }
1215 : :
1216 [ + - ][ - + ]: 3297 : if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
[ # # ][ + - ]
[ + - ][ + - ]
1217 : : {
1218 : : // clear targets
1219 [ + - ]: 3297 : if(pLineTarget)
1220 : : {
1221 [ + - ]: 3297 : pLineTarget->clear();
1222 : : }
1223 : :
1224 [ - + ]: 3297 : if(pGapTarget)
1225 : : {
1226 [ # # ]: 0 : pGapTarget->clear();
1227 : : }
1228 : :
1229 : : // prepare current edge's start
1230 [ + - ]: 3297 : B2DCubicBezier aCurrentEdge;
1231 [ + - ][ + + ]: 3297 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
1232 [ + - ]: 3297 : aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));
1233 : :
1234 : : // prepare DotDashArray iteration and the line/gap switching bool
1235 : 3297 : sal_uInt32 nDotDashIndex(0);
1236 : 3297 : bool bIsLine(true);
1237 [ + - ]: 3297 : double fDotDashMovingLength(rDotDashArray[0]);
1238 [ + - ]: 3297 : B2DPolygon aSnippet;
1239 : :
1240 : : // iterate over all edges
1241 [ + + ]: 6880 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
1242 : : {
1243 : : // update current edge (fill in C1, C2 and end point)
1244 : 3583 : double fLastDotDashMovingLength(0.0);
1245 : 3583 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1246 [ + - ]: 3583 : aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
1247 [ + - ]: 3583 : aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
1248 [ + - ]: 3583 : aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
1249 : :
1250 : : // check if we have a trivial bezier segment -> possible fallback to edge
1251 [ + - ]: 3583 : aCurrentEdge.testAndSolveTrivialBezier();
1252 : :
1253 [ + - ][ - + ]: 3583 : if(aCurrentEdge.isBezier())
1254 : : {
1255 : : // bezier segment
1256 [ # # ]: 0 : const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
1257 [ # # ]: 0 : const double fEdgeLength(aCubicBezierHelper.getLength());
1258 : :
1259 [ # # ]: 0 : if(!fTools::equalZero(fEdgeLength))
1260 : : {
1261 [ # # ]: 0 : while(fTools::less(fDotDashMovingLength, fEdgeLength))
1262 : : {
1263 : : // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1264 [ # # ][ # # ]: 0 : const bool bHandleLine(bIsLine && pLineTarget);
1265 [ # # ][ # # ]: 0 : const bool bHandleGap(!bIsLine && pGapTarget);
1266 : :
1267 [ # # ][ # # ]: 0 : if(bHandleLine || bHandleGap)
1268 : : {
1269 [ # # ]: 0 : const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
1270 [ # # ]: 0 : const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
1271 [ # # ]: 0 : B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));
1272 : :
1273 [ # # ][ # # ]: 0 : if(!aSnippet.count())
1274 : : {
1275 [ # # ]: 0 : aSnippet.append(aBezierSnippet.getStartPoint());
1276 : : }
1277 : :
1278 [ # # ]: 0 : aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());
1279 : :
1280 [ # # ]: 0 : if(bHandleLine)
1281 : : {
1282 [ # # ]: 0 : pLineTarget->append(aSnippet);
1283 : : }
1284 : : else
1285 : : {
1286 [ # # ]: 0 : pGapTarget->append(aSnippet);
1287 : : }
1288 : :
1289 [ # # ][ # # ]: 0 : aSnippet.clear();
1290 : : }
1291 : :
1292 : : // prepare next DotDashArray step and flip line/gap flag
1293 : 0 : fLastDotDashMovingLength = fDotDashMovingLength;
1294 [ # # ]: 0 : fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
1295 : 0 : bIsLine = !bIsLine;
1296 : : }
1297 : :
1298 : : // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
1299 [ # # ][ # # ]: 0 : const bool bHandleLine(bIsLine && pLineTarget);
1300 [ # # ][ # # ]: 0 : const bool bHandleGap(!bIsLine && pGapTarget);
1301 : :
1302 [ # # ][ # # ]: 0 : if(bHandleLine || bHandleGap)
1303 : : {
1304 [ # # ]: 0 : B2DCubicBezier aRight;
1305 [ # # ]: 0 : const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
1306 : :
1307 [ # # ]: 0 : aCurrentEdge.split(fBezierSplit, 0, &aRight);
1308 : :
1309 [ # # ][ # # ]: 0 : if(!aSnippet.count())
1310 : : {
1311 [ # # ]: 0 : aSnippet.append(aRight.getStartPoint());
1312 : : }
1313 : :
1314 [ # # ][ # # ]: 0 : aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
1315 : : }
1316 : :
1317 : : // prepare move to next edge
1318 : 0 : fDotDashMovingLength -= fEdgeLength;
1319 : 0 : }
1320 : : }
1321 : : else
1322 : : {
1323 : : // simple edge
1324 [ + - ]: 3583 : const double fEdgeLength(aCurrentEdge.getEdgeLength());
1325 : :
1326 [ + - ]: 3583 : if(!fTools::equalZero(fEdgeLength))
1327 : : {
1328 [ + + ]: 190048 : while(fTools::less(fDotDashMovingLength, fEdgeLength))
1329 : : {
1330 : : // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
1331 [ + + ][ + - ]: 186465 : const bool bHandleLine(bIsLine && pLineTarget);
1332 [ + + ][ - + ]: 186465 : const bool bHandleGap(!bIsLine && pGapTarget);
1333 : :
1334 [ + + ][ - + ]: 186465 : if(bHandleLine || bHandleGap)
1335 : : {
1336 [ + - ][ + + ]: 94031 : if(!aSnippet.count())
1337 : : {
1338 [ + - ]: 93833 : aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
1339 : : }
1340 : :
1341 [ + - ]: 94031 : aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));
1342 : :
1343 [ + - ]: 94031 : if(bHandleLine)
1344 : : {
1345 [ + - ]: 94031 : pLineTarget->append(aSnippet);
1346 : : }
1347 : : else
1348 : : {
1349 [ # # ]: 0 : pGapTarget->append(aSnippet);
1350 : : }
1351 : :
1352 [ + - ]: 94031 : aSnippet.clear();
1353 : : }
1354 : :
1355 : : // prepare next DotDashArray step and flip line/gap flag
1356 : 186465 : fLastDotDashMovingLength = fDotDashMovingLength;
1357 [ + - ]: 186465 : fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
1358 : 186465 : bIsLine = !bIsLine;
1359 : : }
1360 : :
1361 : : // append snippet [fLastDotDashMovingLength, fEdgeLength]
1362 [ + + ][ + - ]: 3583 : const bool bHandleLine(bIsLine && pLineTarget);
1363 [ + + ][ - + ]: 3583 : const bool bHandleGap(!bIsLine && pGapTarget);
1364 : :
1365 [ + + ][ - + ]: 3583 : if(bHandleLine || bHandleGap)
1366 : : {
1367 [ + - ][ + - ]: 1898 : if(!aSnippet.count())
1368 : : {
1369 [ + - ]: 1898 : aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
1370 : : }
1371 : :
1372 [ + - ]: 1898 : aSnippet.append(aCurrentEdge.getEndPoint());
1373 : : }
1374 : :
1375 : : // prepare move to next edge
1376 : 3583 : fDotDashMovingLength -= fEdgeLength;
1377 : : }
1378 : : }
1379 : :
1380 : : // prepare next edge step (end point gets new start point)
1381 : 3583 : aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
1382 : : }
1383 : :
1384 : : // append last intermediate results (if exists)
1385 [ + - ][ + + ]: 3297 : if(aSnippet.count())
1386 : : {
1387 [ + - ][ + - ]: 1700 : if(bIsLine && pLineTarget)
1388 : : {
1389 [ + - ]: 1700 : pLineTarget->append(aSnippet);
1390 : : }
1391 [ # # ][ # # ]: 0 : else if(!bIsLine && pGapTarget)
1392 : : {
1393 [ # # ]: 1700 : pGapTarget->append(aSnippet);
1394 : : }
1395 : : }
1396 : :
1397 : : // check if start and end polygon may be merged
1398 [ + - ]: 3297 : if(pLineTarget)
1399 : : {
1400 [ + - ]: 3297 : const sal_uInt32 nCount(pLineTarget->count());
1401 : :
1402 [ + + ]: 3297 : if(nCount > 1)
1403 : : {
1404 : : // these polygons were created above, there exists none with less than two points,
1405 : : // thus dircet point access below is allowed
1406 [ + - ]: 3142 : const B2DPolygon aFirst(pLineTarget->getB2DPolygon(0));
1407 [ + - ]: 3142 : B2DPolygon aLast(pLineTarget->getB2DPolygon(nCount - 1));
1408 : :
1409 [ + - ][ + - ]: 3142 : if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
[ + - ][ + + ]
1410 : : {
1411 : : // start of first and end of last are the same -> merge them
1412 [ + - ]: 54 : aLast.append(aFirst);
1413 [ + - ]: 54 : aLast.removeDoublePoints();
1414 [ + - ]: 54 : pLineTarget->setB2DPolygon(0, aLast);
1415 [ + - ]: 54 : pLineTarget->remove(nCount - 1);
1416 [ + - ][ + - ]: 3142 : }
1417 : : }
1418 : : }
1419 : :
1420 [ - + ]: 3297 : if(pGapTarget)
1421 : : {
1422 [ # # ]: 0 : const sal_uInt32 nCount(pGapTarget->count());
1423 : :
1424 [ # # ]: 0 : if(nCount > 1)
1425 : : {
1426 : : // these polygons were created above, there exists none with less than two points,
1427 : : // thus dircet point access below is allowed
1428 [ # # ]: 0 : const B2DPolygon aFirst(pGapTarget->getB2DPolygon(0));
1429 [ # # ]: 0 : B2DPolygon aLast(pGapTarget->getB2DPolygon(nCount - 1));
1430 : :
1431 [ # # ][ # # ]: 0 : if(aFirst.getB2DPoint(0).equal(aLast.getB2DPoint(aLast.count() - 1)))
[ # # ][ # # ]
1432 : : {
1433 : : // start of first and end of last are the same -> merge them
1434 [ # # ]: 0 : aLast.append(aFirst);
1435 [ # # ]: 0 : aLast.removeDoublePoints();
1436 [ # # ]: 0 : pGapTarget->setB2DPolygon(0, aLast);
1437 [ # # ]: 0 : pGapTarget->remove(nCount - 1);
1438 [ # # ][ # # ]: 0 : }
1439 : : }
1440 [ + - ][ + - ]: 3297 : }
1441 : : }
1442 : : else
1443 : : {
1444 : : // parameters make no sense, just add source to targets
1445 [ # # ]: 0 : if(pLineTarget)
1446 : : {
1447 : 0 : pLineTarget->append(rCandidate);
1448 : : }
1449 : :
1450 [ # # ]: 0 : if(pGapTarget)
1451 : : {
1452 : 0 : pGapTarget->append(rCandidate);
1453 : : }
1454 : : }
1455 : 3297 : }
1456 : :
1457 : : // test if point is inside epsilon-range around an edge defined
1458 : : // by the two given points. Can be used for HitTesting. The epsilon-range
1459 : : // is defined to be the rectangle centered to the given edge, using height
1460 : : // 2 x fDistance, and the circle around both points with radius fDistance.
1461 : 8 : bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance)
1462 : : {
1463 : : // build edge vector
1464 : 8 : const B2DVector aEdge(rEdgeEnd - rEdgeStart);
1465 : 8 : bool bDoDistanceTestStart(false);
1466 : 8 : bool bDoDistanceTestEnd(false);
1467 : :
1468 [ - + ][ + - ]: 8 : if(aEdge.equalZero())
1469 : : {
1470 : : // no edge, just a point. Do one of the distance tests.
1471 : 0 : bDoDistanceTestStart = true;
1472 : : }
1473 : : else
1474 : : {
1475 : : // edge has a length. Create perpendicular vector.
1476 [ + - ]: 8 : const B2DVector aPerpend(getPerpendicular(aEdge));
1477 : : double fCut(
1478 : 8 : (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX())
1479 : 8 : + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) /
1480 : 8 : (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY()));
1481 : 8 : const double fZero(0.0);
1482 : 8 : const double fOne(1.0);
1483 : :
1484 [ - + ]: 8 : if(fTools::less(fCut, fZero))
1485 : : {
1486 : : // left of rEdgeStart
1487 : 0 : bDoDistanceTestStart = true;
1488 : : }
1489 [ - + ]: 8 : else if(fTools::more(fCut, fOne))
1490 : : {
1491 : : // right of rEdgeEnd
1492 : 0 : bDoDistanceTestEnd = true;
1493 : : }
1494 : : else
1495 : : {
1496 : : // inside line [0.0 .. 1.0]
1497 : 8 : const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
1498 : 8 : const B2DVector aDelta(rTestPosition - aCutPoint);
1499 [ + - ]: 8 : const double fDistanceSquare(aDelta.scalar(aDelta));
1500 : :
1501 [ - + ]: 8 : if(fDistanceSquare <= fDistance * fDistance)
1502 : : {
1503 : 0 : return true;
1504 : : }
1505 : : else
1506 : : {
1507 : 8 : return false;
1508 : 8 : }
1509 [ - + ]: 8 : }
1510 : : }
1511 : :
1512 [ # # ]: 0 : if(bDoDistanceTestStart)
1513 : : {
1514 : 0 : const B2DVector aDelta(rTestPosition - rEdgeStart);
1515 [ # # ]: 0 : const double fDistanceSquare(aDelta.scalar(aDelta));
1516 : :
1517 [ # # ]: 0 : if(fDistanceSquare <= fDistance * fDistance)
1518 : : {
1519 : 0 : return true;
1520 [ # # ]: 0 : }
1521 : : }
1522 [ # # ]: 0 : else if(bDoDistanceTestEnd)
1523 : : {
1524 : 0 : const B2DVector aDelta(rTestPosition - rEdgeEnd);
1525 [ # # ]: 0 : const double fDistanceSquare(aDelta.scalar(aDelta));
1526 : :
1527 [ # # ]: 0 : if(fDistanceSquare <= fDistance * fDistance)
1528 : : {
1529 : 0 : return true;
1530 [ # # ]: 0 : }
1531 : : }
1532 : :
1533 : 8 : return false;
1534 : : }
1535 : :
1536 : : // test if point is inside epsilon-range around the given Polygon. Can be used
1537 : : // for HitTesting. The epsilon-range is defined to be the tube around the polygon
1538 : : // with distance fDistance and rounded edges (start and end point).
1539 : 2 : bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
1540 : : {
1541 : : // force to non-bezier polygon
1542 [ + - ]: 2 : const B2DPolygon aCandidate(rCandidate.getDefaultAdaptiveSubdivision());
1543 [ + - ]: 2 : const sal_uInt32 nPointCount(aCandidate.count());
1544 : :
1545 [ + - ]: 2 : if(nPointCount)
1546 : : {
1547 [ + - ][ + - ]: 2 : const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
1548 [ + - ]: 2 : B2DPoint aCurrent(aCandidate.getB2DPoint(0));
1549 : :
1550 [ + - ]: 2 : if(nEdgeCount)
1551 : : {
1552 : : // edges
1553 [ + + ]: 10 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
1554 : : {
1555 : 8 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1556 [ + - ]: 8 : const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
1557 : :
1558 [ + - ][ - + ]: 8 : if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
1559 : : {
1560 : 0 : return true;
1561 : : }
1562 : :
1563 : : // prepare next step
1564 [ + - ]: 16 : aCurrent = aNext;
1565 : 8 : }
1566 : : }
1567 : : else
1568 : : {
1569 : : // no edges, but points -> not closed. Check single point. Just
1570 : : // use isInEpsilonRange with twice the same point, it handles those well
1571 [ # # ][ # # ]: 0 : if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
1572 : : {
1573 : 2 : return true;
1574 : : }
1575 [ + - ]: 2 : }
1576 : : }
1577 : :
1578 [ + - ]: 2 : return false;
1579 : : }
1580 : :
1581 : 23911 : B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY )
1582 : : {
1583 : 23911 : const double fZero(0.0);
1584 : 23911 : const double fOne(1.0);
1585 : :
1586 : : // crop to useful values
1587 [ - + ]: 23911 : if(fTools::less(fRadiusX, fZero))
1588 : : {
1589 : 0 : fRadiusX = fZero;
1590 : : }
1591 [ - + ]: 23911 : else if(fTools::more(fRadiusX, fOne))
1592 : : {
1593 : 0 : fRadiusX = fOne;
1594 : : }
1595 : :
1596 [ - + ]: 23911 : if(fTools::less(fRadiusY, fZero))
1597 : : {
1598 : 0 : fRadiusY = fZero;
1599 : : }
1600 [ - + ]: 23911 : else if(fTools::more(fRadiusY, fOne))
1601 : : {
1602 : 0 : fRadiusY = fOne;
1603 : : }
1604 : :
1605 [ - + ][ # # ]: 23911 : if(fZero == fRadiusX || fZero == fRadiusY)
1606 : : {
1607 [ + - ]: 23911 : B2DPolygon aRetval;
1608 : :
1609 : : // at least in one direction no radius, use rectangle.
1610 : : // Do not use createPolygonFromRect() here since original
1611 : : // creator (historical reasons) still creates a start point at the
1612 : : // bottom center, so do the same here to get the same line patterns.
1613 : : // Due to this the order of points is different, too.
1614 [ + - ][ + - ]: 23911 : const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
1615 [ + - ]: 23911 : aRetval.append(aBottomCenter);
1616 : :
1617 [ + - ][ + - ]: 23911 : aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
[ + - ]
1618 [ + - ][ + - ]: 23911 : aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
[ + - ]
1619 [ + - ][ + - ]: 23911 : aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
[ + - ]
1620 [ + - ][ + - ]: 23911 : aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
[ + - ]
1621 : :
1622 : : // close
1623 [ + - ]: 23911 : aRetval.setClosed( true );
1624 : :
1625 [ + - ][ + - ]: 23911 : return aRetval;
1626 : : }
1627 [ # # ][ # # ]: 0 : else if(fOne == fRadiusX && fOne == fRadiusY)
1628 : : {
1629 : : // in both directions full radius, use ellipse
1630 [ # # ]: 0 : const B2DPoint aCenter(rRect.getCenter());
1631 [ # # ]: 0 : const double fRectRadiusX(rRect.getWidth() / 2.0);
1632 [ # # ]: 0 : const double fRectRadiusY(rRect.getHeight() / 2.0);
1633 : :
1634 [ # # ]: 0 : return createPolygonFromEllipse( aCenter, fRectRadiusX, fRectRadiusY );
1635 : : }
1636 : : else
1637 : : {
1638 [ # # ]: 0 : B2DPolygon aRetval;
1639 [ # # ]: 0 : const double fBowX((rRect.getWidth() / 2.0) * fRadiusX);
1640 [ # # ]: 0 : const double fBowY((rRect.getHeight() / 2.0) * fRadiusY);
1641 : 0 : const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1642 : :
1643 : : // create start point at bottom center
1644 [ # # ]: 0 : if(fOne != fRadiusX)
1645 : : {
1646 [ # # ][ # # ]: 0 : const B2DPoint aBottomCenter(rRect.getCenter().getX(), rRect.getMaxY());
1647 [ # # ]: 0 : aRetval.append(aBottomCenter);
1648 : : }
1649 : :
1650 : : // create first bow
1651 : : {
1652 [ # # ][ # # ]: 0 : const B2DPoint aBottomRight(rRect.getMaxX(), rRect.getMaxY());
1653 : 0 : const B2DPoint aStart(aBottomRight + B2DPoint(-fBowX, 0.0));
1654 : 0 : const B2DPoint aStop(aBottomRight + B2DPoint(0.0, -fBowY));
1655 [ # # ]: 0 : aRetval.append(aStart);
1656 [ # # ]: 0 : aRetval.appendBezierSegment(interpolate(aStart, aBottomRight, fKappa), interpolate(aStop, aBottomRight, fKappa), aStop);
1657 : : }
1658 : :
1659 : : // create second bow
1660 : : {
1661 [ # # ][ # # ]: 0 : const B2DPoint aTopRight(rRect.getMaxX(), rRect.getMinY());
1662 : 0 : const B2DPoint aStart(aTopRight + B2DPoint(0.0, fBowY));
1663 : 0 : const B2DPoint aStop(aTopRight + B2DPoint(-fBowX, 0.0));
1664 [ # # ]: 0 : aRetval.append(aStart);
1665 [ # # ]: 0 : aRetval.appendBezierSegment(interpolate(aStart, aTopRight, fKappa), interpolate(aStop, aTopRight, fKappa), aStop);
1666 : : }
1667 : :
1668 : : // create third bow
1669 : : {
1670 [ # # ][ # # ]: 0 : const B2DPoint aTopLeft(rRect.getMinX(), rRect.getMinY());
1671 : 0 : const B2DPoint aStart(aTopLeft + B2DPoint(fBowX, 0.0));
1672 : 0 : const B2DPoint aStop(aTopLeft + B2DPoint(0.0, fBowY));
1673 [ # # ]: 0 : aRetval.append(aStart);
1674 [ # # ]: 0 : aRetval.appendBezierSegment(interpolate(aStart, aTopLeft, fKappa), interpolate(aStop, aTopLeft, fKappa), aStop);
1675 : : }
1676 : :
1677 : : // create forth bow
1678 : : {
1679 [ # # ][ # # ]: 0 : const B2DPoint aBottomLeft(rRect.getMinX(), rRect.getMaxY());
1680 : 0 : const B2DPoint aStart(aBottomLeft + B2DPoint(0.0, -fBowY));
1681 : 0 : const B2DPoint aStop(aBottomLeft + B2DPoint(fBowX, 0.0));
1682 [ # # ]: 0 : aRetval.append(aStart);
1683 [ # # ]: 0 : aRetval.appendBezierSegment(interpolate(aStart, aBottomLeft, fKappa), interpolate(aStop, aBottomLeft, fKappa), aStop);
1684 : : }
1685 : :
1686 : : // close
1687 [ # # ]: 0 : aRetval.setClosed( true );
1688 : :
1689 : : // remove double created points if there are extreme radii envolved
1690 [ # # ][ # # ]: 0 : if(fOne == fRadiusX || fOne == fRadiusY)
1691 : : {
1692 [ # # ]: 0 : aRetval.removeDoublePoints();
1693 : : }
1694 : :
1695 [ # # ][ # # ]: 23911 : return aRetval;
1696 : : }
1697 : : }
1698 : :
1699 : 1008221 : B2DPolygon createPolygonFromRect( const B2DRectangle& rRect )
1700 : : {
1701 : 1008221 : B2DPolygon aRetval;
1702 : :
1703 [ + - ][ + - ]: 1008221 : aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMinY() ) );
[ + - ]
1704 [ + - ][ + - ]: 1008221 : aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMinY() ) );
[ + - ]
1705 [ + - ][ + - ]: 1008221 : aRetval.append( B2DPoint( rRect.getMaxX(), rRect.getMaxY() ) );
[ + - ]
1706 [ + - ][ + - ]: 1008221 : aRetval.append( B2DPoint( rRect.getMinX(), rRect.getMaxY() ) );
[ + - ]
1707 : :
1708 : : // close
1709 [ + - ]: 1008221 : aRetval.setClosed( true );
1710 : :
1711 : 1008221 : return aRetval;
1712 : : }
1713 : :
1714 : : namespace
1715 : : {
1716 : : struct theUnitPolygon :
1717 : : public rtl::StaticWithInit<B2DPolygon, theUnitPolygon>
1718 : : {
1719 : 33 : B2DPolygon operator () ()
1720 : : {
1721 : 33 : B2DPolygon aRetval;
1722 : :
1723 [ + - ]: 33 : aRetval.append( B2DPoint( 0.0, 0.0 ) );
1724 [ + - ]: 33 : aRetval.append( B2DPoint( 1.0, 0.0 ) );
1725 [ + - ]: 33 : aRetval.append( B2DPoint( 1.0, 1.0 ) );
1726 [ + - ]: 33 : aRetval.append( B2DPoint( 0.0, 1.0 ) );
1727 : :
1728 : : // close
1729 [ + - ]: 33 : aRetval.setClosed( true );
1730 : :
1731 : 33 : return aRetval;
1732 : : }
1733 : : };
1734 : : }
1735 : :
1736 : 1331 : B2DPolygon createUnitPolygon()
1737 : : {
1738 : 1331 : return theUnitPolygon::get();
1739 : : }
1740 : :
1741 : 0 : B2DPolygon createPolygonFromCircle( const B2DPoint& rCenter, double fRadius )
1742 : : {
1743 : 0 : return createPolygonFromEllipse( rCenter, fRadius, fRadius );
1744 : : }
1745 : :
1746 : 9 : B2DPolygon impCreateUnitCircle(sal_uInt32 nStartQuadrant)
1747 : : {
1748 [ + - ]: 9 : B2DPolygon aUnitCircle;
1749 : 9 : const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1750 : 9 : const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
1751 [ + - ]: 9 : const B2DHomMatrix aRotateMatrix(createRotateB2DHomMatrix(F_PI2 / STEPSPERQUARTER));
1752 : :
1753 : 9 : B2DPoint aPoint(1.0, 0.0);
1754 : 9 : B2DPoint aForward(1.0, fScaledKappa);
1755 : 9 : B2DPoint aBackward(1.0, -fScaledKappa);
1756 : :
1757 [ + + ]: 9 : if(0 != nStartQuadrant)
1758 : : {
1759 [ + - ]: 8 : const B2DHomMatrix aQuadrantMatrix(createRotateB2DHomMatrix(F_PI2 * (nStartQuadrant % 4)));
1760 [ + - ]: 8 : aPoint *= aQuadrantMatrix;
1761 [ + - ]: 8 : aBackward *= aQuadrantMatrix;
1762 [ + - ][ + - ]: 8 : aForward *= aQuadrantMatrix;
1763 : : }
1764 : :
1765 [ + - ]: 9 : aUnitCircle.append(aPoint);
1766 : :
1767 [ + + ]: 117 : for(sal_uInt32 a(0); a < STEPSPERQUARTER * 4; a++)
1768 : : {
1769 [ + - ]: 108 : aPoint *= aRotateMatrix;
1770 [ + - ]: 108 : aBackward *= aRotateMatrix;
1771 [ + - ]: 108 : aUnitCircle.appendBezierSegment(aForward, aBackward, aPoint);
1772 [ + - ]: 108 : aForward *= aRotateMatrix;
1773 : : }
1774 : :
1775 [ + - ]: 9 : aUnitCircle.setClosed(true);
1776 [ + - ]: 9 : aUnitCircle.removeDoublePoints();
1777 : :
1778 [ + - ]: 9 : return aUnitCircle;
1779 : : }
1780 : :
1781 : 88 : B2DPolygon createPolygonFromUnitCircle(sal_uInt32 nStartQuadrant)
1782 : : {
1783 [ + - - + ]: 88 : switch(nStartQuadrant % 4)
1784 : : {
1785 : : case 1 :
1786 : : {
1787 [ + + ][ + - ]: 75 : static B2DPolygon aUnitCircleStartQuadrantOne;
[ + - ][ # # ]
1788 : :
1789 [ + + ]: 75 : if(!aUnitCircleStartQuadrantOne.count())
1790 : : {
1791 [ + - ]: 8 : ::osl::Mutex m_mutex;
1792 [ + - ][ + - ]: 8 : aUnitCircleStartQuadrantOne = impCreateUnitCircle(1);
[ + - ][ + - ]
1793 : : }
1794 : :
1795 : 75 : return aUnitCircleStartQuadrantOne;
1796 : : }
1797 : : case 2 :
1798 : : {
1799 [ # # ][ # # ]: 0 : static B2DPolygon aUnitCircleStartQuadrantTwo;
[ # # ][ # # ]
1800 : :
1801 [ # # ]: 0 : if(!aUnitCircleStartQuadrantTwo.count())
1802 : : {
1803 [ # # ]: 0 : ::osl::Mutex m_mutex;
1804 [ # # ][ # # ]: 0 : aUnitCircleStartQuadrantTwo = impCreateUnitCircle(2);
[ # # ][ # # ]
1805 : : }
1806 : :
1807 : 0 : return aUnitCircleStartQuadrantTwo;
1808 : : }
1809 : : case 3 :
1810 : : {
1811 [ # # ][ # # ]: 0 : static B2DPolygon aUnitCircleStartQuadrantThree;
[ # # ][ # # ]
1812 : :
1813 [ # # ]: 0 : if(!aUnitCircleStartQuadrantThree.count())
1814 : : {
1815 [ # # ]: 0 : ::osl::Mutex m_mutex;
1816 [ # # ][ # # ]: 0 : aUnitCircleStartQuadrantThree = impCreateUnitCircle(3);
[ # # ][ # # ]
1817 : : }
1818 : :
1819 : 0 : return aUnitCircleStartQuadrantThree;
1820 : : }
1821 : : default : // case 0 :
1822 : : {
1823 [ + + ][ + - ]: 13 : static B2DPolygon aUnitCircleStartQuadrantZero;
[ + - ][ # # ]
1824 : :
1825 [ + + ]: 13 : if(!aUnitCircleStartQuadrantZero.count())
1826 : : {
1827 [ + - ]: 1 : ::osl::Mutex m_mutex;
1828 [ + - ][ + - ]: 1 : aUnitCircleStartQuadrantZero = impCreateUnitCircle(0);
[ + - ][ + - ]
1829 : : }
1830 : :
1831 : 88 : return aUnitCircleStartQuadrantZero;
1832 : : }
1833 : : }
1834 : : }
1835 : :
1836 : 13 : B2DPolygon createPolygonFromEllipse( const B2DPoint& rCenter, double fRadiusX, double fRadiusY )
1837 : : {
1838 [ + - ]: 13 : B2DPolygon aRetval(createPolygonFromUnitCircle());
1839 [ + - ]: 13 : const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
1840 : :
1841 [ + - ]: 13 : aRetval.transform(aMatrix);
1842 : :
1843 [ + - ]: 13 : return aRetval;
1844 : : }
1845 : :
1846 : 868 : B2DPolygon createPolygonFromUnitEllipseSegment( double fStart, double fEnd )
1847 : : {
1848 : 868 : B2DPolygon aRetval;
1849 : :
1850 : : // truncate fStart, fEnd to a range of [0.0 .. F_2PI[ where F_2PI
1851 : : // falls back to 0.0 to ensure a unique definition
1852 [ - + ]: 868 : if(fTools::less(fStart, 0.0))
1853 : : {
1854 : 0 : fStart = 0.0;
1855 : : }
1856 : :
1857 [ - + ]: 868 : if(fTools::moreOrEqual(fStart, F_2PI))
1858 : : {
1859 : 0 : fStart = 0.0;
1860 : : }
1861 : :
1862 [ - + ]: 868 : if(fTools::less(fEnd, 0.0))
1863 : : {
1864 : 0 : fEnd = 0.0;
1865 : : }
1866 : :
1867 [ - + ]: 868 : if(fTools::moreOrEqual(fEnd, F_2PI))
1868 : : {
1869 : 0 : fEnd = 0.0;
1870 : : }
1871 : :
1872 [ - + ]: 868 : if(fTools::equal(fStart, fEnd))
1873 : : {
1874 : : // same start and end angle, add single point
1875 [ # # ]: 0 : aRetval.append(B2DPoint(cos(fStart), sin(fStart)));
1876 : : }
1877 : : else
1878 : : {
1879 : 868 : const sal_uInt32 nSegments(STEPSPERQUARTER * 4);
1880 : 868 : const double fAnglePerSegment(F_PI2 / STEPSPERQUARTER);
1881 : 868 : const sal_uInt32 nStartSegment(sal_uInt32(fStart / fAnglePerSegment) % nSegments);
1882 : 868 : const sal_uInt32 nEndSegment(sal_uInt32(fEnd / fAnglePerSegment) % nSegments);
1883 : 868 : const double fKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
1884 : 868 : const double fScaledKappa(fKappa * (1.0 / STEPSPERQUARTER));
1885 : :
1886 : 868 : B2DPoint aSegStart(cos(fStart), sin(fStart));
1887 [ + - ]: 868 : aRetval.append(aSegStart);
1888 : :
1889 [ + + ][ + - ]: 868 : if(nStartSegment == nEndSegment && fTools::more(fEnd, fStart))
[ + + ]
1890 : : {
1891 : : // start and end in one sector and in the right order, create in one segment
1892 : 76 : const B2DPoint aSegEnd(cos(fEnd), sin(fEnd));
1893 : 76 : const double fFactor(fScaledKappa * ((fEnd - fStart) / fAnglePerSegment));
1894 : :
1895 : : aRetval.appendBezierSegment(
1896 : 76 : aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1897 : 76 : aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1898 [ + - ]: 228 : aSegEnd);
1899 : : }
1900 : : else
1901 : : {
1902 : 792 : double fSegEndRad((nStartSegment + 1) * fAnglePerSegment);
1903 : 792 : double fFactor(fScaledKappa * ((fSegEndRad - fStart) / fAnglePerSegment));
1904 : 792 : B2DPoint aSegEnd(cos(fSegEndRad), sin(fSegEndRad));
1905 : :
1906 : : aRetval.appendBezierSegment(
1907 : 792 : aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1908 : 792 : aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1909 [ + - ]: 2376 : aSegEnd);
1910 : :
1911 : 792 : sal_uInt32 nSegment((nStartSegment + 1) % nSegments);
1912 : 792 : aSegStart = aSegEnd;
1913 : :
1914 [ + + ]: 1664 : while(nSegment != nEndSegment)
1915 : : {
1916 : : // No end in this sector, add full sector.
1917 : 872 : fSegEndRad = (nSegment + 1) * fAnglePerSegment;
1918 : 872 : aSegEnd = B2DPoint(cos(fSegEndRad), sin(fSegEndRad));
1919 : :
1920 : : aRetval.appendBezierSegment(
1921 : 872 : aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fScaledKappa),
1922 : 872 : aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fScaledKappa),
1923 [ + - ]: 2616 : aSegEnd);
1924 : :
1925 : 872 : nSegment = (nSegment + 1) % nSegments;
1926 : 872 : aSegStart = aSegEnd;
1927 : : }
1928 : :
1929 : : // End in this sector
1930 : 792 : const double fSegStartRad(nSegment * fAnglePerSegment);
1931 : 792 : fFactor = fScaledKappa * ((fEnd - fSegStartRad) / fAnglePerSegment);
1932 : 792 : aSegEnd = B2DPoint(cos(fEnd), sin(fEnd));
1933 : :
1934 : : aRetval.appendBezierSegment(
1935 : 792 : aSegStart + (B2DPoint(-aSegStart.getY(), aSegStart.getX()) * fFactor),
1936 : 792 : aSegEnd - (B2DPoint(-aSegEnd.getY(), aSegEnd.getX()) * fFactor),
1937 [ + - ]: 2376 : aSegEnd);
1938 : 868 : }
1939 : : }
1940 : :
1941 : : // remove double points between segments created by segmented creation
1942 [ + - ]: 868 : aRetval.removeDoublePoints();
1943 : :
1944 : 868 : return aRetval;
1945 : : }
1946 : :
1947 : 868 : B2DPolygon createPolygonFromEllipseSegment( const B2DPoint& rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd )
1948 : : {
1949 [ + - ]: 868 : B2DPolygon aRetval(createPolygonFromUnitEllipseSegment(fStart, fEnd));
1950 [ + - ]: 868 : const B2DHomMatrix aMatrix(createScaleTranslateB2DHomMatrix(fRadiusX, fRadiusY, rCenter.getX(), rCenter.getY()));
1951 : :
1952 [ + - ]: 868 : aRetval.transform(aMatrix);
1953 : :
1954 [ + - ]: 868 : return aRetval;
1955 : : }
1956 : :
1957 : 1170 : bool hasNeutralPoints(const B2DPolygon& rCandidate)
1958 : : {
1959 : : OSL_ENSURE(!rCandidate.areControlPointsUsed(), "hasNeutralPoints: ATM works not for curves (!)");
1960 : 1170 : const sal_uInt32 nPointCount(rCandidate.count());
1961 : :
1962 [ + + ]: 1170 : if(nPointCount > 2L)
1963 : : {
1964 [ + - ]: 1130 : B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
1965 [ + - ]: 1130 : B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
1966 : :
1967 [ + + ]: 6650 : for(sal_uInt32 a(0L); a < nPointCount; a++)
1968 : : {
1969 [ + - ]: 5520 : const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
1970 : 5520 : const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
1971 : 5520 : const B2DVector aNextVec(aNextPoint - aCurrPoint);
1972 [ + - ]: 5520 : const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
1973 : :
1974 [ + + ]: 5520 : if(ORIENTATION_NEUTRAL == aOrientation)
1975 : : {
1976 : : // current has neutral orientation
1977 : 165 : return true;
1978 : : }
1979 : : else
1980 : : {
1981 : : // prepare next
1982 : 5355 : aPrevPoint = aCurrPoint;
1983 [ + + ]: 10875 : aCurrPoint = aNextPoint;
1984 : : }
1985 [ + + ][ + + ]: 12170 : }
[ + + ][ + + ]
1986 : : }
1987 : :
1988 : 1170 : return false;
1989 : : }
1990 : :
1991 : 1170 : B2DPolygon removeNeutralPoints(const B2DPolygon& rCandidate)
1992 : : {
1993 [ + + ]: 1170 : if(hasNeutralPoints(rCandidate))
1994 : : {
1995 [ + - ]: 165 : const sal_uInt32 nPointCount(rCandidate.count());
1996 [ + - ]: 165 : B2DPolygon aRetval;
1997 [ + - ]: 165 : B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
1998 [ + - ]: 165 : B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
1999 : :
2000 [ + + ]: 1555 : for(sal_uInt32 a(0L); a < nPointCount; a++)
2001 : : {
2002 [ + - ]: 1390 : const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
2003 : 1390 : const B2DVector aPrevVec(aPrevPoint - aCurrPoint);
2004 : 1390 : const B2DVector aNextVec(aNextPoint - aCurrPoint);
2005 [ + - ]: 1390 : const B2VectorOrientation aOrientation(getOrientation(aNextVec, aPrevVec));
2006 : :
2007 [ + + ]: 1390 : if(ORIENTATION_NEUTRAL == aOrientation)
2008 : : {
2009 : : // current has neutral orientation, leave it out and prepare next
2010 : 650 : aCurrPoint = aNextPoint;
2011 : : }
2012 : : else
2013 : : {
2014 : : // add current point
2015 [ + - ]: 740 : aRetval.append(aCurrPoint);
2016 : :
2017 : : // prepare next
2018 : 740 : aPrevPoint = aCurrPoint;
2019 : 740 : aCurrPoint = aNextPoint;
2020 : : }
2021 : 1390 : }
2022 : :
2023 [ + - ][ + + ]: 195 : while(aRetval.count() && ORIENTATION_NEUTRAL == getOrientationForIndex(aRetval, 0L))
[ + - ][ + + ]
[ + + ]
2024 : : {
2025 [ + - ]: 30 : aRetval.remove(0L);
2026 : : }
2027 : :
2028 : : // copy closed state
2029 [ + - ][ + - ]: 165 : aRetval.setClosed(rCandidate.isClosed());
2030 : :
2031 [ + - ][ + - ]: 165 : return aRetval;
2032 : : }
2033 : : else
2034 : : {
2035 : 1170 : return rCandidate;
2036 : : }
2037 : : }
2038 : :
2039 : 0 : bool isConvex(const B2DPolygon& rCandidate)
2040 : : {
2041 : : OSL_ENSURE(!rCandidate.areControlPointsUsed(), "isConvex: ATM works not for curves (!)");
2042 : 0 : const sal_uInt32 nPointCount(rCandidate.count());
2043 : :
2044 [ # # ]: 0 : if(nPointCount > 2L)
2045 : : {
2046 [ # # ]: 0 : const B2DPoint aPrevPoint(rCandidate.getB2DPoint(nPointCount - 1L));
2047 [ # # ]: 0 : B2DPoint aCurrPoint(rCandidate.getB2DPoint(0L));
2048 : 0 : B2DVector aCurrVec(aPrevPoint - aCurrPoint);
2049 : 0 : B2VectorOrientation aOrientation(ORIENTATION_NEUTRAL);
2050 : :
2051 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nPointCount; a++)
2052 : : {
2053 [ # # ]: 0 : const B2DPoint aNextPoint(rCandidate.getB2DPoint((a + 1) % nPointCount));
2054 : 0 : const B2DVector aNextVec(aNextPoint - aCurrPoint);
2055 [ # # ]: 0 : const B2VectorOrientation aCurrentOrientation(getOrientation(aNextVec, aCurrVec));
2056 : :
2057 [ # # ]: 0 : if(ORIENTATION_NEUTRAL == aOrientation)
2058 : : {
2059 : : // set start value, maybe neutral again
2060 : 0 : aOrientation = aCurrentOrientation;
2061 : : }
2062 : : else
2063 : : {
2064 [ # # ][ # # ]: 0 : if(ORIENTATION_NEUTRAL != aCurrentOrientation && aCurrentOrientation != aOrientation)
2065 : : {
2066 : : // different orientations found, that's it
2067 : 0 : return false;
2068 : : }
2069 : : }
2070 : :
2071 : : // prepare next
2072 : 0 : aCurrPoint = aNextPoint;
2073 [ # # ][ # # ]: 0 : aCurrVec = -aNextVec;
2074 [ # # ][ # # ]: 0 : }
[ # # ][ # # ]
2075 : : }
2076 : :
2077 : 0 : return true;
2078 : : }
2079 : :
2080 : 175 : B2VectorOrientation getOrientationForIndex(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
2081 : : {
2082 : : OSL_ENSURE(nIndex < rCandidate.count(), "getOrientationForIndex: index out of range (!)");
2083 [ + - ][ + - ]: 175 : const B2DPoint aPrev(rCandidate.getB2DPoint(getIndexOfPredecessor(nIndex, rCandidate)));
2084 [ + - ]: 175 : const B2DPoint aCurr(rCandidate.getB2DPoint(nIndex));
2085 [ + - ][ + - ]: 175 : const B2DPoint aNext(rCandidate.getB2DPoint(getIndexOfSuccessor(nIndex, rCandidate)));
2086 : 175 : const B2DVector aBack(aPrev - aCurr);
2087 : 175 : const B2DVector aForw(aNext - aCurr);
2088 : :
2089 [ + - ]: 175 : return getOrientation(aForw, aBack);
2090 : : }
2091 : :
2092 : 39583 : bool isPointOnLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidate, bool bWithPoints)
2093 : : {
2094 [ + + ][ + + ]: 39583 : if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
[ + + ]
2095 : : {
2096 : : // candidate is in epsilon around start or end -> inside
2097 : 3561 : return bWithPoints;
2098 : : }
2099 [ + + ]: 36022 : else if(rStart.equal(rEnd))
2100 : : {
2101 : : // start and end are equal, but candidate is outside their epsilon -> outside
2102 : 30 : return false;
2103 : : }
2104 : : else
2105 : : {
2106 : 35992 : const B2DVector aEdgeVector(rEnd - rStart);
2107 : 35992 : const B2DVector aTestVector(rCandidate - rStart);
2108 : :
2109 [ + + ][ + - ]: 35992 : if(areParallel(aEdgeVector, aTestVector))
2110 : : {
2111 : 2598 : const double fZero(0.0);
2112 : 2598 : const double fOne(1.0);
2113 : 2598 : const double fParamTestOnCurr(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())
2114 : 1076 : ? aTestVector.getX() / aEdgeVector.getX()
2115 [ + + ]: 3674 : : aTestVector.getY() / aEdgeVector.getY());
2116 : :
2117 [ + + ][ + + ]: 2598 : if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
[ + + ]
2118 : : {
2119 : 2598 : return true;
2120 : : }
2121 : : }
2122 : :
2123 : 39583 : return false;
2124 : : }
2125 : : }
2126 : :
2127 : 6176 : bool isPointOnPolygon(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithPoints)
2128 : : {
2129 [ + - ][ - + ]: 6176 : const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
[ # # ][ + - ]
2130 [ + - ]: 6176 : const sal_uInt32 nPointCount(aCandidate.count());
2131 : :
2132 [ + - ]: 6176 : if(nPointCount > 1L)
2133 : : {
2134 [ + - ][ + - ]: 6176 : const sal_uInt32 nLoopCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2135 [ + - ]: 6176 : B2DPoint aCurrentPoint(aCandidate.getB2DPoint(0L));
2136 : :
2137 [ + + ]: 45759 : for(sal_uInt32 a(0L); a < nLoopCount; a++)
2138 : : {
2139 [ + - ]: 39583 : const B2DPoint aNextPoint(aCandidate.getB2DPoint((a + 1L) % nPointCount));
2140 : :
2141 [ + - ][ + + ]: 39583 : if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
2142 : : {
2143 : 3713 : return true;
2144 : : }
2145 : :
2146 [ + + ]: 75453 : aCurrentPoint = aNextPoint;
2147 [ + + ]: 45759 : }
2148 : : }
2149 [ # # ][ # # ]: 0 : else if(nPointCount && bWithPoints)
2150 : : {
2151 [ # # ]: 0 : return rPoint.equal(aCandidate.getB2DPoint(0L));
2152 : : }
2153 : :
2154 [ + - ]: 6176 : return false;
2155 : : }
2156 : :
2157 : 0 : bool isPointInTriangle(const B2DPoint& rA, const B2DPoint& rB, const B2DPoint& rC, const B2DPoint& rCandidate, bool bWithBorder)
2158 : : {
2159 [ # # ]: 0 : if(arePointsOnSameSideOfLine(rA, rB, rC, rCandidate, bWithBorder))
2160 : : {
2161 [ # # ]: 0 : if(arePointsOnSameSideOfLine(rB, rC, rA, rCandidate, bWithBorder))
2162 : : {
2163 [ # # ]: 0 : if(arePointsOnSameSideOfLine(rC, rA, rB, rCandidate, bWithBorder))
2164 : : {
2165 : 0 : return true;
2166 : : }
2167 : : }
2168 : : }
2169 : :
2170 : 0 : return false;
2171 : : }
2172 : :
2173 : 0 : bool arePointsOnSameSideOfLine(const B2DPoint& rStart, const B2DPoint& rEnd, const B2DPoint& rCandidateA, const B2DPoint& rCandidateB, bool bWithLine)
2174 : : {
2175 : 0 : const B2DVector aLineVector(rEnd - rStart);
2176 : 0 : const B2DVector aVectorToA(rEnd - rCandidateA);
2177 [ # # ]: 0 : const double fCrossA(aLineVector.cross(aVectorToA));
2178 : :
2179 [ # # ]: 0 : if(fTools::equalZero(fCrossA))
2180 : : {
2181 : : // one point on the line
2182 : 0 : return bWithLine;
2183 : : }
2184 : :
2185 : 0 : const B2DVector aVectorToB(rEnd - rCandidateB);
2186 [ # # ]: 0 : const double fCrossB(aLineVector.cross(aVectorToB));
2187 : :
2188 [ # # ]: 0 : if(fTools::equalZero(fCrossB))
2189 : : {
2190 : : // one point on the line
2191 : 0 : return bWithLine;
2192 : : }
2193 : :
2194 : : // return true if they both have the same sign
2195 : 0 : return ((fCrossA > 0.0) == (fCrossB > 0.0));
2196 : : }
2197 : :
2198 : 0 : void addTriangleFan(const B2DPolygon& rCandidate, B2DPolygon& rTarget)
2199 : : {
2200 : 0 : const sal_uInt32 nCount(rCandidate.count());
2201 : :
2202 [ # # ]: 0 : if(nCount > 2L)
2203 : : {
2204 [ # # ]: 0 : const B2DPoint aStart(rCandidate.getB2DPoint(0L));
2205 [ # # ]: 0 : B2DPoint aLast(rCandidate.getB2DPoint(1L));
2206 : :
2207 [ # # ]: 0 : for(sal_uInt32 a(2L); a < nCount; a++)
2208 : : {
2209 [ # # ]: 0 : const B2DPoint aCurrent(rCandidate.getB2DPoint(a));
2210 [ # # ]: 0 : rTarget.append(aStart);
2211 [ # # ]: 0 : rTarget.append(aLast);
2212 [ # # ]: 0 : rTarget.append(aCurrent);
2213 : :
2214 : : // prepare next
2215 : 0 : aLast = aCurrent;
2216 : 0 : }
2217 : : }
2218 : 0 : }
2219 : :
2220 : : namespace
2221 : : {
2222 : : /// return 0 for input of 0, -1 for negative and 1 for positive input
2223 : 270 : inline int lcl_sgn( const double n )
2224 : : {
2225 [ + + ]: 270 : return n == 0.0 ? 0 : 1 - 2*::rtl::math::isSignBitSet(n);
2226 : : }
2227 : : }
2228 : :
2229 : 50 : bool isRectangle( const B2DPolygon& rPoly )
2230 : : {
2231 : : // polygon must be closed to resemble a rect, and contain
2232 : : // at least four points.
2233 [ + + + + : 135 : if( !rPoly.isClosed() ||
+ + ][ + + ]
2234 : 45 : rPoly.count() < 4 ||
2235 : 40 : rPoly.areControlPointsUsed() )
2236 : : {
2237 : 15 : return false;
2238 : : }
2239 : :
2240 : : // number of 90 degree turns the polygon has taken
2241 : 35 : int nNumTurns(0);
2242 : :
2243 : 35 : int nVerticalEdgeType=0;
2244 : 35 : int nHorizontalEdgeType=0;
2245 : 35 : bool bNullVertex(true);
2246 : 35 : bool bCWPolygon(false); // when true, polygon is CW
2247 : : // oriented, when false, CCW
2248 : 35 : bool bOrientationSet(false); // when false, polygon
2249 : : // orientation has not yet
2250 : : // been determined.
2251 : :
2252 : : // scan all _edges_ (which involves coming back to point 0
2253 : : // for the last edge - thus the modulo operation below)
2254 : 35 : const sal_Int32 nCount( rPoly.count() );
2255 [ + + ]: 160 : for( sal_Int32 i=0; i<nCount; ++i )
2256 : : {
2257 : 135 : const B2DPoint& rPoint0( rPoly.getB2DPoint(i % nCount) );
2258 [ + - ]: 135 : const B2DPoint& rPoint1( rPoly.getB2DPoint((i+1) % nCount) );
2259 : :
2260 : : // is 0 for zero direction vector, 1 for south edge and -1
2261 : : // for north edge (standard screen coordinate system)
2262 : 135 : int nCurrVerticalEdgeType( lcl_sgn( rPoint1.getY() - rPoint0.getY() ) );
2263 : :
2264 : : // is 0 for zero direction vector, 1 for east edge and -1
2265 : : // for west edge (standard screen coordinate system)
2266 : 135 : int nCurrHorizontalEdgeType( lcl_sgn(rPoint1.getX() - rPoint0.getX()) );
2267 : :
2268 [ + + ][ + + ]: 135 : if( nCurrVerticalEdgeType && nCurrHorizontalEdgeType )
2269 : 5 : return false; // oblique edge - for sure no rect
2270 : :
2271 [ + + ][ - + ]: 130 : const bool bCurrNullVertex( !nCurrVerticalEdgeType && !nCurrHorizontalEdgeType );
2272 : :
2273 : : // current vertex is equal to previous - just skip,
2274 : : // until we have a real edge
2275 [ - + ]: 130 : if( bCurrNullVertex )
2276 : 0 : continue;
2277 : :
2278 : : // if previous edge has two identical points, because
2279 : : // no previous edge direction was available, simply
2280 : : // take this first non-null edge as the start
2281 : : // direction. That's what will happen here, if
2282 : : // bNullVertex is false
2283 [ + + ]: 130 : if( !bNullVertex )
2284 : : {
2285 : : // 2D cross product - is 1 for CW and -1 for CCW turns
2286 : : const int nCrossProduct( nHorizontalEdgeType*nCurrVerticalEdgeType -
2287 : 100 : nVerticalEdgeType*nCurrHorizontalEdgeType );
2288 : :
2289 [ + + ]: 100 : if( !nCrossProduct )
2290 : 5 : continue; // no change in orientation -
2291 : : // collinear edges - just go on
2292 : :
2293 : : // if polygon orientation is not set, we'll
2294 : : // determine it now
2295 [ + + ]: 95 : if( !bOrientationSet )
2296 : : {
2297 : 30 : bCWPolygon = nCrossProduct == 1;
2298 : 30 : bOrientationSet = true;
2299 : : }
2300 : : else
2301 : : {
2302 : : // if current turn orientation is not equal
2303 : : // initial orientation, this is not a
2304 : : // rectangle (as rectangles have consistent
2305 : : // orientation).
2306 [ + + ]: 65 : if( (nCrossProduct == 1) != bCWPolygon )
2307 : 5 : return false;
2308 : : }
2309 : :
2310 : 90 : ++nNumTurns;
2311 : :
2312 : : // More than four 90 degree turns are an
2313 : : // indication that this must not be a rectangle.
2314 [ - + ]: 90 : if( nNumTurns > 4 )
2315 : 0 : return false;
2316 : : }
2317 : :
2318 : : // store current state for the next turn
2319 : 120 : nVerticalEdgeType = nCurrVerticalEdgeType;
2320 : 120 : nHorizontalEdgeType = nCurrHorizontalEdgeType;
2321 [ + + + ]: 255 : bNullVertex = false; // won't reach this line,
2322 : : // if bCurrNullVertex is
2323 : : // true - see above
2324 [ + + ]: 270 : }
2325 : :
2326 : 50 : return true;
2327 : : }
2328 : :
2329 : 12740 : B3DPolygon createB3DPolygonFromB2DPolygon(const B2DPolygon& rCandidate, double fZCoordinate)
2330 : : {
2331 [ - + ]: 12740 : if(rCandidate.areControlPointsUsed())
2332 : : {
2333 : : // call myself recursively with subdivided input
2334 [ # # ]: 0 : const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
2335 [ # # ][ # # ]: 0 : return createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate);
2336 : : }
2337 : : else
2338 : : {
2339 [ + - ]: 12740 : B3DPolygon aRetval;
2340 : :
2341 [ + - ][ + + ]: 56224 : for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
2342 : : {
2343 [ + - ]: 43484 : B2DPoint aPoint(rCandidate.getB2DPoint(a));
2344 [ + - ]: 43484 : aRetval.append(B3DPoint(aPoint.getX(), aPoint.getY(), fZCoordinate));
2345 : 43484 : }
2346 : :
2347 : : // copy closed state
2348 [ + - ][ + - ]: 12740 : aRetval.setClosed(rCandidate.isClosed());
2349 : :
2350 [ + - ][ + - ]: 12740 : return aRetval;
2351 : : }
2352 : : }
2353 : :
2354 : 1939 : B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon& rCandidate, const B3DHomMatrix& rMat)
2355 : : {
2356 : 1939 : B2DPolygon aRetval;
2357 [ + - ]: 1939 : const sal_uInt32 nCount(rCandidate.count());
2358 [ + - ]: 1939 : const bool bIsIdentity(rMat.isIdentity());
2359 : :
2360 [ + + ]: 9695 : for(sal_uInt32 a(0L); a < nCount; a++)
2361 : : {
2362 [ + - ]: 7756 : B3DPoint aCandidate(rCandidate.getB3DPoint(a));
2363 : :
2364 [ - + ]: 7756 : if(!bIsIdentity)
2365 : : {
2366 [ # # ]: 0 : aCandidate *= rMat;
2367 : : }
2368 : :
2369 [ + - ]: 7756 : aRetval.append(B2DPoint(aCandidate.getX(), aCandidate.getY()));
2370 : 7756 : }
2371 : :
2372 : : // copy closed state
2373 [ + - ][ + - ]: 1939 : aRetval.setClosed(rCandidate.isClosed());
2374 : :
2375 : 1939 : return aRetval;
2376 : : }
2377 : :
2378 : 0 : double getSmallestDistancePointToEdge(const B2DPoint& rPointA, const B2DPoint& rPointB, const B2DPoint& rTestPoint, double& rCut)
2379 : : {
2380 [ # # ]: 0 : if(rPointA.equal(rPointB))
2381 : : {
2382 : 0 : rCut = 0.0;
2383 : 0 : const B2DVector aVector(rTestPoint - rPointA);
2384 [ # # ]: 0 : return aVector.getLength();
2385 : : }
2386 : : else
2387 : : {
2388 : : // get the relative cut value on line vector (Vector1) for cut with perpendicular through TestPoint
2389 : 0 : const B2DVector aVector1(rPointB - rPointA);
2390 : 0 : const B2DVector aVector2(rTestPoint - rPointA);
2391 : 0 : const double fDividend((aVector2.getX() * aVector1.getX()) + (aVector2.getY() * aVector1.getY()));
2392 : 0 : const double fDivisor((aVector1.getX() * aVector1.getX()) + (aVector1.getY() * aVector1.getY()));
2393 : 0 : const double fCut(fDividend / fDivisor);
2394 : :
2395 [ # # ]: 0 : if(fCut < 0.0)
2396 : : {
2397 : : // not in line range, get distance to PointA
2398 : 0 : rCut = 0.0;
2399 [ # # ]: 0 : return aVector2.getLength();
2400 : : }
2401 [ # # ]: 0 : else if(fCut > 1.0)
2402 : : {
2403 : : // not in line range, get distance to PointB
2404 : 0 : rCut = 1.0;
2405 : 0 : const B2DVector aVector(rTestPoint - rPointB);
2406 [ # # ]: 0 : return aVector.getLength();
2407 : : }
2408 : : else
2409 : : {
2410 : : // in line range
2411 : 0 : const B2DPoint aCutPoint(rPointA + fCut * aVector1);
2412 : 0 : const B2DVector aVector(rTestPoint - aCutPoint);
2413 : 0 : rCut = fCut;
2414 [ # # ]: 0 : return aVector.getLength();
2415 : 0 : }
2416 : : }
2417 : : }
2418 : :
2419 : 0 : double getSmallestDistancePointToPolygon(const B2DPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rEdgeIndex, double& rCut)
2420 : : {
2421 : 0 : double fRetval(DBL_MAX);
2422 [ # # ]: 0 : const sal_uInt32 nPointCount(rCandidate.count());
2423 : :
2424 [ # # ]: 0 : if(nPointCount > 1L)
2425 : : {
2426 : 0 : const double fZero(0.0);
2427 [ # # ][ # # ]: 0 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2428 [ # # ]: 0 : B2DCubicBezier aBezier;
2429 [ # # ]: 0 : aBezier.setStartPoint(rCandidate.getB2DPoint(0));
2430 : :
2431 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nEdgeCount; a++)
2432 : : {
2433 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
2434 [ # # ]: 0 : aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
2435 : : double fEdgeDist;
2436 : : double fNewCut;
2437 : 0 : bool bEdgeIsCurve(false);
2438 : :
2439 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed())
2440 : : {
2441 [ # # ]: 0 : aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
2442 [ # # ]: 0 : aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
2443 [ # # ]: 0 : aBezier.testAndSolveTrivialBezier();
2444 [ # # ]: 0 : bEdgeIsCurve = aBezier.isBezier();
2445 : : }
2446 : :
2447 [ # # ]: 0 : if(bEdgeIsCurve)
2448 : : {
2449 [ # # ]: 0 : fEdgeDist = aBezier.getSmallestDistancePointToBezierSegment(rTestPoint, fNewCut);
2450 : : }
2451 : : else
2452 : : {
2453 [ # # ]: 0 : fEdgeDist = getSmallestDistancePointToEdge(aBezier.getStartPoint(), aBezier.getEndPoint(), rTestPoint, fNewCut);
2454 : : }
2455 : :
2456 [ # # ][ # # ]: 0 : if(DBL_MAX == fRetval || fEdgeDist < fRetval)
2457 : : {
2458 : 0 : fRetval = fEdgeDist;
2459 : 0 : rEdgeIndex = a;
2460 : 0 : rCut = fNewCut;
2461 : :
2462 [ # # ]: 0 : if(fTools::equal(fRetval, fZero))
2463 : : {
2464 : : // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
2465 : 0 : fRetval = 0.0;
2466 : : break;
2467 : : }
2468 : : }
2469 : :
2470 : : // prepare next step
2471 : 0 : aBezier.setStartPoint(aBezier.getEndPoint());
2472 : : }
2473 : :
2474 [ # # ]: 0 : if(1.0 == rCut)
2475 : : {
2476 : : // correct rEdgeIndex when not last point
2477 [ # # ][ # # ]: 0 : if(rCandidate.isClosed())
2478 : : {
2479 [ # # ]: 0 : rEdgeIndex = getIndexOfSuccessor(rEdgeIndex, rCandidate);
2480 : 0 : rCut = 0.0;
2481 : : }
2482 : : else
2483 : : {
2484 [ # # ]: 0 : if(rEdgeIndex != nEdgeCount - 1L)
2485 : : {
2486 : 0 : rEdgeIndex++;
2487 : 0 : rCut = 0.0;
2488 : : }
2489 : : }
2490 [ # # ]: 0 : }
2491 : : }
2492 : :
2493 : 0 : return fRetval;
2494 : : }
2495 : :
2496 : 0 : B2DPoint distort(const B2DPoint& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
2497 : : {
2498 [ # # ][ # # ]: 0 : if(fTools::equalZero(rOriginal.getWidth()) || fTools::equalZero(rOriginal.getHeight()))
[ # # ][ # # ]
[ # # ][ # # ]
[ # # # #
# # ]
2499 : : {
2500 : 0 : return rCandidate;
2501 : : }
2502 : : else
2503 : : {
2504 : 0 : const double fRelativeX((rCandidate.getX() - rOriginal.getMinX()) / rOriginal.getWidth());
2505 : 0 : const double fRelativeY((rCandidate.getY() - rOriginal.getMinY()) / rOriginal.getHeight());
2506 : 0 : const double fOneMinusRelativeX(1.0 - fRelativeX);
2507 : 0 : const double fOneMinusRelativeY(1.0 - fRelativeY);
2508 : 0 : const double fNewX((fOneMinusRelativeY) * ((fOneMinusRelativeX) * rTopLeft.getX() + fRelativeX * rTopRight.getX()) +
2509 : 0 : fRelativeY * ((fOneMinusRelativeX) * rBottomLeft.getX() + fRelativeX * rBottomRight.getX()));
2510 : 0 : const double fNewY((fOneMinusRelativeX) * ((fOneMinusRelativeY) * rTopLeft.getY() + fRelativeY * rBottomLeft.getY()) +
2511 : 0 : fRelativeX * ((fOneMinusRelativeY) * rTopRight.getY() + fRelativeY * rBottomRight.getY()));
2512 : :
2513 : 0 : return B2DPoint(fNewX, fNewY);
2514 : : }
2515 : : }
2516 : :
2517 : 0 : B2DPolygon distort(const B2DPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
2518 : : {
2519 : 0 : const sal_uInt32 nPointCount(rCandidate.count());
2520 : :
2521 [ # # ][ # # ]: 0 : if(nPointCount && 0.0 != rOriginal.getWidth() && 0.0 != rOriginal.getHeight())
[ # # ][ # # ]
2522 : : {
2523 [ # # ]: 0 : B2DPolygon aRetval;
2524 : :
2525 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nPointCount; a++)
2526 : : {
2527 [ # # ][ # # ]: 0 : aRetval.append(distort(rCandidate.getB2DPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
[ # # ]
2528 : :
2529 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed())
2530 : : {
2531 [ # # ][ # # ]: 0 : if(!rCandidate.getPrevControlPoint(a).equalZero())
[ # # ]
2532 : : {
2533 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(a, distort(rCandidate.getPrevControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
[ # # ]
2534 : : }
2535 : :
2536 [ # # ][ # # ]: 0 : if(!rCandidate.getNextControlPoint(a).equalZero())
[ # # ]
2537 : : {
2538 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(a, distort(rCandidate.getNextControlPoint(a), rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
[ # # ]
2539 : : }
2540 : : }
2541 : : }
2542 : :
2543 [ # # ][ # # ]: 0 : aRetval.setClosed(rCandidate.isClosed());
2544 [ # # ][ # # ]: 0 : return aRetval;
2545 : : }
2546 : : else
2547 : : {
2548 : 0 : return rCandidate;
2549 : : }
2550 : : }
2551 : :
2552 : 40 : B2DPolygon expandToCurve(const B2DPolygon& rCandidate)
2553 : : {
2554 : 40 : B2DPolygon aRetval(rCandidate);
2555 : :
2556 [ + - ][ + + ]: 278 : for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
2557 : : {
2558 [ + - ]: 238 : expandToCurveInPoint(aRetval, a);
2559 : : }
2560 : :
2561 : 40 : return aRetval;
2562 : : }
2563 : :
2564 : 238 : bool expandToCurveInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex)
2565 : : {
2566 : : OSL_ENSURE(nIndex < rCandidate.count(), "expandToCurveInPoint: Access to polygon out of range (!)");
2567 : 238 : bool bRetval(false);
2568 : 238 : const sal_uInt32 nPointCount(rCandidate.count());
2569 : :
2570 [ + - ]: 238 : if(nPointCount)
2571 : : {
2572 : : // predecessor
2573 [ + + ]: 238 : if(!rCandidate.isPrevControlPointUsed(nIndex))
2574 : : {
2575 [ + + ][ + + ]: 94 : if(!rCandidate.isClosed() && 0 == nIndex)
[ + + ]
2576 : : {
2577 : : // do not create previous vector for start point of open polygon
2578 : : }
2579 : : else
2580 : : {
2581 : 88 : const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2582 [ + - ][ + - ]: 88 : rCandidate.setPrevControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
2583 : 88 : bRetval = true;
2584 : : }
2585 : : }
2586 : :
2587 : : // successor
2588 [ + + ]: 238 : if(!rCandidate.isNextControlPointUsed(nIndex))
2589 : : {
2590 [ + + ][ + + ]: 94 : if(!rCandidate.isClosed() && nIndex + 1 == nPointCount)
[ + + ]
2591 : : {
2592 : : // do not create next vector for end point of open polygon
2593 : : }
2594 : : else
2595 : : {
2596 : 88 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2597 [ + - ][ + - ]: 88 : rCandidate.setNextControlPoint(nIndex, interpolate(rCandidate.getB2DPoint(nIndex), rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
2598 : 88 : bRetval = true;
2599 : : }
2600 : : }
2601 : : }
2602 : :
2603 : 238 : return bRetval;
2604 : : }
2605 : :
2606 : 0 : bool setContinuityInPoint(B2DPolygon& rCandidate, sal_uInt32 nIndex, B2VectorContinuity eContinuity)
2607 : : {
2608 : : OSL_ENSURE(nIndex < rCandidate.count(), "setContinuityInPoint: Access to polygon out of range (!)");
2609 : 0 : bool bRetval(false);
2610 : 0 : const sal_uInt32 nPointCount(rCandidate.count());
2611 : :
2612 [ # # ]: 0 : if(nPointCount)
2613 : : {
2614 [ # # ]: 0 : const B2DPoint aCurrentPoint(rCandidate.getB2DPoint(nIndex));
2615 : :
2616 [ # # # # ]: 0 : switch(eContinuity)
2617 : : {
2618 : : case CONTINUITY_NONE :
2619 : : {
2620 [ # # ][ # # ]: 0 : if(rCandidate.isPrevControlPointUsed(nIndex))
2621 : : {
2622 [ # # ][ # # ]: 0 : if(!rCandidate.isClosed() && 0 == nIndex)
[ # # ][ # # ]
2623 : : {
2624 : : // remove existing previous vector for start point of open polygon
2625 [ # # ]: 0 : rCandidate.resetPrevControlPoint(nIndex);
2626 : : }
2627 : : else
2628 : : {
2629 : 0 : const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2630 [ # # ][ # # ]: 0 : rCandidate.setPrevControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nPrevIndex), 1.0 / 3.0));
2631 : : }
2632 : :
2633 : 0 : bRetval = true;
2634 : : }
2635 : :
2636 [ # # ][ # # ]: 0 : if(rCandidate.isNextControlPointUsed(nIndex))
2637 : : {
2638 [ # # ][ # # ]: 0 : if(!rCandidate.isClosed() && nIndex == nPointCount + 1)
[ # # ][ # # ]
2639 : : {
2640 : : // remove next vector for end point of open polygon
2641 [ # # ]: 0 : rCandidate.resetNextControlPoint(nIndex);
2642 : : }
2643 : : else
2644 : : {
2645 : 0 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2646 [ # # ][ # # ]: 0 : rCandidate.setNextControlPoint(nIndex, interpolate(aCurrentPoint, rCandidate.getB2DPoint(nNextIndex), 1.0 / 3.0));
2647 : : }
2648 : :
2649 : 0 : bRetval = true;
2650 : : }
2651 : :
2652 : 0 : break;
2653 : : }
2654 : : case CONTINUITY_C1 :
2655 : : {
2656 [ # # ][ # # ]: 0 : if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
[ # # ][ # # ]
[ # # ]
2657 : : {
2658 : : // lengths both exist since both are used
2659 [ # # ]: 0 : B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
2660 [ # # ]: 0 : B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
2661 [ # # ]: 0 : const double fLenPrev(aVectorPrev.getLength());
2662 [ # # ]: 0 : const double fLenNext(aVectorNext.getLength());
2663 [ # # ]: 0 : aVectorPrev.normalize();
2664 [ # # ]: 0 : aVectorNext.normalize();
2665 [ # # ]: 0 : const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
2666 : :
2667 [ # # ][ # # ]: 0 : if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
[ # # ][ # # ]
2668 : : {
2669 : : // parallel and opposite direction; check length
2670 [ # # ]: 0 : if(fTools::equal(fLenPrev, fLenNext))
2671 : : {
2672 : : // this would be even C2, but we want C1. Use the lengths of the corresponding edges.
2673 : 0 : const sal_uInt32 nPrevIndex((nIndex + (nPointCount - 1)) % nPointCount);
2674 : 0 : const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
2675 [ # # ][ # # ]: 0 : const double fLenPrevEdge(B2DVector(rCandidate.getB2DPoint(nPrevIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
2676 [ # # ][ # # ]: 0 : const double fLenNextEdge(B2DVector(rCandidate.getB2DPoint(nNextIndex) - aCurrentPoint).getLength() * (1.0 / 3.0));
2677 : :
2678 : : rCandidate.setControlPoints(nIndex,
2679 : : aCurrentPoint + (aVectorPrev * fLenPrevEdge),
2680 [ # # ]: 0 : aCurrentPoint + (aVectorNext * fLenNextEdge));
2681 : 0 : bRetval = true;
2682 : : }
2683 : : }
2684 : : else
2685 : : {
2686 : : // not parallel or same direction, set vectors and length
2687 [ # # ]: 0 : const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
2688 : :
2689 [ # # ]: 0 : if(ORIENTATION_POSITIVE == aOrientation)
2690 : : {
2691 : : rCandidate.setControlPoints(nIndex,
2692 : : aCurrentPoint - (aNormalizedPerpendicular * fLenPrev),
2693 [ # # ]: 0 : aCurrentPoint + (aNormalizedPerpendicular * fLenNext));
2694 : : }
2695 : : else
2696 : : {
2697 : : rCandidate.setControlPoints(nIndex,
2698 : : aCurrentPoint + (aNormalizedPerpendicular * fLenPrev),
2699 [ # # ]: 0 : aCurrentPoint - (aNormalizedPerpendicular * fLenNext));
2700 : : }
2701 : :
2702 : 0 : bRetval = true;
2703 : 0 : }
2704 : : }
2705 : 0 : break;
2706 : : }
2707 : : case CONTINUITY_C2 :
2708 : : {
2709 [ # # ][ # # ]: 0 : if(rCandidate.isPrevControlPointUsed(nIndex) && rCandidate.isNextControlPointUsed(nIndex))
[ # # ][ # # ]
[ # # ]
2710 : : {
2711 : : // lengths both exist since both are used
2712 [ # # ]: 0 : B2DVector aVectorPrev(rCandidate.getPrevControlPoint(nIndex) - aCurrentPoint);
2713 [ # # ]: 0 : B2DVector aVectorNext(rCandidate.getNextControlPoint(nIndex) - aCurrentPoint);
2714 [ # # ][ # # ]: 0 : const double fCommonLength((aVectorPrev.getLength() + aVectorNext.getLength()) / 2.0);
2715 [ # # ]: 0 : aVectorPrev.normalize();
2716 [ # # ]: 0 : aVectorNext.normalize();
2717 [ # # ]: 0 : const B2VectorOrientation aOrientation(getOrientation(aVectorPrev, aVectorNext));
2718 : :
2719 [ # # ][ # # ]: 0 : if(ORIENTATION_NEUTRAL == aOrientation && aVectorPrev.scalar(aVectorNext) < 0.0)
[ # # ][ # # ]
2720 : : {
2721 : : // parallel and opposite direction; set length. Use one direction for better numerical correctness
2722 : 0 : const B2DVector aScaledDirection(aVectorPrev * fCommonLength);
2723 : :
2724 : : rCandidate.setControlPoints(nIndex,
2725 : : aCurrentPoint + aScaledDirection,
2726 [ # # ]: 0 : aCurrentPoint - aScaledDirection);
2727 : : }
2728 : : else
2729 : : {
2730 : : // not parallel or same direction, set vectors and length
2731 [ # # ]: 0 : const B2DVector aNormalizedPerpendicular(getNormalizedPerpendicular(aVectorPrev + aVectorNext));
2732 : 0 : const B2DVector aPerpendicular(aNormalizedPerpendicular * fCommonLength);
2733 : :
2734 [ # # ]: 0 : if(ORIENTATION_POSITIVE == aOrientation)
2735 : : {
2736 : : rCandidate.setControlPoints(nIndex,
2737 : : aCurrentPoint - aPerpendicular,
2738 [ # # ]: 0 : aCurrentPoint + aPerpendicular);
2739 : : }
2740 : : else
2741 : : {
2742 : : rCandidate.setControlPoints(nIndex,
2743 : : aCurrentPoint + aPerpendicular,
2744 [ # # ]: 0 : aCurrentPoint - aPerpendicular);
2745 : 0 : }
2746 : : }
2747 : :
2748 : 0 : bRetval = true;
2749 : : }
2750 : 0 : break;
2751 : : }
2752 : 0 : }
2753 : : }
2754 : :
2755 : 0 : return bRetval;
2756 : : }
2757 : :
2758 : 3002 : B2DPolygon growInNormalDirection(const B2DPolygon& rCandidate, double fValue)
2759 : : {
2760 [ + - ]: 3002 : if(0.0 != fValue)
2761 : : {
2762 [ - + ]: 3002 : if(rCandidate.areControlPointsUsed())
2763 : : {
2764 : : // call myself recursively with subdivided input
2765 [ # # ]: 0 : const B2DPolygon aCandidate(adaptiveSubdivideByAngle(rCandidate));
2766 [ # # ][ # # ]: 0 : return growInNormalDirection(aCandidate, fValue);
2767 : : }
2768 : : else
2769 : : {
2770 [ + - ]: 3002 : B2DPolygon aRetval;
2771 [ + - ]: 3002 : const sal_uInt32 nPointCount(rCandidate.count());
2772 : :
2773 [ + - ]: 3002 : if(nPointCount)
2774 : : {
2775 [ + - ]: 3002 : B2DPoint aPrev(rCandidate.getB2DPoint(nPointCount - 1L));
2776 [ + - ]: 3002 : B2DPoint aCurrent(rCandidate.getB2DPoint(0L));
2777 : :
2778 [ + + ]: 12008 : for(sal_uInt32 a(0L); a < nPointCount; a++)
2779 : : {
2780 [ + + ][ + - ]: 9006 : const B2DPoint aNext(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
2781 : 9006 : const B2DVector aBack(aPrev - aCurrent);
2782 : 9006 : const B2DVector aForw(aNext - aCurrent);
2783 [ + - ]: 9006 : const B2DVector aPerpBack(getNormalizedPerpendicular(aBack));
2784 [ + - ]: 9006 : const B2DVector aPerpForw(getNormalizedPerpendicular(aForw));
2785 : 9006 : B2DVector aDirection(aPerpBack - aPerpForw);
2786 [ + - ]: 9006 : aDirection.normalize();
2787 : 9006 : aDirection *= fValue;
2788 [ + - ]: 9006 : aRetval.append(aCurrent + aDirection);
2789 : :
2790 : : // prepare next step
2791 : 9006 : aPrev = aCurrent;
2792 : 9006 : aCurrent = aNext;
2793 : 12008 : }
2794 : : }
2795 : :
2796 : : // copy closed state
2797 [ + - ][ + - ]: 3002 : aRetval.setClosed(rCandidate.isClosed());
2798 : :
2799 [ + - ][ + - ]: 3002 : return aRetval;
2800 : : }
2801 : : }
2802 : : else
2803 : : {
2804 : 3002 : return rCandidate;
2805 : : }
2806 : : }
2807 : :
2808 : 0 : B2DPolygon reSegmentPolygon(const B2DPolygon& rCandidate, sal_uInt32 nSegments)
2809 : : {
2810 : 0 : B2DPolygon aRetval;
2811 [ # # ]: 0 : const sal_uInt32 nPointCount(rCandidate.count());
2812 : :
2813 [ # # ][ # # ]: 0 : if(nPointCount && nSegments)
2814 : : {
2815 : : // get current segment count
2816 [ # # ][ # # ]: 0 : const sal_uInt32 nSegmentCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
2817 : :
2818 [ # # ]: 0 : if(nSegmentCount == nSegments)
2819 : : {
2820 [ # # ]: 0 : aRetval = rCandidate;
2821 : : }
2822 : : else
2823 : : {
2824 [ # # ]: 0 : const double fLength(getLength(rCandidate));
2825 [ # # ][ # # ]: 0 : const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nSegments : nSegments + 1L);
2826 : :
2827 [ # # ]: 0 : for(sal_uInt32 a(0L); a < nLoopCount; a++)
2828 : : {
2829 : 0 : const double fRelativePos((double)a / (double)nSegments); // 0.0 .. 1.0
2830 [ # # ]: 0 : const B2DPoint aNewPoint(getPositionRelative(rCandidate, fRelativePos, fLength));
2831 [ # # ]: 0 : aRetval.append(aNewPoint);
2832 : 0 : }
2833 : :
2834 : : // copy closed flag
2835 [ # # ][ # # ]: 0 : aRetval.setClosed(rCandidate.isClosed());
2836 : : }
2837 : : }
2838 : :
2839 : 0 : return aRetval;
2840 : : }
2841 : :
2842 : 0 : B2DPolygon interpolate(const B2DPolygon& rOld1, const B2DPolygon& rOld2, double t)
2843 : : {
2844 : : OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolygon interpolate: Different geometry (!)");
2845 : :
2846 [ # # ][ # # ]: 0 : if(fTools::lessOrEqual(t, 0.0) || rOld1 == rOld2)
[ # # ][ # # ]
[ # # # # ]
2847 : : {
2848 : 0 : return rOld1;
2849 : : }
2850 [ # # ]: 0 : else if(fTools::moreOrEqual(t, 1.0))
2851 : : {
2852 : 0 : return rOld2;
2853 : : }
2854 : : else
2855 : : {
2856 [ # # ]: 0 : B2DPolygon aRetval;
2857 [ # # ][ # # ]: 0 : const bool bInterpolateVectors(rOld1.areControlPointsUsed() || rOld2.areControlPointsUsed());
[ # # ][ # # ]
2858 [ # # ][ # # ]: 0 : aRetval.setClosed(rOld1.isClosed() && rOld2.isClosed());
[ # # ][ # # ]
[ # # ]
2859 : :
2860 [ # # ][ # # ]: 0 : for(sal_uInt32 a(0L); a < rOld1.count(); a++)
2861 : : {
2862 [ # # ][ # # ]: 0 : aRetval.append(interpolate(rOld1.getB2DPoint(a), rOld2.getB2DPoint(a), t));
[ # # ]
2863 : :
2864 [ # # ]: 0 : if(bInterpolateVectors)
2865 : : {
2866 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(a, interpolate(rOld1.getPrevControlPoint(a), rOld2.getPrevControlPoint(a), t));
[ # # ]
2867 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(a, interpolate(rOld1.getNextControlPoint(a), rOld2.getNextControlPoint(a), t));
[ # # ]
2868 : : }
2869 : : }
2870 : :
2871 [ # # ][ # # ]: 0 : return aRetval;
2872 : : }
2873 : : }
2874 : :
2875 : : // #i76891#
2876 : 20838 : B2DPolygon simplifyCurveSegments(const B2DPolygon& rCandidate)
2877 : : {
2878 : 20838 : const sal_uInt32 nPointCount(rCandidate.count());
2879 : :
2880 [ + + ][ + + ]: 20838 : if(nPointCount && rCandidate.areControlPointsUsed())
[ + - ]
2881 : : {
2882 : : // prepare loop
2883 [ + - ][ + + ]: 180 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
2884 [ + - ]: 180 : B2DPolygon aRetval;
2885 [ + - ]: 180 : B2DCubicBezier aBezier;
2886 [ + - ]: 180 : aBezier.setStartPoint(rCandidate.getB2DPoint(0));
2887 : :
2888 : : // try to avoid costly reallocations
2889 [ + - ]: 180 : aRetval.reserve( nEdgeCount+1);
2890 : :
2891 : : // add start point
2892 [ + - ]: 180 : aRetval.append(aBezier.getStartPoint());
2893 : :
2894 [ + + ]: 1248 : for(sal_uInt32 a(0L); a < nEdgeCount; a++)
2895 : : {
2896 : : // get values for edge
2897 : 1068 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
2898 [ + - ]: 1068 : aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
2899 [ + - ]: 1068 : aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
2900 [ + - ]: 1068 : aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
2901 [ + - ]: 1068 : aBezier.testAndSolveTrivialBezier();
2902 : :
2903 : : // still bezier?
2904 [ + - ][ + + ]: 1068 : if(aBezier.isBezier())
2905 : : {
2906 : : // add edge with control vectors
2907 [ + - ]: 860 : aRetval.appendBezierSegment(aBezier.getControlPointA(), aBezier.getControlPointB(), aBezier.getEndPoint());
2908 : : }
2909 : : else
2910 : : {
2911 : : // add edge
2912 [ + - ]: 208 : aRetval.append(aBezier.getEndPoint());
2913 : : }
2914 : :
2915 : : // next point
2916 : 1068 : aBezier.setStartPoint(aBezier.getEndPoint());
2917 : : }
2918 : :
2919 [ + - ][ + + ]: 180 : if(rCandidate.isClosed())
2920 : : {
2921 : : // set closed flag, rescue control point and correct last double point
2922 [ + - ]: 96 : closeWithGeometryChange(aRetval);
2923 : : }
2924 : :
2925 [ + - ][ + - ]: 180 : return aRetval;
[ + - ]
2926 : : }
2927 : : else
2928 : : {
2929 : 20838 : return rCandidate;
2930 : : }
2931 : : }
2932 : :
2933 : : // makes the given indexed point the new polygon start point. To do that, the points in the
2934 : : // polygon will be rotated. This is only valid for closed polygons, for non-closed ones
2935 : : // an assertion will be triggered
2936 : 0 : B2DPolygon makeStartPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndexOfNewStatPoint)
2937 : : {
2938 : 0 : const sal_uInt32 nPointCount(rCandidate.count());
2939 : :
2940 [ # # ][ # # ]: 0 : if(nPointCount > 2 && nIndexOfNewStatPoint != 0 && nIndexOfNewStatPoint < nPointCount)
[ # # ]
2941 : : {
2942 : : OSL_ENSURE(rCandidate.isClosed(), "makeStartPoint: only valid for closed polygons (!)");
2943 [ # # ]: 0 : B2DPolygon aRetval;
2944 : :
2945 [ # # ]: 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
2946 : : {
2947 : 0 : const sal_uInt32 nSourceIndex((a + nIndexOfNewStatPoint) % nPointCount);
2948 [ # # ][ # # ]: 0 : aRetval.append(rCandidate.getB2DPoint(nSourceIndex));
2949 : :
2950 [ # # ][ # # ]: 0 : if(rCandidate.areControlPointsUsed())
2951 : : {
2952 [ # # ][ # # ]: 0 : aRetval.setPrevControlPoint(a, rCandidate.getPrevControlPoint(nSourceIndex));
2953 [ # # ][ # # ]: 0 : aRetval.setNextControlPoint(a, rCandidate.getNextControlPoint(nSourceIndex));
2954 : : }
2955 : : }
2956 : :
2957 [ # # ][ # # ]: 0 : return aRetval;
2958 : : }
2959 : :
2960 : 0 : return rCandidate;
2961 : : }
2962 : :
2963 : 0 : B2DPolygon createEdgesOfGivenLength(const B2DPolygon& rCandidate, double fLength, double fStart, double fEnd)
2964 : : {
2965 : 0 : B2DPolygon aRetval;
2966 : :
2967 [ # # ]: 0 : if(fLength < 0.0)
2968 : : {
2969 : 0 : fLength = 0.0;
2970 : : }
2971 : :
2972 [ # # ]: 0 : if(!fTools::equalZero(fLength))
2973 : : {
2974 [ # # ]: 0 : if(fStart < 0.0)
2975 : : {
2976 : 0 : fStart = 0.0;
2977 : : }
2978 : :
2979 [ # # ]: 0 : if(fEnd < 0.0)
2980 : : {
2981 : 0 : fEnd = 0.0;
2982 : : }
2983 : :
2984 [ # # ]: 0 : if(fEnd < fStart)
2985 : : {
2986 : 0 : fEnd = fStart;
2987 : : }
2988 : :
2989 : : // iterate and consume pieces with fLength. First subdivide to reduce input to line segments
2990 [ # # ][ # # ]: 0 : const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
[ # # ][ # # ]
2991 [ # # ]: 0 : const sal_uInt32 nPointCount(aCandidate.count());
2992 : :
2993 [ # # ]: 0 : if(nPointCount > 1)
2994 : : {
2995 : 0 : const bool bEndActive(!fTools::equalZero(fEnd));
2996 [ # # ][ # # ]: 0 : const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1);
2997 [ # # ]: 0 : B2DPoint aCurrent(aCandidate.getB2DPoint(0));
2998 : 0 : double fPositionInEdge(fStart);
2999 : 0 : double fAbsolutePosition(fStart);
3000 : :
3001 [ # # ]: 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
3002 : : {
3003 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
3004 [ # # ]: 0 : const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));
3005 : 0 : const B2DVector aEdge(aNext - aCurrent);
3006 [ # # ]: 0 : double fEdgeLength(aEdge.getLength());
3007 : :
3008 [ # # ]: 0 : if(!fTools::equalZero(fEdgeLength))
3009 : : {
3010 [ # # ]: 0 : while(fTools::less(fPositionInEdge, fEdgeLength))
3011 : : {
3012 : : // move position on edge forward as long as on edge
3013 : 0 : const double fScalar(fPositionInEdge / fEdgeLength);
3014 [ # # ]: 0 : aRetval.append(aCurrent + (aEdge * fScalar));
3015 : 0 : fPositionInEdge += fLength;
3016 : :
3017 [ # # ]: 0 : if(bEndActive)
3018 : : {
3019 : 0 : fAbsolutePosition += fLength;
3020 : :
3021 [ # # ]: 0 : if(fTools::more(fAbsolutePosition, fEnd))
3022 : : {
3023 : 0 : break;
3024 : : }
3025 : : }
3026 : : }
3027 : :
3028 : : // substract length of current edge
3029 : 0 : fPositionInEdge -= fEdgeLength;
3030 : : }
3031 : :
3032 [ # # ][ # # ]: 0 : if(bEndActive && fTools::more(fAbsolutePosition, fEnd))
[ # # ]
3033 : : {
3034 : : break;
3035 : : }
3036 : :
3037 : : // prepare next step
3038 [ # # ]: 0 : aCurrent = aNext;
3039 [ # # ]: 0 : }
3040 : :
3041 : : // keep closed state
3042 [ # # ][ # # ]: 0 : aRetval.setClosed(aCandidate.isClosed());
3043 : : }
3044 : : else
3045 : : {
3046 : : // source polygon has only one point, return unchanged
3047 [ # # ]: 0 : aRetval = aCandidate;
3048 [ # # ]: 0 : }
3049 : : }
3050 : :
3051 : 0 : return aRetval;
3052 : : }
3053 : :
3054 : 0 : B2DPolygon createWaveline(const B2DPolygon& rCandidate, double fWaveWidth, double fWaveHeight)
3055 : : {
3056 : 0 : B2DPolygon aRetval;
3057 : :
3058 [ # # ]: 0 : if(fWaveWidth < 0.0)
3059 : : {
3060 : 0 : fWaveWidth = 0.0;
3061 : : }
3062 : :
3063 [ # # ]: 0 : if(fWaveHeight < 0.0)
3064 : : {
3065 : 0 : fWaveHeight = 0.0;
3066 : : }
3067 : :
3068 : 0 : const bool bHasWidth(!fTools::equalZero(fWaveWidth));
3069 : :
3070 [ # # ]: 0 : if(bHasWidth)
3071 : : {
3072 : 0 : const bool bHasHeight(!fTools::equalZero(fWaveHeight));
3073 [ # # ]: 0 : if(bHasHeight)
3074 : : {
3075 : : // width and height, create waveline. First subdivide to reduce input to line segments
3076 : : // of WaveWidth. Last segment may be missing. If this turns out to be a problem, it
3077 : : // may be added here again using the original last point from rCandidate. It may
3078 : : // also be the case that rCandidate was closed. To simplify things it is handled here
3079 : : // as if it was opened.
3080 : : // Result from createEdgesOfGivenLength contains no curved segments, handle as straight
3081 : : // edges.
3082 [ # # ]: 0 : const B2DPolygon aEqualLenghEdges(createEdgesOfGivenLength(rCandidate, fWaveWidth));
3083 [ # # ]: 0 : const sal_uInt32 nPointCount(aEqualLenghEdges.count());
3084 : :
3085 [ # # ]: 0 : if(nPointCount > 1)
3086 : : {
3087 : : // iterate over straight edges, add start point
3088 [ # # ]: 0 : B2DPoint aCurrent(aEqualLenghEdges.getB2DPoint(0));
3089 [ # # ]: 0 : aRetval.append(aCurrent);
3090 : :
3091 [ # # ]: 0 : for(sal_uInt32 a(0); a < nPointCount - 1; a++)
3092 : : {
3093 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
3094 [ # # ]: 0 : const B2DPoint aNext(aEqualLenghEdges.getB2DPoint(nNextIndex));
3095 : 0 : const B2DVector aEdge(aNext - aCurrent);
3096 [ # # ]: 0 : const B2DVector aPerpendicular(getNormalizedPerpendicular(aEdge));
3097 : 0 : const B2DVector aControlOffset((aEdge * 0.467308) - (aPerpendicular * fWaveHeight));
3098 : :
3099 : : // add curve segment
3100 : : aRetval.appendBezierSegment(
3101 : : aCurrent + aControlOffset,
3102 : : aNext - aControlOffset,
3103 [ # # ]: 0 : aNext);
3104 : :
3105 : : // prepare next step
3106 : 0 : aCurrent = aNext;
3107 : 0 : }
3108 [ # # ]: 0 : }
3109 : : }
3110 : : else
3111 : : {
3112 : : // width but no height -> return original polygon
3113 [ # # ]: 0 : aRetval = rCandidate;
3114 : : }
3115 : : }
3116 : : else
3117 : : {
3118 : : // no width -> no waveline, stay empty and return
3119 : : }
3120 : :
3121 : 0 : return aRetval;
3122 : : }
3123 : :
3124 : : // snap points of horizontal or vertical edges to discrete values
3125 : 0 : B2DPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolygon& rCandidate)
3126 : : {
3127 : 0 : const sal_uInt32 nPointCount(rCandidate.count());
3128 : :
3129 [ # # ]: 0 : if(nPointCount > 1)
3130 : : {
3131 : : // Start by copying the source polygon to get a writeable copy. The closed state is
3132 : : // copied by aRetval's initialisation, too, so no need to copy it in this method
3133 [ # # ]: 0 : B2DPolygon aRetval(rCandidate);
3134 : :
3135 : : // prepare geometry data. Get rounded from original
3136 [ # # ][ # # ]: 0 : B2ITuple aPrevTuple(basegfx::fround(rCandidate.getB2DPoint(nPointCount - 1)));
3137 [ # # ]: 0 : B2DPoint aCurrPoint(rCandidate.getB2DPoint(0));
3138 [ # # ]: 0 : B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
3139 : :
3140 : : // loop over all points. This will also snap the implicit closing edge
3141 : : // even when not closed, but that's no problem here
3142 [ # # ]: 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
3143 : : {
3144 : : // get next point. Get rounded from original
3145 : 0 : const bool bLastRun(a + 1 == nPointCount);
3146 [ # # ]: 0 : const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
3147 [ # # ]: 0 : const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
3148 [ # # ]: 0 : const B2ITuple aNextTuple(basegfx::fround(aNextPoint));
3149 : :
3150 : : // get the states
3151 : 0 : const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
3152 : 0 : const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
3153 : 0 : const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
3154 : 0 : const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
3155 [ # # ][ # # ]: 0 : const bool bSnapX(bPrevVertical || bNextVertical);
3156 [ # # ][ # # ]: 0 : const bool bSnapY(bPrevHorizontal || bNextHorizontal);
3157 : :
3158 [ # # ][ # # ]: 0 : if(bSnapX || bSnapY)
3159 : : {
3160 : : const B2DPoint aSnappedPoint(
3161 : 0 : bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
3162 [ # # ][ # # ]: 0 : bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
3163 : :
3164 [ # # ]: 0 : aRetval.setB2DPoint(a, aSnappedPoint);
3165 : : }
3166 : :
3167 : : // prepare next point
3168 [ # # ]: 0 : if(!bLastRun)
3169 : : {
3170 : 0 : aPrevTuple = aCurrTuple;
3171 : 0 : aCurrPoint = aNextPoint;
3172 : 0 : aCurrTuple = aNextTuple;
3173 : : }
3174 : 0 : }
3175 : :
3176 [ # # ][ # # ]: 0 : return aRetval;
3177 : : }
3178 : : else
3179 : : {
3180 : 0 : return rCandidate;
3181 : : }
3182 : : }
3183 : :
3184 : : } // end of namespace tools
3185 : : } // end of namespace basegfx
3186 : :
3187 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|