Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / basegfx / source / polygon / b2dtrapezoid.cxx
blob2392a7ad7eea26a5ea06dfab38c0abacd79cf684
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/b2dtrapezoid.hxx>
21 #include <basegfx/range/b1drange.hxx>
22 #include <basegfx/polygon/b2dpolygontools.hxx>
23 #include <list>
25 //////////////////////////////////////////////////////////////////////////////
27 namespace basegfx
29 namespace trapezoidhelper
31 //////////////////////////////////////////////////////////////////////////////
32 // helper class to hold a simple ege. This is only used for horizontal edges
33 // currently, thus the YPositions will be equal. I did not create a special
34 // class for this since holdingthe pointers is more effective and also can be
35 // used as baseclass for the traversing edges
37 class TrDeSimpleEdge
39 protected:
40 // pointers to start and end point
41 const B2DPoint* mpStart;
42 const B2DPoint* mpEnd;
44 public:
45 // constructor
46 TrDeSimpleEdge(
47 const B2DPoint* pStart,
48 const B2DPoint* pEnd)
49 : mpStart(pStart),
50 mpEnd(pEnd)
54 // data read access
55 const B2DPoint& getStart() const { return *mpStart; }
56 const B2DPoint& getEnd() const { return *mpEnd; }
59 //////////////////////////////////////////////////////////////////////////////
60 // define vector of simple edges
62 typedef ::std::vector< TrDeSimpleEdge > TrDeSimpleEdges;
64 //////////////////////////////////////////////////////////////////////////////
65 // helper class for holding a traversing edge. It will always have some
66 // distance in YPos. The slope (in a numerically useful form, see comments) is
67 // hold and used in SortValue to allow sorting traversing edges by Y, X and slope
68 // (in that order)
70 class TrDeEdgeEntry : public TrDeSimpleEdge
72 private:
73 // the slope in a numerical useful form for sorting
74 sal_uInt32 mnSortValue;
76 public:
77 // convenience data read access
78 double getDeltaX() const { return mpEnd->getX() - mpStart->getX(); }
79 double getDeltaY() const { return mpEnd->getY() - mpStart->getY(); }
81 // convenience data read access. SortValue is created on demand since
82 // it is not always used
83 sal_uInt32 getSortValue() const
85 if(0 != mnSortValue)
86 return mnSortValue;
88 // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
89 // sal_uInt32 range for maximum precision
90 const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32 / F_PI));
92 // convert to sal_uInt32 value
93 const_cast< TrDeEdgeEntry* >(this)->mnSortValue = sal_uInt32(fRadiant);
95 return mnSortValue;
98 // constructor. SortValue can be given when known, use zero otherwise
99 TrDeEdgeEntry(
100 const B2DPoint* pStart,
101 const B2DPoint* pEnd,
102 sal_uInt32 nSortValue = 0)
103 : TrDeSimpleEdge(pStart, pEnd),
104 mnSortValue(nSortValue)
106 // force traversal of deltaY downward
107 if(mpEnd->getY() < mpStart->getY())
109 std::swap(mpStart, mpEnd);
112 // no horizontal edges allowed, all neeed to traverse vertically
113 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
116 // data write access to StartPoint
117 void setStart( const B2DPoint* pNewStart)
119 OSL_ENSURE(0 != pNewStart, "No null pointer allowed here (!)");
121 if(mpStart != pNewStart)
123 mpStart = pNewStart;
125 // no horizontal edges allowed, all neeed to traverse vertivally
126 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
130 // data write access to EndPoint
131 void setEnd( const B2DPoint* pNewEnd)
133 OSL_ENSURE(0 != pNewEnd, "No null pointer allowed here (!)");
135 if(mpEnd != pNewEnd)
137 mpEnd = pNewEnd;
139 // no horizontal edges allowed, all neeed to traverse vertivally
140 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
144 // operator for sort support. Sort by Y, X and slope (in that order)
145 bool operator<(const TrDeEdgeEntry& rComp) const
147 if(fTools::equal(getStart().getY(), rComp.getStart().getY(), fTools::getSmallValue()))
149 if(fTools::equal(getStart().getX(), rComp.getStart().getX(), fTools::getSmallValue()))
151 // when start points are equal, use the direction the edge is pointing
152 // to. That value is created on demand and derived from atan2 in the
153 // range ]0.0 .. pi[ (without extremas, we always have a deltaY in this
154 // class) and scaled to sal_uInt32 range for best precision. 0 means no angle,
155 // while SAL_MAX_UINT32 means pi. Thus, the higher the value, the more left
156 // the edge traverses.
157 return (getSortValue() > rComp.getSortValue());
159 else
161 return fTools::less(getStart().getX(), rComp.getStart().getX());
164 else
166 return fTools::less(getStart().getY(), rComp.getStart().getY());
170 // method for cut support
171 B2DPoint getCutPointForGivenY(double fGivenY)
173 // Calculate cut point locally (do not use interpolate) since it is numerically
174 // necessary to guarantee the new, equal Y-coordinate
175 const double fFactor((fGivenY - getStart().getY()) / getDeltaY());
176 const double fDeltaXNew(fFactor * getDeltaX());
178 return B2DPoint(getStart().getX() + fDeltaXNew, fGivenY);
182 //////////////////////////////////////////////////////////////////////////////
183 // define double linked list of edges (for fast random insert)
185 typedef ::std::list< TrDeEdgeEntry > TrDeEdgeEntries;
187 } // end of anonymous namespace
188 } // end of namespace basegfx
190 //////////////////////////////////////////////////////////////////////////////
192 namespace basegfx
194 namespace trapezoidhelper
196 // helper class to handle the complete trapezoid subdivision of a PolyPolygon
197 class TrapezoidSubdivider
199 private:
200 // local data
201 sal_uInt32 mnInitialEdgeEntryCount;
202 TrDeEdgeEntries maTrDeEdgeEntries;
203 ::std::vector< B2DPoint > maPoints;
204 ::std::vector< B2DPoint* > maNewPoints;
206 void addEdgeSorted(
207 TrDeEdgeEntries::iterator aCurrent,
208 const TrDeEdgeEntry& rNewEdge)
210 // Loop while new entry is bigger, use operator<
211 while(aCurrent != maTrDeEdgeEntries.end() && (*aCurrent) < rNewEdge)
213 ++aCurrent;
216 // Insert before first which is smaller or equal or at end
217 maTrDeEdgeEntries.insert(aCurrent, rNewEdge);
220 bool splitEdgeAtGivenPoint(
221 TrDeEdgeEntries::reference aEdge,
222 const B2DPoint& rCutPoint,
223 TrDeEdgeEntries::iterator aCurrent)
225 // do not create edges without deltaY: do not split when start is identical
226 if(aEdge.getStart().equal(rCutPoint, fTools::getSmallValue()))
228 return false;
231 // do not create edges without deltaY: do not split when end is identical
232 if(aEdge.getEnd().equal(rCutPoint, fTools::getSmallValue()))
234 return false;
237 const double fOldDeltaYStart(rCutPoint.getY() - aEdge.getStart().getY());
239 if(fTools::lessOrEqual(fOldDeltaYStart, 0.0))
241 // do not split: the resulting edge would be horizontal
242 // correct it to new start point
243 aEdge.setStart(&rCutPoint);
244 return false;
247 const double fNewDeltaYStart(aEdge.getEnd().getY() - rCutPoint.getY());
249 if(fTools::lessOrEqual(fNewDeltaYStart, 0.0))
251 // do not split: the resulting edge would be horizontal
252 // correct it to new end point
253 aEdge.setEnd(&rCutPoint);
254 return false;
257 // Create new entry
258 const TrDeEdgeEntry aNewEdge(
259 &rCutPoint,
260 &aEdge.getEnd(),
261 aEdge.getSortValue());
263 // Correct old entry
264 aEdge.setEnd(&rCutPoint);
266 // Insert sorted (to avoid new sort)
267 addEdgeSorted(aCurrent, aNewEdge);
269 return true;
272 bool testAndCorrectEdgeIntersection(
273 TrDeEdgeEntries::reference aEdgeA,
274 TrDeEdgeEntries::reference aEdgeB,
275 TrDeEdgeEntries::iterator aCurrent)
277 // Exclude simple cases: same start or end point
278 if(aEdgeA.getStart().equal(aEdgeB.getStart(), fTools::getSmallValue()))
280 return false;
283 if(aEdgeA.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
285 return false;
288 if(aEdgeA.getEnd().equal(aEdgeB.getStart(), fTools::getSmallValue()))
290 return false;
293 if(aEdgeA.getEnd().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
295 return false;
298 // Exclude simple cases: one of the edges has no length anymore
299 if(aEdgeA.getStart().equal(aEdgeA.getEnd(), fTools::getSmallValue()))
301 return false;
304 if(aEdgeB.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
306 return false;
309 // check if one point is on the other edge (a touch, not a cut)
310 const B2DVector aDeltaB(aEdgeB.getDeltaX(), aEdgeB.getDeltaY());
312 if(tools::isPointOnEdge(aEdgeA.getStart(), aEdgeB.getStart(), aDeltaB))
314 return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getStart(), aCurrent);
317 if(tools::isPointOnEdge(aEdgeA.getEnd(), aEdgeB.getStart(), aDeltaB))
319 return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getEnd(), aCurrent);
322 const B2DVector aDeltaA(aEdgeA.getDeltaX(), aEdgeA.getDeltaY());
324 if(tools::isPointOnEdge(aEdgeB.getStart(), aEdgeA.getStart(), aDeltaA))
326 return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getStart(), aCurrent);
329 if(tools::isPointOnEdge(aEdgeB.getEnd(), aEdgeA.getStart(), aDeltaA))
331 return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getEnd(), aCurrent);
334 // check for cut inside edges. Use both t-values to choose the more precise
335 // one later
336 double fCutA(0.0);
337 double fCutB(0.0);
339 if(tools::findCut(
340 aEdgeA.getStart(), aDeltaA,
341 aEdgeB.getStart(), aDeltaB,
342 CUTFLAG_LINE,
343 &fCutA,
344 &fCutB))
346 // use a simple metric (length criteria) for choosing the numerically
347 // better cut
348 const double fSimpleLengthA(aDeltaA.getX() + aDeltaA.getY());
349 const double fSimpleLengthB(aDeltaB.getX() + aDeltaB.getY());
350 const bool bAIsLonger(fSimpleLengthA > fSimpleLengthB);
351 B2DPoint* pNewPoint = bAIsLonger
352 ? new B2DPoint(aEdgeA.getStart() + (fCutA * aDeltaA))
353 : new B2DPoint(aEdgeB.getStart() + (fCutB * aDeltaB));
354 bool bRetval(false);
356 // try to split both edges
357 bRetval = splitEdgeAtGivenPoint(aEdgeA, *pNewPoint, aCurrent);
358 bRetval |= splitEdgeAtGivenPoint(aEdgeB, *pNewPoint, aCurrent);
360 if(bRetval)
362 maNewPoints.push_back(pNewPoint);
364 else
366 delete pNewPoint;
369 return bRetval;
372 return false;
375 void solveHorizontalEdges(TrDeSimpleEdges& rTrDeSimpleEdges)
377 if(rTrDeSimpleEdges.size() && maTrDeEdgeEntries.size())
379 // there were horizontal edges. These can be excluded, but
380 // cuts with other edges need to be solved and added before
381 // ignoring them
382 sal_uInt32 a(0);
384 for(a = 0; a < rTrDeSimpleEdges.size(); a++)
386 // get horizontal edge as candidate; prepare it's range and fixed Y
387 const TrDeSimpleEdge& rHorEdge = rTrDeSimpleEdges[a];
388 const B1DRange aRange(rHorEdge.getStart().getX(), rHorEdge.getEnd().getX());
389 const double fFixedY(rHorEdge.getStart().getY());
391 // loop over traversing edges
392 TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
396 // get compare edge
397 TrDeEdgeEntries::reference aCompare(*aCurrent++);
399 if(fTools::lessOrEqual(aCompare.getEnd().getY(), fFixedY))
401 // edge ends above horizontal edge, continue
402 continue;
405 if(fTools::moreOrEqual(aCompare.getStart().getY(), fFixedY))
407 // edge starts below horizontal edge, continue
408 continue;
411 // vertical overlap, get horizontal range
412 const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
414 if(aRange.overlaps(aCompareRange))
416 // possible cut, get cut point
417 const B2DPoint aSplit(aCompare.getCutPointForGivenY(fFixedY));
419 if(fTools::more(aSplit.getX(), aRange.getMinimum())
420 && fTools::less(aSplit.getX(), aRange.getMaximum()))
422 // cut is in XRange of horizontal edge, potenitally needed cut
423 B2DPoint* pNewPoint = new B2DPoint(aSplit);
425 if(splitEdgeAtGivenPoint(aCompare, *pNewPoint, aCurrent))
427 maNewPoints.push_back(pNewPoint);
429 else
431 delete pNewPoint;
436 while(aCurrent != maTrDeEdgeEntries.end()
437 && fTools::less(aCurrent->getStart().getY(), fFixedY));
442 public:
443 explicit TrapezoidSubdivider(
444 const B2DPolyPolygon& rSourcePolyPolygon)
445 : mnInitialEdgeEntryCount(0),
446 maTrDeEdgeEntries(),
447 maPoints(),
448 maNewPoints()
450 B2DPolyPolygon aSource(rSourcePolyPolygon);
451 const sal_uInt32 nPolygonCount(rSourcePolyPolygon.count());
452 TrDeSimpleEdges aTrDeSimpleEdges;
453 sal_uInt32 a(0), b(0);
454 sal_uInt32 nAllPointCount(0);
456 // ensure there are no curves used
457 if(aSource.areControlPointsUsed())
459 aSource = aSource.getDefaultAdaptiveSubdivision();
462 for(a = 0; a < nPolygonCount; a++)
464 // 1st run: count points
465 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
466 const sal_uInt32 nCount(aPolygonCandidate.count());
468 if(nCount > 2)
470 nAllPointCount += nCount;
474 if(nAllPointCount)
476 // reserve needed points. CAUTION: maPoints size is NOT to be changed anymore
477 // after 2nd loop since pointers to it are used in the edges
478 maPoints.reserve(nAllPointCount);
480 for(a = 0; a < nPolygonCount; a++)
482 // 2nd run: add points
483 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
484 const sal_uInt32 nCount(aPolygonCandidate.count());
486 if(nCount > 2)
488 for(b = 0; b < nCount; b++)
490 maPoints.push_back(aPolygonCandidate.getB2DPoint(b));
495 // Moved the edge construction to a 3rd run: doing it in the 2nd run is
496 // possible(and i used it), but requires a working vector::reserve()
497 // implementation, else the vector will be reallocated and the pointers
498 // in the edges may be wrong. Security first here.
499 sal_uInt32 nStartIndex(0);
501 for(a = 0; a < nPolygonCount; a++)
503 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
504 const sal_uInt32 nCount(aPolygonCandidate.count());
506 if(nCount > 2)
508 // get the last point of the current polygon
509 B2DPoint* pPrev(&maPoints[nCount + nStartIndex - 1]);
511 for(b = 0; b < nCount; b++)
513 // get next point
514 B2DPoint* pCurr(&maPoints[nStartIndex++]);
516 if(fTools::equal(pPrev->getY(), pCurr->getY(), fTools::getSmallValue()))
518 // horizontal edge, check for single point
519 if(!fTools::equal(pPrev->getX(), pCurr->getX(), fTools::getSmallValue()))
521 // X-order not needed, just add
522 aTrDeSimpleEdges.push_back(TrDeSimpleEdge(pPrev, pCurr));
524 const double fMiddle((pPrev->getY() + pCurr->getY()) * 0.5);
525 pPrev->setY(fMiddle);
526 pCurr->setY(fMiddle);
529 else
531 // vertical edge. Positive Y-direction is guaranteed by the
532 // TrDeEdgeEntry constructor
533 maTrDeEdgeEntries.push_back(TrDeEdgeEntry(pPrev, pCurr, 0));
534 mnInitialEdgeEntryCount++;
537 // prepare next step
538 pPrev = pCurr;
544 if(!maTrDeEdgeEntries.empty())
546 // single and initial sort of traversing edges
547 maTrDeEdgeEntries.sort();
549 // solve horizontal edges if there are any detected
550 solveHorizontalEdges(aTrDeSimpleEdges);
554 ~TrapezoidSubdivider()
556 // delete the extra points created for cuts
557 const sal_uInt32 nCount(maNewPoints.size());
559 for(sal_uInt32 a(0); a < nCount; a++)
561 delete maNewPoints[a];
565 void Subdivide(B2DTrapezoidVector& ro_Result)
567 // This is the central subdivider. The strategy is to use the first two entries
568 // from the traversing edges as a potential trapezoid and do the needed corrections
569 // and adaptions on the way.
571 // There always must be two edges with the same YStart value: When adding the polygons
572 // in the constructor, there is always a topmost point from which two edges start; when
573 // the topmost is an edge, there is a start and end of this edge from which two edges
574 // start. All cases have two edges with same StartY (QED).
576 // Based on this these edges get corrected when:
577 // - one is longer than the other
578 // - they intersect
579 // - they intersect with other edges
580 // - another edge starts inside the thought trapezoid
582 // All this cases again produce a valid state so that the first two edges have a common
583 // Ystart again. Some cases lead to a restart of the process, some allow consuming the
584 // edges and create the intended trapezoid.
586 // Be careful when doing chages here: It is essential to keep all possible paths
587 // in valid states and to be numerically correct. This is especially needed e.g.
588 // by using fTools::equal(..) in the more robust small-value incarnation.
589 B1DRange aLeftRange;
590 B1DRange aRightRange;
592 if(!maTrDeEdgeEntries.empty())
594 // measuring shows that the relation between edges and created trapezoids is
595 // mostly in the 1:1 range, thus reserve as much trapezoids as edges exist. Do
596 // not use maTrDeEdgeEntries.size() since that may be a non-constant time
597 // operation for Lists. Instead, use mnInitialEdgeEntryCount which will contain
598 // the roughly counted adds to the List
599 ro_Result.reserve(ro_Result.size() + mnInitialEdgeEntryCount);
602 while(!maTrDeEdgeEntries.empty())
604 // Prepare current operator and get first edge
605 TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
606 TrDeEdgeEntries::reference aLeft(*aCurrent++);
608 if(aCurrent == maTrDeEdgeEntries.end())
610 // Should not happen: No 2nd edge; consume the single edge
611 // to not have an endless loop and start next. During development
612 // i constantly had breakpoints here, so i am sure enough to add an
613 // assertion here
614 OSL_FAIL("Trapeziod decomposer in illegal state (!)");
615 maTrDeEdgeEntries.pop_front();
616 continue;
619 // get second edge
620 TrDeEdgeEntries::reference aRight(*aCurrent++);
622 if(!fTools::equal(aLeft.getStart().getY(), aRight.getStart().getY(), fTools::getSmallValue()))
624 // Should not happen: We have a 2nd edge, but YStart is on another
625 // line; consume the single edge to not have an endless loop and start
626 // next. During development i constantly had breakpoints here, so i am
627 // sure enough to add an assertion here
628 OSL_FAIL("Trapeziod decomposer in illegal state (!)");
629 maTrDeEdgeEntries.pop_front();
630 continue;
633 // aLeft and aRight build a thought trapezoid now. They have a common
634 // start line (same Y for start points). Potentially, one of the edges
635 // is longer than the other. It is only needed to look at the shorter
636 // length which build the potential trapezoid. To do so, get the end points
637 // locally and adapt the evtl. longer one. Use only aLeftEnd and aRightEnd
638 // from here on, not the aLeft.getEnd() or aRight.getEnd() accesses.
639 B2DPoint aLeftEnd(aLeft.getEnd());
640 B2DPoint aRightEnd(aRight.getEnd());
642 // check if end points are on the same line. If yes, no adaption
643 // needs to be prepared. Also remember which one actually is longer.
644 const bool bEndOnSameLine(fTools::equal(aLeftEnd.getY(), aRightEnd.getY(), fTools::getSmallValue()));
645 bool bLeftIsLonger(false);
647 if(!bEndOnSameLine)
649 // check which edge is longer and correct accordingly
650 bLeftIsLonger = fTools::more(aLeftEnd.getY(), aRightEnd.getY());
652 if(bLeftIsLonger)
654 aLeftEnd = aLeft.getCutPointForGivenY(aRightEnd.getY());
656 else
658 aRightEnd = aRight.getCutPointForGivenY(aLeftEnd.getY());
662 // check for same start and end points
663 const bool bSameStartPoint(aLeft.getStart().equal(aRight.getStart(), fTools::getSmallValue()));
664 const bool bSameEndPoint(aLeftEnd.equal(aRightEnd, fTools::getSmallValue()));
666 // check the simple case that the edges form a 'blind' edge (deadend)
667 if(bSameStartPoint && bSameEndPoint)
669 // correct the longer edge if prepared
670 if(!bEndOnSameLine)
672 if(bLeftIsLonger)
674 B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
676 if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
678 maNewPoints.push_back(pNewPoint);
680 else
682 delete pNewPoint;
685 else
687 B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
689 if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
691 maNewPoints.push_back(pNewPoint);
693 else
695 delete pNewPoint;
700 // consume both edges and start next run
701 maTrDeEdgeEntries.pop_front();
702 maTrDeEdgeEntries.pop_front();
704 continue;
707 // check if the edges self-intersect. This can only happen when
708 // start and end point are different
709 bool bRangesSet(false);
711 if(!(bSameStartPoint || bSameEndPoint))
713 // get XRanges of edges
714 aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
715 aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
716 bRangesSet = true;
718 // use fast range test first
719 if(aLeftRange.overlaps(aRightRange))
721 // real cut test and correction. If correction was needed,
722 // start new run
723 if(testAndCorrectEdgeIntersection(aLeft, aRight, aCurrent))
725 continue;
730 // now we need to check if there are intersections with other edges
731 // or if other edges start inside the candidate trapezoid
732 if(aCurrent != maTrDeEdgeEntries.end()
733 && fTools::less(aCurrent->getStart().getY(), aLeftEnd.getY()))
735 // get XRanges of edges
736 if(!bRangesSet)
738 aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
739 aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
742 // build full XRange for fast check
743 B1DRange aAllRange(aLeftRange);
744 aAllRange.expand(aRightRange);
746 // prepare loop iterator; aCurrent needs to stay unchanged for
747 // eventual sorted insertions of new EdgeNodes. Also prepare stop flag
748 TrDeEdgeEntries::iterator aLoop(aCurrent);
749 bool bDone(false);
753 // get compare edge and it's XRange
754 TrDeEdgeEntries::reference aCompare(*aLoop++);
756 // avoid edges using the same start point as one of
757 // the edges. These can neither have their start point
758 // in the thought trapezoid nor cut with one of the edges
759 if(aCompare.getStart().equal(aRight.getStart(), fTools::getSmallValue()))
761 continue;
764 // get compare XRange
765 const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
767 // use fast range test first
768 if(aAllRange.overlaps(aCompareRange))
770 // check for start point inside thought trapezoid
771 if(fTools::more(aCompare.getStart().getY(), aLeft.getStart().getY()))
773 // calculate the two possible split points at compare's Y
774 const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aCompare.getStart().getY()));
775 const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aCompare.getStart().getY()));
777 // check for start point of aCompare being inside thought
778 // trapezoid
779 if(aCompare.getStart().getX() >= aSplitLeft.getX() &&
780 aCompare.getStart().getX() <= aSplitRight.getX())
782 // is inside, correct and restart loop
783 B2DPoint* pNewLeft = new B2DPoint(aSplitLeft);
785 if(splitEdgeAtGivenPoint(aLeft, *pNewLeft, aCurrent))
787 maNewPoints.push_back(pNewLeft);
788 bDone = true;
790 else
792 delete pNewLeft;
795 B2DPoint* pNewRight = new B2DPoint(aSplitRight);
797 if(splitEdgeAtGivenPoint(aRight, *pNewRight, aCurrent))
799 maNewPoints.push_back(pNewRight);
800 bDone = true;
802 else
804 delete pNewRight;
809 if(!bDone && aLeftRange.overlaps(aCompareRange))
811 // test for concrete cut of compare edge with left edge
812 bDone = testAndCorrectEdgeIntersection(aLeft, aCompare, aCurrent);
815 if(!bDone && aRightRange.overlaps(aCompareRange))
817 // test for concrete cut of compare edge with Right edge
818 bDone = testAndCorrectEdgeIntersection(aRight, aCompare, aCurrent);
822 while(!bDone
823 && aLoop != maTrDeEdgeEntries.end()
824 && fTools::less(aLoop->getStart().getY(), aLeftEnd.getY()));
826 if(bDone)
828 // something needed to be changed; start next loop
829 continue;
833 // when we get here, the intended trapezoid can be used. It needs to
834 // be corrected, eventually (if prepared); but this is no reason not to
835 // use it in the same loop iteration
836 if(!bEndOnSameLine)
838 if(bLeftIsLonger)
840 B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
842 if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
844 maNewPoints.push_back(pNewPoint);
846 else
848 delete pNewPoint;
851 else
853 B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
855 if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
857 maNewPoints.push_back(pNewPoint);
859 else
861 delete pNewPoint;
866 // the two edges start at the same Y, they use the same DeltaY, they
867 // do not cut themselves and not any other edge in range. Create a
868 // B2DTrapezoid and consume both edges
869 ro_Result.push_back(
870 B2DTrapezoid(
871 aLeft.getStart().getX(),
872 aRight.getStart().getX(),
873 aLeft.getStart().getY(),
874 aLeftEnd.getX(),
875 aRightEnd.getX(),
876 aLeftEnd.getY()));
878 maTrDeEdgeEntries.pop_front();
879 maTrDeEdgeEntries.pop_front();
883 } // end of anonymous namespace
884 } // end of namespace basegfx
886 //////////////////////////////////////////////////////////////////////////////
888 namespace basegfx
890 B2DTrapezoid::B2DTrapezoid(
891 const double& rfTopXLeft,
892 const double& rfTopXRight,
893 const double& rfTopY,
894 const double& rfBottomXLeft,
895 const double& rfBottomXRight,
896 const double& rfBottomY)
897 : mfTopXLeft(rfTopXLeft),
898 mfTopXRight(rfTopXRight),
899 mfTopY(rfTopY),
900 mfBottomXLeft(rfBottomXLeft),
901 mfBottomXRight(rfBottomXRight),
902 mfBottomY(rfBottomY)
904 // guarantee mfTopXRight >= mfTopXLeft
905 if(mfTopXLeft > mfTopXRight)
907 std::swap(mfTopXLeft, mfTopXRight);
910 // guarantee mfBottomXRight >= mfBottomXLeft
911 if(mfBottomXLeft > mfBottomXRight)
913 std::swap(mfBottomXLeft, mfBottomXRight);
916 // guarantee mfBottomY >= mfTopY
917 if(mfTopY > mfBottomY)
919 std::swap(mfTopY, mfBottomY);
920 std::swap(mfTopXLeft, mfBottomXLeft);
921 std::swap(mfTopXRight, mfBottomXRight);
925 B2DPolygon B2DTrapezoid::getB2DPolygon() const
927 B2DPolygon aRetval;
929 aRetval.append(B2DPoint(getTopXLeft(), getTopY()));
930 aRetval.append(B2DPoint(getTopXRight(), getTopY()));
931 aRetval.append(B2DPoint(getBottomXRight(), getBottomY()));
932 aRetval.append(B2DPoint(getBottomXLeft(), getBottomY()));
933 aRetval.setClosed(true);
935 return aRetval;
937 } // end of namespace basegfx
939 //////////////////////////////////////////////////////////////////////////////
941 namespace basegfx
943 namespace tools
945 // convert Source PolyPolygon to trapezoids
946 void trapezoidSubdivide(B2DTrapezoidVector& ro_Result, const B2DPolyPolygon& rSourcePolyPolygon)
948 trapezoidhelper::TrapezoidSubdivider aTrapezoidSubdivider(rSourcePolyPolygon);
950 aTrapezoidSubdivider.Subdivide(ro_Result);
953 void createLineTrapezoidFromEdge(
954 B2DTrapezoidVector& ro_Result,
955 const B2DPoint& rPointA,
956 const B2DPoint& rPointB,
957 double fLineWidth)
959 if(fTools::lessOrEqual(fLineWidth, 0.0))
961 // no line witdh
962 return;
965 if(rPointA.equal(rPointB, fTools::getSmallValue()))
967 // points are equal, no edge
968 return;
971 const double fHalfLineWidth(0.5 * fLineWidth);
973 if(fTools::equal(rPointA.getX(), rPointB.getX(), fTools::getSmallValue()))
975 // vertical line
976 const double fLeftX(rPointA.getX() - fHalfLineWidth);
977 const double fRightX(rPointA.getX() + fHalfLineWidth);
979 ro_Result.push_back(
980 B2DTrapezoid(
981 fLeftX,
982 fRightX,
983 std::min(rPointA.getY(), rPointB.getY()),
984 fLeftX,
985 fRightX,
986 std::max(rPointA.getY(), rPointB.getY())));
988 else if(fTools::equal(rPointA.getY(), rPointB.getY(), fTools::getSmallValue()))
990 // horizontal line
991 const double fLeftX(std::min(rPointA.getX(), rPointB.getX()));
992 const double fRightX(std::max(rPointA.getX(), rPointB.getX()));
994 ro_Result.push_back(
995 B2DTrapezoid(
996 fLeftX,
997 fRightX,
998 rPointA.getY() - fHalfLineWidth,
999 fLeftX,
1000 fRightX,
1001 rPointA.getY() + fHalfLineWidth));
1003 else
1005 // diagonal line
1006 // create perpendicular vector
1007 const B2DVector aDelta(rPointB - rPointA);
1008 B2DVector aPerpendicular(-aDelta.getY(), aDelta.getX());
1009 aPerpendicular.setLength(fHalfLineWidth);
1011 // create StartLow, StartHigh, EndLow and EndHigh
1012 const B2DPoint aStartLow(rPointA + aPerpendicular);
1013 const B2DPoint aStartHigh(rPointA - aPerpendicular);
1014 const B2DPoint aEndHigh(rPointB - aPerpendicular);
1015 const B2DPoint aEndLow(rPointB + aPerpendicular);
1017 // create EdgeEntries
1018 basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries;
1020 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow, &aStartHigh, 0));
1021 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh, &aEndHigh, 0));
1022 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh, &aEndLow, 0));
1023 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow, &aStartLow, 0));
1024 aTrDeEdgeEntries.sort();
1026 // here we know we have exactly four edges, and they do not cut, touch or
1027 // intersect. This makes processing much easier. Get the first two as start
1028 // edges for the thought trapezoid
1029 basegfx::trapezoidhelper::TrDeEdgeEntries::iterator aCurrent(aTrDeEdgeEntries.begin());
1030 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft(*aCurrent++);
1031 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight(*aCurrent++);
1032 const bool bEndOnSameLine(fTools::equal(aLeft.getEnd().getY(), aRight.getEnd().getY(), fTools::getSmallValue()));
1034 if(bEndOnSameLine)
1036 // create two triangle trapezoids
1037 ro_Result.push_back(
1038 B2DTrapezoid(
1039 aLeft.getStart().getX(),
1040 aRight.getStart().getX(),
1041 aLeft.getStart().getY(),
1042 aLeft.getEnd().getX(),
1043 aRight.getEnd().getX(),
1044 aLeft.getEnd().getY()));
1046 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1047 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1049 ro_Result.push_back(
1050 B2DTrapezoid(
1051 aLeft2.getStart().getX(),
1052 aRight2.getStart().getX(),
1053 aLeft2.getStart().getY(),
1054 aLeft2.getEnd().getX(),
1055 aRight2.getEnd().getX(),
1056 aLeft2.getEnd().getY()));
1058 else
1060 // create three trapezoids. Check which edge is longer and
1061 // correct accordingly
1062 const bool bLeftIsLonger(fTools::more(aLeft.getEnd().getY(), aRight.getEnd().getY()));
1064 if(bLeftIsLonger)
1066 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1067 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1068 const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aRight.getEnd().getY()));
1069 const B2DPoint aSplitRight(aRight2.getCutPointForGivenY(aLeft.getEnd().getY()));
1071 ro_Result.push_back(
1072 B2DTrapezoid(
1073 aLeft.getStart().getX(),
1074 aRight.getStart().getX(),
1075 aLeft.getStart().getY(),
1076 aSplitLeft.getX(),
1077 aRight.getEnd().getX(),
1078 aRight.getEnd().getY()));
1080 ro_Result.push_back(
1081 B2DTrapezoid(
1082 aSplitLeft.getX(),
1083 aRight.getEnd().getX(),
1084 aRight.getEnd().getY(),
1085 aLeft2.getStart().getX(),
1086 aSplitRight.getX(),
1087 aLeft2.getStart().getY()));
1089 ro_Result.push_back(
1090 B2DTrapezoid(
1091 aLeft2.getStart().getX(),
1092 aSplitRight.getX(),
1093 aLeft2.getStart().getY(),
1094 aLeft2.getEnd().getX(),
1095 aRight2.getEnd().getX(),
1096 aLeft2.getEnd().getY()));
1098 else
1100 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1101 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1102 const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aLeft.getEnd().getY()));
1103 const B2DPoint aSplitLeft(aLeft2.getCutPointForGivenY(aRight.getEnd().getY()));
1105 ro_Result.push_back(
1106 B2DTrapezoid(
1107 aLeft.getStart().getX(),
1108 aRight.getStart().getX(),
1109 aLeft.getStart().getY(),
1110 aLeft.getEnd().getX(),
1111 aSplitRight.getX(),
1112 aLeft.getEnd().getY()));
1114 ro_Result.push_back(
1115 B2DTrapezoid(
1116 aLeft.getEnd().getX(),
1117 aSplitRight.getX(),
1118 aLeft.getEnd().getY(),
1119 aSplitLeft.getX(),
1120 aRight.getEnd().getX(),
1121 aRight2.getStart().getY()));
1123 ro_Result.push_back(
1124 B2DTrapezoid(
1125 aSplitLeft.getX(),
1126 aRight.getEnd().getX(),
1127 aRight2.getStart().getY(),
1128 aLeft2.getEnd().getX(),
1129 aRight2.getEnd().getX(),
1130 aLeft2.getEnd().getY()));
1136 void createLineTrapezoidFromB2DPolygon(
1137 B2DTrapezoidVector& ro_Result,
1138 const B2DPolygon& rPolygon,
1139 double fLineWidth)
1141 if(fTools::lessOrEqual(fLineWidth, 0.0))
1143 return;
1146 // ensure there are no curves used
1147 B2DPolygon aSource(rPolygon);
1149 if(aSource.areControlPointsUsed())
1151 const double fPrecisionFactor = 0.25;
1152 aSource = adaptiveSubdivideByDistance( aSource, fLineWidth * fPrecisionFactor );
1155 const sal_uInt32 nPointCount(aSource.count());
1157 if(!nPointCount)
1159 return;
1162 const sal_uInt32 nEdgeCount(aSource.isClosed() ? nPointCount : nPointCount - 1);
1163 B2DPoint aCurrent(aSource.getB2DPoint(0));
1165 ro_Result.reserve(ro_Result.size() + (3 * nEdgeCount));
1167 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1169 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1170 const B2DPoint aNext(aSource.getB2DPoint(nNextIndex));
1172 createLineTrapezoidFromEdge(ro_Result, aCurrent, aNext, fLineWidth);
1173 aCurrent = aNext;
1177 void createLineTrapezoidFromB2DPolyPolygon(
1178 B2DTrapezoidVector& ro_Result,
1179 const B2DPolyPolygon& rPolyPolygon,
1180 double fLineWidth)
1182 if(fTools::lessOrEqual(fLineWidth, 0.0))
1184 return;
1187 // ensure there are no curves used
1188 B2DPolyPolygon aSource(rPolyPolygon);
1190 if(aSource.areControlPointsUsed())
1192 aSource = aSource.getDefaultAdaptiveSubdivision();
1195 const sal_uInt32 nCount(aSource.count());
1197 if(!nCount)
1199 return;
1202 for(sal_uInt32 a(0); a < nCount; a++)
1204 createLineTrapezoidFromB2DPolygon(
1205 ro_Result,
1206 aSource.getB2DPolygon(a),
1207 fLineWidth);
1211 } // end of namespace tools
1212 } // end of namespace basegfx
1214 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */