merged tag ooo/OOO330_m14
[LibreOffice.git] / basegfx / source / polygon / b2dtrapezoid.cxx
blobc1e0f7f6c7c1b6f3fda384303ac8a733699113af
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b2dpolygontriangulator.cxx,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basegfx.hxx"
33 #include <basegfx/polygon/b2dtrapezoid.hxx>
34 #include <basegfx/range/b1drange.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <list>
38 //////////////////////////////////////////////////////////////////////////////
40 namespace basegfx
42 namespace trapezoidhelper
44 //////////////////////////////////////////////////////////////////////////////
45 // helper class to hold a simple ege. This is only used for horizontal edges
46 // currently, thus the YPositions will be equal. I did not create a special
47 // class for this since holdingthe pointers is more effective and also can be
48 // used as baseclass for the traversing edges
50 class TrDeSimpleEdge
52 protected:
53 // pointers to start and end point
54 const B2DPoint* mpStart;
55 const B2DPoint* mpEnd;
57 public:
58 // constructor
59 TrDeSimpleEdge(
60 const B2DPoint* pStart,
61 const B2DPoint* pEnd)
62 : mpStart(pStart),
63 mpEnd(pEnd)
67 // data read access
68 const B2DPoint& getStart() const { return *mpStart; }
69 const B2DPoint& getEnd() const { return *mpEnd; }
72 //////////////////////////////////////////////////////////////////////////////
73 // define vector of simple edges
75 typedef ::std::vector< TrDeSimpleEdge > TrDeSimpleEdges;
77 //////////////////////////////////////////////////////////////////////////////
78 // helper class for holding a traversing edge. It will always have some
79 // distance in YPos. The slope (in a numerically useful form, see comments) is
80 // hold and used in SortValue to allow sorting traversing edges by Y, X and slope
81 // (in that order)
83 class TrDeEdgeEntry : public TrDeSimpleEdge
85 private:
86 // the slope in a numerical useful form for sorting
87 sal_uInt32 mnSortValue;
89 public:
90 // convenience data read access
91 double getDeltaX() const { return mpEnd->getX() - mpStart->getX(); }
92 double getDeltaY() const { return mpEnd->getY() - mpStart->getY(); }
94 // convenience data read access. SortValue is created on demand since
95 // it is not always used
96 sal_uInt32 getSortValue() const
98 if(0 != mnSortValue)
99 return mnSortValue;
101 // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
102 // sal_uInt32 range for maximum precision
103 const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32 / F_PI));
105 // convert to sal_uInt32 value
106 const_cast< TrDeEdgeEntry* >(this)->mnSortValue = sal_uInt32(fRadiant);
108 return mnSortValue;
111 // constructor. SortValue can be given when known, use zero otherwise
112 TrDeEdgeEntry(
113 const B2DPoint* pStart,
114 const B2DPoint* pEnd,
115 sal_uInt32 nSortValue = 0)
116 : TrDeSimpleEdge(pStart, pEnd),
117 mnSortValue(nSortValue)
119 // force traversal of deltaY downward
120 if(mpEnd->getY() < mpStart->getY())
122 std::swap(mpStart, mpEnd);
125 // no horizontal edges allowed, all neeed to traverse vertically
126 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
129 // data write access to StartPoint
130 void setStart( const B2DPoint* pNewStart)
132 OSL_ENSURE(0 != pNewStart, "No null pointer allowed here (!)");
134 if(mpStart != pNewStart)
136 mpStart = pNewStart;
138 // no horizontal edges allowed, all neeed to traverse vertivally
139 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
143 // data write access to EndPoint
144 void setEnd( const B2DPoint* pNewEnd)
146 OSL_ENSURE(0 != pNewEnd, "No null pointer allowed here (!)");
148 if(mpEnd != pNewEnd)
150 mpEnd = pNewEnd;
152 // no horizontal edges allowed, all neeed to traverse vertivally
153 OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
157 // operator for sort support. Sort by Y, X and slope (in that order)
158 bool operator<(const TrDeEdgeEntry& rComp) const
160 if(fTools::equal(getStart().getY(), rComp.getStart().getY(), fTools::getSmallValue()))
162 if(fTools::equal(getStart().getX(), rComp.getStart().getX(), fTools::getSmallValue()))
164 // when start points are equal, use the direction the edge is pointing
165 // to. That value is created on demand and derived from atan2 in the
166 // range ]0.0 .. pi[ (without extremas, we always have a deltaY in this
167 // class) and scaled to sal_uInt32 range for best precision. 0 means no angle,
168 // while SAL_MAX_UINT32 means pi. Thus, the higher the value, the more left
169 // the edge traverses.
170 return (getSortValue() > rComp.getSortValue());
172 else
174 return fTools::less(getStart().getX(), rComp.getStart().getX());
177 else
179 return fTools::less(getStart().getY(), rComp.getStart().getY());
183 // method for cut support
184 B2DPoint getCutPointForGivenY(double fGivenY)
186 // Calculate cut point locally (do not use interpolate) since it is numerically
187 // necessary to guarantee the new, equal Y-coordinate
188 const double fFactor((fGivenY - getStart().getY()) / getDeltaY());
189 const double fDeltaXNew(fFactor * getDeltaX());
191 return B2DPoint(getStart().getX() + fDeltaXNew, fGivenY);
195 //////////////////////////////////////////////////////////////////////////////
196 // define double linked list of edges (for fast random insert)
198 typedef ::std::list< TrDeEdgeEntry > TrDeEdgeEntries;
200 } // end of anonymous namespace
201 } // end of namespace basegfx
203 //////////////////////////////////////////////////////////////////////////////
205 namespace basegfx
207 namespace trapezoidhelper
209 // helper class to handle the complete trapezoid subdivision of a PolyPolygon
210 class TrapezoidSubdivider
212 private:
213 // local data
214 sal_uInt32 mnInitialEdgeEntryCount;
215 TrDeEdgeEntries maTrDeEdgeEntries;
216 ::std::vector< B2DPoint > maPoints;
217 ::std::vector< B2DPoint* > maNewPoints;
219 void addEdgeSorted(
220 TrDeEdgeEntries::iterator aCurrent,
221 const TrDeEdgeEntry& rNewEdge)
223 // Loop while new entry is bigger, use operator<
224 while(aCurrent != maTrDeEdgeEntries.end() && (*aCurrent) < rNewEdge)
226 aCurrent++;
229 // Insert before first which is smaller or equal or at end
230 maTrDeEdgeEntries.insert(aCurrent, rNewEdge);
233 bool splitEdgeAtGivenPoint(
234 TrDeEdgeEntries::reference aEdge,
235 const B2DPoint& rCutPoint,
236 TrDeEdgeEntries::iterator aCurrent)
238 // do not create edges without deltaY: do not split when start is identical
239 if(aEdge.getStart().equal(rCutPoint, fTools::getSmallValue()))
241 return false;
244 // do not create edges without deltaY: do not split when end is identical
245 if(aEdge.getEnd().equal(rCutPoint, fTools::getSmallValue()))
247 return false;
250 const double fOldDeltaYStart(rCutPoint.getY() - aEdge.getStart().getY());
252 if(fTools::lessOrEqual(fOldDeltaYStart, 0.0))
254 // do not split: the resulting edge would be horizontal
255 // correct it to new start point
256 aEdge.setStart(&rCutPoint);
257 return false;
260 const double fNewDeltaYStart(aEdge.getEnd().getY() - rCutPoint.getY());
262 if(fTools::lessOrEqual(fNewDeltaYStart, 0.0))
264 // do not split: the resulting edge would be horizontal
265 // correct it to new end point
266 aEdge.setEnd(&rCutPoint);
267 return false;
270 // Create new entry
271 const TrDeEdgeEntry aNewEdge(
272 &rCutPoint,
273 &aEdge.getEnd(),
274 aEdge.getSortValue());
276 // Correct old entry
277 aEdge.setEnd(&rCutPoint);
279 // Insert sorted (to avoid new sort)
280 addEdgeSorted(aCurrent, aNewEdge);
282 return true;
285 bool testAndCorrectEdgeIntersection(
286 TrDeEdgeEntries::reference aEdgeA,
287 TrDeEdgeEntries::reference aEdgeB,
288 TrDeEdgeEntries::iterator aCurrent)
290 // Exclude simple cases: same start or end point
291 if(aEdgeA.getStart().equal(aEdgeB.getStart(), fTools::getSmallValue()))
293 return false;
296 if(aEdgeA.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
298 return false;
301 if(aEdgeA.getEnd().equal(aEdgeB.getStart(), fTools::getSmallValue()))
303 return false;
306 if(aEdgeA.getEnd().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
308 return false;
311 // Exclude simple cases: one of the edges has no length anymore
312 if(aEdgeA.getStart().equal(aEdgeA.getEnd(), fTools::getSmallValue()))
314 return false;
317 if(aEdgeB.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
319 return false;
322 // check if one point is on the other edge (a touch, not a cut)
323 const B2DVector aDeltaB(aEdgeB.getDeltaX(), aEdgeB.getDeltaY());
325 if(tools::isPointOnEdge(aEdgeA.getStart(), aEdgeB.getStart(), aDeltaB))
327 return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getStart(), aCurrent);
330 if(tools::isPointOnEdge(aEdgeA.getEnd(), aEdgeB.getStart(), aDeltaB))
332 return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getEnd(), aCurrent);
335 const B2DVector aDeltaA(aEdgeA.getDeltaX(), aEdgeA.getDeltaY());
337 if(tools::isPointOnEdge(aEdgeB.getStart(), aEdgeA.getStart(), aDeltaA))
339 return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getStart(), aCurrent);
342 if(tools::isPointOnEdge(aEdgeB.getEnd(), aEdgeA.getStart(), aDeltaA))
344 return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getEnd(), aCurrent);
347 // check for cut inside edges. Use both t-values to choose the more precise
348 // one later
349 double fCutA(0.0);
350 double fCutB(0.0);
352 if(tools::findCut(
353 aEdgeA.getStart(), aDeltaA,
354 aEdgeB.getStart(), aDeltaB,
355 CUTFLAG_LINE,
356 &fCutA,
357 &fCutB))
359 // use a simple metric (length criteria) for choosing the numerically
360 // better cut
361 const double fSimpleLengthA(aDeltaA.getX() + aDeltaA.getY());
362 const double fSimpleLengthB(aDeltaB.getX() + aDeltaB.getY());
363 const bool bAIsLonger(fSimpleLengthA > fSimpleLengthB);
364 B2DPoint* pNewPoint = bAIsLonger
365 ? new B2DPoint(aEdgeA.getStart() + (fCutA * aDeltaA))
366 : new B2DPoint(aEdgeB.getStart() + (fCutB * aDeltaB));
367 bool bRetval(false);
369 // try to split both edges
370 bRetval = splitEdgeAtGivenPoint(aEdgeA, *pNewPoint, aCurrent);
371 bRetval |= splitEdgeAtGivenPoint(aEdgeB, *pNewPoint, aCurrent);
373 if(bRetval)
375 maNewPoints.push_back(pNewPoint);
377 else
379 delete pNewPoint;
382 return bRetval;
385 return false;
388 void solveHorizontalEdges(TrDeSimpleEdges& rTrDeSimpleEdges)
390 if(rTrDeSimpleEdges.size() && maTrDeEdgeEntries.size())
392 // there were horizontal edges. These can be excluded, but
393 // cuts with other edges need to be solved and added before
394 // ignoring them
395 sal_uInt32 a(0);
397 for(a = 0; a < rTrDeSimpleEdges.size(); a++)
399 // get horizontal edge as candidate; prepare it's range and fixed Y
400 const TrDeSimpleEdge& rHorEdge = rTrDeSimpleEdges[a];
401 const B1DRange aRange(rHorEdge.getStart().getX(), rHorEdge.getEnd().getX());
402 const double fFixedY(rHorEdge.getStart().getY());
404 // loop over traversing edges
405 TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
409 // get compare edge
410 TrDeEdgeEntries::reference aCompare(*aCurrent++);
412 if(fTools::lessOrEqual(aCompare.getEnd().getY(), fFixedY))
414 // edge ends above horizontal edge, continue
415 continue;
418 if(fTools::moreOrEqual(aCompare.getStart().getY(), fFixedY))
420 // edge starts below horizontal edge, continue
421 continue;
424 // vertical overlap, get horizontal range
425 const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
427 if(aRange.overlaps(aCompareRange))
429 // possible cut, get cut point
430 const B2DPoint aSplit(aCompare.getCutPointForGivenY(fFixedY));
432 if(fTools::more(aSplit.getX(), aRange.getMinimum())
433 && fTools::less(aSplit.getX(), aRange.getMaximum()))
435 // cut is in XRange of horizontal edge, potenitally needed cut
436 B2DPoint* pNewPoint = new B2DPoint(aSplit);
438 if(splitEdgeAtGivenPoint(aCompare, *pNewPoint, aCurrent))
440 maNewPoints.push_back(pNewPoint);
442 else
444 delete pNewPoint;
449 while(aCurrent != maTrDeEdgeEntries.end()
450 && fTools::less(aCurrent->getStart().getY(), fFixedY));
455 public:
456 TrapezoidSubdivider(
457 const B2DPolyPolygon& rSourcePolyPolygon)
458 : mnInitialEdgeEntryCount(0),
459 maTrDeEdgeEntries(),
460 maPoints(),
461 maNewPoints()
463 B2DPolyPolygon aSource(rSourcePolyPolygon);
464 const sal_uInt32 nPolygonCount(rSourcePolyPolygon.count());
465 TrDeSimpleEdges aTrDeSimpleEdges;
466 sal_uInt32 a(0), b(0);
467 sal_uInt32 nAllPointCount(0);
469 // ensure there are no curves used
470 if(aSource.areControlPointsUsed())
472 aSource = aSource.getDefaultAdaptiveSubdivision();
475 for(a = 0; a < nPolygonCount; a++)
477 // 1st run: count points
478 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
479 const sal_uInt32 nCount(aPolygonCandidate.count());
481 if(nCount > 2)
483 nAllPointCount += nCount;
487 if(nAllPointCount)
489 // reserve needed points. CAUTION: maPoints size is NOT to be changed anymore
490 // after 2nd loop since pointers to it are used in the edges
491 maPoints.reserve(nAllPointCount);
493 for(a = 0; a < nPolygonCount; a++)
495 // 2nd run: add points
496 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
497 const sal_uInt32 nCount(aPolygonCandidate.count());
499 if(nCount > 2)
501 for(b = 0; b < nCount; b++)
503 maPoints.push_back(aPolygonCandidate.getB2DPoint(b));
508 // Moved the edge construction to a 3rd run: doing it in the 2nd run is
509 // possible(and i used it), but requires a working vector::reserve()
510 // implementation, else the vector will be reallocated and the pointers
511 // in the edges may be wrong. Security first here.
512 sal_uInt32 nStartIndex(0);
514 for(a = 0; a < nPolygonCount; a++)
516 const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
517 const sal_uInt32 nCount(aPolygonCandidate.count());
519 if(nCount > 2)
521 // get the last point of the current polygon
522 B2DPoint* pPrev(&maPoints[nCount + nStartIndex - 1]);
524 for(b = 0; b < nCount; b++)
526 // get next point
527 B2DPoint* pCurr(&maPoints[nStartIndex++]);
529 if(fTools::equal(pPrev->getY(), pCurr->getY(), fTools::getSmallValue()))
531 // horizontal edge, check for single point
532 if(!fTools::equal(pPrev->getX(), pCurr->getX(), fTools::getSmallValue()))
534 // X-order not needed, just add
535 aTrDeSimpleEdges.push_back(TrDeSimpleEdge(pPrev, pCurr));
537 const double fMiddle((pPrev->getY() + pCurr->getY()) * 0.5);
538 pPrev->setY(fMiddle);
539 pCurr->setY(fMiddle);
542 else
544 // vertical edge. Positive Y-direction is guaranteed by the
545 // TrDeEdgeEntry constructor
546 maTrDeEdgeEntries.push_back(TrDeEdgeEntry(pPrev, pCurr, 0));
547 mnInitialEdgeEntryCount++;
550 // prepare next step
551 pPrev = pCurr;
557 if(maTrDeEdgeEntries.size())
559 // single and initial sort of traversing edges
560 maTrDeEdgeEntries.sort();
562 // solve horizontal edges if there are any detected
563 solveHorizontalEdges(aTrDeSimpleEdges);
567 ~TrapezoidSubdivider()
569 // delete the extra points created for cuts
570 const sal_uInt32 nCount(maNewPoints.size());
572 for(sal_uInt32 a(0); a < nCount; a++)
574 delete maNewPoints[a];
578 void Subdivide(B2DTrapezoidVector& ro_Result)
580 // This is the central subdivider. The strategy is to use the first two entries
581 // from the traversing edges as a potential trapezoid and do the needed corrections
582 // and adaptions on the way.
584 // There always must be two edges with the same YStart value: When adding the polygons
585 // in the constructor, there is always a topmost point from which two edges start; when
586 // the topmost is an edge, there is a start and end of this edge from which two edges
587 // start. All cases have two edges with same StartY (QED).
589 // Based on this these edges get corrected when:
590 // - one is longer than the other
591 // - they intersect
592 // - they intersect with other edges
593 // - another edge starts inside the thought trapezoid
595 // All this cases again produce a valid state so that the first two edges have a common
596 // Ystart again. Some cases lead to a restart of the process, some allow consuming the
597 // edges and create the intended trapezoid.
599 // Be careful when doing chages here: It is essential to keep all possible paths
600 // in valid states and to be numerically correct. This is especially needed e.g.
601 // by using fTools::equal(..) in the more robust small-value incarnation.
602 B1DRange aLeftRange;
603 B1DRange aRightRange;
605 if(!maTrDeEdgeEntries.empty())
607 // measuring shows that the relation between edges and created trapezoids is
608 // mostly in the 1:1 range, thus reserve as much trapezoids as edges exist. Do
609 // not use maTrDeEdgeEntries.size() since that may be a non-constant time
610 // operation for Lists. Instead, use mnInitialEdgeEntryCount which will contain
611 // the roughly counted adds to the List
612 ro_Result.reserve(ro_Result.size() + mnInitialEdgeEntryCount);
615 while(!maTrDeEdgeEntries.empty())
617 // Prepare current operator and get first edge
618 TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
619 TrDeEdgeEntries::reference aLeft(*aCurrent++);
621 if(aCurrent == maTrDeEdgeEntries.end())
623 // Should not happen: No 2nd edge; consume the single edge
624 // to not have an endless loop and start next. During development
625 // i constantly had breakpoints here, so i am sure enough to add an
626 // assertion here
627 OSL_ENSURE(false, "Trapeziod decomposer in illegal state (!)");
628 maTrDeEdgeEntries.pop_front();
629 continue;
632 // get second edge
633 TrDeEdgeEntries::reference aRight(*aCurrent++);
635 if(!fTools::equal(aLeft.getStart().getY(), aRight.getStart().getY(), fTools::getSmallValue()))
637 // Should not happen: We have a 2nd edge, but YStart is on another
638 // line; consume the single edge to not have an endless loop and start
639 // next. During development i constantly had breakpoints here, so i am
640 // sure enough to add an assertion here
641 OSL_ENSURE(false, "Trapeziod decomposer in illegal state (!)");
642 maTrDeEdgeEntries.pop_front();
643 continue;
646 // aLeft and aRight build a thought trapezoid now. They have a common
647 // start line (same Y for start points). Potentially, one of the edges
648 // is longer than the other. It is only needed to look at the shorter
649 // length which build the potential trapezoid. To do so, get the end points
650 // locally and adapt the evtl. longer one. Use only aLeftEnd and aRightEnd
651 // from here on, not the aLeft.getEnd() or aRight.getEnd() accesses.
652 B2DPoint aLeftEnd(aLeft.getEnd());
653 B2DPoint aRightEnd(aRight.getEnd());
655 // check if end points are on the same line. If yes, no adaption
656 // needs to be prepared. Also remember which one actually is longer.
657 const bool bEndOnSameLine(fTools::equal(aLeftEnd.getY(), aRightEnd.getY(), fTools::getSmallValue()));
658 bool bLeftIsLonger(false);
660 if(!bEndOnSameLine)
662 // check which edge is longer and correct accordingly
663 bLeftIsLonger = fTools::more(aLeftEnd.getY(), aRightEnd.getY());
665 if(bLeftIsLonger)
667 aLeftEnd = aLeft.getCutPointForGivenY(aRightEnd.getY());
669 else
671 aRightEnd = aRight.getCutPointForGivenY(aLeftEnd.getY());
675 // check for same start and end points
676 const bool bSameStartPoint(aLeft.getStart().equal(aRight.getStart(), fTools::getSmallValue()));
677 const bool bSameEndPoint(aLeftEnd.equal(aRightEnd, fTools::getSmallValue()));
679 // check the simple case that the edges form a 'blind' edge (deadend)
680 if(bSameStartPoint && bSameEndPoint)
682 // correct the longer edge if prepared
683 if(!bEndOnSameLine)
685 if(bLeftIsLonger)
687 B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
689 if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
691 maNewPoints.push_back(pNewPoint);
693 else
695 delete pNewPoint;
698 else
700 B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
702 if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
704 maNewPoints.push_back(pNewPoint);
706 else
708 delete pNewPoint;
713 // consume both edges and start next run
714 maTrDeEdgeEntries.pop_front();
715 maTrDeEdgeEntries.pop_front();
717 continue;
720 // check if the edges self-intersect. This can only happen when
721 // start and end point are different
722 bool bRangesSet(false);
724 if(!(bSameStartPoint || bSameEndPoint))
726 // get XRanges of edges
727 aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
728 aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
729 bRangesSet = true;
731 // use fast range test first
732 if(aLeftRange.overlaps(aRightRange))
734 // real cut test and correction. If correction was needed,
735 // start new run
736 if(testAndCorrectEdgeIntersection(aLeft, aRight, aCurrent))
738 continue;
743 // now we need to check if there are intersections with other edges
744 // or if other edges start inside the candidate trapezoid
745 if(aCurrent != maTrDeEdgeEntries.end()
746 && fTools::less(aCurrent->getStart().getY(), aLeftEnd.getY()))
748 // get XRanges of edges
749 if(!bRangesSet)
751 aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
752 aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
755 // build full XRange for fast check
756 B1DRange aAllRange(aLeftRange);
757 aAllRange.expand(aRightRange);
759 // prepare loop iterator; aCurrent needs to stay unchanged for
760 // eventual sorted insertions of new EdgeNodes. Also prepare stop flag
761 TrDeEdgeEntries::iterator aLoop(aCurrent);
762 bool bDone(false);
766 // get compare edge and it's XRange
767 TrDeEdgeEntries::reference aCompare(*aLoop++);
769 // avoid edges using the same start point as one of
770 // the edges. These can neither have their start point
771 // in the thought trapezoid nor cut with one of the edges
772 if(aCompare.getStart().equal(aRight.getStart(), fTools::getSmallValue()))
774 continue;
777 // get compare XRange
778 const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
780 // use fast range test first
781 if(aAllRange.overlaps(aCompareRange))
783 // check for start point inside thought trapezoid
784 if(fTools::more(aCompare.getStart().getY(), aLeft.getStart().getY()))
786 // calculate the two possible split points at compare's Y
787 const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aCompare.getStart().getY()));
788 const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aCompare.getStart().getY()));
790 // check for start point of aCompare being inside thought
791 // trapezoid
792 if(aCompare.getStart().getX() >= aSplitLeft.getX() &&
793 aCompare.getStart().getX() <= aSplitRight.getX())
795 // is inside, correct and restart loop
796 B2DPoint* pNewLeft = new B2DPoint(aSplitLeft);
798 if(splitEdgeAtGivenPoint(aLeft, *pNewLeft, aCurrent))
800 maNewPoints.push_back(pNewLeft);
802 else
804 delete pNewLeft;
807 B2DPoint* pNewRight = new B2DPoint(aSplitRight);
809 if(splitEdgeAtGivenPoint(aRight, *pNewRight, aCurrent))
811 maNewPoints.push_back(pNewRight);
813 else
815 delete pNewRight;
818 bDone = true;
822 if(!bDone && aLeftRange.overlaps(aCompareRange))
824 // test for concrete cut of compare edge with left edge
825 bDone = testAndCorrectEdgeIntersection(aLeft, aCompare, aCurrent);
828 if(!bDone && aRightRange.overlaps(aCompareRange))
830 // test for concrete cut of compare edge with Right edge
831 bDone = testAndCorrectEdgeIntersection(aRight, aCompare, aCurrent);
835 while(!bDone
836 && aLoop != maTrDeEdgeEntries.end()
837 && fTools::less(aLoop->getStart().getY(), aLeftEnd.getY()));
839 if(bDone)
841 // something needed to be changed; start next loop
842 continue;
846 // when we get here, the intended trapezoid can be used. It needs to
847 // be corrected, eventually (if prepared); but this is no reason not to
848 // use it in the same loop iteration
849 if(!bEndOnSameLine)
851 if(bLeftIsLonger)
853 B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
855 if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
857 maNewPoints.push_back(pNewPoint);
859 else
861 delete pNewPoint;
864 else
866 B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
868 if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
870 maNewPoints.push_back(pNewPoint);
872 else
874 delete pNewPoint;
879 // the two edges start at the same Y, they use the same DeltaY, they
880 // do not cut themselves and not any other edge in range. Create a
881 // B2DTrapezoid and consume both edges
882 ro_Result.push_back(
883 B2DTrapezoid(
884 aLeft.getStart().getX(),
885 aRight.getStart().getX(),
886 aLeft.getStart().getY(),
887 aLeftEnd.getX(),
888 aRightEnd.getX(),
889 aLeftEnd.getY()));
891 maTrDeEdgeEntries.pop_front();
892 maTrDeEdgeEntries.pop_front();
896 } // end of anonymous namespace
897 } // end of namespace basegfx
899 //////////////////////////////////////////////////////////////////////////////
901 namespace basegfx
903 B2DTrapezoid::B2DTrapezoid(
904 const double& rfTopXLeft,
905 const double& rfTopXRight,
906 const double& rfTopY,
907 const double& rfBottomXLeft,
908 const double& rfBottomXRight,
909 const double& rfBottomY)
910 : mfTopXLeft(rfTopXLeft),
911 mfTopXRight(rfTopXRight),
912 mfTopY(rfTopY),
913 mfBottomXLeft(rfBottomXLeft),
914 mfBottomXRight(rfBottomXRight),
915 mfBottomY(rfBottomY)
917 // guarantee mfTopXRight >= mfTopXLeft
918 if(mfTopXLeft > mfTopXRight)
920 std::swap(mfTopXLeft, mfTopXRight);
923 // guarantee mfBottomXRight >= mfBottomXLeft
924 if(mfBottomXLeft > mfBottomXRight)
926 std::swap(mfBottomXLeft, mfBottomXRight);
929 // guarantee mfBottomY >= mfTopY
930 if(mfTopY > mfBottomY)
932 std::swap(mfTopY, mfBottomY);
933 std::swap(mfTopXLeft, mfBottomXLeft);
934 std::swap(mfTopXRight, mfBottomXRight);
938 B2DPolygon B2DTrapezoid::getB2DPolygon() const
940 B2DPolygon aRetval;
942 aRetval.append(B2DPoint(getTopXLeft(), getTopY()));
943 aRetval.append(B2DPoint(getTopXRight(), getTopY()));
944 aRetval.append(B2DPoint(getBottomXRight(), getBottomY()));
945 aRetval.append(B2DPoint(getBottomXLeft(), getBottomY()));
946 aRetval.setClosed(true);
948 return aRetval;
950 } // end of namespace basegfx
952 //////////////////////////////////////////////////////////////////////////////
954 namespace basegfx
956 namespace tools
958 // convert Source PolyPolygon to trapezoids
959 void trapezoidSubdivide(B2DTrapezoidVector& ro_Result, const B2DPolyPolygon& rSourcePolyPolygon)
961 trapezoidhelper::TrapezoidSubdivider aTrapezoidSubdivider(rSourcePolyPolygon);
963 aTrapezoidSubdivider.Subdivide(ro_Result);
966 void createLineTrapezoidFromEdge(
967 B2DTrapezoidVector& ro_Result,
968 const B2DPoint& rPointA,
969 const B2DPoint& rPointB,
970 double fLineWidth)
972 if(fTools::lessOrEqual(fLineWidth, 0.0))
974 // no line witdh
975 return;
978 if(rPointA.equal(rPointB, fTools::getSmallValue()))
980 // points are equal, no edge
981 return;
984 const double fHalfLineWidth(0.5 * fLineWidth);
986 if(fTools::equal(rPointA.getX(), rPointB.getX(), fTools::getSmallValue()))
988 // vertical line
989 const double fLeftX(rPointA.getX() - fHalfLineWidth);
990 const double fRightX(rPointA.getX() + fHalfLineWidth);
992 ro_Result.push_back(
993 B2DTrapezoid(
994 fLeftX,
995 fRightX,
996 std::min(rPointA.getY(), rPointB.getY()),
997 fLeftX,
998 fRightX,
999 std::max(rPointA.getY(), rPointB.getY())));
1001 else if(fTools::equal(rPointA.getY(), rPointB.getY(), fTools::getSmallValue()))
1003 // horizontal line
1004 const double fLeftX(std::min(rPointA.getX(), rPointB.getX()));
1005 const double fRightX(std::max(rPointA.getX(), rPointB.getX()));
1007 ro_Result.push_back(
1008 B2DTrapezoid(
1009 fLeftX,
1010 fRightX,
1011 rPointA.getY() - fHalfLineWidth,
1012 fLeftX,
1013 fRightX,
1014 rPointA.getY() + fHalfLineWidth));
1016 else
1018 // diagonal line
1019 // create perpendicular vector
1020 const B2DVector aDelta(rPointB - rPointA);
1021 B2DVector aPerpendicular(-aDelta.getY(), aDelta.getX());
1022 aPerpendicular.setLength(fHalfLineWidth);
1024 // create StartLow, StartHigh, EndLow and EndHigh
1025 const B2DPoint aStartLow(rPointA + aPerpendicular);
1026 const B2DPoint aStartHigh(rPointA - aPerpendicular);
1027 const B2DPoint aEndHigh(rPointB - aPerpendicular);
1028 const B2DPoint aEndLow(rPointB + aPerpendicular);
1030 // create EdgeEntries
1031 basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries;
1033 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow, &aStartHigh, 0));
1034 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh, &aEndHigh, 0));
1035 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh, &aEndLow, 0));
1036 aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow, &aStartLow, 0));
1037 aTrDeEdgeEntries.sort();
1039 // here we know we have exactly four edges, and they do not cut, touch or
1040 // intersect. This makes processing much easier. Get the first two as start
1041 // edges for the thought trapezoid
1042 basegfx::trapezoidhelper::TrDeEdgeEntries::iterator aCurrent(aTrDeEdgeEntries.begin());
1043 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft(*aCurrent++);
1044 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight(*aCurrent++);
1045 const bool bEndOnSameLine(fTools::equal(aLeft.getEnd().getY(), aRight.getEnd().getY(), fTools::getSmallValue()));
1047 if(bEndOnSameLine)
1049 // create two triangle trapezoids
1050 ro_Result.push_back(
1051 B2DTrapezoid(
1052 aLeft.getStart().getX(),
1053 aRight.getStart().getX(),
1054 aLeft.getStart().getY(),
1055 aLeft.getEnd().getX(),
1056 aRight.getEnd().getX(),
1057 aLeft.getEnd().getY()));
1059 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1060 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1062 ro_Result.push_back(
1063 B2DTrapezoid(
1064 aLeft2.getStart().getX(),
1065 aRight2.getStart().getX(),
1066 aLeft2.getStart().getY(),
1067 aLeft2.getEnd().getX(),
1068 aRight2.getEnd().getX(),
1069 aLeft2.getEnd().getY()));
1071 else
1073 // create three trapezoids. Check which edge is longer and
1074 // correct accordingly
1075 const bool bLeftIsLonger(fTools::more(aLeft.getEnd().getY(), aRight.getEnd().getY()));
1077 if(bLeftIsLonger)
1079 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1080 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1081 const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aRight.getEnd().getY()));
1082 const B2DPoint aSplitRight(aRight2.getCutPointForGivenY(aLeft.getEnd().getY()));
1084 ro_Result.push_back(
1085 B2DTrapezoid(
1086 aLeft.getStart().getX(),
1087 aRight.getStart().getX(),
1088 aLeft.getStart().getY(),
1089 aSplitLeft.getX(),
1090 aRight.getEnd().getX(),
1091 aRight.getEnd().getY()));
1093 ro_Result.push_back(
1094 B2DTrapezoid(
1095 aSplitLeft.getX(),
1096 aRight.getEnd().getX(),
1097 aRight.getEnd().getY(),
1098 aLeft2.getStart().getX(),
1099 aSplitRight.getX(),
1100 aLeft2.getStart().getY()));
1102 ro_Result.push_back(
1103 B2DTrapezoid(
1104 aLeft2.getStart().getX(),
1105 aSplitRight.getX(),
1106 aLeft2.getStart().getY(),
1107 aLeft2.getEnd().getX(),
1108 aRight2.getEnd().getX(),
1109 aLeft2.getEnd().getY()));
1111 else
1113 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1114 basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1115 const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aLeft.getEnd().getY()));
1116 const B2DPoint aSplitLeft(aLeft2.getCutPointForGivenY(aRight.getEnd().getY()));
1118 ro_Result.push_back(
1119 B2DTrapezoid(
1120 aLeft.getStart().getX(),
1121 aRight.getStart().getX(),
1122 aLeft.getStart().getY(),
1123 aLeft.getEnd().getX(),
1124 aSplitRight.getX(),
1125 aLeft.getEnd().getY()));
1127 ro_Result.push_back(
1128 B2DTrapezoid(
1129 aLeft.getEnd().getX(),
1130 aSplitRight.getX(),
1131 aLeft.getEnd().getY(),
1132 aSplitLeft.getX(),
1133 aRight.getEnd().getX(),
1134 aRight2.getStart().getY()));
1136 ro_Result.push_back(
1137 B2DTrapezoid(
1138 aSplitLeft.getX(),
1139 aRight.getEnd().getX(),
1140 aRight2.getStart().getY(),
1141 aLeft2.getEnd().getX(),
1142 aRight2.getEnd().getX(),
1143 aLeft2.getEnd().getY()));
1149 void createLineTrapezoidFromB2DPolygon(
1150 B2DTrapezoidVector& ro_Result,
1151 const B2DPolygon& rPolygon,
1152 double fLineWidth)
1154 if(fTools::lessOrEqual(fLineWidth, 0.0))
1156 return;
1159 // ensure there are no curves used
1160 B2DPolygon aSource(rPolygon);
1162 if(aSource.areControlPointsUsed())
1164 const double fPrecisionFactor = 0.25;
1165 aSource = adaptiveSubdivideByDistance( aSource, fLineWidth * fPrecisionFactor );
1168 const sal_uInt32 nPointCount(aSource.count());
1170 if(!nPointCount)
1172 return;
1175 const sal_uInt32 nEdgeCount(aSource.isClosed() ? nPointCount : nPointCount - 1);
1176 B2DPoint aCurrent(aSource.getB2DPoint(0));
1178 ro_Result.reserve(ro_Result.size() + (3 * nEdgeCount));
1180 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1182 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1183 const B2DPoint aNext(aSource.getB2DPoint(nNextIndex));
1185 createLineTrapezoidFromEdge(ro_Result, aCurrent, aNext, fLineWidth);
1186 aCurrent = aNext;
1190 void createLineTrapezoidFromB2DPolyPolygon(
1191 B2DTrapezoidVector& ro_Result,
1192 const B2DPolyPolygon& rPolyPolygon,
1193 double fLineWidth)
1195 if(fTools::lessOrEqual(fLineWidth, 0.0))
1197 return;
1200 // ensure there are no curves used
1201 B2DPolyPolygon aSource(rPolyPolygon);
1203 if(aSource.areControlPointsUsed())
1205 aSource = aSource.getDefaultAdaptiveSubdivision();
1208 const sal_uInt32 nCount(aSource.count());
1210 if(!nCount)
1212 return;
1215 for(sal_uInt32 a(0); a < nCount; a++)
1217 createLineTrapezoidFromB2DPolygon(
1218 ro_Result,
1219 aSource.getB2DPolygon(a),
1220 fLineWidth);
1224 } // end of namespace tools
1225 } // end of namespace basegfx
1227 //////////////////////////////////////////////////////////////////////////////
1228 // eof