1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
21 #include <osl/diagnose.h>
22 #include <basegfx/numeric/ftools.hxx>
23 #include <basegfx/point/b2dpoint.hxx>
24 #include <basegfx/vector/b2dvector.hxx>
25 #include <basegfx/range/b2drange.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolypolygontools.hxx>
28 #include <basegfx/curve/b2dcubicbezier.hxx>
34 #define SUBDIVIDE_FOR_CUT_TEST_COUNT (50)
43 B2DPoint maPoint
; // the new point
44 sal_uInt32 mnIndex
; // index after which to insert
45 double mfCut
; // parametric cut description [0.0 .. 1.0]
48 temporaryPoint(const B2DPoint
& rNewPoint
, sal_uInt32 nIndex
, double fCut
)
55 bool operator<(const temporaryPoint
& rComp
) const
57 if(mnIndex
== rComp
.mnIndex
)
59 return (mfCut
< rComp
.mfCut
);
62 return (mnIndex
< rComp
.mnIndex
);
65 const B2DPoint
& getPoint() const { return maPoint
; }
66 sal_uInt32
getIndex() const { return mnIndex
; }
67 double getCut() const { return mfCut
; }
70 typedef std::vector
< temporaryPoint
> temporaryPointVector
;
72 class temporaryPolygonData
76 temporaryPointVector maPoints
;
79 const B2DPolygon
& getPolygon() const { return maPolygon
; }
80 void setPolygon(const B2DPolygon
& rNew
) { maPolygon
= rNew
; maRange
= utils::getRange(maPolygon
); }
81 const B2DRange
& getRange() const { return maRange
; }
82 temporaryPointVector
& getTemporaryPointVector() { return maPoints
; }
85 B2DPolygon
mergeTemporaryPointsAndPolygon(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
)
87 // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle
88 // single edges with/without control points
89 // #i101491# added counter for non-changing element count
90 const sal_uInt32
nTempPointCount(rTempPoints
.size());
95 const sal_uInt32
nCount(rCandidate
.count());
99 // sort temp points to assure increasing fCut values and increasing indices
100 std::sort(rTempPoints
.begin(), rTempPoints
.end());
103 B2DCubicBezier aEdge
;
104 sal_uInt32
nNewInd(0);
107 aRetval
.append(rCandidate
.getB2DPoint(0));
109 for(sal_uInt32
a(0); a
< nCount
; a
++)
112 rCandidate
.getBezierSegment(a
, aEdge
);
116 // control vectors involved for this edge
117 double fLeftStart(0.0);
119 // now add all points targeted to be at this index
120 while (nNewInd
< nTempPointCount
&& rTempPoints
[nNewInd
].getIndex() == a
&& fLeftStart
< 1.0)
122 const temporaryPoint
& rTempPoint
= rTempPoints
[nNewInd
++];
124 // split curve segment. Splits need to come sorted and need to be < 1.0. Also,
125 // since original segment is consumed from left to right, the cut values need
126 // to be scaled to the remaining part
127 B2DCubicBezier aLeftPart
;
128 const double fRelativeSplitPoint((rTempPoint
.getCut() - fLeftStart
) / (1.0 - fLeftStart
));
129 aEdge
.split(fRelativeSplitPoint
, &aLeftPart
, &aEdge
);
130 fLeftStart
= rTempPoint
.getCut();
133 aRetval
.appendBezierSegment(aLeftPart
.getControlPointA(), aLeftPart
.getControlPointB(), rTempPoint
.getPoint());
137 aRetval
.appendBezierSegment(aEdge
.getControlPointA(), aEdge
.getControlPointB(), aEdge
.getEndPoint());
141 // add all points targeted to be at this index
142 while(nNewInd
< nTempPointCount
&& rTempPoints
[nNewInd
].getIndex() == a
)
144 const temporaryPoint
& rTempPoint
= rTempPoints
[nNewInd
++];
145 const B2DPoint
& aNewPoint(rTempPoint
.getPoint());
147 // do not add points double
148 if(!aRetval
.getB2DPoint(aRetval
.count() - 1).equal(aNewPoint
))
150 aRetval
.append(aNewPoint
);
154 // add edge end point
155 aRetval
.append(aEdge
.getEndPoint());
160 if(rCandidate
.isClosed())
162 // set closed flag and correct last point (which is added double now).
163 utils::closeWithGeometryChange(aRetval
);
174 void adaptAndTransferCutsWithBezierSegment(
175 const temporaryPointVector
& rPointVector
, const B2DPolygon
& rPolygon
,
176 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
178 // assuming that the subdivision to create rPolygon used equidistant pieces
179 // (as in adaptiveSubdivideByCount) it is now possible to calculate back the
180 // cut positions in the polygon to relative cut positions on the original bezier
182 const sal_uInt32
nTempPointCount(rPointVector
.size());
183 const sal_uInt32
nEdgeCount(rPolygon
.count() ? rPolygon
.count() - 1 : 0);
185 if(nTempPointCount
&& nEdgeCount
)
187 for(sal_uInt32
a(0); a
< nTempPointCount
; a
++)
189 const temporaryPoint
& rTempPoint
= rPointVector
[a
];
190 const double fCutPosInPolygon(static_cast<double>(rTempPoint
.getIndex()) + rTempPoint
.getCut());
191 const double fRelativeCutPos(fCutPosInPolygon
/ static_cast<double>(nEdgeCount
));
192 rTempPoints
.emplace_back(rTempPoint
.getPoint(), nInd
, fRelativeCutPos
);
197 } // end of anonymous namespace
198 } // end of namespace basegfx
205 // predefines for calls to this methods before method implementation
207 void findCuts(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
);
208 void findTouches(const B2DPolygon
& rEdgePolygon
, const B2DPolygon
& rPointPolygon
, temporaryPointVector
& rTempPoints
);
209 void findCuts(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
);
211 void findEdgeCutsTwoEdges(
212 const B2DPoint
& rCurrA
, const B2DPoint
& rNextA
,
213 const B2DPoint
& rCurrB
, const B2DPoint
& rNextB
,
214 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
215 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
217 // no null length edges
218 if(!(rCurrA
.equal(rNextA
) || rCurrB
.equal(rNextB
)))
220 // no common start/end points, this can be no cuts
221 if(!(rCurrB
.equal(rCurrA
) || rCurrB
.equal(rNextA
) || rNextB
.equal(rCurrA
) || rNextB
.equal(rNextA
)))
223 const B2DVector
aVecA(rNextA
- rCurrA
);
224 const B2DVector
aVecB(rNextB
- rCurrB
);
225 double fCut(aVecA
.cross(aVecB
));
227 if(!fTools::equalZero(fCut
))
229 const double fZero(0.0);
230 const double fOne(1.0);
231 fCut
= (aVecB
.getY() * (rCurrB
.getX() - rCurrA
.getX()) + aVecB
.getX() * (rCurrA
.getY() - rCurrB
.getY())) / fCut
;
233 if (fTools::betweenOrEqualEither(fCut
, fZero
, fOne
))
235 // it's a candidate, but also need to test parameter value of cut on line 2
238 // choose the more precise version
239 if(fabs(aVecB
.getX()) > fabs(aVecB
.getY()))
241 fCut2
= (rCurrA
.getX() + (fCut
* aVecA
.getX()) - rCurrB
.getX()) / aVecB
.getX();
245 fCut2
= (rCurrA
.getY() + (fCut
* aVecA
.getY()) - rCurrB
.getY()) / aVecB
.getY();
248 if (fTools::betweenOrEqualEither(fCut2
, fZero
, fOne
))
250 // cut is in range, add point. Two edges can have only one cut, but
251 // add a cut point to each list. The lists may be the same for
252 // self intersections.
253 const B2DPoint
aCutPoint(interpolate(rCurrA
, rNextA
, fCut
));
254 rTempPointsA
.emplace_back(aCutPoint
, nIndA
, fCut
);
255 rTempPointsB
.emplace_back(aCutPoint
, nIndB
, fCut2
);
263 void findCutsAndTouchesAndCommonForBezier(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
266 // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers
267 // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the
268 // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or
269 // equal points of them.
270 // It would be possible to find the touches using findTouches(), but at last with common points
271 // the adding of cut points (temporary points) would fail. But for these temporarily adaptive
272 // subdivided bezier segments, common points may be not very likely, but the bug shows that it
274 // Touch points are a little bit more likely than common points. All in all it is best to use
275 // a specialized method here which can profit from knowing that it is working on a special
276 // family of B2DPolygons: no curve segments included and not closed.
277 OSL_ENSURE(!rCandidateA
.areControlPointsUsed() && !rCandidateB
.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
278 OSL_ENSURE(!rCandidateA
.isClosed() && !rCandidateB
.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)");
279 const sal_uInt32
nPointCountA(rCandidateA
.count());
280 const sal_uInt32
nPointCountB(rCandidateB
.count());
282 if(nPointCountA
> 1 && nPointCountB
> 1)
284 const sal_uInt32
nEdgeCountA(nPointCountA
- 1);
285 const sal_uInt32
nEdgeCountB(nPointCountB
- 1);
286 B2DPoint
aCurrA(rCandidateA
.getB2DPoint(0));
288 for(sal_uInt32
a(0); a
< nEdgeCountA
; a
++)
290 const B2DPoint
aNextA(rCandidateA
.getB2DPoint(a
+ 1));
291 const B2DRange
aRangeA(aCurrA
, aNextA
);
292 B2DPoint
aCurrB(rCandidateB
.getB2DPoint(0));
294 for(sal_uInt32
b(0); b
< nEdgeCountB
; b
++)
296 const B2DPoint
aNextB(rCandidateB
.getB2DPoint(b
+ 1));
297 const B2DRange
aRangeB(aCurrB
, aNextB
);
299 if(aRangeA
.overlaps(aRangeB
))
301 // no null length edges
302 if(!(aCurrA
.equal(aNextA
) || aCurrB
.equal(aNextB
)))
304 const B2DVector
aVecA(aNextA
- aCurrA
);
305 const B2DVector
aVecB(aNextB
- aCurrB
);
306 double fCutA(aVecA
.cross(aVecB
));
308 if(!fTools::equalZero(fCutA
))
310 const double fZero(0.0);
311 const double fOne(1.0);
312 fCutA
= (aVecB
.getY() * (aCurrB
.getX() - aCurrA
.getX()) + aVecB
.getX() * (aCurrA
.getY() - aCurrB
.getY())) / fCutA
;
314 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
315 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
316 if(fTools::moreOrEqual(fCutA
, fZero
) && fTools::less(fCutA
, fOne
))
318 // it's a candidate, but also need to test parameter value of cut on line 2
321 // choose the more precise version
322 if(fabs(aVecB
.getX()) > fabs(aVecB
.getY()))
324 fCutB
= (aCurrA
.getX() + (fCutA
* aVecA
.getX()) - aCurrB
.getX()) / aVecB
.getX();
328 fCutB
= (aCurrA
.getY() + (fCutA
* aVecA
.getY()) - aCurrB
.getY()) / aVecB
.getY();
331 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
332 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
333 if(fTools::moreOrEqual(fCutB
, fZero
) && fTools::less(fCutB
, fOne
))
335 // cut is in both ranges. Add points for A and B
336 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
337 if(fTools::equal(fCutA
, fZero
))
339 // ignore for start point in first edge; this is handled
340 // by outer methods and would just produce a double point
343 rTempPointsA
.emplace_back(aCurrA
, a
, 0.0);
348 const B2DPoint
aCutPoint(interpolate(aCurrA
, aNextA
, fCutA
));
349 rTempPointsA
.emplace_back(aCutPoint
, a
, fCutA
);
352 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
353 if(fTools::equal(fCutB
, fZero
))
355 // ignore for start point in first edge; this is handled
356 // by outer methods and would just produce a double point
359 rTempPointsB
.emplace_back(aCurrB
, b
, 0.0);
364 const B2DPoint
aCutPoint(interpolate(aCurrB
, aNextB
, fCutB
));
365 rTempPointsB
.emplace_back(aCutPoint
, b
, fCutB
);
383 void findEdgeCutsBezierAndEdge(
384 const B2DCubicBezier
& rCubicA
,
385 const B2DPoint
& rCurrB
, const B2DPoint
& rNextB
,
386 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
387 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
389 // find all cuts between given bezier segment and edge. Add an entry to the tempPoints
390 // for each common point with the cut value describing the relative position on given
391 // bezier segment and edge.
392 B2DPolygon aTempPolygonA
;
393 B2DPolygon aTempPolygonEdge
;
394 temporaryPointVector aTempPointVectorA
;
395 temporaryPointVector aTempPointVectorEdge
;
397 // create subdivided polygons and find cuts between them
398 // Keep adaptiveSubdivideByCount due to needed quality
399 aTempPolygonA
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
400 aTempPolygonA
.append(rCubicA
.getStartPoint());
401 rCubicA
.adaptiveSubdivideByCount(aTempPolygonA
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
402 aTempPolygonEdge
.append(rCurrB
);
403 aTempPolygonEdge
.append(rNextB
);
405 // #i76891# using findCuts recursively is not sufficient here
406 findCutsAndTouchesAndCommonForBezier(aTempPolygonA
, aTempPolygonEdge
, aTempPointVectorA
, aTempPointVectorEdge
);
408 if(!aTempPointVectorA
.empty())
410 // adapt tempVector entries to segment
411 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA
, aTempPolygonA
, nIndA
, rTempPointsA
);
414 // append remapped tempVector entries for edge to tempPoints for edge
415 for(temporaryPoint
& rTempPoint
: aTempPointVectorEdge
)
417 rTempPointsB
.emplace_back(rTempPoint
.getPoint(), nIndB
, rTempPoint
.getCut());
421 void findEdgeCutsTwoBeziers(
422 const B2DCubicBezier
& rCubicA
,
423 const B2DCubicBezier
& rCubicB
,
424 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
425 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
427 // find all cuts between the two given bezier segments. Add an entry to the tempPoints
428 // for each common point with the cut value describing the relative position on given
430 B2DPolygon aTempPolygonA
;
431 B2DPolygon aTempPolygonB
;
432 temporaryPointVector aTempPointVectorA
;
433 temporaryPointVector aTempPointVectorB
;
435 // create subdivided polygons and find cuts between them
436 // Keep adaptiveSubdivideByCount due to needed quality
437 aTempPolygonA
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
438 aTempPolygonA
.append(rCubicA
.getStartPoint());
439 rCubicA
.adaptiveSubdivideByCount(aTempPolygonA
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
440 aTempPolygonB
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
441 aTempPolygonB
.append(rCubicB
.getStartPoint());
442 rCubicB
.adaptiveSubdivideByCount(aTempPolygonB
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
444 // #i76891# using findCuts recursively is not sufficient here
445 findCutsAndTouchesAndCommonForBezier(aTempPolygonA
, aTempPolygonB
, aTempPointVectorA
, aTempPointVectorB
);
447 if(!aTempPointVectorA
.empty())
449 // adapt tempVector entries to segment
450 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA
, aTempPolygonA
, nIndA
, rTempPointsA
);
453 if(!aTempPointVectorB
.empty())
455 // adapt tempVector entries to segment
456 adaptAndTransferCutsWithBezierSegment(aTempPointVectorB
, aTempPolygonB
, nIndB
, rTempPointsB
);
460 void findEdgeCutsOneBezier(
461 const B2DCubicBezier
& rCubicA
,
462 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
464 // avoid expensive part of this method if possible
465 // TODO: use hasAnyExtremum() method instead when it becomes available
467 const bool bHasAnyExtremum
= rCubicA
.getMinimumExtremumPosition( fDummy
);
468 if( !bHasAnyExtremum
)
471 // find all self-intersections on the given bezier segment. Add an entry to the tempPoints
472 // for each self intersection point with the cut value describing the relative position on given
474 B2DPolygon aTempPolygon
;
475 temporaryPointVector aTempPointVector
;
477 // create subdivided polygon and find cuts on it
478 // Keep adaptiveSubdivideByCount due to needed quality
479 aTempPolygon
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
480 aTempPolygon
.append(rCubicA
.getStartPoint());
481 rCubicA
.adaptiveSubdivideByCount(aTempPolygon
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
482 findCuts(aTempPolygon
, aTempPointVector
);
484 if(!aTempPointVector
.empty())
486 // adapt tempVector entries to segment
487 adaptAndTransferCutsWithBezierSegment(aTempPointVector
, aTempPolygon
, nInd
, rTempPoints
);
491 void findCuts(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
)
493 // find out if there are edges with intersections (self-cuts). If yes, add
494 // entries to rTempPoints accordingly
495 const sal_uInt32
nPointCount(rCandidate
.count());
499 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1);
503 const bool bCurvesInvolved(rCandidate
.areControlPointsUsed());
507 B2DCubicBezier aCubicA
;
508 B2DCubicBezier aCubicB
;
510 for(sal_uInt32
a(0); a
< nEdgeCount
- 1; a
++)
512 rCandidate
.getBezierSegment(a
, aCubicA
);
513 aCubicA
.testAndSolveTrivialBezier();
514 const bool bEdgeAIsCurve(aCubicA
.isBezier());
515 const B2DRange
aRangeA(aCubicA
.getRange());
519 // curved segments may have self-intersections, do not forget those (!)
520 findEdgeCutsOneBezier(aCubicA
, a
, rTempPoints
);
523 for(sal_uInt32
b(a
+ 1); b
< nEdgeCount
; b
++)
525 rCandidate
.getBezierSegment(b
, aCubicB
);
526 aCubicB
.testAndSolveTrivialBezier();
527 const B2DRange
aRangeB(aCubicB
.getRange());
529 // only overlapping segments need to be tested
530 // consecutive segments touch of course
531 bool bOverlap
= false;
533 bOverlap
= aRangeA
.overlaps(aRangeB
);
535 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
538 const bool bEdgeBIsCurve(aCubicB
.isBezier());
539 if(bEdgeAIsCurve
&& bEdgeBIsCurve
)
541 // test for bezier-bezier cuts
542 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, rTempPoints
, rTempPoints
);
544 else if(bEdgeAIsCurve
)
546 // test for bezier-edge cuts
547 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, rTempPoints
, rTempPoints
);
549 else if(bEdgeBIsCurve
)
551 // test for bezier-edge cuts
552 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, rTempPoints
, rTempPoints
);
556 // test for simple edge-edge cuts
557 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
558 a
, b
, rTempPoints
, rTempPoints
);
566 B2DPoint
aCurrA(rCandidate
.getB2DPoint(0));
568 for(sal_uInt32
a(0); a
< nEdgeCount
- 1; a
++)
570 const B2DPoint
aNextA(rCandidate
.getB2DPoint(a
+ 1 == nPointCount
? 0 : a
+ 1));
571 const B2DRange
aRangeA(aCurrA
, aNextA
);
572 B2DPoint
aCurrB(rCandidate
.getB2DPoint(a
+ 1));
574 for(sal_uInt32
b(a
+ 1); b
< nEdgeCount
; b
++)
576 const B2DPoint
aNextB(rCandidate
.getB2DPoint(b
+ 1 == nPointCount
? 0 : b
+ 1));
577 const B2DRange
aRangeB(aCurrB
, aNextB
);
579 // consecutive segments touch of course
580 bool bOverlap
= false;
582 bOverlap
= aRangeA
.overlaps(aRangeB
);
584 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
587 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPoints
, rTempPoints
);
602 } // end of anonymous namespace
603 } // end of namespace basegfx
610 void findTouchesOnEdge(
611 const B2DPoint
& rCurr
, const B2DPoint
& rNext
, const B2DPolygon
& rPointPolygon
,
612 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
614 // find out if points from rPointPolygon are positioned on given edge. If Yes, add
615 // points there to represent touches (which may be enter or leave nodes later).
616 const sal_uInt32
nPointCount(rPointPolygon
.count());
620 const B2DRange
aRange(rCurr
, rNext
);
621 const B2DVector
aEdgeVector(rNext
- rCurr
);
622 bool bTestUsingX(fabs(aEdgeVector
.getX()) > fabs(aEdgeVector
.getY()));
624 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
626 const B2DPoint
aTestPoint(rPointPolygon
.getB2DPoint(a
));
628 if(aRange
.isInside(aTestPoint
))
630 if(!aTestPoint
.equal(rCurr
) && !aTestPoint
.equal(rNext
))
632 const B2DVector
aTestVector(aTestPoint
- rCurr
);
634 if(areParallel(aEdgeVector
, aTestVector
))
636 const double fCut(bTestUsingX
637 ? aTestVector
.getX() / aEdgeVector
.getX()
638 : aTestVector
.getY() / aEdgeVector
.getY());
639 const double fZero(0.0);
640 const double fOne(1.0);
642 if(fTools::more(fCut
, fZero
) && fTools::less(fCut
, fOne
))
644 rTempPoints
.emplace_back(aTestPoint
, nInd
, fCut
);
653 void findTouchesOnCurve(
654 const B2DCubicBezier
& rCubicA
, const B2DPolygon
& rPointPolygon
,
655 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
657 // find all points from rPointPolygon which touch the given bezier segment. Add an entry
658 // for each touch to the given pointVector. The cut for that entry is the relative position on
659 // the given bezier segment.
660 B2DPolygon aTempPolygon
;
661 temporaryPointVector aTempPointVector
;
663 // create subdivided polygon and find cuts on it
664 // Keep adaptiveSubdivideByCount due to needed quality
665 aTempPolygon
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
666 aTempPolygon
.append(rCubicA
.getStartPoint());
667 rCubicA
.adaptiveSubdivideByCount(aTempPolygon
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
668 findTouches(aTempPolygon
, rPointPolygon
, aTempPointVector
);
670 if(!aTempPointVector
.empty())
672 // adapt tempVector entries to segment
673 adaptAndTransferCutsWithBezierSegment(aTempPointVector
, aTempPolygon
, nInd
, rTempPoints
);
677 void findTouches(const B2DPolygon
& rEdgePolygon
, const B2DPolygon
& rPointPolygon
, temporaryPointVector
& rTempPoints
)
679 // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes,
680 // add entries to rTempPoints
681 const sal_uInt32
nPointCount(rPointPolygon
.count());
682 const sal_uInt32
nEdgePointCount(rEdgePolygon
.count());
684 if(nPointCount
&& nEdgePointCount
)
686 const sal_uInt32
nEdgeCount(rEdgePolygon
.isClosed() ? nEdgePointCount
: nEdgePointCount
- 1);
687 B2DPoint
aCurr(rEdgePolygon
.getB2DPoint(0));
689 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
691 const sal_uInt32
nNextIndex((a
+ 1) % nEdgePointCount
);
692 const B2DPoint
aNext(rEdgePolygon
.getB2DPoint(nNextIndex
));
694 if(!aCurr
.equal(aNext
))
696 bool bHandleAsSimpleEdge(true);
698 if(rEdgePolygon
.areControlPointsUsed())
700 const B2DPoint
aNextControlPoint(rEdgePolygon
.getNextControlPoint(a
));
701 const B2DPoint
aPrevControlPoint(rEdgePolygon
.getPrevControlPoint(nNextIndex
));
702 const bool bEdgeIsCurve(!aNextControlPoint
.equal(aCurr
) || !aPrevControlPoint
.equal(aNext
));
706 bHandleAsSimpleEdge
= false;
707 const B2DCubicBezier
aCubicA(aCurr
, aNextControlPoint
, aPrevControlPoint
, aNext
);
708 findTouchesOnCurve(aCubicA
, rPointPolygon
, a
, rTempPoints
);
712 if(bHandleAsSimpleEdge
)
714 findTouchesOnEdge(aCurr
, aNext
, rPointPolygon
, a
, rTempPoints
);
724 } // end of anonymous namespace
725 } // end of namespace basegfx
732 void findCuts(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
734 // find out if edges from both polygons cut. If so, add entries to rTempPoints which
735 // should be added to the polygons accordingly
736 const sal_uInt32
nPointCountA(rCandidateA
.count());
737 const sal_uInt32
nPointCountB(rCandidateB
.count());
739 if(nPointCountA
&& nPointCountB
)
741 const sal_uInt32
nEdgeCountA(rCandidateA
.isClosed() ? nPointCountA
: nPointCountA
- 1);
742 const sal_uInt32
nEdgeCountB(rCandidateB
.isClosed() ? nPointCountB
: nPointCountB
- 1);
744 if(nEdgeCountA
&& nEdgeCountB
)
746 const bool bCurvesInvolved(rCandidateA
.areControlPointsUsed() || rCandidateB
.areControlPointsUsed());
750 B2DCubicBezier aCubicA
;
751 B2DCubicBezier aCubicB
;
753 for(sal_uInt32
a(0); a
< nEdgeCountA
; a
++)
755 rCandidateA
.getBezierSegment(a
, aCubicA
);
756 aCubicA
.testAndSolveTrivialBezier();
757 const bool bEdgeAIsCurve(aCubicA
.isBezier());
758 const B2DRange
aRangeA(aCubicA
.getRange());
760 for(sal_uInt32
b(0); b
< nEdgeCountB
; b
++)
762 rCandidateB
.getBezierSegment(b
, aCubicB
);
763 aCubicB
.testAndSolveTrivialBezier();
764 const B2DRange
aRangeB(aCubicB
.getRange());
766 // consecutive segments touch of course
767 bool bOverlap
= false;
769 bOverlap
= aRangeA
.overlaps(aRangeB
);
771 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
774 const bool bEdgeBIsCurve(aCubicB
.isBezier());
775 if(bEdgeAIsCurve
&& bEdgeBIsCurve
)
777 // test for bezier-bezier cuts
778 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, rTempPointsA
, rTempPointsB
);
780 else if(bEdgeAIsCurve
)
782 // test for bezier-edge cuts
783 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, rTempPointsA
, rTempPointsB
);
785 else if(bEdgeBIsCurve
)
787 // test for bezier-edge cuts
788 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, rTempPointsB
, rTempPointsA
);
792 // test for simple edge-edge cuts
793 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
794 a
, b
, rTempPointsA
, rTempPointsB
);
802 B2DPoint
aCurrA(rCandidateA
.getB2DPoint(0));
804 for(sal_uInt32
a(0); a
< nEdgeCountA
; a
++)
806 const B2DPoint
aNextA(rCandidateA
.getB2DPoint(a
+ 1 == nPointCountA
? 0 : a
+ 1));
807 const B2DRange
aRangeA(aCurrA
, aNextA
);
808 B2DPoint
aCurrB(rCandidateB
.getB2DPoint(0));
810 for(sal_uInt32
b(0); b
< nEdgeCountB
; b
++)
812 const B2DPoint
aNextB(rCandidateB
.getB2DPoint(b
+ 1 == nPointCountB
? 0 : b
+ 1));
813 const B2DRange
aRangeB(aCurrB
, aNextB
);
815 // consecutive segments touch of course
816 bool bOverlap
= false;
818 bOverlap
= aRangeA
.overlaps(aRangeB
);
820 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
823 // test for simple edge-edge cuts
824 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPointsA
, rTempPointsB
);
839 } // end of anonymous namespace
840 } // end of namespace basegfx
847 B2DPolygon
addPointsAtCutsAndTouches(const B2DPolygon
& rCandidate
)
849 if(rCandidate
.count())
851 temporaryPointVector aTempPoints
;
853 findTouches(rCandidate
, rCandidate
, aTempPoints
);
854 findCuts(rCandidate
, aTempPoints
);
856 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
864 B2DPolyPolygon
addPointsAtCutsAndTouches(const B2DPolyPolygon
& rCandidate
)
866 const sal_uInt32
nCount(rCandidate
.count());
870 B2DPolyPolygon aRetval
;
874 // remove self intersections
875 aRetval
.append(addPointsAtCutsAndTouches(rCandidate
.getB2DPolygon(0)));
879 // first solve self cuts and self touches for all contained single polygons
880 std::unique_ptr
<temporaryPolygonData
[]> pTempData(new temporaryPolygonData
[nCount
]);
883 for(a
= 0; a
< nCount
; a
++)
885 // use polygons with solved self intersections
886 pTempData
[a
].setPolygon(addPointsAtCutsAndTouches(rCandidate
.getB2DPolygon(a
)));
889 // now cuts and touches between the polygons
890 for(a
= 0; a
< nCount
; a
++)
892 for(b
= 0; b
< nCount
; b
++)
896 // look for touches, compare each edge polygon to all other points
897 if(pTempData
[a
].getRange().overlaps(pTempData
[b
].getRange()))
899 findTouches(pTempData
[a
].getPolygon(), pTempData
[b
].getPolygon(), pTempData
[a
].getTemporaryPointVector());
905 // look for cuts, compare each edge polygon to following ones
906 if(pTempData
[a
].getRange().overlaps(pTempData
[b
].getRange()))
908 findCuts(pTempData
[a
].getPolygon(), pTempData
[b
].getPolygon(), pTempData
[a
].getTemporaryPointVector(), pTempData
[b
].getTemporaryPointVector());
914 // consolidate the result
915 for(a
= 0; a
< nCount
; a
++)
917 aRetval
.append(mergeTemporaryPointsAndPolygon(pTempData
[a
].getPolygon(), pTempData
[a
].getTemporaryPointVector()));
929 B2DPolygon
addPointsAtCuts(const B2DPolygon
& rCandidate
, const B2DPoint
& rStart
, const B2DPoint
& rEnd
)
931 const sal_uInt32
nCount(rCandidate
.count());
933 if(nCount
&& !rStart
.equal(rEnd
))
935 const B2DRange
aPolygonRange(rCandidate
.getB2DRange());
936 const B2DRange
aEdgeRange(rStart
, rEnd
);
938 if(aPolygonRange
.overlaps(aEdgeRange
))
940 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nCount
: nCount
- 1);
941 temporaryPointVector aTempPoints
;
942 temporaryPointVector aUnusedTempPoints
;
943 B2DCubicBezier aCubic
;
945 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
947 rCandidate
.getBezierSegment(a
, aCubic
);
948 B2DRange
aCubicRange(aCubic
.getStartPoint(), aCubic
.getEndPoint());
950 if(aCubic
.isBezier())
952 aCubicRange
.expand(aCubic
.getControlPointA());
953 aCubicRange
.expand(aCubic
.getControlPointB());
955 if(aCubicRange
.overlaps(aEdgeRange
))
957 findEdgeCutsBezierAndEdge(aCubic
, rStart
, rEnd
, a
, 0, aTempPoints
, aUnusedTempPoints
);
962 if(aCubicRange
.overlaps(aEdgeRange
))
964 findEdgeCutsTwoEdges(aCubic
.getStartPoint(), aCubic
.getEndPoint(), rStart
, rEnd
, a
, 0, aTempPoints
, aUnusedTempPoints
);
969 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
976 B2DPolygon
addPointsAtCuts(const B2DPolygon
& rCandidate
, const B2DPolyPolygon
& rPolyMask
)
978 const sal_uInt32
nCountA(rCandidate
.count());
979 const sal_uInt32
nCountM(rPolyMask
.count());
981 if(nCountA
&& nCountM
)
983 const B2DRange
aRangeA(rCandidate
.getB2DRange());
984 const B2DRange
aRangeM(rPolyMask
.getB2DRange());
986 if(aRangeA
.overlaps(aRangeM
))
988 const sal_uInt32
nEdgeCountA(rCandidate
.isClosed() ? nCountA
: nCountA
- 1);
989 temporaryPointVector aTempPointsA
;
990 temporaryPointVector aUnusedTempPointsB
;
992 for(sal_uInt32
m(0); m
< nCountM
; m
++)
994 const B2DPolygon
aMask(rPolyMask
.getB2DPolygon(m
));
995 const sal_uInt32
nCountB(aMask
.count());
999 B2DCubicBezier aCubicA
;
1000 B2DCubicBezier aCubicB
;
1002 for(sal_uInt32
a(0); a
< nEdgeCountA
; a
++)
1004 rCandidate
.getBezierSegment(a
, aCubicA
);
1005 const bool bCubicAIsCurve(aCubicA
.isBezier());
1006 B2DRange
aCubicRangeA(aCubicA
.getStartPoint(), aCubicA
.getEndPoint());
1010 aCubicRangeA
.expand(aCubicA
.getControlPointA());
1011 aCubicRangeA
.expand(aCubicA
.getControlPointB());
1014 for(sal_uInt32
b(0); b
< nCountB
; b
++)
1016 aMask
.getBezierSegment(b
, aCubicB
);
1017 const bool bCubicBIsCurve(aCubicB
.isBezier());
1018 B2DRange
aCubicRangeB(aCubicB
.getStartPoint(), aCubicB
.getEndPoint());
1022 aCubicRangeB
.expand(aCubicB
.getControlPointA());
1023 aCubicRangeB
.expand(aCubicB
.getControlPointB());
1026 if(aCubicRangeA
.overlaps(aCubicRangeB
))
1028 if(bCubicAIsCurve
&& bCubicBIsCurve
)
1030 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1032 else if(bCubicAIsCurve
)
1034 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1036 else if(bCubicBIsCurve
)
1038 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, aUnusedTempPointsB
, aTempPointsA
);
1042 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1050 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPointsA
);
1057 } // end of namespace utils
1058 } // end of namespace basegfx
1060 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */