merged tag ooo/OOO330_m14
[LibreOffice.git] / basegfx / source / polygon / b2dpolygoncutandtouch.cxx
blobe03aadfe1577c240d5b0994de88cdeb48d14f27c
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
31 #include <osl/diagnose.h>
32 #include <basegfx/numeric/ftools.hxx>
33 #include <basegfx/point/b2dpoint.hxx>
34 #include <basegfx/vector/b2dvector.hxx>
35 #include <basegfx/range/b2drange.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <basegfx/polygon/b2dpolypolygontools.hxx>
38 #include <basegfx/curve/b2dcubicbezier.hxx>
40 #include <vector>
41 #include <algorithm>
43 //////////////////////////////////////////////////////////////////////////////
44 // defines
46 #define SUBDIVIDE_FOR_CUT_TEST_COUNT (50)
48 //////////////////////////////////////////////////////////////////////////////
50 namespace basegfx
52 namespace
54 ////////////////////////////////////////////////////////////////////////////////
56 class temporaryPoint
58 B2DPoint maPoint; // the new point
59 sal_uInt32 mnIndex; // index after which to insert
60 double mfCut; // parametric cut description [0.0 .. 1.0]
62 public:
63 temporaryPoint(const B2DPoint& rNewPoint, sal_uInt32 nIndex, double fCut)
64 : maPoint(rNewPoint),
65 mnIndex(nIndex),
66 mfCut(fCut)
70 bool operator<(const temporaryPoint& rComp) const
72 if(mnIndex == rComp.mnIndex)
74 return (mfCut < rComp.mfCut);
77 return (mnIndex < rComp.mnIndex);
80 const B2DPoint& getPoint() const { return maPoint; }
81 sal_uInt32 getIndex() const { return mnIndex; }
82 double getCut() const { return mfCut; }
85 ////////////////////////////////////////////////////////////////////////////////
87 typedef ::std::vector< temporaryPoint > temporaryPointVector;
89 ////////////////////////////////////////////////////////////////////////////////
91 class temporaryPolygonData
93 B2DPolygon maPolygon;
94 B2DRange maRange;
95 temporaryPointVector maPoints;
97 public:
98 const B2DPolygon& getPolygon() const { return maPolygon; }
99 void setPolygon(const B2DPolygon& rNew) { maPolygon = rNew; maRange = tools::getRange(maPolygon); }
100 const B2DRange& getRange() const { return maRange; }
101 temporaryPointVector& getTemporaryPointVector() { return maPoints; }
104 ////////////////////////////////////////////////////////////////////////////////
106 B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
108 // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle
109 // single edges with/without control points
110 // #i101491# added counter for non-changing element count
111 const sal_uInt32 nTempPointCount(rTempPoints.size());
113 if(nTempPointCount)
115 B2DPolygon aRetval;
116 const sal_uInt32 nCount(rCandidate.count());
118 if(nCount)
120 // sort temp points to assure increasing fCut values and increasing indices
121 ::std::sort(rTempPoints.begin(), rTempPoints.end());
123 // prepare loop
124 B2DCubicBezier aEdge;
125 sal_uInt32 nNewInd(0L);
127 // add start point
128 aRetval.append(rCandidate.getB2DPoint(0));
130 for(sal_uInt32 a(0L); a < nCount; a++)
132 // get edge
133 rCandidate.getBezierSegment(a, aEdge);
135 if(aEdge.isBezier())
137 // control vectors involved for this edge
138 double fLeftStart(0.0);
140 // now add all points targeted to be at this index
141 while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
143 const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
145 // split curve segment. Splits need to come sorted and need to be < 1.0. Also,
146 // since original segment is consumed from left to right, the cut values need
147 // to be scaled to the remaining part
148 B2DCubicBezier aLeftPart;
149 const double fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart));
150 aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge);
151 fLeftStart = rTempPoint.getCut();
153 // add left bow
154 aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint());
157 // add remaining bow
158 aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
160 else
162 // add all points targeted to be at this index
163 while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a)
165 const temporaryPoint& rTempPoint = rTempPoints[nNewInd++];
166 const B2DPoint aNewPoint(rTempPoint.getPoint());
168 // do not add points double
169 if(!aRetval.getB2DPoint(aRetval.count() - 1L).equal(aNewPoint))
171 aRetval.append(aNewPoint);
175 // add edge end point
176 aRetval.append(aEdge.getEndPoint());
181 if(rCandidate.isClosed())
183 // set closed flag and correct last point (which is added double now).
184 tools::closeWithGeometryChange(aRetval);
187 return aRetval;
189 else
191 return rCandidate;
195 ////////////////////////////////////////////////////////////////////////////////
197 void adaptAndTransferCutsWithBezierSegment(
198 const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon,
199 sal_uInt32 nInd, temporaryPointVector& rTempPoints)
201 // assuming that the subdivision to create rPolygon used aequidistant pieces
202 // (as in adaptiveSubdivideByCount) it is now possible to calculate back the
203 // cut positions in the polygon to relative cut positions on the original bezier
204 // segment.
205 const sal_uInt32 nTempPointCount(rPointVector.size());
206 const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1L : 0L);
208 if(nTempPointCount && nEdgeCount)
210 for(sal_uInt32 a(0L); a < nTempPointCount; a++)
212 const temporaryPoint& rTempPoint = rPointVector[a];
213 const double fCutPosInPolygon((double)rTempPoint.getIndex() + rTempPoint.getCut());
214 const double fRelativeCutPos(fCutPosInPolygon / (double)nEdgeCount);
215 rTempPoints.push_back(temporaryPoint(rTempPoint.getPoint(), nInd, fRelativeCutPos));
220 ////////////////////////////////////////////////////////////////////////////////
222 } // end of anonymous namespace
223 } // end of namespace basegfx
225 //////////////////////////////////////////////////////////////////////////////
227 namespace basegfx
229 namespace
231 ////////////////////////////////////////////////////////////////////////////////
232 // predefines for calls to this methods before method implementation
234 void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints);
235 void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints);
236 void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB);
238 ////////////////////////////////////////////////////////////////////////////////
240 void findEdgeCutsTwoEdges(
241 const B2DPoint& rCurrA, const B2DPoint& rNextA,
242 const B2DPoint& rCurrB, const B2DPoint& rNextB,
243 sal_uInt32 nIndA, sal_uInt32 nIndB,
244 temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
246 // no null length edges
247 if(!(rCurrA.equal(rNextA) || rCurrB.equal(rNextB)))
249 // no common start/end points, this can be no cuts
250 if(!(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA)))
252 const B2DVector aVecA(rNextA - rCurrA);
253 const B2DVector aVecB(rNextB - rCurrB);
254 double fCut(aVecA.cross(aVecB));
256 if(!fTools::equalZero(fCut))
258 const double fZero(0.0);
259 const double fOne(1.0);
260 fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.getY())) / fCut;
262 if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
264 // it's a candidate, but also need to test parameter value of cut on line 2
265 double fCut2;
267 // choose the more precise version
268 if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
270 fCut2 = (rCurrA.getX() + (fCut * aVecA.getX()) - rCurrB.getX()) / aVecB.getX();
272 else
274 fCut2 = (rCurrA.getY() + (fCut * aVecA.getY()) - rCurrB.getY()) / aVecB.getY();
277 if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
279 // cut is in range, add point. Two edges can have only one cut, but
280 // add a cut point to each list. The lists may be the same for
281 // self intersections.
282 const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut));
283 rTempPointsA.push_back(temporaryPoint(aCutPoint, nIndA, fCut));
284 rTempPointsB.push_back(temporaryPoint(aCutPoint, nIndB, fCut2));
292 ////////////////////////////////////////////////////////////////////////////////
294 void findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
296 // #i76891#
297 // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers
298 // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the
299 // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or
300 // equal points of them.
301 // It would be possible to find the toches using findTouches(), but at last with commpn points
302 // the adding of cut points (temporary points) would fail. But for these temporarily adaptive
303 // subdivided bezier segments, common points may be not very likely, but the bug shows that it
304 // happens.
305 // Touch points are a little bit more likely than common points. All in all it is best to use
306 // a specialized method here which can profit from knowing that it is working on a special
307 // family of B2DPolygons: no curve segments included and not closed.
308 OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
309 OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)");
310 const sal_uInt32 nPointCountA(rCandidateA.count());
311 const sal_uInt32 nPointCountB(rCandidateB.count());
313 if(nPointCountA > 1 && nPointCountB > 1)
315 const sal_uInt32 nEdgeCountA(nPointCountA - 1);
316 const sal_uInt32 nEdgeCountB(nPointCountB - 1);
317 B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
319 for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
321 const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L));
322 const B2DRange aRangeA(aCurrA, aNextA);
323 B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
325 for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
327 const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L));
328 const B2DRange aRangeB(aCurrB, aNextB);
330 if(aRangeA.overlaps(aRangeB))
332 // no null length edges
333 if(!(aCurrA.equal(aNextA) || aCurrB.equal(aNextB)))
335 const B2DVector aVecA(aNextA - aCurrA);
336 const B2DVector aVecB(aNextB - aCurrB);
337 double fCutA(aVecA.cross(aVecB));
339 if(!fTools::equalZero(fCutA))
341 const double fZero(0.0);
342 const double fOne(1.0);
343 fCutA = (aVecB.getY() * (aCurrB.getX() - aCurrA.getX()) + aVecB.getX() * (aCurrA.getY() - aCurrB.getY())) / fCutA;
345 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
346 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
347 if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne))
349 // it's a candidate, but also need to test parameter value of cut on line 2
350 double fCutB;
352 // choose the more precise version
353 if(fabs(aVecB.getX()) > fabs(aVecB.getY()))
355 fCutB = (aCurrA.getX() + (fCutA * aVecA.getX()) - aCurrB.getX()) / aVecB.getX();
357 else
359 fCutB = (aCurrA.getY() + (fCutA * aVecA.getY()) - aCurrB.getY()) / aVecB.getY();
362 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
363 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
364 if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne))
366 // cut is in both ranges. Add points for A and B
367 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
368 if(fTools::equal(fCutA, fZero))
370 // ignore for start point in first edge; this is handled
371 // by outer methods and would just produce a double point
372 if(a)
374 rTempPointsA.push_back(temporaryPoint(aCurrA, a, 0.0));
377 else
379 const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA));
380 rTempPointsA.push_back(temporaryPoint(aCutPoint, a, fCutA));
383 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
384 if(fTools::equal(fCutB, fZero))
386 // ignore for start point in first edge; this is handled
387 // by outer methods and would just produce a double point
388 if(b)
390 rTempPointsB.push_back(temporaryPoint(aCurrB, b, 0.0));
393 else
395 const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB));
396 rTempPointsB.push_back(temporaryPoint(aCutPoint, b, fCutB));
404 // prepare next step
405 aCurrB = aNextB;
408 // prepare next step
409 aCurrA = aNextA;
414 ////////////////////////////////////////////////////////////////////////////////
416 void findEdgeCutsBezierAndEdge(
417 const B2DCubicBezier& rCubicA,
418 const B2DPoint& rCurrB, const B2DPoint& rNextB,
419 sal_uInt32 nIndA, sal_uInt32 nIndB,
420 temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
422 // find all cuts between given bezier segment and edge. Add an entry to the tempPoints
423 // for each common point with the cut value describing the relative position on given
424 // bezier segment and edge.
425 B2DPolygon aTempPolygonA;
426 B2DPolygon aTempPolygonEdge;
427 temporaryPointVector aTempPointVectorA;
428 temporaryPointVector aTempPointVectorEdge;
430 // create subdivided polygons and find cuts between them
431 // Keep adaptiveSubdivideByCount due to needed quality
432 aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
433 aTempPolygonA.append(rCubicA.getStartPoint());
434 rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
435 aTempPolygonEdge.append(rCurrB);
436 aTempPolygonEdge.append(rNextB);
438 // #i76891# using findCuts recursively is not sufficient here
439 findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge);
441 if(aTempPointVectorA.size())
443 // adapt tempVector entries to segment
444 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
447 // append remapped tempVector entries for edge to tempPoints for edge
448 for(sal_uInt32 a(0L); a < aTempPointVectorEdge.size(); a++)
450 const temporaryPoint& rTempPoint = aTempPointVectorEdge[a];
451 rTempPointsB.push_back(temporaryPoint(rTempPoint.getPoint(), nIndB, rTempPoint.getCut()));
455 ////////////////////////////////////////////////////////////////////////////////
457 void findEdgeCutsTwoBeziers(
458 const B2DCubicBezier& rCubicA,
459 const B2DCubicBezier& rCubicB,
460 sal_uInt32 nIndA, sal_uInt32 nIndB,
461 temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
463 // find all cuts between the two given bezier segments. Add an entry to the tempPoints
464 // for each common point with the cut value describing the relative position on given
465 // bezier segments.
466 B2DPolygon aTempPolygonA;
467 B2DPolygon aTempPolygonB;
468 temporaryPointVector aTempPointVectorA;
469 temporaryPointVector aTempPointVectorB;
471 // create subdivided polygons and find cuts between them
472 // Keep adaptiveSubdivideByCount due to needed quality
473 aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
474 aTempPolygonA.append(rCubicA.getStartPoint());
475 rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT);
476 aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
477 aTempPolygonB.append(rCubicB.getStartPoint());
478 rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT);
480 // #i76891# using findCuts recursively is not sufficient here
481 findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB);
483 if(aTempPointVectorA.size())
485 // adapt tempVector entries to segment
486 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA);
489 if(aTempPointVectorB.size())
491 // adapt tempVector entries to segment
492 adaptAndTransferCutsWithBezierSegment(aTempPointVectorB, aTempPolygonB, nIndB, rTempPointsB);
496 ////////////////////////////////////////////////////////////////////////////////
498 void findEdgeCutsOneBezier(
499 const B2DCubicBezier& rCubicA,
500 sal_uInt32 nInd, temporaryPointVector& rTempPoints)
502 // avoid expensive part of this method if possible
503 // TODO: use hasAnyExtremum() method instead when it becomes available
504 double fDummy;
505 const bool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy );
506 if( !bHasAnyExtremum )
507 return;
509 // find all self-intersections on the given bezier segment. Add an entry to the tempPoints
510 // for each self intersection point with the cut value describing the relative position on given
511 // bezier segment.
512 B2DPolygon aTempPolygon;
513 temporaryPointVector aTempPointVector;
515 // create subdivided polygon and find cuts on it
516 // Keep adaptiveSubdivideByCount due to needed quality
517 aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
518 aTempPolygon.append(rCubicA.getStartPoint());
519 rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
520 findCuts(aTempPolygon, aTempPointVector);
522 if(aTempPointVector.size())
524 // adapt tempVector entries to segment
525 adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
529 ////////////////////////////////////////////////////////////////////////////////
531 void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints)
533 // find out if there are edges with intersections (self-cuts). If yes, add
534 // entries to rTempPoints accordingly
535 const sal_uInt32 nPointCount(rCandidate.count());
537 if(nPointCount)
539 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
541 if(nEdgeCount)
543 const bool bCurvesInvolved(rCandidate.areControlPointsUsed());
545 if(bCurvesInvolved)
547 B2DCubicBezier aCubicA;
548 B2DCubicBezier aCubicB;
550 for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
552 rCandidate.getBezierSegment(a, aCubicA);
553 aCubicA.testAndSolveTrivialBezier();
554 const bool bEdgeAIsCurve(aCubicA.isBezier());
555 const B2DRange aRangeA(aCubicA.getRange());
557 if(bEdgeAIsCurve)
559 // curved segments may have self-intersections, do not forget those (!)
560 findEdgeCutsOneBezier(aCubicA, a, rTempPoints);
563 for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
565 rCandidate.getBezierSegment(b, aCubicB);
566 aCubicB.testAndSolveTrivialBezier();
567 const bool bEdgeBIsCurve(aCubicB.isBezier());
568 const B2DRange aRangeB(aCubicB.getRange());
570 // only overlapping segments need to be tested
571 // consecutive segments touch of course
572 bool bOverlap = false;
573 if( b > a+1)
574 bOverlap = aRangeA.overlaps(aRangeB);
575 else
576 bOverlap = aRangeA.overlapsMore(aRangeB);
577 if( bOverlap)
579 if(bEdgeAIsCurve && bEdgeBIsCurve)
581 // test for bezier-bezier cuts
582 findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPoints, rTempPoints);
584 else if(bEdgeAIsCurve)
586 // test for bezier-edge cuts
587 findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPoints, rTempPoints);
589 else if(bEdgeBIsCurve)
591 // test for bezier-edge cuts
592 findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPoints, rTempPoints);
594 else
596 // test for simple edge-edge cuts
597 findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
598 a, b, rTempPoints, rTempPoints);
604 else
606 B2DPoint aCurrA(rCandidate.getB2DPoint(0L));
608 for(sal_uInt32 a(0L); a < nEdgeCount - 1L; a++)
610 const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1L == nPointCount ? 0L : a + 1L));
611 const B2DRange aRangeA(aCurrA, aNextA);
612 B2DPoint aCurrB(rCandidate.getB2DPoint(a + 1L));
614 for(sal_uInt32 b(a + 1L); b < nEdgeCount; b++)
616 const B2DPoint aNextB(rCandidate.getB2DPoint(b + 1L == nPointCount ? 0L : b + 1L));
617 const B2DRange aRangeB(aCurrB, aNextB);
619 // consecutive segments touch of course
620 bool bOverlap = false;
621 if( b > a+1)
622 bOverlap = aRangeA.overlaps(aRangeB);
623 else
624 bOverlap = aRangeA.overlapsMore(aRangeB);
625 if( bOverlap)
627 findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPoints, rTempPoints);
630 // prepare next step
631 aCurrB = aNextB;
634 // prepare next step
635 aCurrA = aNextA;
642 ////////////////////////////////////////////////////////////////////////////////
644 } // end of anonymous namespace
645 } // end of namespace basegfx
647 //////////////////////////////////////////////////////////////////////////////
649 namespace basegfx
651 namespace
653 ////////////////////////////////////////////////////////////////////////////////
655 void findTouchesOnEdge(
656 const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon,
657 sal_uInt32 nInd, temporaryPointVector& rTempPoints)
659 // find out if points from rPointPolygon are positioned on given edge. If Yes, add
660 // points there to represent touches (which may be enter or leave nodes later).
661 const sal_uInt32 nPointCount(rPointPolygon.count());
663 if(nPointCount)
665 const B2DRange aRange(rCurr, rNext);
666 const B2DVector aEdgeVector(rNext - rCurr);
667 B2DVector aNormalizedEdgeVector(aEdgeVector);
668 aNormalizedEdgeVector.normalize();
669 bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY()));
671 for(sal_uInt32 a(0L); a < nPointCount; a++)
673 const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(a));
675 if(aRange.isInside(aTestPoint))
677 if(!aTestPoint.equal(rCurr) && !aTestPoint.equal(rNext))
679 const B2DVector aTestVector(aTestPoint - rCurr);
681 if(areParallel(aNormalizedEdgeVector, aTestVector))
683 const double fCut((bTestUsingX)
684 ? aTestVector.getX() / aEdgeVector.getX()
685 : aTestVector.getY() / aEdgeVector.getY());
686 const double fZero(0.0);
687 const double fOne(1.0);
689 if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne))
691 rTempPoints.push_back(temporaryPoint(aTestPoint, nInd, fCut));
700 ////////////////////////////////////////////////////////////////////////////////
702 void findTouchesOnCurve(
703 const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon,
704 sal_uInt32 nInd, temporaryPointVector& rTempPoints)
706 // find all points from rPointPolygon which touch the given bezier segment. Add an entry
707 // for each touch to the given pointVector. The cut for that entry is the relative position on
708 // the given bezier segment.
709 B2DPolygon aTempPolygon;
710 temporaryPointVector aTempPointVector;
712 // create subdivided polygon and find cuts on it
713 // Keep adaptiveSubdivideByCount due to needed quality
714 aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8);
715 aTempPolygon.append(rCubicA.getStartPoint());
716 rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT);
717 findTouches(aTempPolygon, rPointPolygon, aTempPointVector);
719 if(aTempPointVector.size())
721 // adapt tempVector entries to segment
722 adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints);
726 ////////////////////////////////////////////////////////////////////////////////
728 void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints)
730 // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes,
731 // add entries to rTempPoints
732 const sal_uInt32 nPointCount(rPointPolygon.count());
733 const sal_uInt32 nEdgePointCount(rEdgePolygon.count());
735 if(nPointCount && nEdgePointCount)
737 const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1L);
738 B2DPoint aCurr(rEdgePolygon.getB2DPoint(0));
740 for(sal_uInt32 a(0L); a < nEdgeCount; a++)
742 const sal_uInt32 nNextIndex((a + 1) % nEdgePointCount);
743 const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextIndex));
745 if(!aCurr.equal(aNext))
747 bool bHandleAsSimpleEdge(true);
749 if(rEdgePolygon.areControlPointsUsed())
751 const B2DPoint aNextControlPoint(rEdgePolygon.getNextControlPoint(a));
752 const B2DPoint aPrevControlPoint(rEdgePolygon.getPrevControlPoint(nNextIndex));
753 const bool bEdgeIsCurve(!aNextControlPoint.equal(aCurr) || !aPrevControlPoint.equal(aNext));
755 if(bEdgeIsCurve)
757 bHandleAsSimpleEdge = false;
758 const B2DCubicBezier aCubicA(aCurr, aNextControlPoint, aPrevControlPoint, aNext);
759 findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints);
763 if(bHandleAsSimpleEdge)
765 findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints);
769 // next step
770 aCurr = aNext;
775 ////////////////////////////////////////////////////////////////////////////////
777 } // end of anonymous namespace
778 } // end of namespace basegfx
780 //////////////////////////////////////////////////////////////////////////////
782 namespace basegfx
784 namespace
786 ////////////////////////////////////////////////////////////////////////////////
788 void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB)
790 // find out if edges from both polygons cut. If so, add entries to rTempPoints which
791 // should be added to the polygons accordingly
792 const sal_uInt32 nPointCountA(rCandidateA.count());
793 const sal_uInt32 nPointCountB(rCandidateB.count());
795 if(nPointCountA && nPointCountB)
797 const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1L);
798 const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1L);
800 if(nEdgeCountA && nEdgeCountB)
802 const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed());
804 if(bCurvesInvolved)
806 B2DCubicBezier aCubicA;
807 B2DCubicBezier aCubicB;
809 for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
811 rCandidateA.getBezierSegment(a, aCubicA);
812 aCubicA.testAndSolveTrivialBezier();
813 const bool bEdgeAIsCurve(aCubicA.isBezier());
814 const B2DRange aRangeA(aCubicA.getRange());
816 for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
818 rCandidateB.getBezierSegment(b, aCubicB);
819 aCubicB.testAndSolveTrivialBezier();
820 const bool bEdgeBIsCurve(aCubicB.isBezier());
821 const B2DRange aRangeB(aCubicB.getRange());
823 // consecutive segments touch of course
824 bool bOverlap = false;
825 if( b > a+1)
826 bOverlap = aRangeA.overlaps(aRangeB);
827 else
828 bOverlap = aRangeA.overlapsMore(aRangeB);
829 if( bOverlap)
831 if(bEdgeAIsCurve && bEdgeBIsCurve)
833 // test for bezier-bezier cuts
834 findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB);
836 else if(bEdgeAIsCurve)
838 // test for bezier-edge cuts
839 findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPointsA, rTempPointsB);
841 else if(bEdgeBIsCurve)
843 // test for bezier-edge cuts
844 findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPointsB, rTempPointsA);
846 else
848 // test for simple edge-edge cuts
849 findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(),
850 a, b, rTempPointsA, rTempPointsB);
856 else
858 B2DPoint aCurrA(rCandidateA.getB2DPoint(0L));
860 for(sal_uInt32 a(0L); a < nEdgeCountA; a++)
862 const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1L == nPointCountA ? 0L : a + 1L));
863 const B2DRange aRangeA(aCurrA, aNextA);
864 B2DPoint aCurrB(rCandidateB.getB2DPoint(0L));
866 for(sal_uInt32 b(0L); b < nEdgeCountB; b++)
868 const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1L == nPointCountB ? 0L : b + 1L));
869 const B2DRange aRangeB(aCurrB, aNextB);
871 // consecutive segments touch of course
872 bool bOverlap = false;
873 if( b > a+1)
874 bOverlap = aRangeA.overlaps(aRangeB);
875 else
876 bOverlap = aRangeA.overlapsMore(aRangeB);
877 if( bOverlap)
879 // test for simple edge-edge cuts
880 findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB);
883 // prepare next step
884 aCurrB = aNextB;
887 // prepare next step
888 aCurrA = aNextA;
895 ////////////////////////////////////////////////////////////////////////////////
897 } // end of anonymous namespace
898 } // end of namespace basegfx
900 //////////////////////////////////////////////////////////////////////////////
902 namespace basegfx
904 namespace tools
906 ////////////////////////////////////////////////////////////////////////////////
908 B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate)
910 if(rCandidate.count())
912 temporaryPointVector aTempPoints;
914 findTouches(rCandidate, rCandidate, aTempPoints);
915 findCuts(rCandidate, aTempPoints);
917 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
919 else
921 return rCandidate;
925 ////////////////////////////////////////////////////////////////////////////////
927 B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, bool bSelfIntersections)
929 const sal_uInt32 nCount(rCandidate.count());
931 if(nCount)
933 B2DPolyPolygon aRetval;
935 if(1L == nCount)
937 if(bSelfIntersections)
939 // remove self intersections
940 aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0L)));
942 else
944 // copy source
945 aRetval = rCandidate;
948 else
950 // first solve self cuts and self touches for all contained single polygons
951 temporaryPolygonData *pTempData = new temporaryPolygonData[nCount];
952 sal_uInt32 a, b;
954 for(a = 0L; a < nCount; a++)
956 if(bSelfIntersections)
958 // use polygons with solved self intersections
959 pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a)));
961 else
963 // copy given polygons
964 pTempData[a].setPolygon(rCandidate.getB2DPolygon(a));
968 // now cuts and touches between the polygons
969 for(a = 0L; a < nCount; a++)
971 for(b = 0L; b < nCount; b++)
973 if(a != b)
975 // look for touches, compare each edge polygon to all other points
976 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
978 findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector());
982 if(a < b)
984 // look for cuts, compare each edge polygon to following ones
985 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
987 findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
993 // consolidate the result
994 for(a = 0L; a < nCount; a++)
996 aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
999 delete[] pTempData;
1002 return aRetval;
1004 else
1006 return rCandidate;
1010 ////////////////////////////////////////////////////////////////////////////////
1012 B2DPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolygon& rCandidate)
1014 if(rCandidate.count())
1016 temporaryPointVector aTempPoints;
1017 temporaryPointVector aTempPointsUnused;
1019 for(sal_uInt32 a(0L); a < rMask.count(); a++)
1021 const B2DPolygon aPartMask(rMask.getB2DPolygon(a));
1023 findTouches(rCandidate, aPartMask, aTempPoints);
1024 findCuts(rCandidate, aPartMask, aTempPoints, aTempPointsUnused);
1027 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1029 else
1031 return rCandidate;
1035 ////////////////////////////////////////////////////////////////////////////////
1037 B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rMask, const B2DPolyPolygon& rCandidate)
1039 B2DPolyPolygon aRetval;
1041 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
1043 aRetval.append(addPointsAtCutsAndTouches(rMask, rCandidate.getB2DPolygon(a)));
1046 return aRetval;
1049 ////////////////////////////////////////////////////////////////////////////////
1051 B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
1053 const sal_uInt32 nCount(rCandidate.count());
1055 if(nCount && !rStart.equal(rEnd))
1057 const B2DRange aPolygonRange(rCandidate.getB2DRange());
1058 const B2DRange aEdgeRange(rStart, rEnd);
1060 if(aPolygonRange.overlaps(aEdgeRange))
1062 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1);
1063 temporaryPointVector aTempPoints;
1064 temporaryPointVector aUnusedTempPoints;
1065 B2DCubicBezier aCubic;
1067 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1069 rCandidate.getBezierSegment(a, aCubic);
1070 B2DRange aCubicRange(aCubic.getStartPoint(), aCubic.getEndPoint());
1072 if(aCubic.isBezier())
1074 aCubicRange.expand(aCubic.getControlPointA());
1075 aCubicRange.expand(aCubic.getControlPointB());
1077 if(aCubicRange.overlaps(aEdgeRange))
1079 findEdgeCutsBezierAndEdge(aCubic, rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
1082 else
1084 if(aCubicRange.overlaps(aEdgeRange))
1086 findEdgeCutsTwoEdges(aCubic.getStartPoint(), aCubic.getEndPoint(), rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints);
1091 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1095 return rCandidate;
1098 B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd)
1100 B2DPolyPolygon aRetval;
1102 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
1104 aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rStart, rEnd));
1107 return aRetval;
1110 ////////////////////////////////////////////////////////////////////////////////
1112 B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rPolyMask)
1114 const sal_uInt32 nCountA(rCandidate.count());
1115 const sal_uInt32 nCountM(rPolyMask.count());
1117 if(nCountA && nCountM)
1119 const B2DRange aRangeA(rCandidate.getB2DRange());
1120 const B2DRange aRangeM(rPolyMask.getB2DRange());
1122 if(aRangeA.overlaps(aRangeM))
1124 const sal_uInt32 nEdgeCountA(rCandidate.isClosed() ? nCountA : nCountA - 1);
1125 temporaryPointVector aTempPointsA;
1126 temporaryPointVector aUnusedTempPointsB;
1128 for(sal_uInt32 m(0); m < nCountM; m++)
1130 const B2DPolygon aMask(rPolyMask.getB2DPolygon(m));
1131 const sal_uInt32 nCountB(aMask.count());
1133 if(nCountB)
1135 B2DCubicBezier aCubicA;
1136 B2DCubicBezier aCubicB;
1138 for(sal_uInt32 a(0); a < nEdgeCountA; a++)
1140 rCandidate.getBezierSegment(a, aCubicA);
1141 const bool bCubicAIsCurve(aCubicA.isBezier());
1142 B2DRange aCubicRangeA(aCubicA.getStartPoint(), aCubicA.getEndPoint());
1144 if(bCubicAIsCurve)
1146 aCubicRangeA.expand(aCubicA.getControlPointA());
1147 aCubicRangeA.expand(aCubicA.getControlPointB());
1150 for(sal_uInt32 b(0); b < nCountB; b++)
1152 aMask.getBezierSegment(b, aCubicB);
1153 const bool bCubicBIsCurve(aCubicB.isBezier());
1154 B2DRange aCubicRangeB(aCubicB.getStartPoint(), aCubicB.getEndPoint());
1156 if(bCubicBIsCurve)
1158 aCubicRangeB.expand(aCubicB.getControlPointA());
1159 aCubicRangeB.expand(aCubicB.getControlPointB());
1162 if(aCubicRangeA.overlaps(aCubicRangeB))
1164 if(bCubicAIsCurve && bCubicBIsCurve)
1166 findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, aTempPointsA, aUnusedTempPointsB);
1168 else if(bCubicAIsCurve)
1170 findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
1172 else if(bCubicBIsCurve)
1174 findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, aUnusedTempPointsB, aTempPointsA);
1176 else
1178 findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB);
1186 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPointsA);
1190 return rCandidate;
1193 B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rMask)
1195 B2DPolyPolygon aRetval;
1197 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
1199 aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(a), rMask));
1202 return aRetval;
1205 B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate)
1207 if(rCandidate.count())
1209 temporaryPointVector aTempPoints;
1211 findCuts(rCandidate, aTempPoints);
1213 return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints);
1215 else
1217 return rCandidate;
1221 B2DPolyPolygon addPointsAtCuts(const B2DPolyPolygon& rCandidate, bool bSelfIntersections)
1223 const sal_uInt32 nCount(rCandidate.count());
1225 if(nCount)
1227 B2DPolyPolygon aRetval;
1229 if(1 == nCount)
1231 if(bSelfIntersections)
1233 // remove self intersections
1234 aRetval.append(addPointsAtCuts(rCandidate.getB2DPolygon(0)));
1236 else
1238 // copy source
1239 aRetval = rCandidate;
1242 else
1244 // first solve self cuts for all contained single polygons
1245 temporaryPolygonData *pTempData = new temporaryPolygonData[nCount];
1246 sal_uInt32 a, b;
1248 for(a = 0; a < nCount; a++)
1250 if(bSelfIntersections)
1252 // use polygons with solved self intersections
1253 pTempData[a].setPolygon(addPointsAtCuts(rCandidate.getB2DPolygon(a)));
1255 else
1257 // copy given polygons
1258 pTempData[a].setPolygon(rCandidate.getB2DPolygon(a));
1262 // now cuts and touches between the polygons
1263 for(a = 0; a < nCount; a++)
1265 for(b = 0; b < nCount; b++)
1267 if(a < b)
1269 // look for cuts, compare each edge polygon to following ones
1270 if(pTempData[a].getRange().overlaps(pTempData[b].getRange()))
1272 findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector());
1278 // consolidate the result
1279 for(a = 0L; a < nCount; a++)
1281 aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector()));
1284 delete[] pTempData;
1287 return aRetval;
1289 else
1291 return rCandidate;
1295 ////////////////////////////////////////////////////////////////////////////////
1297 } // end of namespace tools
1298 } // end of namespace basegfx
1300 //////////////////////////////////////////////////////////////////////////////
1301 // eof