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>
43 //////////////////////////////////////////////////////////////////////////////
46 #define SUBDIVIDE_FOR_CUT_TEST_COUNT (50)
48 //////////////////////////////////////////////////////////////////////////////
54 ////////////////////////////////////////////////////////////////////////////////
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]
63 temporaryPoint(const B2DPoint
& rNewPoint
, sal_uInt32 nIndex
, double 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
95 temporaryPointVector maPoints
;
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());
116 const sal_uInt32
nCount(rCandidate
.count());
120 // sort temp points to assure increasing fCut values and increasing indices
121 ::std::sort(rTempPoints
.begin(), rTempPoints
.end());
124 B2DCubicBezier aEdge
;
125 sal_uInt32
nNewInd(0L);
128 aRetval
.append(rCandidate
.getB2DPoint(0));
130 for(sal_uInt32
a(0L); a
< nCount
; a
++)
133 rCandidate
.getBezierSegment(a
, aEdge
);
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();
154 aRetval
.appendBezierSegment(aLeftPart
.getControlPointA(), aLeftPart
.getControlPointB(), rTempPoint
.getPoint());
158 aRetval
.appendBezierSegment(aEdge
.getControlPointA(), aEdge
.getControlPointB(), aEdge
.getEndPoint());
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
);
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
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 //////////////////////////////////////////////////////////////////////////////
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
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();
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
)
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
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
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();
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
374 rTempPointsA
.push_back(temporaryPoint(aCurrA
, a
, 0.0));
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
390 rTempPointsB
.push_back(temporaryPoint(aCurrB
, b
, 0.0));
395 const B2DPoint
aCutPoint(interpolate(aCurrB
, aNextB
, fCutB
));
396 rTempPointsB
.push_back(temporaryPoint(aCutPoint
, b
, fCutB
));
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
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
505 const bool bHasAnyExtremum
= rCubicA
.getMinimumExtremumPosition( fDummy
);
506 if( !bHasAnyExtremum
)
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
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());
539 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
543 const bool bCurvesInvolved(rCandidate
.areControlPointsUsed());
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());
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;
574 bOverlap
= aRangeA
.overlaps(aRangeB
);
576 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
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
);
596 // test for simple edge-edge cuts
597 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
598 a
, b
, rTempPoints
, rTempPoints
);
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;
622 bOverlap
= aRangeA
.overlaps(aRangeB
);
624 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
627 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPoints
, rTempPoints
);
642 ////////////////////////////////////////////////////////////////////////////////
644 } // end of anonymous namespace
645 } // end of namespace basegfx
647 //////////////////////////////////////////////////////////////////////////////
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());
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
));
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
);
775 ////////////////////////////////////////////////////////////////////////////////
777 } // end of anonymous namespace
778 } // end of namespace basegfx
780 //////////////////////////////////////////////////////////////////////////////
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());
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;
826 bOverlap
= aRangeA
.overlaps(aRangeB
);
828 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
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
);
848 // test for simple edge-edge cuts
849 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
850 a
, b
, rTempPointsA
, rTempPointsB
);
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;
874 bOverlap
= aRangeA
.overlaps(aRangeB
);
876 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
879 // test for simple edge-edge cuts
880 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPointsA
, rTempPointsB
);
895 ////////////////////////////////////////////////////////////////////////////////
897 } // end of anonymous namespace
898 } // end of namespace basegfx
900 //////////////////////////////////////////////////////////////////////////////
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
);
925 ////////////////////////////////////////////////////////////////////////////////
927 B2DPolyPolygon
addPointsAtCutsAndTouches(const B2DPolyPolygon
& rCandidate
, bool bSelfIntersections
)
929 const sal_uInt32
nCount(rCandidate
.count());
933 B2DPolyPolygon aRetval
;
937 if(bSelfIntersections
)
939 // remove self intersections
940 aRetval
.append(addPointsAtCutsAndTouches(rCandidate
.getB2DPolygon(0L)));
945 aRetval
= rCandidate
;
950 // first solve self cuts and self touches for all contained single polygons
951 temporaryPolygonData
*pTempData
= new temporaryPolygonData
[nCount
];
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
)));
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
++)
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());
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()));
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
);
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
)));
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
);
1084 if(aCubicRange
.overlaps(aEdgeRange
))
1086 findEdgeCutsTwoEdges(aCubic
.getStartPoint(), aCubic
.getEndPoint(), rStart
, rEnd
, a
, 0, aTempPoints
, aUnusedTempPoints
);
1091 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
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
));
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());
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());
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());
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
);
1178 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1186 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPointsA
);
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
));
1205 B2DPolygon
addPointsAtCuts(const B2DPolygon
& rCandidate
)
1207 if(rCandidate
.count())
1209 temporaryPointVector aTempPoints
;
1211 findCuts(rCandidate
, aTempPoints
);
1213 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
1221 B2DPolyPolygon
addPointsAtCuts(const B2DPolyPolygon
& rCandidate
, bool bSelfIntersections
)
1223 const sal_uInt32
nCount(rCandidate
.count());
1227 B2DPolyPolygon aRetval
;
1231 if(bSelfIntersections
)
1233 // remove self intersections
1234 aRetval
.append(addPointsAtCuts(rCandidate
.getB2DPolygon(0)));
1239 aRetval
= rCandidate
;
1244 // first solve self cuts for all contained single polygons
1245 temporaryPolygonData
*pTempData
= new temporaryPolygonData
[nCount
];
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
)));
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
++)
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()));
1295 ////////////////////////////////////////////////////////////////////////////////
1297 } // end of namespace tools
1298 } // end of namespace basegfx
1300 //////////////////////////////////////////////////////////////////////////////