Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <basegfx/polygon/b2dtrapezoid.hxx>
21 : #include <basegfx/range/b1drange.hxx>
22 : #include <basegfx/polygon/b2dpolygontools.hxx>
23 : #include <list>
24 :
25 : //////////////////////////////////////////////////////////////////////////////
26 :
27 : namespace basegfx
28 : {
29 : namespace trapezoidhelper
30 : {
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
36 :
37 : class TrDeSimpleEdge
38 : {
39 : protected:
40 : // pointers to start and end point
41 : const B2DPoint* mpStart;
42 : const B2DPoint* mpEnd;
43 :
44 : public:
45 : // constructor
46 0 : TrDeSimpleEdge(
47 : const B2DPoint* pStart,
48 : const B2DPoint* pEnd)
49 : : mpStart(pStart),
50 0 : mpEnd(pEnd)
51 : {
52 0 : }
53 :
54 : // data read access
55 0 : const B2DPoint& getStart() const { return *mpStart; }
56 0 : const B2DPoint& getEnd() const { return *mpEnd; }
57 : };
58 :
59 : //////////////////////////////////////////////////////////////////////////////
60 : // define vector of simple edges
61 :
62 : typedef ::std::vector< TrDeSimpleEdge > TrDeSimpleEdges;
63 :
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)
69 :
70 : class TrDeEdgeEntry : public TrDeSimpleEdge
71 : {
72 : private:
73 : // the slope in a numerical useful form for sorting
74 : sal_uInt32 mnSortValue;
75 :
76 : public:
77 : // convenience data read access
78 0 : double getDeltaX() const { return mpEnd->getX() - mpStart->getX(); }
79 0 : double getDeltaY() const { return mpEnd->getY() - mpStart->getY(); }
80 :
81 : // convenience data read access. SortValue is created on demand since
82 : // it is not always used
83 0 : sal_uInt32 getSortValue() const
84 : {
85 0 : if(0 != mnSortValue)
86 0 : return mnSortValue;
87 :
88 : // get radiant; has to be in the range ]0.0 .. pi[, thus scale to full
89 : // sal_uInt32 range for maximum precision
90 0 : const double fRadiant(atan2(getDeltaY(), getDeltaX()) * (SAL_MAX_UINT32 / F_PI));
91 :
92 : // convert to sal_uInt32 value
93 0 : const_cast< TrDeEdgeEntry* >(this)->mnSortValue = sal_uInt32(fRadiant);
94 :
95 0 : return mnSortValue;
96 : }
97 :
98 : // constructor. SortValue can be given when known, use zero otherwise
99 0 : TrDeEdgeEntry(
100 : const B2DPoint* pStart,
101 : const B2DPoint* pEnd,
102 : sal_uInt32 nSortValue = 0)
103 : : TrDeSimpleEdge(pStart, pEnd),
104 0 : mnSortValue(nSortValue)
105 : {
106 : // force traversal of deltaY downward
107 0 : if(mpEnd->getY() < mpStart->getY())
108 : {
109 0 : std::swap(mpStart, mpEnd);
110 : }
111 :
112 : // no horizontal edges allowed, all neeed to traverse vertically
113 : OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
114 0 : }
115 :
116 : // data write access to StartPoint
117 0 : void setStart( const B2DPoint* pNewStart)
118 : {
119 : OSL_ENSURE(0 != pNewStart, "No null pointer allowed here (!)");
120 :
121 0 : if(mpStart != pNewStart)
122 : {
123 0 : mpStart = pNewStart;
124 :
125 : // no horizontal edges allowed, all neeed to traverse vertivally
126 : OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
127 : }
128 0 : }
129 :
130 : // data write access to EndPoint
131 0 : void setEnd( const B2DPoint* pNewEnd)
132 : {
133 : OSL_ENSURE(0 != pNewEnd, "No null pointer allowed here (!)");
134 :
135 0 : if(mpEnd != pNewEnd)
136 : {
137 0 : mpEnd = pNewEnd;
138 :
139 : // no horizontal edges allowed, all neeed to traverse vertivally
140 : OSL_ENSURE(mpEnd->getY() > mpStart->getY(), "Illegal TrDeEdgeEntry constructed (!)");
141 : }
142 0 : }
143 :
144 : // operator for sort support. Sort by Y, X and slope (in that order)
145 0 : bool operator<(const TrDeEdgeEntry& rComp) const
146 : {
147 0 : if(fTools::equal(getStart().getY(), rComp.getStart().getY(), fTools::getSmallValue()))
148 : {
149 0 : if(fTools::equal(getStart().getX(), rComp.getStart().getX(), fTools::getSmallValue()))
150 : {
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 0 : return (getSortValue() > rComp.getSortValue());
158 : }
159 : else
160 : {
161 0 : return fTools::less(getStart().getX(), rComp.getStart().getX());
162 : }
163 : }
164 : else
165 : {
166 0 : return fTools::less(getStart().getY(), rComp.getStart().getY());
167 : }
168 : }
169 :
170 : // method for cut support
171 0 : B2DPoint getCutPointForGivenY(double fGivenY)
172 : {
173 : // Calculate cut point locally (do not use interpolate) since it is numerically
174 : // necessary to guarantee the new, equal Y-coordinate
175 0 : const double fFactor((fGivenY - getStart().getY()) / getDeltaY());
176 0 : const double fDeltaXNew(fFactor * getDeltaX());
177 :
178 0 : return B2DPoint(getStart().getX() + fDeltaXNew, fGivenY);
179 : }
180 : };
181 :
182 : //////////////////////////////////////////////////////////////////////////////
183 : // define double linked list of edges (for fast random insert)
184 :
185 : typedef ::std::list< TrDeEdgeEntry > TrDeEdgeEntries;
186 :
187 : } // end of anonymous namespace
188 : } // end of namespace basegfx
189 :
190 : //////////////////////////////////////////////////////////////////////////////
191 :
192 : namespace basegfx
193 : {
194 : namespace trapezoidhelper
195 : {
196 : // helper class to handle the complete trapezoid subdivision of a PolyPolygon
197 : class TrapezoidSubdivider
198 : {
199 : private:
200 : // local data
201 : sal_uInt32 mnInitialEdgeEntryCount;
202 : TrDeEdgeEntries maTrDeEdgeEntries;
203 : ::std::vector< B2DPoint > maPoints;
204 : ::std::vector< B2DPoint* > maNewPoints;
205 :
206 0 : void addEdgeSorted(
207 : TrDeEdgeEntries::iterator aCurrent,
208 : const TrDeEdgeEntry& rNewEdge)
209 : {
210 : // Loop while new entry is bigger, use operator<
211 0 : while(aCurrent != maTrDeEdgeEntries.end() && (*aCurrent) < rNewEdge)
212 : {
213 0 : ++aCurrent;
214 : }
215 :
216 : // Insert before first which is smaller or equal or at end
217 0 : maTrDeEdgeEntries.insert(aCurrent, rNewEdge);
218 0 : }
219 :
220 0 : bool splitEdgeAtGivenPoint(
221 : TrDeEdgeEntries::reference aEdge,
222 : const B2DPoint& rCutPoint,
223 : TrDeEdgeEntries::iterator aCurrent)
224 : {
225 : // do not create edges without deltaY: do not split when start is identical
226 0 : if(aEdge.getStart().equal(rCutPoint, fTools::getSmallValue()))
227 : {
228 0 : return false;
229 : }
230 :
231 : // do not create edges without deltaY: do not split when end is identical
232 0 : if(aEdge.getEnd().equal(rCutPoint, fTools::getSmallValue()))
233 : {
234 0 : return false;
235 : }
236 :
237 0 : const double fOldDeltaYStart(rCutPoint.getY() - aEdge.getStart().getY());
238 :
239 0 : if(fTools::lessOrEqual(fOldDeltaYStart, 0.0))
240 : {
241 : // do not split: the resulting edge would be horizontal
242 : // correct it to new start point
243 0 : aEdge.setStart(&rCutPoint);
244 0 : return false;
245 : }
246 :
247 0 : const double fNewDeltaYStart(aEdge.getEnd().getY() - rCutPoint.getY());
248 :
249 0 : if(fTools::lessOrEqual(fNewDeltaYStart, 0.0))
250 : {
251 : // do not split: the resulting edge would be horizontal
252 : // correct it to new end point
253 0 : aEdge.setEnd(&rCutPoint);
254 0 : return false;
255 : }
256 :
257 : // Create new entry
258 : const TrDeEdgeEntry aNewEdge(
259 : &rCutPoint,
260 0 : &aEdge.getEnd(),
261 0 : aEdge.getSortValue());
262 :
263 : // Correct old entry
264 0 : aEdge.setEnd(&rCutPoint);
265 :
266 : // Insert sorted (to avoid new sort)
267 0 : addEdgeSorted(aCurrent, aNewEdge);
268 :
269 0 : return true;
270 : }
271 :
272 0 : bool testAndCorrectEdgeIntersection(
273 : TrDeEdgeEntries::reference aEdgeA,
274 : TrDeEdgeEntries::reference aEdgeB,
275 : TrDeEdgeEntries::iterator aCurrent)
276 : {
277 : // Exclude simple cases: same start or end point
278 0 : if(aEdgeA.getStart().equal(aEdgeB.getStart(), fTools::getSmallValue()))
279 : {
280 0 : return false;
281 : }
282 :
283 0 : if(aEdgeA.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
284 : {
285 0 : return false;
286 : }
287 :
288 0 : if(aEdgeA.getEnd().equal(aEdgeB.getStart(), fTools::getSmallValue()))
289 : {
290 0 : return false;
291 : }
292 :
293 0 : if(aEdgeA.getEnd().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
294 : {
295 0 : return false;
296 : }
297 :
298 : // Exclude simple cases: one of the edges has no length anymore
299 0 : if(aEdgeA.getStart().equal(aEdgeA.getEnd(), fTools::getSmallValue()))
300 : {
301 0 : return false;
302 : }
303 :
304 0 : if(aEdgeB.getStart().equal(aEdgeB.getEnd(), fTools::getSmallValue()))
305 : {
306 0 : return false;
307 : }
308 :
309 : // check if one point is on the other edge (a touch, not a cut)
310 0 : const B2DVector aDeltaB(aEdgeB.getDeltaX(), aEdgeB.getDeltaY());
311 :
312 0 : if(tools::isPointOnEdge(aEdgeA.getStart(), aEdgeB.getStart(), aDeltaB))
313 : {
314 0 : return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getStart(), aCurrent);
315 : }
316 :
317 0 : if(tools::isPointOnEdge(aEdgeA.getEnd(), aEdgeB.getStart(), aDeltaB))
318 : {
319 0 : return splitEdgeAtGivenPoint(aEdgeB, aEdgeA.getEnd(), aCurrent);
320 : }
321 :
322 0 : const B2DVector aDeltaA(aEdgeA.getDeltaX(), aEdgeA.getDeltaY());
323 :
324 0 : if(tools::isPointOnEdge(aEdgeB.getStart(), aEdgeA.getStart(), aDeltaA))
325 : {
326 0 : return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getStart(), aCurrent);
327 : }
328 :
329 0 : if(tools::isPointOnEdge(aEdgeB.getEnd(), aEdgeA.getStart(), aDeltaA))
330 : {
331 0 : return splitEdgeAtGivenPoint(aEdgeA, aEdgeB.getEnd(), aCurrent);
332 : }
333 :
334 : // check for cut inside edges. Use both t-values to choose the more precise
335 : // one later
336 0 : double fCutA(0.0);
337 0 : double fCutB(0.0);
338 :
339 0 : if(tools::findCut(
340 0 : aEdgeA.getStart(), aDeltaA,
341 0 : aEdgeB.getStart(), aDeltaB,
342 : CUTFLAG_LINE,
343 : &fCutA,
344 0 : &fCutB))
345 : {
346 : // use a simple metric (length criteria) for choosing the numerically
347 : // better cut
348 0 : const double fSimpleLengthA(aDeltaA.getX() + aDeltaA.getY());
349 0 : const double fSimpleLengthB(aDeltaB.getX() + aDeltaB.getY());
350 0 : const bool bAIsLonger(fSimpleLengthA > fSimpleLengthB);
351 : B2DPoint* pNewPoint = bAIsLonger
352 0 : ? new B2DPoint(aEdgeA.getStart() + (fCutA * aDeltaA))
353 0 : : new B2DPoint(aEdgeB.getStart() + (fCutB * aDeltaB));
354 0 : bool bRetval(false);
355 :
356 : // try to split both edges
357 0 : bRetval = splitEdgeAtGivenPoint(aEdgeA, *pNewPoint, aCurrent);
358 0 : bRetval |= splitEdgeAtGivenPoint(aEdgeB, *pNewPoint, aCurrent);
359 :
360 0 : if(bRetval)
361 : {
362 0 : maNewPoints.push_back(pNewPoint);
363 : }
364 : else
365 : {
366 0 : delete pNewPoint;
367 : }
368 :
369 0 : return bRetval;
370 : }
371 :
372 0 : return false;
373 : }
374 :
375 0 : void solveHorizontalEdges(TrDeSimpleEdges& rTrDeSimpleEdges)
376 : {
377 0 : if(rTrDeSimpleEdges.size() && maTrDeEdgeEntries.size())
378 : {
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 0 : sal_uInt32 a(0);
383 :
384 0 : for(a = 0; a < rTrDeSimpleEdges.size(); a++)
385 : {
386 : // get horizontal edge as candidate; prepare it's range and fixed Y
387 0 : const TrDeSimpleEdge& rHorEdge = rTrDeSimpleEdges[a];
388 0 : const B1DRange aRange(rHorEdge.getStart().getX(), rHorEdge.getEnd().getX());
389 0 : const double fFixedY(rHorEdge.getStart().getY());
390 :
391 : // loop over traversing edges
392 0 : TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
393 :
394 0 : do
395 : {
396 : // get compare edge
397 0 : TrDeEdgeEntries::reference aCompare(*aCurrent++);
398 :
399 0 : if(fTools::lessOrEqual(aCompare.getEnd().getY(), fFixedY))
400 : {
401 : // edge ends above horizontal edge, continue
402 0 : continue;
403 : }
404 :
405 0 : if(fTools::moreOrEqual(aCompare.getStart().getY(), fFixedY))
406 : {
407 : // edge starts below horizontal edge, continue
408 0 : continue;
409 : }
410 :
411 : // vertical overlap, get horizontal range
412 0 : const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
413 :
414 0 : if(aRange.overlaps(aCompareRange))
415 : {
416 : // possible cut, get cut point
417 0 : const B2DPoint aSplit(aCompare.getCutPointForGivenY(fFixedY));
418 :
419 0 : if(fTools::more(aSplit.getX(), aRange.getMinimum())
420 0 : && fTools::less(aSplit.getX(), aRange.getMaximum()))
421 : {
422 : // cut is in XRange of horizontal edge, potenitally needed cut
423 0 : B2DPoint* pNewPoint = new B2DPoint(aSplit);
424 :
425 0 : if(splitEdgeAtGivenPoint(aCompare, *pNewPoint, aCurrent))
426 : {
427 0 : maNewPoints.push_back(pNewPoint);
428 : }
429 : else
430 : {
431 0 : delete pNewPoint;
432 : }
433 0 : }
434 : }
435 : }
436 0 : while(aCurrent != maTrDeEdgeEntries.end()
437 0 : && fTools::less(aCurrent->getStart().getY(), fFixedY));
438 : }
439 : }
440 0 : }
441 :
442 : public:
443 0 : explicit TrapezoidSubdivider(
444 : const B2DPolyPolygon& rSourcePolyPolygon)
445 : : mnInitialEdgeEntryCount(0),
446 : maTrDeEdgeEntries(),
447 : maPoints(),
448 0 : maNewPoints()
449 : {
450 0 : B2DPolyPolygon aSource(rSourcePolyPolygon);
451 0 : const sal_uInt32 nPolygonCount(rSourcePolyPolygon.count());
452 0 : TrDeSimpleEdges aTrDeSimpleEdges;
453 0 : sal_uInt32 a(0), b(0);
454 0 : sal_uInt32 nAllPointCount(0);
455 :
456 : // ensure there are no curves used
457 0 : if(aSource.areControlPointsUsed())
458 : {
459 0 : aSource = aSource.getDefaultAdaptiveSubdivision();
460 : }
461 :
462 0 : for(a = 0; a < nPolygonCount; a++)
463 : {
464 : // 1st run: count points
465 0 : const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
466 0 : const sal_uInt32 nCount(aPolygonCandidate.count());
467 :
468 0 : if(nCount > 2)
469 : {
470 0 : nAllPointCount += nCount;
471 : }
472 0 : }
473 :
474 0 : if(nAllPointCount)
475 : {
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 0 : maPoints.reserve(nAllPointCount);
479 :
480 0 : for(a = 0; a < nPolygonCount; a++)
481 : {
482 : // 2nd run: add points
483 0 : const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
484 0 : const sal_uInt32 nCount(aPolygonCandidate.count());
485 :
486 0 : if(nCount > 2)
487 : {
488 0 : for(b = 0; b < nCount; b++)
489 : {
490 0 : maPoints.push_back(aPolygonCandidate.getB2DPoint(b));
491 : }
492 : }
493 0 : }
494 :
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 0 : sal_uInt32 nStartIndex(0);
500 :
501 0 : for(a = 0; a < nPolygonCount; a++)
502 : {
503 0 : const B2DPolygon aPolygonCandidate(aSource.getB2DPolygon(a));
504 0 : const sal_uInt32 nCount(aPolygonCandidate.count());
505 :
506 0 : if(nCount > 2)
507 : {
508 : // get the last point of the current polygon
509 0 : B2DPoint* pPrev(&maPoints[nCount + nStartIndex - 1]);
510 :
511 0 : for(b = 0; b < nCount; b++)
512 : {
513 : // get next point
514 0 : B2DPoint* pCurr(&maPoints[nStartIndex++]);
515 :
516 0 : if(fTools::equal(pPrev->getY(), pCurr->getY(), fTools::getSmallValue()))
517 : {
518 : // horizontal edge, check for single point
519 0 : if(!fTools::equal(pPrev->getX(), pCurr->getX(), fTools::getSmallValue()))
520 : {
521 : // X-order not needed, just add
522 0 : aTrDeSimpleEdges.push_back(TrDeSimpleEdge(pPrev, pCurr));
523 :
524 0 : const double fMiddle((pPrev->getY() + pCurr->getY()) * 0.5);
525 0 : pPrev->setY(fMiddle);
526 0 : pCurr->setY(fMiddle);
527 : }
528 : }
529 : else
530 : {
531 : // vertical edge. Positive Y-direction is guaranteed by the
532 : // TrDeEdgeEntry constructor
533 0 : maTrDeEdgeEntries.push_back(TrDeEdgeEntry(pPrev, pCurr, 0));
534 0 : mnInitialEdgeEntryCount++;
535 : }
536 :
537 : // prepare next step
538 0 : pPrev = pCurr;
539 : }
540 : }
541 0 : }
542 : }
543 :
544 0 : if(!maTrDeEdgeEntries.empty())
545 : {
546 : // single and initial sort of traversing edges
547 0 : maTrDeEdgeEntries.sort();
548 :
549 : // solve horizontal edges if there are any detected
550 0 : solveHorizontalEdges(aTrDeSimpleEdges);
551 0 : }
552 0 : }
553 :
554 0 : ~TrapezoidSubdivider()
555 0 : {
556 : // delete the extra points created for cuts
557 0 : const sal_uInt32 nCount(maNewPoints.size());
558 :
559 0 : for(sal_uInt32 a(0); a < nCount; a++)
560 : {
561 0 : delete maNewPoints[a];
562 : }
563 0 : }
564 :
565 0 : void Subdivide(B2DTrapezoidVector& ro_Result)
566 : {
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.
570 : //
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).
575 : //
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
581 : //
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.
585 : //
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 0 : B1DRange aLeftRange;
590 0 : B1DRange aRightRange;
591 :
592 0 : if(!maTrDeEdgeEntries.empty())
593 : {
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 0 : ro_Result.reserve(ro_Result.size() + mnInitialEdgeEntryCount);
600 : }
601 :
602 0 : while(!maTrDeEdgeEntries.empty())
603 : {
604 : // Prepare current operator and get first edge
605 0 : TrDeEdgeEntries::iterator aCurrent(maTrDeEdgeEntries.begin());
606 0 : TrDeEdgeEntries::reference aLeft(*aCurrent++);
607 :
608 0 : if(aCurrent == maTrDeEdgeEntries.end())
609 : {
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 0 : maTrDeEdgeEntries.pop_front();
616 0 : continue;
617 : }
618 :
619 : // get second edge
620 0 : TrDeEdgeEntries::reference aRight(*aCurrent++);
621 :
622 0 : if(!fTools::equal(aLeft.getStart().getY(), aRight.getStart().getY(), fTools::getSmallValue()))
623 : {
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 0 : maTrDeEdgeEntries.pop_front();
630 0 : continue;
631 : }
632 :
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 0 : B2DPoint aLeftEnd(aLeft.getEnd());
640 0 : B2DPoint aRightEnd(aRight.getEnd());
641 :
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 0 : const bool bEndOnSameLine(fTools::equal(aLeftEnd.getY(), aRightEnd.getY(), fTools::getSmallValue()));
645 0 : bool bLeftIsLonger(false);
646 :
647 0 : if(!bEndOnSameLine)
648 : {
649 : // check which edge is longer and correct accordingly
650 0 : bLeftIsLonger = fTools::more(aLeftEnd.getY(), aRightEnd.getY());
651 :
652 0 : if(bLeftIsLonger)
653 : {
654 0 : aLeftEnd = aLeft.getCutPointForGivenY(aRightEnd.getY());
655 : }
656 : else
657 : {
658 0 : aRightEnd = aRight.getCutPointForGivenY(aLeftEnd.getY());
659 : }
660 : }
661 :
662 : // check for same start and end points
663 0 : const bool bSameStartPoint(aLeft.getStart().equal(aRight.getStart(), fTools::getSmallValue()));
664 0 : const bool bSameEndPoint(aLeftEnd.equal(aRightEnd, fTools::getSmallValue()));
665 :
666 : // check the simple case that the edges form a 'blind' edge (deadend)
667 0 : if(bSameStartPoint && bSameEndPoint)
668 : {
669 : // correct the longer edge if prepared
670 0 : if(!bEndOnSameLine)
671 : {
672 0 : if(bLeftIsLonger)
673 : {
674 0 : B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
675 :
676 0 : if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
677 : {
678 0 : maNewPoints.push_back(pNewPoint);
679 : }
680 : else
681 : {
682 0 : delete pNewPoint;
683 : }
684 : }
685 : else
686 : {
687 0 : B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
688 :
689 0 : if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
690 : {
691 0 : maNewPoints.push_back(pNewPoint);
692 : }
693 : else
694 : {
695 0 : delete pNewPoint;
696 : }
697 : }
698 : }
699 :
700 : // consume both edges and start next run
701 0 : maTrDeEdgeEntries.pop_front();
702 0 : maTrDeEdgeEntries.pop_front();
703 :
704 0 : continue;
705 : }
706 :
707 : // check if the edges self-intersect. This can only happen when
708 : // start and end point are different
709 0 : bool bRangesSet(false);
710 :
711 0 : if(!(bSameStartPoint || bSameEndPoint))
712 : {
713 : // get XRanges of edges
714 0 : aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
715 0 : aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
716 0 : bRangesSet = true;
717 :
718 : // use fast range test first
719 0 : if(aLeftRange.overlaps(aRightRange))
720 : {
721 : // real cut test and correction. If correction was needed,
722 : // start new run
723 0 : if(testAndCorrectEdgeIntersection(aLeft, aRight, aCurrent))
724 : {
725 0 : continue;
726 : }
727 : }
728 : }
729 :
730 : // now we need to check if there are intersections with other edges
731 : // or if other edges start inside the candidate trapezoid
732 0 : if(aCurrent != maTrDeEdgeEntries.end()
733 0 : && fTools::less(aCurrent->getStart().getY(), aLeftEnd.getY()))
734 : {
735 : // get XRanges of edges
736 0 : if(!bRangesSet)
737 : {
738 0 : aLeftRange = B1DRange(aLeft.getStart().getX(), aLeftEnd.getX());
739 0 : aRightRange = B1DRange(aRight.getStart().getX(), aRightEnd.getX());
740 : }
741 :
742 : // build full XRange for fast check
743 0 : B1DRange aAllRange(aLeftRange);
744 0 : aAllRange.expand(aRightRange);
745 :
746 : // prepare loop iterator; aCurrent needs to stay unchanged for
747 : // eventual sorted insertions of new EdgeNodes. Also prepare stop flag
748 0 : TrDeEdgeEntries::iterator aLoop(aCurrent);
749 0 : bool bDone(false);
750 :
751 0 : do
752 : {
753 : // get compare edge and it's XRange
754 0 : TrDeEdgeEntries::reference aCompare(*aLoop++);
755 :
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 0 : if(aCompare.getStart().equal(aRight.getStart(), fTools::getSmallValue()))
760 : {
761 0 : continue;
762 : }
763 :
764 : // get compare XRange
765 0 : const B1DRange aCompareRange(aCompare.getStart().getX(), aCompare.getEnd().getX());
766 :
767 : // use fast range test first
768 0 : if(aAllRange.overlaps(aCompareRange))
769 : {
770 : // check for start point inside thought trapezoid
771 0 : if(fTools::more(aCompare.getStart().getY(), aLeft.getStart().getY()))
772 : {
773 : // calculate the two possible split points at compare's Y
774 0 : const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aCompare.getStart().getY()));
775 0 : const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aCompare.getStart().getY()));
776 :
777 : // check for start point of aCompare being inside thought
778 : // trapezoid
779 0 : if(aCompare.getStart().getX() >= aSplitLeft.getX() &&
780 0 : aCompare.getStart().getX() <= aSplitRight.getX())
781 : {
782 : // is inside, correct and restart loop
783 0 : B2DPoint* pNewLeft = new B2DPoint(aSplitLeft);
784 :
785 0 : if(splitEdgeAtGivenPoint(aLeft, *pNewLeft, aCurrent))
786 : {
787 0 : maNewPoints.push_back(pNewLeft);
788 0 : bDone = true;
789 : }
790 : else
791 : {
792 0 : delete pNewLeft;
793 : }
794 :
795 0 : B2DPoint* pNewRight = new B2DPoint(aSplitRight);
796 :
797 0 : if(splitEdgeAtGivenPoint(aRight, *pNewRight, aCurrent))
798 : {
799 0 : maNewPoints.push_back(pNewRight);
800 0 : bDone = true;
801 : }
802 : else
803 : {
804 0 : delete pNewRight;
805 : }
806 0 : }
807 : }
808 :
809 0 : if(!bDone && aLeftRange.overlaps(aCompareRange))
810 : {
811 : // test for concrete cut of compare edge with left edge
812 0 : bDone = testAndCorrectEdgeIntersection(aLeft, aCompare, aCurrent);
813 : }
814 :
815 0 : if(!bDone && aRightRange.overlaps(aCompareRange))
816 : {
817 : // test for concrete cut of compare edge with Right edge
818 0 : bDone = testAndCorrectEdgeIntersection(aRight, aCompare, aCurrent);
819 : }
820 : }
821 : }
822 0 : while(!bDone
823 0 : && aLoop != maTrDeEdgeEntries.end()
824 0 : && fTools::less(aLoop->getStart().getY(), aLeftEnd.getY()));
825 :
826 0 : if(bDone)
827 : {
828 : // something needed to be changed; start next loop
829 0 : continue;
830 : }
831 : }
832 :
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 0 : if(!bEndOnSameLine)
837 : {
838 0 : if(bLeftIsLonger)
839 : {
840 0 : B2DPoint* pNewPoint = new B2DPoint(aLeftEnd);
841 :
842 0 : if(splitEdgeAtGivenPoint(aLeft, *pNewPoint, aCurrent))
843 : {
844 0 : maNewPoints.push_back(pNewPoint);
845 : }
846 : else
847 : {
848 0 : delete pNewPoint;
849 : }
850 : }
851 : else
852 : {
853 0 : B2DPoint* pNewPoint = new B2DPoint(aRightEnd);
854 :
855 0 : if(splitEdgeAtGivenPoint(aRight, *pNewPoint, aCurrent))
856 : {
857 0 : maNewPoints.push_back(pNewPoint);
858 : }
859 : else
860 : {
861 0 : delete pNewPoint;
862 : }
863 : }
864 : }
865 :
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 0 : aLeft.getStart().getX(),
872 0 : aRight.getStart().getX(),
873 0 : aLeft.getStart().getY(),
874 0 : aLeftEnd.getX(),
875 0 : aRightEnd.getX(),
876 0 : aLeftEnd.getY()));
877 :
878 0 : maTrDeEdgeEntries.pop_front();
879 0 : maTrDeEdgeEntries.pop_front();
880 0 : }
881 0 : }
882 : };
883 : } // end of anonymous namespace
884 : } // end of namespace basegfx
885 :
886 : //////////////////////////////////////////////////////////////////////////////
887 :
888 : namespace basegfx
889 : {
890 0 : 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 0 : mfBottomY(rfBottomY)
903 : {
904 : // guarantee mfTopXRight >= mfTopXLeft
905 0 : if(mfTopXLeft > mfTopXRight)
906 : {
907 0 : std::swap(mfTopXLeft, mfTopXRight);
908 : }
909 :
910 : // guarantee mfBottomXRight >= mfBottomXLeft
911 0 : if(mfBottomXLeft > mfBottomXRight)
912 : {
913 0 : std::swap(mfBottomXLeft, mfBottomXRight);
914 : }
915 :
916 : // guarantee mfBottomY >= mfTopY
917 0 : if(mfTopY > mfBottomY)
918 : {
919 0 : std::swap(mfTopY, mfBottomY);
920 0 : std::swap(mfTopXLeft, mfBottomXLeft);
921 0 : std::swap(mfTopXRight, mfBottomXRight);
922 : }
923 0 : }
924 :
925 0 : B2DPolygon B2DTrapezoid::getB2DPolygon() const
926 : {
927 0 : B2DPolygon aRetval;
928 :
929 0 : aRetval.append(B2DPoint(getTopXLeft(), getTopY()));
930 0 : aRetval.append(B2DPoint(getTopXRight(), getTopY()));
931 0 : aRetval.append(B2DPoint(getBottomXRight(), getBottomY()));
932 0 : aRetval.append(B2DPoint(getBottomXLeft(), getBottomY()));
933 0 : aRetval.setClosed(true);
934 :
935 0 : return aRetval;
936 : }
937 : } // end of namespace basegfx
938 :
939 : //////////////////////////////////////////////////////////////////////////////
940 :
941 : namespace basegfx
942 : {
943 : namespace tools
944 : {
945 : // convert Source PolyPolygon to trapezoids
946 0 : void trapezoidSubdivide(B2DTrapezoidVector& ro_Result, const B2DPolyPolygon& rSourcePolyPolygon)
947 : {
948 0 : trapezoidhelper::TrapezoidSubdivider aTrapezoidSubdivider(rSourcePolyPolygon);
949 :
950 0 : aTrapezoidSubdivider.Subdivide(ro_Result);
951 0 : }
952 :
953 0 : void createLineTrapezoidFromEdge(
954 : B2DTrapezoidVector& ro_Result,
955 : const B2DPoint& rPointA,
956 : const B2DPoint& rPointB,
957 : double fLineWidth)
958 : {
959 0 : if(fTools::lessOrEqual(fLineWidth, 0.0))
960 : {
961 : // no line witdh
962 0 : return;
963 : }
964 :
965 0 : if(rPointA.equal(rPointB, fTools::getSmallValue()))
966 : {
967 : // points are equal, no edge
968 0 : return;
969 : }
970 :
971 0 : const double fHalfLineWidth(0.5 * fLineWidth);
972 :
973 0 : if(fTools::equal(rPointA.getX(), rPointB.getX(), fTools::getSmallValue()))
974 : {
975 : // vertical line
976 0 : const double fLeftX(rPointA.getX() - fHalfLineWidth);
977 0 : const double fRightX(rPointA.getX() + fHalfLineWidth);
978 :
979 : ro_Result.push_back(
980 : B2DTrapezoid(
981 : fLeftX,
982 : fRightX,
983 0 : std::min(rPointA.getY(), rPointB.getY()),
984 : fLeftX,
985 : fRightX,
986 0 : std::max(rPointA.getY(), rPointB.getY())));
987 : }
988 0 : else if(fTools::equal(rPointA.getY(), rPointB.getY(), fTools::getSmallValue()))
989 : {
990 : // horizontal line
991 0 : const double fLeftX(std::min(rPointA.getX(), rPointB.getX()));
992 0 : const double fRightX(std::max(rPointA.getX(), rPointB.getX()));
993 :
994 : ro_Result.push_back(
995 : B2DTrapezoid(
996 : fLeftX,
997 : fRightX,
998 0 : rPointA.getY() - fHalfLineWidth,
999 : fLeftX,
1000 : fRightX,
1001 0 : rPointA.getY() + fHalfLineWidth));
1002 : }
1003 : else
1004 : {
1005 : // diagonal line
1006 : // create perpendicular vector
1007 0 : const B2DVector aDelta(rPointB - rPointA);
1008 0 : B2DVector aPerpendicular(-aDelta.getY(), aDelta.getX());
1009 0 : aPerpendicular.setLength(fHalfLineWidth);
1010 :
1011 : // create StartLow, StartHigh, EndLow and EndHigh
1012 0 : const B2DPoint aStartLow(rPointA + aPerpendicular);
1013 0 : const B2DPoint aStartHigh(rPointA - aPerpendicular);
1014 0 : const B2DPoint aEndHigh(rPointB - aPerpendicular);
1015 0 : const B2DPoint aEndLow(rPointB + aPerpendicular);
1016 :
1017 : // create EdgeEntries
1018 0 : basegfx::trapezoidhelper::TrDeEdgeEntries aTrDeEdgeEntries;
1019 :
1020 0 : aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartLow, &aStartHigh, 0));
1021 0 : aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aStartHigh, &aEndHigh, 0));
1022 0 : aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndHigh, &aEndLow, 0));
1023 0 : aTrDeEdgeEntries.push_back(basegfx::trapezoidhelper::TrDeEdgeEntry(&aEndLow, &aStartLow, 0));
1024 0 : aTrDeEdgeEntries.sort();
1025 :
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 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::iterator aCurrent(aTrDeEdgeEntries.begin());
1030 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft(*aCurrent++);
1031 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight(*aCurrent++);
1032 0 : const bool bEndOnSameLine(fTools::equal(aLeft.getEnd().getY(), aRight.getEnd().getY(), fTools::getSmallValue()));
1033 :
1034 0 : if(bEndOnSameLine)
1035 : {
1036 : // create two triangle trapezoids
1037 : ro_Result.push_back(
1038 : B2DTrapezoid(
1039 0 : aLeft.getStart().getX(),
1040 0 : aRight.getStart().getX(),
1041 0 : aLeft.getStart().getY(),
1042 0 : aLeft.getEnd().getX(),
1043 0 : aRight.getEnd().getX(),
1044 0 : aLeft.getEnd().getY()));
1045 :
1046 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1047 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1048 :
1049 : ro_Result.push_back(
1050 : B2DTrapezoid(
1051 0 : aLeft2.getStart().getX(),
1052 0 : aRight2.getStart().getX(),
1053 0 : aLeft2.getStart().getY(),
1054 0 : aLeft2.getEnd().getX(),
1055 0 : aRight2.getEnd().getX(),
1056 0 : aLeft2.getEnd().getY()));
1057 : }
1058 : else
1059 : {
1060 : // create three trapezoids. Check which edge is longer and
1061 : // correct accordingly
1062 0 : const bool bLeftIsLonger(fTools::more(aLeft.getEnd().getY(), aRight.getEnd().getY()));
1063 :
1064 0 : if(bLeftIsLonger)
1065 : {
1066 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1067 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1068 0 : const B2DPoint aSplitLeft(aLeft.getCutPointForGivenY(aRight.getEnd().getY()));
1069 0 : const B2DPoint aSplitRight(aRight2.getCutPointForGivenY(aLeft.getEnd().getY()));
1070 :
1071 : ro_Result.push_back(
1072 : B2DTrapezoid(
1073 0 : aLeft.getStart().getX(),
1074 0 : aRight.getStart().getX(),
1075 0 : aLeft.getStart().getY(),
1076 0 : aSplitLeft.getX(),
1077 0 : aRight.getEnd().getX(),
1078 0 : aRight.getEnd().getY()));
1079 :
1080 : ro_Result.push_back(
1081 : B2DTrapezoid(
1082 0 : aSplitLeft.getX(),
1083 0 : aRight.getEnd().getX(),
1084 0 : aRight.getEnd().getY(),
1085 0 : aLeft2.getStart().getX(),
1086 0 : aSplitRight.getX(),
1087 0 : aLeft2.getStart().getY()));
1088 :
1089 : ro_Result.push_back(
1090 : B2DTrapezoid(
1091 0 : aLeft2.getStart().getX(),
1092 0 : aSplitRight.getX(),
1093 0 : aLeft2.getStart().getY(),
1094 0 : aLeft2.getEnd().getX(),
1095 0 : aRight2.getEnd().getX(),
1096 0 : aLeft2.getEnd().getY()));
1097 : }
1098 : else
1099 : {
1100 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aLeft2(*aCurrent++);
1101 0 : basegfx::trapezoidhelper::TrDeEdgeEntries::reference aRight2(*aCurrent++);
1102 0 : const B2DPoint aSplitRight(aRight.getCutPointForGivenY(aLeft.getEnd().getY()));
1103 0 : const B2DPoint aSplitLeft(aLeft2.getCutPointForGivenY(aRight.getEnd().getY()));
1104 :
1105 : ro_Result.push_back(
1106 : B2DTrapezoid(
1107 0 : aLeft.getStart().getX(),
1108 0 : aRight.getStart().getX(),
1109 0 : aLeft.getStart().getY(),
1110 0 : aLeft.getEnd().getX(),
1111 0 : aSplitRight.getX(),
1112 0 : aLeft.getEnd().getY()));
1113 :
1114 : ro_Result.push_back(
1115 : B2DTrapezoid(
1116 0 : aLeft.getEnd().getX(),
1117 0 : aSplitRight.getX(),
1118 0 : aLeft.getEnd().getY(),
1119 0 : aSplitLeft.getX(),
1120 0 : aRight.getEnd().getX(),
1121 0 : aRight2.getStart().getY()));
1122 :
1123 : ro_Result.push_back(
1124 : B2DTrapezoid(
1125 0 : aSplitLeft.getX(),
1126 0 : aRight.getEnd().getX(),
1127 0 : aRight2.getStart().getY(),
1128 0 : aLeft2.getEnd().getX(),
1129 0 : aRight2.getEnd().getX(),
1130 0 : aLeft2.getEnd().getY()));
1131 : }
1132 0 : }
1133 : }
1134 : }
1135 :
1136 0 : void createLineTrapezoidFromB2DPolygon(
1137 : B2DTrapezoidVector& ro_Result,
1138 : const B2DPolygon& rPolygon,
1139 : double fLineWidth)
1140 : {
1141 0 : if(fTools::lessOrEqual(fLineWidth, 0.0))
1142 : {
1143 : return;
1144 : }
1145 :
1146 : // ensure there are no curves used
1147 0 : B2DPolygon aSource(rPolygon);
1148 :
1149 0 : if(aSource.areControlPointsUsed())
1150 : {
1151 0 : const double fPrecisionFactor = 0.25;
1152 0 : aSource = adaptiveSubdivideByDistance( aSource, fLineWidth * fPrecisionFactor );
1153 : }
1154 :
1155 0 : const sal_uInt32 nPointCount(aSource.count());
1156 :
1157 0 : if(!nPointCount)
1158 : {
1159 : return;
1160 : }
1161 :
1162 0 : const sal_uInt32 nEdgeCount(aSource.isClosed() ? nPointCount : nPointCount - 1);
1163 0 : B2DPoint aCurrent(aSource.getB2DPoint(0));
1164 :
1165 0 : ro_Result.reserve(ro_Result.size() + (3 * nEdgeCount));
1166 :
1167 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
1168 : {
1169 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
1170 0 : const B2DPoint aNext(aSource.getB2DPoint(nNextIndex));
1171 :
1172 0 : createLineTrapezoidFromEdge(ro_Result, aCurrent, aNext, fLineWidth);
1173 0 : aCurrent = aNext;
1174 0 : }
1175 : }
1176 :
1177 0 : void createLineTrapezoidFromB2DPolyPolygon(
1178 : B2DTrapezoidVector& ro_Result,
1179 : const B2DPolyPolygon& rPolyPolygon,
1180 : double fLineWidth)
1181 : {
1182 0 : if(fTools::lessOrEqual(fLineWidth, 0.0))
1183 : {
1184 : return;
1185 : }
1186 :
1187 : // ensure there are no curves used
1188 0 : B2DPolyPolygon aSource(rPolyPolygon);
1189 :
1190 0 : if(aSource.areControlPointsUsed())
1191 : {
1192 0 : aSource = aSource.getDefaultAdaptiveSubdivision();
1193 : }
1194 :
1195 0 : const sal_uInt32 nCount(aSource.count());
1196 :
1197 0 : if(!nCount)
1198 : {
1199 : return;
1200 : }
1201 :
1202 0 : for(sal_uInt32 a(0); a < nCount; a++)
1203 : {
1204 : createLineTrapezoidFromB2DPolygon(
1205 : ro_Result,
1206 : aSource.getB2DPolygon(a),
1207 0 : fLineWidth);
1208 0 : }
1209 : }
1210 :
1211 : } // end of namespace tools
1212 : } // end of namespace basegfx
1213 :
1214 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|