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/b2dtrapezoid.hxx>
21 #include <basegfx/range/b1drange.hxx>
22 #include <basegfx/polygon/b2dpolygontools.hxx>
24 #include <osl/diagnose.h>
30 namespace trapezoidhelper
33 // helper class to hold a simple ege. This is only used for horizontal edges
34 // currently, thus the YPositions will be equal. I did not create a special
35 // class for this since holdingthe pointers is more effective and also can be
36 // used as baseclass for the traversing edges
41 // pointers to start and end point
42 const B2DPoint
* mpStart
;
43 const B2DPoint
* mpEnd
;
48 const B2DPoint
* pStart
,
56 const B2DPoint
& getStart() const { return *mpStart
; }
57 const B2DPoint
& getEnd() const { return *mpEnd
; }
60 // define vector of simple edges
62 typedef ::std::vector
< TrDeSimpleEdge
> TrDeSimpleEdges
;
64 // helper class for holding a traversing edge. It will always have some
65 // distance in YPos. The slope (in a numerically useful form, see comments) is
66 // hold and used in SortValue to allow sorting traversing edges by Y, X and slope
69 class TrDeEdgeEntry
: public TrDeSimpleEdge
72 // the slope in a numerical useful form for sorting
73 sal_uInt32 mnSortValue
;
76 // convenience data read access
77 double getDeltaX() const { return mpEnd
->getX() - mpStart
->getX(); }
78 double getDeltaY() const { return mpEnd
->getY() - mpStart
->getY(); }
80 // convenience data read access. SortValue is created on demand since
81 // it is not always used
82 sal_uInt32
getSortValue() const
87 // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
88 // sal_uInt32 range for maximum precision
89 const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32
/ F_PI
));
91 // convert to sal_uInt32 value
92 const_cast< TrDeEdgeEntry
* >(this)->mnSortValue
= sal_uInt32(fRadiant
);
97 // constructor. SortValue can be given when known, use zero otherwise
99 const B2DPoint
* pStart
,
100 const B2DPoint
* pEnd
,
101 sal_uInt32 nSortValue
= 0)
102 : TrDeSimpleEdge(pStart
, pEnd
),
103 mnSortValue(nSortValue
)
105 // force traversal of deltaY downward
106 if(mpEnd
->getY() < mpStart
->getY())
108 std::swap(mpStart
, mpEnd
);
111 // no horizontal edges allowed, all neeed to traverse vertically
112 OSL_ENSURE(mpEnd
->getY() > mpStart
->getY(), "Illegal TrDeEdgeEntry constructed (!)");
115 // data write access to StartPoint
116 void setStart( const B2DPoint
* pNewStart
)
118 OSL_ENSURE(0 != pNewStart
, "No null pointer allowed here (!)");
120 if(mpStart
!= pNewStart
)
124 // no horizontal edges allowed, all neeed to traverse vertivally
125 OSL_ENSURE(mpEnd
->getY() > mpStart
->getY(), "Illegal TrDeEdgeEntry constructed (!)");
129 // data write access to EndPoint
130 void setEnd( const B2DPoint
* pNewEnd
)
132 OSL_ENSURE(0 != pNewEnd
, "No null pointer allowed here (!)");
138 // no horizontal edges allowed, all neeed to traverse vertivally
139 OSL_ENSURE(mpEnd
->getY() > mpStart
->getY(), "Illegal TrDeEdgeEntry constructed (!)");
143 // operator for sort support. Sort by Y, X and slope (in that order)
144 bool operator<(const TrDeEdgeEntry
& rComp
) const
146 if(fTools::equal(getStart().getY(), rComp
.getStart().getY(), fTools::getSmallValue()))
148 if(fTools::equal(getStart().getX(), rComp
.getStart().getX(), fTools::getSmallValue()))
150 // when start points are equal, use the direction the edge is pointing
151 // to. That value is created on demand and derived from atan2 in the
152 // range ]0.0 .. pi[ (without extremas, we always have a deltaY in this
153 // class) and scaled to sal_uInt32 range for best precision. 0 means no angle,
154 // while SAL_MAX_UINT32 means pi. Thus, the higher the value, the more left
155 // the edge traverses.
156 return (getSortValue() > rComp
.getSortValue());
160 return fTools::less(getStart().getX(), rComp
.getStart().getX());
165 return fTools::less(getStart().getY(), rComp
.getStart().getY());
169 // method for cut support
170 B2DPoint
getCutPointForGivenY(double fGivenY
)
172 // Calculate cut point locally (do not use interpolate) since it is numerically
173 // necessary to guarantee the new, equal Y-coordinate
174 const double fFactor((fGivenY
- getStart().getY()) / getDeltaY());
175 const double fDeltaXNew(fFactor
* getDeltaX());
177 return B2DPoint(getStart().getX() + fDeltaXNew
, fGivenY
);
181 // define double linked list of edges (for fast random insert)
183 typedef ::std::list
< TrDeEdgeEntry
> TrDeEdgeEntries
;
185 } // end of anonymous namespace
186 } // end of namespace basegfx
190 namespace trapezoidhelper
192 // FIXME: templatize this and use it for TrDeEdgeEntries too ...
194 /// Class to allow efficient allocation and release of B2DPoints
195 class PointBlockAllocator
197 static const size_t nBlockSize
= 32;
199 B2DPoint
*mpPointBase
;
200 /// Special case the first allocation to avoid it.
201 B2DPoint maFirstStackBlock
[nBlockSize
];
202 std::vector
< B2DPoint
* > maBlocks
;
204 PointBlockAllocator() :
205 nCurPoint( nBlockSize
),
206 mpPointBase( maFirstStackBlock
)
210 ~PointBlockAllocator()
212 while(maBlocks
.size() > 0)
214 delete [] maBlocks
.back();
219 B2DPoint
*allocatePoint()
221 if(nCurPoint
>= nBlockSize
)
223 mpPointBase
= new B2DPoint
[nBlockSize
];
224 maBlocks
.push_back(mpPointBase
);
227 return mpPointBase
+ nCurPoint
++;
230 B2DPoint
*allocatePoint(const B2DTuple
&rPoint
)
232 B2DPoint
*pPoint
= allocatePoint();
237 /// This is a very uncommon case but why not ...
238 void freeIfLast(B2DPoint
*pPoint
)
240 // just re-use the last point if we can.
241 if ( nCurPoint
> 0 && pPoint
== mpPointBase
+ nCurPoint
- 1 )
246 // helper class to handle the complete trapezoid subdivision of a PolyPolygon
247 class TrapezoidSubdivider
251 sal_uInt32 mnInitialEdgeEntryCount
;
252 TrDeEdgeEntries maTrDeEdgeEntries
;
253 ::std::vector
< B2DPoint
> maPoints
;
254 /// new points allocated for cuts
255 PointBlockAllocator maNewPoints
;
258 TrDeEdgeEntries::iterator aCurrent
,
259 const TrDeEdgeEntry
& rNewEdge
)
261 // Loop while new entry is bigger, use operator<
262 while(aCurrent
!= maTrDeEdgeEntries
.end() && (*aCurrent
) < rNewEdge
)
267 // Insert before first which is smaller or equal or at end
268 maTrDeEdgeEntries
.insert(aCurrent
, rNewEdge
);
271 bool splitEdgeAtGivenPoint(
272 TrDeEdgeEntries::reference aEdge
,
273 const B2DPoint
& rCutPoint
,
274 TrDeEdgeEntries::iterator aCurrent
)
276 // do not create edges without deltaY: do not split when start is identical
277 if(aEdge
.getStart().equal(rCutPoint
, fTools::getSmallValue()))
282 // do not create edges without deltaY: do not split when end is identical
283 if(aEdge
.getEnd().equal(rCutPoint
, fTools::getSmallValue()))
288 const double fOldDeltaYStart(rCutPoint
.getY() - aEdge
.getStart().getY());
290 if(fTools::lessOrEqual(fOldDeltaYStart
, 0.0))
292 // do not split: the resulting edge would be horizontal
293 // correct it to new start point
294 aEdge
.setStart(&rCutPoint
);
298 const double fNewDeltaYStart(aEdge
.getEnd().getY() - rCutPoint
.getY());
300 if(fTools::lessOrEqual(fNewDeltaYStart
, 0.0))
302 // do not split: the resulting edge would be horizontal
303 // correct it to new end point
304 aEdge
.setEnd(&rCutPoint
);
309 const TrDeEdgeEntry
aNewEdge(
312 aEdge
.getSortValue());
315 aEdge
.setEnd(&rCutPoint
);
317 // Insert sorted (to avoid new sort)
318 addEdgeSorted(aCurrent
, aNewEdge
);
323 bool testAndCorrectEdgeIntersection(
324 TrDeEdgeEntries::reference aEdgeA
,
325 TrDeEdgeEntries::reference aEdgeB
,
326 TrDeEdgeEntries::iterator aCurrent
)
328 // Exclude simple cases: same start or end point
329 if(aEdgeA
.getStart().equal(aEdgeB
.getStart(), fTools::getSmallValue()))
334 if(aEdgeA
.getStart().equal(aEdgeB
.getEnd(), fTools::getSmallValue()))
339 if(aEdgeA
.getEnd().equal(aEdgeB
.getStart(), fTools::getSmallValue()))
344 if(aEdgeA
.getEnd().equal(aEdgeB
.getEnd(), fTools::getSmallValue()))
349 // Exclude simple cases: one of the edges has no length anymore
350 if(aEdgeA
.getStart().equal(aEdgeA
.getEnd(), fTools::getSmallValue()))
355 if(aEdgeB
.getStart().equal(aEdgeB
.getEnd(), fTools::getSmallValue()))
360 // check if one point is on the other edge (a touch, not a cut)
361 const B2DVector
aDeltaB(aEdgeB
.getDeltaX(), aEdgeB
.getDeltaY());
363 if(tools::isPointOnEdge(aEdgeA
.getStart(), aEdgeB
.getStart(), aDeltaB
))
365 return splitEdgeAtGivenPoint(aEdgeB
, aEdgeA
.getStart(), aCurrent
);
368 if(tools::isPointOnEdge(aEdgeA
.getEnd(), aEdgeB
.getStart(), aDeltaB
))
370 return splitEdgeAtGivenPoint(aEdgeB
, aEdgeA
.getEnd(), aCurrent
);
373 const B2DVector
aDeltaA(aEdgeA
.getDeltaX(), aEdgeA
.getDeltaY());
375 if(tools::isPointOnEdge(aEdgeB
.getStart(), aEdgeA
.getStart(), aDeltaA
))
377 return splitEdgeAtGivenPoint(aEdgeA
, aEdgeB
.getStart(), aCurrent
);
380 if(tools::isPointOnEdge(aEdgeB
.getEnd(), aEdgeA
.getStart(), aDeltaA
))
382 return splitEdgeAtGivenPoint(aEdgeA
, aEdgeB
.getEnd(), aCurrent
);
385 // check for cut inside edges. Use both t-values to choose the more precise
391 aEdgeA
.getStart(), aDeltaA
,
392 aEdgeB
.getStart(), aDeltaB
,
395 &fCutB
) != CutFlagValue::NONE
)
397 // use a simple metric (length criteria) for choosing the numerically
399 const double fSimpleLengthA(aDeltaA
.getX() + aDeltaA
.getY());
400 const double fSimpleLengthB(aDeltaB
.getX() + aDeltaB
.getY());
401 const bool bAIsLonger(fSimpleLengthA
> fSimpleLengthB
);
402 B2DPoint
* pNewPoint
= bAIsLonger
403 ? maNewPoints
.allocatePoint(aEdgeA
.getStart() + (fCutA
* aDeltaA
))
404 : maNewPoints
.allocatePoint(aEdgeB
.getStart() + (fCutB
* aDeltaB
));
406 // try to split both edges
407 bool bRetval
= splitEdgeAtGivenPoint(aEdgeA
, *pNewPoint
, aCurrent
);
408 bRetval
|= splitEdgeAtGivenPoint(aEdgeB
, *pNewPoint
, aCurrent
);
411 maNewPoints
.freeIfLast(pNewPoint
);
419 void solveHorizontalEdges(TrDeSimpleEdges
& rTrDeSimpleEdges
)
421 if(!rTrDeSimpleEdges
.empty() && !maTrDeEdgeEntries
.empty())
423 // there were horizontal edges. These can be excluded, but
424 // cuts with other edges need to be solved and added before
426 for(sal_uInt32 a
= 0; a
< rTrDeSimpleEdges
.size(); a
++)
428 // get horizontal edge as candidate; prepare its range and fixed Y
429 const TrDeSimpleEdge
& rHorEdge
= rTrDeSimpleEdges
[a
];
430 const B1DRange
aRange(rHorEdge
.getStart().getX(), rHorEdge
.getEnd().getX());
431 const double fFixedY(rHorEdge
.getStart().getY());
433 // loop over traversing edges
434 TrDeEdgeEntries::iterator
aCurrent(maTrDeEdgeEntries
.begin());
439 TrDeEdgeEntries::reference
aCompare(*aCurrent
++);
441 if(fTools::lessOrEqual(aCompare
.getEnd().getY(), fFixedY
))
443 // edge ends above horizontal edge, continue
447 if(fTools::moreOrEqual(aCompare
.getStart().getY(), fFixedY
))
449 // edge starts below horizontal edge, continue
453 // vertical overlap, get horizontal range
454 const B1DRange
aCompareRange(aCompare
.getStart().getX(), aCompare
.getEnd().getX());
456 if(aRange
.overlaps(aCompareRange
))
458 // possible cut, get cut point
459 const B2DPoint
aSplit(aCompare
.getCutPointForGivenY(fFixedY
));
461 if(fTools::more(aSplit
.getX(), aRange
.getMinimum())
462 && fTools::less(aSplit
.getX(), aRange
.getMaximum()))
464 // cut is in XRange of horizontal edge, potentially needed cut
465 B2DPoint
* pNewPoint
= maNewPoints
.allocatePoint(aSplit
);
467 if(!splitEdgeAtGivenPoint(aCompare
, *pNewPoint
, aCurrent
))
469 maNewPoints
.freeIfLast(pNewPoint
);
474 while(aCurrent
!= maTrDeEdgeEntries
.end()
475 && fTools::less(aCurrent
->getStart().getY(), fFixedY
));
481 explicit TrapezoidSubdivider(
482 const B2DPolyPolygon
& rSourcePolyPolygon
)
483 : mnInitialEdgeEntryCount(0),
488 B2DPolyPolygon
aSource(rSourcePolyPolygon
);
489 const sal_uInt32
nPolygonCount(rSourcePolyPolygon
.count());
490 TrDeSimpleEdges aTrDeSimpleEdges
;
491 sal_uInt32
a(0), b(0);
492 sal_uInt32
nAllPointCount(0);
494 // ensure there are no curves used
495 if(aSource
.areControlPointsUsed())
497 aSource
= aSource
.getDefaultAdaptiveSubdivision();
500 for(a
= 0; a
< nPolygonCount
; a
++)
502 // 1st run: count points
503 const B2DPolygon
aPolygonCandidate(aSource
.getB2DPolygon(a
));
504 const sal_uInt32
nCount(aPolygonCandidate
.count());
508 nAllPointCount
+= nCount
;
514 // reserve needed points. CAUTION: maPoints size is NOT to be changed anymore
515 // after 2nd loop since pointers to it are used in the edges
516 maPoints
.reserve(nAllPointCount
);
518 for(a
= 0; a
< nPolygonCount
; a
++)
520 // 2nd run: add points
521 const B2DPolygon
aPolygonCandidate(aSource
.getB2DPolygon(a
));
522 const sal_uInt32
nCount(aPolygonCandidate
.count());
526 for(b
= 0; b
< nCount
; b
++)
528 maPoints
.push_back(aPolygonCandidate
.getB2DPoint(b
));
533 // Moved the edge construction to a 3rd run: doing it in the 2nd run is
534 // possible(and i used it), but requires a working vector::reserve()
535 // implementation, else the vector will be reallocated and the pointers
536 // in the edges may be wrong. Security first here.
537 sal_uInt32
nStartIndex(0);
539 for(a
= 0; a
< nPolygonCount
; a
++)
541 const B2DPolygon
aPolygonCandidate(aSource
.getB2DPolygon(a
));
542 const sal_uInt32
nCount(aPolygonCandidate
.count());
546 // get the last point of the current polygon
547 B2DPoint
* pPrev(&maPoints
[nCount
+ nStartIndex
- 1]);
549 for(b
= 0; b
< nCount
; b
++)
552 B2DPoint
* pCurr(&maPoints
[nStartIndex
++]);
554 if(fTools::equal(pPrev
->getY(), pCurr
->getY(), fTools::getSmallValue()))
556 // horizontal edge, check for single point
557 if(!fTools::equal(pPrev
->getX(), pCurr
->getX(), fTools::getSmallValue()))
559 // X-order not needed, just add
560 aTrDeSimpleEdges
.push_back(TrDeSimpleEdge(pPrev
, pCurr
));
562 const double fMiddle((pPrev
->getY() + pCurr
->getY()) * 0.5);
563 pPrev
->setY(fMiddle
);
564 pCurr
->setY(fMiddle
);
569 // vertical edge. Positive Y-direction is guaranteed by the
570 // TrDeEdgeEntry constructor
571 maTrDeEdgeEntries
.push_back(TrDeEdgeEntry(pPrev
, pCurr
, 0));
572 mnInitialEdgeEntryCount
++;
582 if(!maTrDeEdgeEntries
.empty())
584 // single and initial sort of traversing edges
585 maTrDeEdgeEntries
.sort();
587 // solve horizontal edges if there are any detected
588 solveHorizontalEdges(aTrDeSimpleEdges
);
592 void Subdivide(B2DTrapezoidVector
& ro_Result
)
594 // This is the central subdivider. The strategy is to use the first two entries
595 // from the traversing edges as a potential trapezoid and do the needed corrections
596 // and adaptions on the way.
598 // There always must be two edges with the same YStart value: When adding the polygons
599 // in the constructor, there is always a topmost point from which two edges start; when
600 // the topmost is an edge, there is a start and end of this edge from which two edges
601 // start. All cases have two edges with same StartY (QED).
603 // Based on this these edges get corrected when:
604 // - one is longer than the other
606 // - they intersect with other edges
607 // - another edge starts inside the thought trapezoid
609 // All this cases again produce a valid state so that the first two edges have a common
610 // Ystart again. Some cases lead to a restart of the process, some allow consuming the
611 // edges and create the intended trapezoid.
613 // Be careful when doing chages here: It is essential to keep all possible paths
614 // in valid states and to be numerically correct. This is especially needed e.g.
615 // by using fTools::equal(..) in the more robust small-value incarnation.
617 B1DRange aRightRange
;
619 if(!maTrDeEdgeEntries
.empty())
621 // measuring shows that the relation between edges and created trapezoids is
622 // mostly in the 1:1 range, thus reserve as much trapezoids as edges exist. Do
623 // not use maTrDeEdgeEntries.size() since that may be a non-constant time
624 // operation for Lists. Instead, use mnInitialEdgeEntryCount which will contain
625 // the roughly counted adds to the List
626 ro_Result
.reserve(ro_Result
.size() + mnInitialEdgeEntryCount
);
629 while(!maTrDeEdgeEntries
.empty())
631 // Prepare current operator and get first edge
632 TrDeEdgeEntries::iterator
aCurrent(maTrDeEdgeEntries
.begin());
633 TrDeEdgeEntries::reference
aLeft(*aCurrent
++);
635 if(aCurrent
== maTrDeEdgeEntries
.end())
637 // Should not happen: No 2nd edge; consume the single edge
638 // to not have an endless loop and start next. During development
639 // i constantly had breakpoints here, so i am sure enough to add an
641 OSL_FAIL("Trapeziod decomposer in illegal state (!)");
642 maTrDeEdgeEntries
.pop_front();
647 TrDeEdgeEntries::reference
aRight(*aCurrent
++);
649 if(!fTools::equal(aLeft
.getStart().getY(), aRight
.getStart().getY(), fTools::getSmallValue()))
651 // Should not happen: We have a 2nd edge, but YStart is on another
652 // line; consume the single edge to not have an endless loop and start
653 // next. During development i constantly had breakpoints here, so i am
654 // sure enough to add an assertion here
655 OSL_FAIL("Trapeziod decomposer in illegal state (!)");
656 maTrDeEdgeEntries
.pop_front();
660 // aLeft and aRight build a thought trapezoid now. They have a common
661 // start line (same Y for start points). Potentially, one of the edges
662 // is longer than the other. It is only needed to look at the shorter
663 // length which build the potential trapezoid. To do so, get the end points
664 // locally and adapt the evtl. longer one. Use only aLeftEnd and aRightEnd
665 // from here on, not the aLeft.getEnd() or aRight.getEnd() accesses.
666 B2DPoint
aLeftEnd(aLeft
.getEnd());
667 B2DPoint
aRightEnd(aRight
.getEnd());
669 // check if end points are on the same line. If yes, no adaption
670 // needs to be prepared. Also remember which one actually is longer.
671 const bool bEndOnSameLine(fTools::equal(aLeftEnd
.getY(), aRightEnd
.getY(), fTools::getSmallValue()));
672 bool bLeftIsLonger(false);
676 // check which edge is longer and correct accordingly
677 bLeftIsLonger
= fTools::more(aLeftEnd
.getY(), aRightEnd
.getY());
681 aLeftEnd
= aLeft
.getCutPointForGivenY(aRightEnd
.getY());
685 aRightEnd
= aRight
.getCutPointForGivenY(aLeftEnd
.getY());
689 // check for same start and end points
690 const bool bSameStartPoint(aLeft
.getStart().equal(aRight
.getStart(), fTools::getSmallValue()));
691 const bool bSameEndPoint(aLeftEnd
.equal(aRightEnd
, fTools::getSmallValue()));
693 // check the simple case that the edges form a 'blind' edge (deadend)
694 if(bSameStartPoint
&& bSameEndPoint
)
696 // correct the longer edge if prepared
701 B2DPoint
* pNewPoint
= maNewPoints
.allocatePoint(aLeftEnd
);
703 if(!splitEdgeAtGivenPoint(aLeft
, *pNewPoint
, aCurrent
))
705 maNewPoints
.freeIfLast(pNewPoint
);
710 B2DPoint
* pNewPoint
= maNewPoints
.allocatePoint(aRightEnd
);
712 if(!splitEdgeAtGivenPoint(aRight
, *pNewPoint
, aCurrent
))
714 maNewPoints
.freeIfLast(pNewPoint
);
719 // consume both edges and start next run
720 maTrDeEdgeEntries
.pop_front();
721 maTrDeEdgeEntries
.pop_front();
726 // check if the edges self-intersect. This can only happen when
727 // start and end point are different
728 bool bRangesSet(false);
730 if(!(bSameStartPoint
|| bSameEndPoint
))
732 // get XRanges of edges
733 aLeftRange
= B1DRange(aLeft
.getStart().getX(), aLeftEnd
.getX());
734 aRightRange
= B1DRange(aRight
.getStart().getX(), aRightEnd
.getX());
737 // use fast range test first
738 if(aLeftRange
.overlaps(aRightRange
))
740 // real cut test and correction. If correction was needed,
742 if(testAndCorrectEdgeIntersection(aLeft
, aRight
, aCurrent
))
749 // now we need to check if there are intersections with other edges
750 // or if other edges start inside the candidate trapezoid
751 if(aCurrent
!= maTrDeEdgeEntries
.end()
752 && fTools::less(aCurrent
->getStart().getY(), aLeftEnd
.getY()))
754 // get XRanges of edges
757 aLeftRange
= B1DRange(aLeft
.getStart().getX(), aLeftEnd
.getX());
758 aRightRange
= B1DRange(aRight
.getStart().getX(), aRightEnd
.getX());
761 // build full XRange for fast check
762 B1DRange
aAllRange(aLeftRange
);
763 aAllRange
.expand(aRightRange
);
765 // prepare loop iterator; aCurrent needs to stay unchanged for
766 // eventual sorted insertions of new EdgeNodes. Also prepare stop flag
767 TrDeEdgeEntries::iterator
aLoop(aCurrent
);
772 // get compare edge and its XRange
773 TrDeEdgeEntries::reference
aCompare(*aLoop
++);
775 // avoid edges using the same start point as one of
776 // the edges. These can neither have their start point
777 // in the thought trapezoid nor cut with one of the edges
778 if(aCompare
.getStart().equal(aRight
.getStart(), fTools::getSmallValue()))
783 // get compare XRange
784 const B1DRange
aCompareRange(aCompare
.getStart().getX(), aCompare
.getEnd().getX());
786 // use fast range test first
787 if(aAllRange
.overlaps(aCompareRange
))
789 // check for start point inside thought trapezoid
790 if(fTools::more(aCompare
.getStart().getY(), aLeft
.getStart().getY()))
792 // calculate the two possible split points at compare's Y
793 const B2DPoint
aSplitLeft(aLeft
.getCutPointForGivenY(aCompare
.getStart().getY()));
794 const B2DPoint
aSplitRight(aRight
.getCutPointForGivenY(aCompare
.getStart().getY()));
796 // check for start point of aCompare being inside thought
798 if(aCompare
.getStart().getX() >= aSplitLeft
.getX() &&
799 aCompare
.getStart().getX() <= aSplitRight
.getX())
801 // is inside, correct and restart loop
802 B2DPoint
* pNewLeft
= maNewPoints
.allocatePoint(aSplitLeft
);
804 if(splitEdgeAtGivenPoint(aLeft
, *pNewLeft
, aCurrent
))
810 maNewPoints
.freeIfLast(pNewLeft
);
813 B2DPoint
* pNewRight
= maNewPoints
.allocatePoint(aSplitRight
);
815 if(splitEdgeAtGivenPoint(aRight
, *pNewRight
, aCurrent
))
821 maNewPoints
.freeIfLast(pNewRight
);
826 if(!bDone
&& aLeftRange
.overlaps(aCompareRange
))
828 // test for concrete cut of compare edge with left edge
829 bDone
= testAndCorrectEdgeIntersection(aLeft
, aCompare
, aCurrent
);
832 if(!bDone
&& aRightRange
.overlaps(aCompareRange
))
834 // test for concrete cut of compare edge with Right edge
835 bDone
= testAndCorrectEdgeIntersection(aRight
, aCompare
, aCurrent
);
840 && aLoop
!= maTrDeEdgeEntries
.end()
841 && fTools::less(aLoop
->getStart().getY(), aLeftEnd
.getY()));
845 // something needed to be changed; start next loop
850 // when we get here, the intended trapezoid can be used. It needs to
851 // be corrected, eventually (if prepared); but this is no reason not to
852 // use it in the same loop iteration
857 B2DPoint
* pNewPoint
= maNewPoints
.allocatePoint(aLeftEnd
);
859 if(!splitEdgeAtGivenPoint(aLeft
, *pNewPoint
, aCurrent
))
861 maNewPoints
.freeIfLast(pNewPoint
);
866 B2DPoint
* pNewPoint
= maNewPoints
.allocatePoint(aRightEnd
);
868 if(!splitEdgeAtGivenPoint(aRight
, *pNewPoint
, aCurrent
))
870 maNewPoints
.freeIfLast(pNewPoint
);
875 // the two edges start at the same Y, they use the same DeltaY, they
876 // do not cut themselves and not any other edge in range. Create a
877 // B2DTrapezoid and consume both edges
880 aLeft
.getStart().getX(),
881 aRight
.getStart().getX(),
882 aLeft
.getStart().getY(),
887 maTrDeEdgeEntries
.pop_front();
888 maTrDeEdgeEntries
.pop_front();
892 } // end of anonymous namespace
893 } // end of namespace basegfx
897 B2DTrapezoid::B2DTrapezoid(
898 const double& rfTopXLeft
,
899 const double& rfTopXRight
,
900 const double& rfTopY
,
901 const double& rfBottomXLeft
,
902 const double& rfBottomXRight
,
903 const double& rfBottomY
)
904 : mfTopXLeft(rfTopXLeft
),
905 mfTopXRight(rfTopXRight
),
907 mfBottomXLeft(rfBottomXLeft
),
908 mfBottomXRight(rfBottomXRight
),
911 // guarantee mfTopXRight >= mfTopXLeft
912 if(mfTopXLeft
> mfTopXRight
)
914 std::swap(mfTopXLeft
, mfTopXRight
);
917 // guarantee mfBottomXRight >= mfBottomXLeft
918 if(mfBottomXLeft
> mfBottomXRight
)
920 std::swap(mfBottomXLeft
, mfBottomXRight
);
923 // guarantee mfBottomY >= mfTopY
924 if(mfTopY
> mfBottomY
)
926 std::swap(mfTopY
, mfBottomY
);
927 std::swap(mfTopXLeft
, mfBottomXLeft
);
928 std::swap(mfTopXRight
, mfBottomXRight
);
932 B2DPolygon
B2DTrapezoid::getB2DPolygon() const
936 aRetval
.append(B2DPoint(getTopXLeft(), getTopY()));
937 aRetval
.append(B2DPoint(getTopXRight(), getTopY()));
938 aRetval
.append(B2DPoint(getBottomXRight(), getBottomY()));
939 aRetval
.append(B2DPoint(getBottomXLeft(), getBottomY()));
940 aRetval
.setClosed(true);
944 } // end of namespace basegfx
950 // convert Source tools::PolyPolygon to trapezoids
951 void trapezoidSubdivide(B2DTrapezoidVector
& ro_Result
, const B2DPolyPolygon
& rSourcePolyPolygon
)
953 trapezoidhelper::TrapezoidSubdivider
aTrapezoidSubdivider(rSourcePolyPolygon
);
955 aTrapezoidSubdivider
.Subdivide(ro_Result
);
958 void createLineTrapezoidFromEdge(
959 B2DTrapezoidVector
& ro_Result
,
960 const B2DPoint
& rPointA
,
961 const B2DPoint
& rPointB
,
964 if(fTools::lessOrEqual(fLineWidth
, 0.0))
970 if(rPointA
.equal(rPointB
, fTools::getSmallValue()))
972 // points are equal, no edge
976 const double fHalfLineWidth(0.5 * fLineWidth
);
978 if(fTools::equal(rPointA
.getX(), rPointB
.getX(), fTools::getSmallValue()))
981 const double fLeftX(rPointA
.getX() - fHalfLineWidth
);
982 const double fRightX(rPointA
.getX() + fHalfLineWidth
);
988 std::min(rPointA
.getY(), rPointB
.getY()),
991 std::max(rPointA
.getY(), rPointB
.getY())));
993 else if(fTools::equal(rPointA
.getY(), rPointB
.getY(), fTools::getSmallValue()))
996 const double fLeftX(std::min(rPointA
.getX(), rPointB
.getX()));
997 const double fRightX(std::max(rPointA
.getX(), rPointB
.getX()));
1003 rPointA
.getY() - fHalfLineWidth
,
1006 rPointA
.getY() + fHalfLineWidth
));
1011 // create perpendicular vector
1012 const B2DVector
aDelta(rPointB
- rPointA
);
1013 B2DVector
aPerpendicular(-aDelta
.getY(), aDelta
.getX());
1014 aPerpendicular
.setLength(fHalfLineWidth
);
1016 // create StartLow, StartHigh, EndLow and EndHigh
1017 const B2DPoint
aStartLow(rPointA
+ aPerpendicular
);
1018 const B2DPoint
aStartHigh(rPointA
- aPerpendicular
);
1019 const B2DPoint
aEndHigh(rPointB
- aPerpendicular
);
1020 const B2DPoint
aEndLow(rPointB
+ aPerpendicular
);
1022 // create EdgeEntries
1023 basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries
;
1025 aTrDeEdgeEntries
.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow
, &aStartHigh
, 0));
1026 aTrDeEdgeEntries
.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh
, &aEndHigh
, 0));
1027 aTrDeEdgeEntries
.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh
, &aEndLow
, 0));
1028 aTrDeEdgeEntries
.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow
, &aStartLow
, 0));
1029 aTrDeEdgeEntries
.sort();
1031 // here we know we have exactly four edges, and they do not cut, touch or
1032 // intersect. This makes processing much easier. Get the first two as start
1033 // edges for the thought trapezoid
1034 basegfx::trapezoidhelper::TrDeEdgeEntries::iterator
aCurrent(aTrDeEdgeEntries
.begin());
1035 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aLeft(*aCurrent
++);
1036 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aRight(*aCurrent
++);
1037 const bool bEndOnSameLine(fTools::equal(aLeft
.getEnd().getY(), aRight
.getEnd().getY(), fTools::getSmallValue()));
1041 // create two triangle trapezoids
1042 ro_Result
.push_back(
1044 aLeft
.getStart().getX(),
1045 aRight
.getStart().getX(),
1046 aLeft
.getStart().getY(),
1047 aLeft
.getEnd().getX(),
1048 aRight
.getEnd().getX(),
1049 aLeft
.getEnd().getY()));
1051 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aLeft2(*aCurrent
++);
1052 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aRight2(*aCurrent
++);
1054 ro_Result
.push_back(
1056 aLeft2
.getStart().getX(),
1057 aRight2
.getStart().getX(),
1058 aLeft2
.getStart().getY(),
1059 aLeft2
.getEnd().getX(),
1060 aRight2
.getEnd().getX(),
1061 aLeft2
.getEnd().getY()));
1065 // create three trapezoids. Check which edge is longer and
1066 // correct accordingly
1067 const bool bLeftIsLonger(fTools::more(aLeft
.getEnd().getY(), aRight
.getEnd().getY()));
1071 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aRight2(*aCurrent
++);
1072 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aLeft2(*aCurrent
++);
1073 const B2DPoint
aSplitLeft(aLeft
.getCutPointForGivenY(aRight
.getEnd().getY()));
1074 const B2DPoint
aSplitRight(aRight2
.getCutPointForGivenY(aLeft
.getEnd().getY()));
1076 ro_Result
.push_back(
1078 aLeft
.getStart().getX(),
1079 aRight
.getStart().getX(),
1080 aLeft
.getStart().getY(),
1082 aRight
.getEnd().getX(),
1083 aRight
.getEnd().getY()));
1085 ro_Result
.push_back(
1088 aRight
.getEnd().getX(),
1089 aRight
.getEnd().getY(),
1090 aLeft2
.getStart().getX(),
1092 aLeft2
.getStart().getY()));
1094 ro_Result
.push_back(
1096 aLeft2
.getStart().getX(),
1098 aLeft2
.getStart().getY(),
1099 aLeft2
.getEnd().getX(),
1100 aRight2
.getEnd().getX(),
1101 aLeft2
.getEnd().getY()));
1105 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aLeft2(*aCurrent
++);
1106 basegfx::trapezoidhelper::TrDeEdgeEntries::reference
aRight2(*aCurrent
++);
1107 const B2DPoint
aSplitRight(aRight
.getCutPointForGivenY(aLeft
.getEnd().getY()));
1108 const B2DPoint
aSplitLeft(aLeft2
.getCutPointForGivenY(aRight
.getEnd().getY()));
1110 ro_Result
.push_back(
1112 aLeft
.getStart().getX(),
1113 aRight
.getStart().getX(),
1114 aLeft
.getStart().getY(),
1115 aLeft
.getEnd().getX(),
1117 aLeft
.getEnd().getY()));
1119 ro_Result
.push_back(
1121 aLeft
.getEnd().getX(),
1123 aLeft
.getEnd().getY(),
1125 aRight
.getEnd().getX(),
1126 aRight2
.getStart().getY()));
1128 ro_Result
.push_back(
1131 aRight
.getEnd().getX(),
1132 aRight2
.getStart().getY(),
1133 aLeft2
.getEnd().getX(),
1134 aRight2
.getEnd().getX(),
1135 aLeft2
.getEnd().getY()));
1141 void createLineTrapezoidFromB2DPolygon(
1142 B2DTrapezoidVector
& ro_Result
,
1143 const B2DPolygon
& rPolygon
,
1146 if(fTools::lessOrEqual(fLineWidth
, 0.0))
1151 // ensure there are no curves used
1152 B2DPolygon
aSource(rPolygon
);
1154 if(aSource
.areControlPointsUsed())
1156 const double fPrecisionFactor
= 0.25;
1157 aSource
= adaptiveSubdivideByDistance( aSource
, fLineWidth
* fPrecisionFactor
);
1160 const sal_uInt32
nPointCount(aSource
.count());
1167 const sal_uInt32
nEdgeCount(aSource
.isClosed() ? nPointCount
: nPointCount
- 1);
1168 B2DPoint
aCurrent(aSource
.getB2DPoint(0));
1170 ro_Result
.reserve(ro_Result
.size() + (3 * nEdgeCount
));
1172 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1174 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
1175 const B2DPoint
aNext(aSource
.getB2DPoint(nNextIndex
));
1177 createLineTrapezoidFromEdge(ro_Result
, aCurrent
, aNext
, fLineWidth
);
1182 void createLineTrapezoidFromB2DPolyPolygon(
1183 B2DTrapezoidVector
& ro_Result
,
1184 const B2DPolyPolygon
& rPolyPolygon
,
1187 if(fTools::lessOrEqual(fLineWidth
, 0.0))
1192 // ensure there are no curves used
1193 B2DPolyPolygon
aSource(rPolyPolygon
);
1195 if(aSource
.areControlPointsUsed())
1197 aSource
= aSource
.getDefaultAdaptiveSubdivision();
1200 const sal_uInt32
nCount(aSource
.count());
1207 for(sal_uInt32
a(0); a
< nCount
; a
++)
1209 createLineTrapezoidFromB2DPolygon(
1211 aSource
.getB2DPolygon(a
),
1216 } // end of namespace tools
1217 } // end of namespace basegfx
1219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */