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)
36 //////////////////////////////////////////////////////////////////////////////
42 ////////////////////////////////////////////////////////////////////////////////
46 B2DPoint maPoint
; // the new point
47 sal_uInt32 mnIndex
; // index after which to insert
48 double mfCut
; // parametric cut description [0.0 .. 1.0]
51 temporaryPoint(const B2DPoint
& rNewPoint
, sal_uInt32 nIndex
, double fCut
)
58 bool operator<(const temporaryPoint
& rComp
) const
60 if(mnIndex
== rComp
.mnIndex
)
62 return (mfCut
< rComp
.mfCut
);
65 return (mnIndex
< rComp
.mnIndex
);
68 const B2DPoint
& getPoint() const { return maPoint
; }
69 sal_uInt32
getIndex() const { return mnIndex
; }
70 double getCut() const { return mfCut
; }
73 ////////////////////////////////////////////////////////////////////////////////
75 typedef ::std::vector
< temporaryPoint
> temporaryPointVector
;
77 ////////////////////////////////////////////////////////////////////////////////
79 class temporaryPolygonData
83 temporaryPointVector maPoints
;
86 const B2DPolygon
& getPolygon() const { return maPolygon
; }
87 void setPolygon(const B2DPolygon
& rNew
) { maPolygon
= rNew
; maRange
= tools::getRange(maPolygon
); }
88 const B2DRange
& getRange() const { return maRange
; }
89 temporaryPointVector
& getTemporaryPointVector() { return maPoints
; }
92 ////////////////////////////////////////////////////////////////////////////////
94 B2DPolygon
mergeTemporaryPointsAndPolygon(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
)
96 // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle
97 // single edges with/without control points
98 // #i101491# added counter for non-changing element count
99 const sal_uInt32
nTempPointCount(rTempPoints
.size());
104 const sal_uInt32
nCount(rCandidate
.count());
108 // sort temp points to assure increasing fCut values and increasing indices
109 ::std::sort(rTempPoints
.begin(), rTempPoints
.end());
112 B2DCubicBezier aEdge
;
113 sal_uInt32
nNewInd(0L);
116 aRetval
.append(rCandidate
.getB2DPoint(0));
118 for(sal_uInt32
a(0L); a
< nCount
; a
++)
121 rCandidate
.getBezierSegment(a
, aEdge
);
125 // control vectors involved for this edge
126 double fLeftStart(0.0);
128 // now add all points targeted to be at this index
129 while(nNewInd
< nTempPointCount
&& rTempPoints
[nNewInd
].getIndex() == a
)
131 const temporaryPoint
& rTempPoint
= rTempPoints
[nNewInd
++];
133 // split curve segment. Splits need to come sorted and need to be < 1.0. Also,
134 // since original segment is consumed from left to right, the cut values need
135 // to be scaled to the remaining part
136 B2DCubicBezier aLeftPart
;
137 const double fRelativeSplitPoint((rTempPoint
.getCut() - fLeftStart
) / (1.0 - fLeftStart
));
138 aEdge
.split(fRelativeSplitPoint
, &aLeftPart
, &aEdge
);
139 fLeftStart
= rTempPoint
.getCut();
142 aRetval
.appendBezierSegment(aLeftPart
.getControlPointA(), aLeftPart
.getControlPointB(), rTempPoint
.getPoint());
146 aRetval
.appendBezierSegment(aEdge
.getControlPointA(), aEdge
.getControlPointB(), aEdge
.getEndPoint());
150 // add all points targeted to be at this index
151 while(nNewInd
< nTempPointCount
&& rTempPoints
[nNewInd
].getIndex() == a
)
153 const temporaryPoint
& rTempPoint
= rTempPoints
[nNewInd
++];
154 const B2DPoint
aNewPoint(rTempPoint
.getPoint());
156 // do not add points double
157 if(!aRetval
.getB2DPoint(aRetval
.count() - 1L).equal(aNewPoint
))
159 aRetval
.append(aNewPoint
);
163 // add edge end point
164 aRetval
.append(aEdge
.getEndPoint());
169 if(rCandidate
.isClosed())
171 // set closed flag and correct last point (which is added double now).
172 tools::closeWithGeometryChange(aRetval
);
183 ////////////////////////////////////////////////////////////////////////////////
185 void adaptAndTransferCutsWithBezierSegment(
186 const temporaryPointVector
& rPointVector
, const B2DPolygon
& rPolygon
,
187 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
189 // assuming that the subdivision to create rPolygon used aequidistant pieces
190 // (as in adaptiveSubdivideByCount) it is now possible to calculate back the
191 // cut positions in the polygon to relative cut positions on the original bezier
193 const sal_uInt32
nTempPointCount(rPointVector
.size());
194 const sal_uInt32
nEdgeCount(rPolygon
.count() ? rPolygon
.count() - 1L : 0L);
196 if(nTempPointCount
&& nEdgeCount
)
198 for(sal_uInt32
a(0L); a
< nTempPointCount
; a
++)
200 const temporaryPoint
& rTempPoint
= rPointVector
[a
];
201 const double fCutPosInPolygon((double)rTempPoint
.getIndex() + rTempPoint
.getCut());
202 const double fRelativeCutPos(fCutPosInPolygon
/ (double)nEdgeCount
);
203 rTempPoints
.push_back(temporaryPoint(rTempPoint
.getPoint(), nInd
, fRelativeCutPos
));
208 ////////////////////////////////////////////////////////////////////////////////
210 } // end of anonymous namespace
211 } // end of namespace basegfx
213 //////////////////////////////////////////////////////////////////////////////
219 ////////////////////////////////////////////////////////////////////////////////
220 // predefines for calls to this methods before method implementation
222 void findCuts(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
);
223 void findTouches(const B2DPolygon
& rEdgePolygon
, const B2DPolygon
& rPointPolygon
, temporaryPointVector
& rTempPoints
);
224 void findCuts(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
);
226 ////////////////////////////////////////////////////////////////////////////////
228 void findEdgeCutsTwoEdges(
229 const B2DPoint
& rCurrA
, const B2DPoint
& rNextA
,
230 const B2DPoint
& rCurrB
, const B2DPoint
& rNextB
,
231 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
232 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
234 // no null length edges
235 if(!(rCurrA
.equal(rNextA
) || rCurrB
.equal(rNextB
)))
237 // no common start/end points, this can be no cuts
238 if(!(rCurrB
.equal(rCurrA
) || rCurrB
.equal(rNextA
) || rNextB
.equal(rCurrA
) || rNextB
.equal(rNextA
)))
240 const B2DVector
aVecA(rNextA
- rCurrA
);
241 const B2DVector
aVecB(rNextB
- rCurrB
);
242 double fCut(aVecA
.cross(aVecB
));
244 if(!fTools::equalZero(fCut
))
246 const double fZero(0.0);
247 const double fOne(1.0);
248 fCut
= (aVecB
.getY() * (rCurrB
.getX() - rCurrA
.getX()) + aVecB
.getX() * (rCurrA
.getY() - rCurrB
.getY())) / fCut
;
250 if(fTools::more(fCut
, fZero
) && fTools::less(fCut
, fOne
))
252 // it's a candidate, but also need to test parameter value of cut on line 2
255 // choose the more precise version
256 if(fabs(aVecB
.getX()) > fabs(aVecB
.getY()))
258 fCut2
= (rCurrA
.getX() + (fCut
* aVecA
.getX()) - rCurrB
.getX()) / aVecB
.getX();
262 fCut2
= (rCurrA
.getY() + (fCut
* aVecA
.getY()) - rCurrB
.getY()) / aVecB
.getY();
265 if(fTools::more(fCut2
, fZero
) && fTools::less(fCut2
, fOne
))
267 // cut is in range, add point. Two edges can have only one cut, but
268 // add a cut point to each list. The lists may be the same for
269 // self intersections.
270 const B2DPoint
aCutPoint(interpolate(rCurrA
, rNextA
, fCut
));
271 rTempPointsA
.push_back(temporaryPoint(aCutPoint
, nIndA
, fCut
));
272 rTempPointsB
.push_back(temporaryPoint(aCutPoint
, nIndB
, fCut2
));
280 ////////////////////////////////////////////////////////////////////////////////
282 void findCutsAndTouchesAndCommonForBezier(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
285 // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers
286 // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the
287 // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or
288 // equal points of them.
289 // It would be possible to find the toches using findTouches(), but at last with commpn points
290 // the adding of cut points (temporary points) would fail. But for these temporarily adaptive
291 // subdivided bezier segments, common points may be not very likely, but the bug shows that it
293 // Touch points are a little bit more likely than common points. All in all it is best to use
294 // a specialized method here which can profit from knowing that it is working on a special
295 // family of B2DPolygons: no curve segments included and not closed.
296 OSL_ENSURE(!rCandidateA
.areControlPointsUsed() && !rCandidateB
.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)");
297 OSL_ENSURE(!rCandidateA
.isClosed() && !rCandidateB
.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)");
298 const sal_uInt32
nPointCountA(rCandidateA
.count());
299 const sal_uInt32
nPointCountB(rCandidateB
.count());
301 if(nPointCountA
> 1 && nPointCountB
> 1)
303 const sal_uInt32
nEdgeCountA(nPointCountA
- 1);
304 const sal_uInt32
nEdgeCountB(nPointCountB
- 1);
305 B2DPoint
aCurrA(rCandidateA
.getB2DPoint(0L));
307 for(sal_uInt32
a(0L); a
< nEdgeCountA
; a
++)
309 const B2DPoint
aNextA(rCandidateA
.getB2DPoint(a
+ 1L));
310 const B2DRange
aRangeA(aCurrA
, aNextA
);
311 B2DPoint
aCurrB(rCandidateB
.getB2DPoint(0L));
313 for(sal_uInt32
b(0L); b
< nEdgeCountB
; b
++)
315 const B2DPoint
aNextB(rCandidateB
.getB2DPoint(b
+ 1L));
316 const B2DRange
aRangeB(aCurrB
, aNextB
);
318 if(aRangeA
.overlaps(aRangeB
))
320 // no null length edges
321 if(!(aCurrA
.equal(aNextA
) || aCurrB
.equal(aNextB
)))
323 const B2DVector
aVecA(aNextA
- aCurrA
);
324 const B2DVector
aVecB(aNextB
- aCurrB
);
325 double fCutA(aVecA
.cross(aVecB
));
327 if(!fTools::equalZero(fCutA
))
329 const double fZero(0.0);
330 const double fOne(1.0);
331 fCutA
= (aVecB
.getY() * (aCurrB
.getX() - aCurrA
.getX()) + aVecB
.getX() * (aCurrA
.getY() - aCurrB
.getY())) / fCutA
;
333 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
334 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
335 if(fTools::moreOrEqual(fCutA
, fZero
) && fTools::less(fCutA
, fOne
))
337 // it's a candidate, but also need to test parameter value of cut on line 2
340 // choose the more precise version
341 if(fabs(aVecB
.getX()) > fabs(aVecB
.getY()))
343 fCutB
= (aCurrA
.getX() + (fCutA
* aVecA
.getX()) - aCurrB
.getX()) / aVecB
.getX();
347 fCutB
= (aCurrA
.getY() + (fCutA
* aVecA
.getY()) - aCurrB
.getY()) / aVecB
.getY();
350 // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered
351 // as 0.0 cut. The 1.0 cut will be registered in the next loop step
352 if(fTools::moreOrEqual(fCutB
, fZero
) && fTools::less(fCutB
, fOne
))
354 // cut is in both ranges. Add points for A and B
355 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
356 if(fTools::equal(fCutA
, fZero
))
358 // ignore for start point in first edge; this is handled
359 // by outer methods and would just produce a double point
362 rTempPointsA
.push_back(temporaryPoint(aCurrA
, a
, 0.0));
367 const B2DPoint
aCutPoint(interpolate(aCurrA
, aNextA
, fCutA
));
368 rTempPointsA
.push_back(temporaryPoint(aCutPoint
, a
, fCutA
));
371 // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy
372 if(fTools::equal(fCutB
, fZero
))
374 // ignore for start point in first edge; this is handled
375 // by outer methods and would just produce a double point
378 rTempPointsB
.push_back(temporaryPoint(aCurrB
, b
, 0.0));
383 const B2DPoint
aCutPoint(interpolate(aCurrB
, aNextB
, fCutB
));
384 rTempPointsB
.push_back(temporaryPoint(aCutPoint
, b
, fCutB
));
402 ////////////////////////////////////////////////////////////////////////////////
404 void findEdgeCutsBezierAndEdge(
405 const B2DCubicBezier
& rCubicA
,
406 const B2DPoint
& rCurrB
, const B2DPoint
& rNextB
,
407 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
408 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
410 // find all cuts between given bezier segment and edge. Add an entry to the tempPoints
411 // for each common point with the cut value describing the relative position on given
412 // bezier segment and edge.
413 B2DPolygon aTempPolygonA
;
414 B2DPolygon aTempPolygonEdge
;
415 temporaryPointVector aTempPointVectorA
;
416 temporaryPointVector aTempPointVectorEdge
;
418 // create subdivided polygons and find cuts between them
419 // Keep adaptiveSubdivideByCount due to needed quality
420 aTempPolygonA
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
421 aTempPolygonA
.append(rCubicA
.getStartPoint());
422 rCubicA
.adaptiveSubdivideByCount(aTempPolygonA
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
423 aTempPolygonEdge
.append(rCurrB
);
424 aTempPolygonEdge
.append(rNextB
);
426 // #i76891# using findCuts recursively is not sufficient here
427 findCutsAndTouchesAndCommonForBezier(aTempPolygonA
, aTempPolygonEdge
, aTempPointVectorA
, aTempPointVectorEdge
);
429 if(!aTempPointVectorA
.empty())
431 // adapt tempVector entries to segment
432 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA
, aTempPolygonA
, nIndA
, rTempPointsA
);
435 // append remapped tempVector entries for edge to tempPoints for edge
436 for(sal_uInt32
a(0L); a
< aTempPointVectorEdge
.size(); a
++)
438 const temporaryPoint
& rTempPoint
= aTempPointVectorEdge
[a
];
439 rTempPointsB
.push_back(temporaryPoint(rTempPoint
.getPoint(), nIndB
, rTempPoint
.getCut()));
443 ////////////////////////////////////////////////////////////////////////////////
445 void findEdgeCutsTwoBeziers(
446 const B2DCubicBezier
& rCubicA
,
447 const B2DCubicBezier
& rCubicB
,
448 sal_uInt32 nIndA
, sal_uInt32 nIndB
,
449 temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
451 // find all cuts between the two given bezier segments. Add an entry to the tempPoints
452 // for each common point with the cut value describing the relative position on given
454 B2DPolygon aTempPolygonA
;
455 B2DPolygon aTempPolygonB
;
456 temporaryPointVector aTempPointVectorA
;
457 temporaryPointVector aTempPointVectorB
;
459 // create subdivided polygons and find cuts between them
460 // Keep adaptiveSubdivideByCount due to needed quality
461 aTempPolygonA
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
462 aTempPolygonA
.append(rCubicA
.getStartPoint());
463 rCubicA
.adaptiveSubdivideByCount(aTempPolygonA
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
464 aTempPolygonB
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
465 aTempPolygonB
.append(rCubicB
.getStartPoint());
466 rCubicB
.adaptiveSubdivideByCount(aTempPolygonB
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
468 // #i76891# using findCuts recursively is not sufficient here
469 findCutsAndTouchesAndCommonForBezier(aTempPolygonA
, aTempPolygonB
, aTempPointVectorA
, aTempPointVectorB
);
471 if(!aTempPointVectorA
.empty())
473 // adapt tempVector entries to segment
474 adaptAndTransferCutsWithBezierSegment(aTempPointVectorA
, aTempPolygonA
, nIndA
, rTempPointsA
);
477 if(!aTempPointVectorB
.empty())
479 // adapt tempVector entries to segment
480 adaptAndTransferCutsWithBezierSegment(aTempPointVectorB
, aTempPolygonB
, nIndB
, rTempPointsB
);
484 ////////////////////////////////////////////////////////////////////////////////
486 void findEdgeCutsOneBezier(
487 const B2DCubicBezier
& rCubicA
,
488 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
490 // avoid expensive part of this method if possible
491 // TODO: use hasAnyExtremum() method instead when it becomes available
493 const bool bHasAnyExtremum
= rCubicA
.getMinimumExtremumPosition( fDummy
);
494 if( !bHasAnyExtremum
)
497 // find all self-intersections on the given bezier segment. Add an entry to the tempPoints
498 // for each self intersection point with the cut value describing the relative position on given
500 B2DPolygon aTempPolygon
;
501 temporaryPointVector aTempPointVector
;
503 // create subdivided polygon and find cuts on it
504 // Keep adaptiveSubdivideByCount due to needed quality
505 aTempPolygon
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
506 aTempPolygon
.append(rCubicA
.getStartPoint());
507 rCubicA
.adaptiveSubdivideByCount(aTempPolygon
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
508 findCuts(aTempPolygon
, aTempPointVector
);
510 if(!aTempPointVector
.empty())
512 // adapt tempVector entries to segment
513 adaptAndTransferCutsWithBezierSegment(aTempPointVector
, aTempPolygon
, nInd
, rTempPoints
);
517 ////////////////////////////////////////////////////////////////////////////////
519 void findCuts(const B2DPolygon
& rCandidate
, temporaryPointVector
& rTempPoints
)
521 // find out if there are edges with intersections (self-cuts). If yes, add
522 // entries to rTempPoints accordingly
523 const sal_uInt32
nPointCount(rCandidate
.count());
527 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
: nPointCount
- 1L);
531 const bool bCurvesInvolved(rCandidate
.areControlPointsUsed());
535 B2DCubicBezier aCubicA
;
536 B2DCubicBezier aCubicB
;
538 for(sal_uInt32
a(0L); a
< nEdgeCount
- 1L; a
++)
540 rCandidate
.getBezierSegment(a
, aCubicA
);
541 aCubicA
.testAndSolveTrivialBezier();
542 const bool bEdgeAIsCurve(aCubicA
.isBezier());
543 const B2DRange
aRangeA(aCubicA
.getRange());
547 // curved segments may have self-intersections, do not forget those (!)
548 findEdgeCutsOneBezier(aCubicA
, a
, rTempPoints
);
551 for(sal_uInt32
b(a
+ 1L); b
< nEdgeCount
; b
++)
553 rCandidate
.getBezierSegment(b
, aCubicB
);
554 aCubicB
.testAndSolveTrivialBezier();
555 const B2DRange
aRangeB(aCubicB
.getRange());
557 // only overlapping segments need to be tested
558 // consecutive segments touch of course
559 bool bOverlap
= false;
561 bOverlap
= aRangeA
.overlaps(aRangeB
);
563 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
566 const bool bEdgeBIsCurve(aCubicB
.isBezier());
567 if(bEdgeAIsCurve
&& bEdgeBIsCurve
)
569 // test for bezier-bezier cuts
570 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, rTempPoints
, rTempPoints
);
572 else if(bEdgeAIsCurve
)
574 // test for bezier-edge cuts
575 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, rTempPoints
, rTempPoints
);
577 else if(bEdgeBIsCurve
)
579 // test for bezier-edge cuts
580 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, rTempPoints
, rTempPoints
);
584 // test for simple edge-edge cuts
585 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
586 a
, b
, rTempPoints
, rTempPoints
);
594 B2DPoint
aCurrA(rCandidate
.getB2DPoint(0L));
596 for(sal_uInt32
a(0L); a
< nEdgeCount
- 1L; a
++)
598 const B2DPoint
aNextA(rCandidate
.getB2DPoint(a
+ 1L == nPointCount
? 0L : a
+ 1L));
599 const B2DRange
aRangeA(aCurrA
, aNextA
);
600 B2DPoint
aCurrB(rCandidate
.getB2DPoint(a
+ 1L));
602 for(sal_uInt32
b(a
+ 1L); b
< nEdgeCount
; b
++)
604 const B2DPoint
aNextB(rCandidate
.getB2DPoint(b
+ 1L == nPointCount
? 0L : b
+ 1L));
605 const B2DRange
aRangeB(aCurrB
, aNextB
);
607 // consecutive segments touch of course
608 bool bOverlap
= false;
610 bOverlap
= aRangeA
.overlaps(aRangeB
);
612 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
615 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPoints
, rTempPoints
);
630 ////////////////////////////////////////////////////////////////////////////////
632 } // end of anonymous namespace
633 } // end of namespace basegfx
635 //////////////////////////////////////////////////////////////////////////////
641 ////////////////////////////////////////////////////////////////////////////////
643 void findTouchesOnEdge(
644 const B2DPoint
& rCurr
, const B2DPoint
& rNext
, const B2DPolygon
& rPointPolygon
,
645 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
647 // find out if points from rPointPolygon are positioned on given edge. If Yes, add
648 // points there to represent touches (which may be enter or leave nodes later).
649 const sal_uInt32
nPointCount(rPointPolygon
.count());
653 const B2DRange
aRange(rCurr
, rNext
);
654 const B2DVector
aEdgeVector(rNext
- rCurr
);
655 B2DVector
aNormalizedEdgeVector(aEdgeVector
);
656 aNormalizedEdgeVector
.normalize();
657 bool bTestUsingX(fabs(aEdgeVector
.getX()) > fabs(aEdgeVector
.getY()));
659 for(sal_uInt32
a(0L); a
< nPointCount
; a
++)
661 const B2DPoint
aTestPoint(rPointPolygon
.getB2DPoint(a
));
663 if(aRange
.isInside(aTestPoint
))
665 if(!aTestPoint
.equal(rCurr
) && !aTestPoint
.equal(rNext
))
667 const B2DVector
aTestVector(aTestPoint
- rCurr
);
669 if(areParallel(aNormalizedEdgeVector
, aTestVector
))
671 const double fCut((bTestUsingX
)
672 ? aTestVector
.getX() / aEdgeVector
.getX()
673 : aTestVector
.getY() / aEdgeVector
.getY());
674 const double fZero(0.0);
675 const double fOne(1.0);
677 if(fTools::more(fCut
, fZero
) && fTools::less(fCut
, fOne
))
679 rTempPoints
.push_back(temporaryPoint(aTestPoint
, nInd
, fCut
));
688 ////////////////////////////////////////////////////////////////////////////////
690 void findTouchesOnCurve(
691 const B2DCubicBezier
& rCubicA
, const B2DPolygon
& rPointPolygon
,
692 sal_uInt32 nInd
, temporaryPointVector
& rTempPoints
)
694 // find all points from rPointPolygon which touch the given bezier segment. Add an entry
695 // for each touch to the given pointVector. The cut for that entry is the relative position on
696 // the given bezier segment.
697 B2DPolygon aTempPolygon
;
698 temporaryPointVector aTempPointVector
;
700 // create subdivided polygon and find cuts on it
701 // Keep adaptiveSubdivideByCount due to needed quality
702 aTempPolygon
.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT
+ 8);
703 aTempPolygon
.append(rCubicA
.getStartPoint());
704 rCubicA
.adaptiveSubdivideByCount(aTempPolygon
, SUBDIVIDE_FOR_CUT_TEST_COUNT
);
705 findTouches(aTempPolygon
, rPointPolygon
, aTempPointVector
);
707 if(!aTempPointVector
.empty())
709 // adapt tempVector entries to segment
710 adaptAndTransferCutsWithBezierSegment(aTempPointVector
, aTempPolygon
, nInd
, rTempPoints
);
714 ////////////////////////////////////////////////////////////////////////////////
716 void findTouches(const B2DPolygon
& rEdgePolygon
, const B2DPolygon
& rPointPolygon
, temporaryPointVector
& rTempPoints
)
718 // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes,
719 // add entries to rTempPoints
720 const sal_uInt32
nPointCount(rPointPolygon
.count());
721 const sal_uInt32
nEdgePointCount(rEdgePolygon
.count());
723 if(nPointCount
&& nEdgePointCount
)
725 const sal_uInt32
nEdgeCount(rEdgePolygon
.isClosed() ? nEdgePointCount
: nEdgePointCount
- 1L);
726 B2DPoint
aCurr(rEdgePolygon
.getB2DPoint(0));
728 for(sal_uInt32
a(0L); a
< nEdgeCount
; a
++)
730 const sal_uInt32
nNextIndex((a
+ 1) % nEdgePointCount
);
731 const B2DPoint
aNext(rEdgePolygon
.getB2DPoint(nNextIndex
));
733 if(!aCurr
.equal(aNext
))
735 bool bHandleAsSimpleEdge(true);
737 if(rEdgePolygon
.areControlPointsUsed())
739 const B2DPoint
aNextControlPoint(rEdgePolygon
.getNextControlPoint(a
));
740 const B2DPoint
aPrevControlPoint(rEdgePolygon
.getPrevControlPoint(nNextIndex
));
741 const bool bEdgeIsCurve(!aNextControlPoint
.equal(aCurr
) || !aPrevControlPoint
.equal(aNext
));
745 bHandleAsSimpleEdge
= false;
746 const B2DCubicBezier
aCubicA(aCurr
, aNextControlPoint
, aPrevControlPoint
, aNext
);
747 findTouchesOnCurve(aCubicA
, rPointPolygon
, a
, rTempPoints
);
751 if(bHandleAsSimpleEdge
)
753 findTouchesOnEdge(aCurr
, aNext
, rPointPolygon
, a
, rTempPoints
);
763 ////////////////////////////////////////////////////////////////////////////////
765 } // end of anonymous namespace
766 } // end of namespace basegfx
768 //////////////////////////////////////////////////////////////////////////////
774 ////////////////////////////////////////////////////////////////////////////////
776 void findCuts(const B2DPolygon
& rCandidateA
, const B2DPolygon
& rCandidateB
, temporaryPointVector
& rTempPointsA
, temporaryPointVector
& rTempPointsB
)
778 // find out if edges from both polygons cut. If so, add entries to rTempPoints which
779 // should be added to the polygons accordingly
780 const sal_uInt32
nPointCountA(rCandidateA
.count());
781 const sal_uInt32
nPointCountB(rCandidateB
.count());
783 if(nPointCountA
&& nPointCountB
)
785 const sal_uInt32
nEdgeCountA(rCandidateA
.isClosed() ? nPointCountA
: nPointCountA
- 1L);
786 const sal_uInt32
nEdgeCountB(rCandidateB
.isClosed() ? nPointCountB
: nPointCountB
- 1L);
788 if(nEdgeCountA
&& nEdgeCountB
)
790 const bool bCurvesInvolved(rCandidateA
.areControlPointsUsed() || rCandidateB
.areControlPointsUsed());
794 B2DCubicBezier aCubicA
;
795 B2DCubicBezier aCubicB
;
797 for(sal_uInt32
a(0L); a
< nEdgeCountA
; a
++)
799 rCandidateA
.getBezierSegment(a
, aCubicA
);
800 aCubicA
.testAndSolveTrivialBezier();
801 const bool bEdgeAIsCurve(aCubicA
.isBezier());
802 const B2DRange
aRangeA(aCubicA
.getRange());
804 for(sal_uInt32
b(0L); b
< nEdgeCountB
; b
++)
806 rCandidateB
.getBezierSegment(b
, aCubicB
);
807 aCubicB
.testAndSolveTrivialBezier();
808 const B2DRange
aRangeB(aCubicB
.getRange());
810 // consecutive segments touch of course
811 bool bOverlap
= false;
813 bOverlap
= aRangeA
.overlaps(aRangeB
);
815 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
818 const bool bEdgeBIsCurve(aCubicB
.isBezier());
819 if(bEdgeAIsCurve
&& bEdgeBIsCurve
)
821 // test for bezier-bezier cuts
822 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, rTempPointsA
, rTempPointsB
);
824 else if(bEdgeAIsCurve
)
826 // test for bezier-edge cuts
827 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, rTempPointsA
, rTempPointsB
);
829 else if(bEdgeBIsCurve
)
831 // test for bezier-edge cuts
832 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, rTempPointsB
, rTempPointsA
);
836 // test for simple edge-edge cuts
837 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(),
838 a
, b
, rTempPointsA
, rTempPointsB
);
846 B2DPoint
aCurrA(rCandidateA
.getB2DPoint(0L));
848 for(sal_uInt32
a(0L); a
< nEdgeCountA
; a
++)
850 const B2DPoint
aNextA(rCandidateA
.getB2DPoint(a
+ 1L == nPointCountA
? 0L : a
+ 1L));
851 const B2DRange
aRangeA(aCurrA
, aNextA
);
852 B2DPoint
aCurrB(rCandidateB
.getB2DPoint(0L));
854 for(sal_uInt32
b(0L); b
< nEdgeCountB
; b
++)
856 const B2DPoint
aNextB(rCandidateB
.getB2DPoint(b
+ 1L == nPointCountB
? 0L : b
+ 1L));
857 const B2DRange
aRangeB(aCurrB
, aNextB
);
859 // consecutive segments touch of course
860 bool bOverlap
= false;
862 bOverlap
= aRangeA
.overlaps(aRangeB
);
864 bOverlap
= aRangeA
.overlapsMore(aRangeB
);
867 // test for simple edge-edge cuts
868 findEdgeCutsTwoEdges(aCurrA
, aNextA
, aCurrB
, aNextB
, a
, b
, rTempPointsA
, rTempPointsB
);
883 ////////////////////////////////////////////////////////////////////////////////
885 } // end of anonymous namespace
886 } // end of namespace basegfx
888 //////////////////////////////////////////////////////////////////////////////
894 ////////////////////////////////////////////////////////////////////////////////
896 B2DPolygon
addPointsAtCutsAndTouches(const B2DPolygon
& rCandidate
)
898 if(rCandidate
.count())
900 temporaryPointVector aTempPoints
;
902 findTouches(rCandidate
, rCandidate
, aTempPoints
);
903 findCuts(rCandidate
, aTempPoints
);
905 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
913 ////////////////////////////////////////////////////////////////////////////////
915 B2DPolyPolygon
addPointsAtCutsAndTouches(const B2DPolyPolygon
& rCandidate
, bool bSelfIntersections
)
917 const sal_uInt32
nCount(rCandidate
.count());
921 B2DPolyPolygon aRetval
;
925 if(bSelfIntersections
)
927 // remove self intersections
928 aRetval
.append(addPointsAtCutsAndTouches(rCandidate
.getB2DPolygon(0L)));
933 aRetval
= rCandidate
;
938 // first solve self cuts and self touches for all contained single polygons
939 temporaryPolygonData
*pTempData
= new temporaryPolygonData
[nCount
];
942 for(a
= 0L; a
< nCount
; a
++)
944 if(bSelfIntersections
)
946 // use polygons with solved self intersections
947 pTempData
[a
].setPolygon(addPointsAtCutsAndTouches(rCandidate
.getB2DPolygon(a
)));
951 // copy given polygons
952 pTempData
[a
].setPolygon(rCandidate
.getB2DPolygon(a
));
956 // now cuts and touches between the polygons
957 for(a
= 0L; a
< nCount
; a
++)
959 for(b
= 0L; b
< nCount
; b
++)
963 // look for touches, compare each edge polygon to all other points
964 if(pTempData
[a
].getRange().overlaps(pTempData
[b
].getRange()))
966 findTouches(pTempData
[a
].getPolygon(), pTempData
[b
].getPolygon(), pTempData
[a
].getTemporaryPointVector());
972 // look for cuts, compare each edge polygon to following ones
973 if(pTempData
[a
].getRange().overlaps(pTempData
[b
].getRange()))
975 findCuts(pTempData
[a
].getPolygon(), pTempData
[b
].getPolygon(), pTempData
[a
].getTemporaryPointVector(), pTempData
[b
].getTemporaryPointVector());
981 // consolidate the result
982 for(a
= 0L; a
< nCount
; a
++)
984 aRetval
.append(mergeTemporaryPointsAndPolygon(pTempData
[a
].getPolygon(), pTempData
[a
].getTemporaryPointVector()));
998 ////////////////////////////////////////////////////////////////////////////////
1000 B2DPolygon
addPointsAtCuts(const B2DPolygon
& rCandidate
, const B2DPoint
& rStart
, const B2DPoint
& rEnd
)
1002 const sal_uInt32
nCount(rCandidate
.count());
1004 if(nCount
&& !rStart
.equal(rEnd
))
1006 const B2DRange
aPolygonRange(rCandidate
.getB2DRange());
1007 const B2DRange
aEdgeRange(rStart
, rEnd
);
1009 if(aPolygonRange
.overlaps(aEdgeRange
))
1011 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nCount
: nCount
- 1);
1012 temporaryPointVector aTempPoints
;
1013 temporaryPointVector aUnusedTempPoints
;
1014 B2DCubicBezier aCubic
;
1016 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1018 rCandidate
.getBezierSegment(a
, aCubic
);
1019 B2DRange
aCubicRange(aCubic
.getStartPoint(), aCubic
.getEndPoint());
1021 if(aCubic
.isBezier())
1023 aCubicRange
.expand(aCubic
.getControlPointA());
1024 aCubicRange
.expand(aCubic
.getControlPointB());
1026 if(aCubicRange
.overlaps(aEdgeRange
))
1028 findEdgeCutsBezierAndEdge(aCubic
, rStart
, rEnd
, a
, 0, aTempPoints
, aUnusedTempPoints
);
1033 if(aCubicRange
.overlaps(aEdgeRange
))
1035 findEdgeCutsTwoEdges(aCubic
.getStartPoint(), aCubic
.getEndPoint(), rStart
, rEnd
, a
, 0, aTempPoints
, aUnusedTempPoints
);
1040 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPoints
);
1047 ////////////////////////////////////////////////////////////////////////////////
1049 B2DPolygon
addPointsAtCuts(const B2DPolygon
& rCandidate
, const B2DPolyPolygon
& rPolyMask
)
1051 const sal_uInt32
nCountA(rCandidate
.count());
1052 const sal_uInt32
nCountM(rPolyMask
.count());
1054 if(nCountA
&& nCountM
)
1056 const B2DRange
aRangeA(rCandidate
.getB2DRange());
1057 const B2DRange
aRangeM(rPolyMask
.getB2DRange());
1059 if(aRangeA
.overlaps(aRangeM
))
1061 const sal_uInt32
nEdgeCountA(rCandidate
.isClosed() ? nCountA
: nCountA
- 1);
1062 temporaryPointVector aTempPointsA
;
1063 temporaryPointVector aUnusedTempPointsB
;
1065 for(sal_uInt32
m(0); m
< nCountM
; m
++)
1067 const B2DPolygon
aMask(rPolyMask
.getB2DPolygon(m
));
1068 const sal_uInt32
nCountB(aMask
.count());
1072 B2DCubicBezier aCubicA
;
1073 B2DCubicBezier aCubicB
;
1075 for(sal_uInt32
a(0); a
< nEdgeCountA
; a
++)
1077 rCandidate
.getBezierSegment(a
, aCubicA
);
1078 const bool bCubicAIsCurve(aCubicA
.isBezier());
1079 B2DRange
aCubicRangeA(aCubicA
.getStartPoint(), aCubicA
.getEndPoint());
1083 aCubicRangeA
.expand(aCubicA
.getControlPointA());
1084 aCubicRangeA
.expand(aCubicA
.getControlPointB());
1087 for(sal_uInt32
b(0); b
< nCountB
; b
++)
1089 aMask
.getBezierSegment(b
, aCubicB
);
1090 const bool bCubicBIsCurve(aCubicB
.isBezier());
1091 B2DRange
aCubicRangeB(aCubicB
.getStartPoint(), aCubicB
.getEndPoint());
1095 aCubicRangeB
.expand(aCubicB
.getControlPointA());
1096 aCubicRangeB
.expand(aCubicB
.getControlPointB());
1099 if(aCubicRangeA
.overlaps(aCubicRangeB
))
1101 if(bCubicAIsCurve
&& bCubicBIsCurve
)
1103 findEdgeCutsTwoBeziers(aCubicA
, aCubicB
, a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1105 else if(bCubicAIsCurve
)
1107 findEdgeCutsBezierAndEdge(aCubicA
, aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1109 else if(bCubicBIsCurve
)
1111 findEdgeCutsBezierAndEdge(aCubicB
, aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), b
, a
, aUnusedTempPointsB
, aTempPointsA
);
1115 findEdgeCutsTwoEdges(aCubicA
.getStartPoint(), aCubicA
.getEndPoint(), aCubicB
.getStartPoint(), aCubicB
.getEndPoint(), a
, b
, aTempPointsA
, aUnusedTempPointsB
);
1123 return mergeTemporaryPointsAndPolygon(rCandidate
, aTempPointsA
);
1130 ////////////////////////////////////////////////////////////////////////////////
1132 } // end of namespace tools
1133 } // end of namespace basegfx
1135 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */