/src/libreoffice/basegfx/source/range/b2drangeclipper.cxx
Line | Count | Source (jump to first uncovered line) |
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 <osl/diagnose.h> |
21 | | |
22 | | #include <basegfx/range/b2drange.hxx> |
23 | | #include <basegfx/range/b2drangeclipper.hxx> |
24 | | #include <basegfx/polygon/b2dpolypolygon.hxx> |
25 | | #include <basegfx/range/b2drectangle.hxx> |
26 | | |
27 | | #include <o3tl/vector_pool.hxx> |
28 | | |
29 | | #include <algorithm> |
30 | | #include <cassert> |
31 | | #include <list> |
32 | | #include <iterator> |
33 | | |
34 | | namespace basegfx |
35 | | { |
36 | | namespace |
37 | | { |
38 | | // Generating a poly-polygon from a bunch of rectangles |
39 | | |
40 | | // Helper functionality for sweep-line algorithm |
41 | | // ==================================================== |
42 | | |
43 | | class ImplPolygon; |
44 | | typedef o3tl::vector_pool<ImplPolygon> VectorOfPolygons; |
45 | | |
46 | | /** This class represents an active edge |
47 | | |
48 | | As the sweep line traverses across the overall area, |
49 | | rectangle edges parallel to it generate events, and |
50 | | rectangle edges orthogonal to it generate active |
51 | | edges. This class represents the latter. |
52 | | */ |
53 | | class ActiveEdge |
54 | | { |
55 | | public: |
56 | | |
57 | | enum EdgeDirection { |
58 | | /// edge proceeds to the left |
59 | | PROCEED_LEFT=0, |
60 | | /// edge proceeds to the right |
61 | | PROCEED_RIGHT=1 |
62 | | }; |
63 | | |
64 | | /** Create active edge |
65 | | |
66 | | @param rRect |
67 | | Rectangle this edge is part of |
68 | | |
69 | | @param fInvariantCoord |
70 | | The invariant coordinate value of this edge |
71 | | |
72 | | @param eEdgeType |
73 | | Is fInvariantCoord the lower or the higher value, for |
74 | | this rect? |
75 | | */ |
76 | | ActiveEdge( const B2DRectangle& rRect, |
77 | | const double& fInvariantCoord, |
78 | | std::ptrdiff_t nPolyIdx, |
79 | | EdgeDirection eEdgeDirection ) : |
80 | 0 | mfInvariantCoord(fInvariantCoord), |
81 | 0 | mpAssociatedRect( &rRect ), |
82 | 0 | mnPolygonIdx( nPolyIdx ), |
83 | 0 | meEdgeDirection( eEdgeDirection ) |
84 | 0 | {} |
85 | | |
86 | 0 | double getInvariantCoord() const { return mfInvariantCoord; } |
87 | 0 | const B2DRectangle& getRect() const { return *mpAssociatedRect; } |
88 | 0 | std::ptrdiff_t getTargetPolygonIndex() const { return mnPolygonIdx; } |
89 | 0 | void setTargetPolygonIndex( std::ptrdiff_t nIdx ) { mnPolygonIdx = nIdx; } |
90 | 0 | EdgeDirection getEdgeDirection() const { return meEdgeDirection; } |
91 | | |
92 | | private: |
93 | | /** The invariant coordinate value of this edge (e.g. the |
94 | | common y value, for a horizontal edge) |
95 | | */ |
96 | | double mfInvariantCoord; |
97 | | |
98 | | /** Associated rectangle |
99 | | |
100 | | This on the one hand saves some storage space (the |
101 | | vector of rectangles is persistent, anyway), and on |
102 | | the other hand provides an identifier to match active |
103 | | edges and x events (see below) |
104 | | |
105 | | Ptr because class needs to be assignable |
106 | | */ |
107 | | const B2DRectangle* mpAssociatedRect; |
108 | | |
109 | | /** Index of the polygon this edge is currently involved |
110 | | with. |
111 | | |
112 | | Note that this can change for some kinds of edge |
113 | | intersection, as the algorithm tends to swap |
114 | | associated polygons there. |
115 | | |
116 | | -1 denotes no assigned polygon |
117 | | */ |
118 | | std::ptrdiff_t mnPolygonIdx; |
119 | | |
120 | | /// 'left' or 'right' |
121 | | EdgeDirection meEdgeDirection; |
122 | | }; |
123 | | |
124 | | // Needs to be list - various places hold ptrs to elements |
125 | | typedef std::list< ActiveEdge > ListOfEdges; |
126 | | |
127 | | /** Element of the sweep line event list |
128 | | |
129 | | As the sweep line traverses across the overall area, |
130 | | rectangle edges parallel to it generate events, and |
131 | | rectangle edges orthogonal to it generate active |
132 | | edges. This class represents the former. |
133 | | |
134 | | The class defines an element of the sweep line list. The |
135 | | sweep line's position jumps in steps defined by the |
136 | | coordinates of the sorted SweepLineEvent entries. |
137 | | */ |
138 | | class SweepLineEvent |
139 | | { |
140 | | public: |
141 | | /** The two possible sweep line rectangle edges differ by |
142 | | one coordinate value - the starting edge has the |
143 | | lower, the finishing edge the higher value. |
144 | | */ |
145 | | enum EdgeType { |
146 | | /// edge with lower coordinate value |
147 | | STARTING_EDGE=0, |
148 | | /// edge with higher coordinate value |
149 | | FINISHING_EDGE=1 |
150 | | }; |
151 | | |
152 | | /** The two possible sweep line directions |
153 | | */ |
154 | | enum EdgeDirection { |
155 | | PROCEED_UP=0, |
156 | | PROCEED_DOWN=1 |
157 | | }; |
158 | | |
159 | | /** Create sweep line event |
160 | | |
161 | | @param fPos |
162 | | Coordinate position of the event |
163 | | |
164 | | @param rRect |
165 | | Rectangle this event is generated for. |
166 | | |
167 | | @param eEdgeType |
168 | | Is fPos the lower or the higher value, for the |
169 | | rectangle this event is generated for? |
170 | | */ |
171 | | SweepLineEvent( double fPos, |
172 | | const B2DRectangle& rRect, |
173 | | EdgeType eEdgeType, |
174 | | EdgeDirection eDirection) : |
175 | 0 | mfPos( fPos ), |
176 | 0 | mpAssociatedRect( &rRect ), |
177 | 0 | meEdgeType( eEdgeType ), |
178 | 0 | meEdgeDirection( eDirection ) |
179 | 0 | {} |
180 | | |
181 | 0 | double getPos() const { return mfPos; } |
182 | 0 | const B2DRectangle& getRect() const { return *mpAssociatedRect; } |
183 | 0 | EdgeType getEdgeType() const { return meEdgeType; } |
184 | 0 | EdgeDirection getEdgeDirection() const { return meEdgeDirection; } |
185 | | |
186 | | /// For STL sort |
187 | 0 | bool operator<( const SweepLineEvent& rRHS ) const { return mfPos < rRHS.mfPos; } |
188 | | |
189 | | private: |
190 | | /// position of the event, in the direction of the line sweep |
191 | | double mfPos; |
192 | | |
193 | | /** Rectangle this event is generated for |
194 | | |
195 | | This on the one hand saves some storage space (the |
196 | | vector of rectangles is persistent, anyway), and on |
197 | | the other hand provides an identifier to match active |
198 | | edges and events (see below) |
199 | | |
200 | | Ptr because class needs to be assignable |
201 | | */ |
202 | | const B2DRectangle* mpAssociatedRect; |
203 | | |
204 | | /// 'upper' or 'lower' edge of original rectangle. |
205 | | EdgeType meEdgeType; |
206 | | |
207 | | /// 'up' or 'down' |
208 | | EdgeDirection meEdgeDirection; |
209 | | }; |
210 | | |
211 | | typedef std::vector< SweepLineEvent > VectorOfEvents; |
212 | | |
213 | | /** Smart point container for B2DMultiRange::getPolyPolygon() |
214 | | |
215 | | This class provides methods needed only here, and is used |
216 | | as a place to store some additional information per |
217 | | polygon. Also, most of the intersection logic is |
218 | | implemented here. |
219 | | */ |
220 | | class ImplPolygon |
221 | | { |
222 | | public: |
223 | | /** Create polygon |
224 | | */ |
225 | | ImplPolygon() : |
226 | 0 | mpLeadingRightEdge(nullptr), |
227 | 0 | mnIdx(-1), |
228 | 0 | mbIsFinished(false) |
229 | 0 | { |
230 | | // completely ad-hoc. but what the hell. |
231 | 0 | maPoints.reserve(11); |
232 | 0 | } |
233 | | |
234 | 0 | void setPolygonPoolIndex( std::ptrdiff_t nIdx ) { mnIdx = nIdx; } |
235 | | |
236 | | /// Add point to the end of the existing points |
237 | | void append( const B2DPoint& rPoint ) |
238 | 0 | { |
239 | 0 | OSL_PRECOND( maPoints.empty() || |
240 | 0 | maPoints.back().getX() == rPoint.getX() || |
241 | 0 | maPoints.back().getY() == rPoint.getY(), |
242 | 0 | "ImplPolygon::append(): added point violates 90 degree line angle constraint!" ); |
243 | |
|
244 | 0 | if( maPoints.empty() || |
245 | 0 | maPoints.back() != rPoint ) |
246 | 0 | { |
247 | | // avoid duplicate points |
248 | 0 | maPoints.push_back( rPoint ); |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | | /** Perform the intersection of this polygon with an |
253 | | active edge. |
254 | | |
255 | | @param rEvent |
256 | | The vertical line event that generated the |
257 | | intersection |
258 | | |
259 | | @param rActiveEdge |
260 | | The active edge that generated the intersection |
261 | | |
262 | | @param rPolygonPool |
263 | | Polygon pool, we sometimes need to allocate a new one |
264 | | |
265 | | @param bIsFinishingEdge |
266 | | True, when this is hitting the last edge of the |
267 | | vertical sweep - every vertical sweep starts and ends |
268 | | with upper and lower edge of the _same_ rectangle. |
269 | | |
270 | | @return the new current polygon (that's the one |
271 | | processing must proceed with, when going through the |
272 | | list of upcoming active edges). |
273 | | */ |
274 | | std::ptrdiff_t intersect( SweepLineEvent const & rEvent, |
275 | | ActiveEdge& rActiveEdge, |
276 | | VectorOfPolygons& rPolygonPool, |
277 | | B2DPolyPolygon& rRes, |
278 | | bool isFinishingEdge ) |
279 | 0 | { |
280 | 0 | OSL_PRECOND( !mbIsFinished, |
281 | 0 | "ImplPolygon::intersect(): called on already finished polygon!" ); |
282 | 0 | OSL_PRECOND( !isFinishingEdge || &rEvent.getRect() == &rActiveEdge.getRect(), |
283 | 0 | "ImplPolygon::intersect(): inconsistent ending!" ); |
284 | |
|
285 | 0 | const B2DPoint aIntersectionPoint( rEvent.getPos(), |
286 | 0 | rActiveEdge.getInvariantCoord() ); |
287 | | |
288 | | // intersection point, goes to our polygon |
289 | | // unconditionally |
290 | 0 | append(aIntersectionPoint); |
291 | |
|
292 | 0 | if( isFinishingEdge ) |
293 | 0 | { |
294 | | // isSweepLineEnteringRect ? |
295 | 0 | if( rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE) |
296 | 0 | handleFinalOwnRightEdge(rActiveEdge); |
297 | 0 | else |
298 | 0 | handleFinalOwnLeftEdge(rActiveEdge, |
299 | 0 | rPolygonPool, |
300 | 0 | rRes); |
301 | | |
302 | | // we're done with this rect & sweep line |
303 | 0 | return -1; |
304 | 0 | } |
305 | 0 | else if( metOwnEdge(rEvent,rActiveEdge) ) |
306 | 0 | { |
307 | 0 | handleInitialOwnEdge(rEvent, rActiveEdge); |
308 | | |
309 | | // point already added, all init done, continue |
310 | | // with same poly |
311 | 0 | return mnIdx; |
312 | 0 | } |
313 | 0 | else |
314 | 0 | { |
315 | 0 | OSL_ENSURE( rActiveEdge.getTargetPolygonIndex() != -1, |
316 | 0 | "ImplPolygon::intersect(): non-trivial intersection hit empty polygon!" ); |
317 | |
|
318 | 0 | const bool isHittingLeftEdge( |
319 | 0 | rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT); |
320 | |
|
321 | 0 | if( isHittingLeftEdge ) |
322 | 0 | return handleComplexLeftEdge(rActiveEdge, |
323 | 0 | aIntersectionPoint, |
324 | 0 | rPolygonPool, |
325 | 0 | rRes); |
326 | 0 | else |
327 | 0 | return handleComplexRightEdge(rActiveEdge, |
328 | 0 | aIntersectionPoint, |
329 | 0 | rPolygonPool); |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | | private: |
334 | | void handleInitialOwnEdge(SweepLineEvent const & rEvent, |
335 | | ActiveEdge const & rActiveEdge) const |
336 | 0 | { |
337 | 0 | const bool isActiveEdgeProceedLeft( |
338 | 0 | rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT); |
339 | 0 | const bool isSweepLineEnteringRect( |
340 | 0 | rEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE); |
341 | |
|
342 | 0 | OSL_ENSURE( isSweepLineEnteringRect == isActiveEdgeProceedLeft, |
343 | 0 | "ImplPolygon::intersect(): sweep initial own edge hit: wrong polygon order" ); |
344 | |
|
345 | 0 | OSL_ENSURE( isSweepLineEnteringRect || |
346 | 0 | mpLeadingRightEdge == &rActiveEdge, |
347 | 0 | "ImplPolygon::intersect(): sweep initial own edge hit: wrong leading edge" ); |
348 | 0 | } |
349 | | |
350 | | void handleFinalOwnRightEdge(ActiveEdge& rActiveEdge) |
351 | 0 | { |
352 | 0 | OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_RIGHT, |
353 | 0 | "ImplPolygon::handleInitialOwnRightEdge(): start edge wrong polygon order" ); |
354 | |
|
355 | 0 | rActiveEdge.setTargetPolygonIndex(mnIdx); |
356 | 0 | mpLeadingRightEdge = &rActiveEdge; |
357 | 0 | } |
358 | | |
359 | | void handleFinalOwnLeftEdge(ActiveEdge const & rActiveEdge, |
360 | | VectorOfPolygons& rPolygonPool, |
361 | | B2DPolyPolygon& rRes) |
362 | 0 | { |
363 | 0 | OSL_ENSURE( rActiveEdge.getEdgeDirection() == ActiveEdge::PROCEED_LEFT, |
364 | 0 | "ImplPolygon::handleFinalOwnLeftEdge(): end edge wrong polygon order" ); |
365 | |
|
366 | 0 | const bool isHittingOurTail( |
367 | 0 | rActiveEdge.getTargetPolygonIndex() == mnIdx); |
368 | |
|
369 | 0 | if( isHittingOurTail ) |
370 | 0 | finish(rRes); // just finish. no fuss. |
371 | 0 | else |
372 | 0 | { |
373 | | // temp poly hits final left edge |
374 | 0 | const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex(); |
375 | 0 | ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx); |
376 | | |
377 | | // active edge's polygon has points |
378 | | // already. ours need to go in front of them. |
379 | 0 | maPoints.insert(maPoints.end(), |
380 | 0 | rTmp.maPoints.begin(), |
381 | 0 | rTmp.maPoints.end()); |
382 | | |
383 | | // adjust leading edges, we're switching the polygon |
384 | 0 | ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge; |
385 | |
|
386 | 0 | mpLeadingRightEdge = pFarEdge; |
387 | 0 | pFarEdge->setTargetPolygonIndex(mnIdx); |
388 | | |
389 | | // nTmpIdx is an empty shell, get rid of it |
390 | 0 | rPolygonPool.free(nTmpIdx); |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | std::ptrdiff_t handleComplexLeftEdge(ActiveEdge& rActiveEdge, |
395 | | const B2DPoint& rIntersectionPoint, |
396 | | VectorOfPolygons& rPolygonPool, |
397 | | B2DPolyPolygon& rRes) |
398 | 0 | { |
399 | 0 | const bool isHittingOurTail( |
400 | 0 | rActiveEdge.getTargetPolygonIndex() == mnIdx); |
401 | 0 | if( isHittingOurTail ) |
402 | 0 | { |
403 | 0 | finish(rRes); |
404 | | |
405 | | // so "this" is done - need new polygon to collect |
406 | | // further points |
407 | 0 | const std::ptrdiff_t nIdxNewPolygon=rPolygonPool.alloc(); |
408 | 0 | rPolygonPool.get(nIdxNewPolygon).setPolygonPoolIndex(nIdxNewPolygon); |
409 | 0 | rPolygonPool.get(nIdxNewPolygon).append(rIntersectionPoint); |
410 | |
|
411 | 0 | rActiveEdge.setTargetPolygonIndex(nIdxNewPolygon); |
412 | |
|
413 | 0 | return nIdxNewPolygon; |
414 | 0 | } |
415 | 0 | else |
416 | 0 | { |
417 | 0 | const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex(); |
418 | 0 | ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx); |
419 | | |
420 | | // active edge's polygon has points |
421 | | // already. ours need to go in front of them. |
422 | 0 | maPoints.insert(maPoints.end(), |
423 | 0 | rTmp.maPoints.begin(), |
424 | 0 | rTmp.maPoints.end()); |
425 | |
|
426 | 0 | rTmp.maPoints.clear(); |
427 | 0 | rTmp.append(rIntersectionPoint); |
428 | | |
429 | | // adjust leading edges, we're switching the polygon |
430 | 0 | ActiveEdge* const pFarEdge=rTmp.mpLeadingRightEdge; |
431 | 0 | ActiveEdge* const pNearEdge=&rActiveEdge; |
432 | |
|
433 | 0 | rTmp.mpLeadingRightEdge = nullptr; |
434 | 0 | pNearEdge->setTargetPolygonIndex(nTmpIdx); |
435 | |
|
436 | 0 | mpLeadingRightEdge = pFarEdge; |
437 | 0 | pFarEdge->setTargetPolygonIndex(mnIdx); |
438 | |
|
439 | 0 | return nTmpIdx; |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | | std::ptrdiff_t handleComplexRightEdge(ActiveEdge& rActiveEdge, |
444 | | const B2DPoint& rIntersectionPoint, |
445 | | VectorOfPolygons& rPolygonPool) |
446 | 0 | { |
447 | 0 | const std::ptrdiff_t nTmpIdx=rActiveEdge.getTargetPolygonIndex(); |
448 | 0 | ImplPolygon& rTmp=rPolygonPool.get(nTmpIdx); |
449 | |
|
450 | 0 | rTmp.append(rIntersectionPoint); |
451 | |
|
452 | 0 | rActiveEdge.setTargetPolygonIndex(mnIdx); |
453 | 0 | mpLeadingRightEdge = &rActiveEdge; |
454 | |
|
455 | 0 | rTmp.mpLeadingRightEdge = nullptr; |
456 | |
|
457 | 0 | return nTmpIdx; |
458 | 0 | } |
459 | | |
460 | | /// True when sweep line hits our own active edge |
461 | | static bool metOwnEdge(SweepLineEvent const & rEvent, |
462 | | ActiveEdge const & rActiveEdge) |
463 | 0 | { |
464 | 0 | const bool bHitOwnEdge=&rEvent.getRect() == &rActiveEdge.getRect(); |
465 | 0 | return bHitOwnEdge; |
466 | 0 | } |
467 | | |
468 | | /// Retrieve B2DPolygon from this object |
469 | | B2DPolygon getPolygon() const |
470 | 0 | { |
471 | 0 | B2DPolygon aRes; |
472 | 0 | for (auto const& aPoint : maPoints) |
473 | 0 | aRes.append(aPoint, 1); |
474 | 0 | aRes.setClosed( true ); |
475 | 0 | return aRes; |
476 | 0 | } |
477 | | |
478 | | /** Finish this polygon, push to result set. |
479 | | */ |
480 | | void finish(B2DPolyPolygon& rRes) |
481 | 0 | { |
482 | 0 | OSL_PRECOND( maPoints.empty() || |
483 | 0 | maPoints.front().getX() == maPoints.back().getX() || |
484 | 0 | maPoints.front().getY() == maPoints.back().getY(), |
485 | 0 | "ImplPolygon::finish(): first and last point violate 90 degree line angle constraint!" ); |
486 | |
|
487 | 0 | mbIsFinished = true; |
488 | 0 | mpLeadingRightEdge = nullptr; |
489 | |
|
490 | 0 | rRes.append(getPolygon()); |
491 | 0 | } |
492 | | |
493 | | /** Refers to the current leading edge element of this |
494 | | polygon, or NULL. The leading edge denotes the 'front' |
495 | | of the polygon vertex sequence, i.e. the coordinates |
496 | | at the polygon's leading edge are returned from |
497 | | maPoints.front() |
498 | | */ |
499 | | ActiveEdge* mpLeadingRightEdge; |
500 | | |
501 | | /// current index into vector pool |
502 | | std::ptrdiff_t mnIdx; |
503 | | |
504 | | /// Container for the actual polygon points |
505 | | std::vector<B2DPoint> maPoints; |
506 | | |
507 | | /// When true, this polygon is 'done', i.e. nothing must be added anymore. |
508 | | bool mbIsFinished; |
509 | | }; |
510 | | |
511 | | /** Init sweep line event list |
512 | | |
513 | | This method fills the event list with the sweep line |
514 | | events generated from the input rectangles, and sorts them |
515 | | with increasing x. |
516 | | */ |
517 | | void setupSweepLineEventListFromRanges( VectorOfEvents& o_rEventVector, |
518 | | const std::vector<B2DRange>& rRanges, |
519 | | const std::vector<B2VectorOrientation>& rOrientations ) |
520 | 0 | { |
521 | | // we need exactly 2*rectVec.size() events: one for the |
522 | | // left, and one for the right edge of each rectangle |
523 | 0 | o_rEventVector.clear(); |
524 | 0 | o_rEventVector.reserve( 2*rRanges.size() ); |
525 | | |
526 | | // generate events |
527 | | // =============== |
528 | | |
529 | | // first pass: add all left edges in increasing order |
530 | 0 | std::vector<B2DRange>::const_iterator aCurrRect=rRanges.begin(); |
531 | 0 | std::vector<B2VectorOrientation>::const_iterator aCurrOrientation=rOrientations.begin(); |
532 | 0 | const std::vector<B2DRange>::const_iterator aEnd=rRanges.end(); |
533 | 0 | const std::vector<B2VectorOrientation>::const_iterator aEndOrientation=rOrientations.end(); |
534 | 0 | while( aCurrRect != aEnd && aCurrOrientation != aEndOrientation ) |
535 | 0 | { |
536 | 0 | const B2DRectangle& rCurrRect( *aCurrRect++ ); |
537 | |
|
538 | 0 | o_rEventVector.emplace_back( rCurrRect.getMinX(), |
539 | 0 | rCurrRect, |
540 | 0 | SweepLineEvent::STARTING_EDGE, |
541 | 0 | (*aCurrOrientation++) == B2VectorOrientation::Positive ? |
542 | 0 | SweepLineEvent::PROCEED_UP : SweepLineEvent::PROCEED_DOWN ); |
543 | 0 | } |
544 | | |
545 | | // second pass: add all right edges in reversed order |
546 | 0 | std::vector<B2DRange>::const_reverse_iterator aCurrRectR=rRanges.rbegin(); |
547 | 0 | std::vector<B2VectorOrientation>::const_reverse_iterator aCurrOrientationR=rOrientations.rbegin(); |
548 | 0 | const std::vector<B2DRange>::const_reverse_iterator aEndR=rRanges.rend(); |
549 | 0 | while( aCurrRectR != aEndR ) |
550 | 0 | { |
551 | 0 | const B2DRectangle& rCurrRect( *aCurrRectR++ ); |
552 | |
|
553 | 0 | o_rEventVector.emplace_back( rCurrRect.getMaxX(), |
554 | 0 | rCurrRect, |
555 | 0 | SweepLineEvent::FINISHING_EDGE, |
556 | 0 | (*aCurrOrientationR++) == B2VectorOrientation::Positive ? |
557 | 0 | SweepLineEvent::PROCEED_DOWN : SweepLineEvent::PROCEED_UP ); |
558 | 0 | } |
559 | | |
560 | | // sort events |
561 | | // =========== |
562 | | |
563 | | // since we use stable_sort, the order of events with the |
564 | | // same x value will not change. The elaborate two-pass |
565 | | // add above thus ensures, that for each two rectangles |
566 | | // with similar left and right x coordinates, the |
567 | | // rectangle whose left event comes first will have its |
568 | | // right event come last. This is advantageous for the |
569 | | // clip algorithm below, see handleRightEdgeCrossing(). |
570 | |
|
571 | 0 | std::stable_sort( o_rEventVector.begin(), |
572 | 0 | o_rEventVector.end() ); |
573 | 0 | } |
574 | | |
575 | | /** Insert two active edge segments for the given rectangle. |
576 | | |
577 | | This method creates two active edge segments from the |
578 | | given rect, and inserts them into the active edge list, |
579 | | such that this stays sorted (if it was before). |
580 | | |
581 | | @param io_rEdgeList |
582 | | Active edge list to insert into |
583 | | |
584 | | @param io_rPolygons |
585 | | Vector of polygons. Each rectangle added creates one |
586 | | tentative result polygon in this vector, and the edge list |
587 | | entries holds a reference to that polygon (this _requires_ |
588 | | that the polygon vector does not reallocate, i.e. it must |
589 | | have at least the maximal number of rectangles reserved) |
590 | | |
591 | | @param o_CurrentPolygon |
592 | | The then-current polygon when processing this sweep line |
593 | | event |
594 | | |
595 | | @param rCurrEvent |
596 | | The actual event that caused this call |
597 | | */ |
598 | | void createActiveEdgesFromStartEvent( ListOfEdges & io_rEdgeList, |
599 | | VectorOfPolygons & io_rPolygonPool, |
600 | | SweepLineEvent const & rCurrEvent ) |
601 | 0 | { |
602 | 0 | ListOfEdges aNewEdges; |
603 | 0 | const B2DRectangle& rRect=rCurrEvent.getRect(); |
604 | 0 | const bool bGoesDown=rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN; |
605 | | |
606 | | // start event - new rect starts here, needs polygon to |
607 | | // collect points into |
608 | 0 | const std::ptrdiff_t nIdxPolygon=io_rPolygonPool.alloc(); |
609 | 0 | io_rPolygonPool.get(nIdxPolygon).setPolygonPoolIndex(nIdxPolygon); |
610 | | |
611 | | // upper edge |
612 | 0 | aNewEdges.emplace_back( |
613 | 0 | rRect, |
614 | 0 | rRect.getMinY(), |
615 | 0 | bGoesDown ? nIdxPolygon : -1, |
616 | 0 | bGoesDown ? ActiveEdge::PROCEED_LEFT : ActiveEdge::PROCEED_RIGHT ); |
617 | | // lower edge |
618 | 0 | aNewEdges.emplace_back( |
619 | 0 | rRect, |
620 | 0 | rRect.getMaxY(), |
621 | 0 | bGoesDown ? -1 : nIdxPolygon, |
622 | 0 | bGoesDown ? ActiveEdge::PROCEED_RIGHT : ActiveEdge::PROCEED_LEFT ); |
623 | | |
624 | | // furthermore, have to respect a special tie-breaking |
625 | | // rule here, for edges which share the same y value: |
626 | | // newly added upper edges must be inserted _before_ any |
627 | | // other edge with the same y value, and newly added lower |
628 | | // edges must be _after_ all other edges with the same |
629 | | // y. This ensures that the left vertical edge processing |
630 | | // below encounters the upper edge of the current rect |
631 | | // first, and the lower edge last, which automatically |
632 | | // starts and finishes this rect correctly (as only then, |
633 | | // the polygon will have their associated active edges |
634 | | // set). |
635 | 0 | const double nMinY( rRect.getMinY() ); |
636 | 0 | const double nMaxY( rRect.getMaxY() ); |
637 | 0 | ListOfEdges::iterator aCurr( io_rEdgeList.begin() ); |
638 | 0 | const ListOfEdges::iterator aEnd ( io_rEdgeList.end() ); |
639 | 0 | while( aCurr != aEnd ) |
640 | 0 | { |
641 | 0 | const double nCurrY( aCurr->getInvariantCoord() ); |
642 | |
|
643 | 0 | if( nCurrY >= nMinY && |
644 | 0 | aNewEdges.size() == 2 ) // only add, if not yet done. |
645 | 0 | { |
646 | | // insert upper edge _before_ aCurr. Thus, it will |
647 | | // be the first entry for a range of equal y |
648 | | // values. Using splice here, since we hold |
649 | | // references to the moved list element! |
650 | 0 | io_rEdgeList.splice( aCurr, |
651 | 0 | aNewEdges, |
652 | 0 | aNewEdges.begin() ); |
653 | 0 | } |
654 | |
|
655 | 0 | if( nCurrY > nMaxY ) |
656 | 0 | { |
657 | | // insert lower edge _before_ aCurr. Thus, it will |
658 | | // be the last entry for a range of equal y values |
659 | | // (aCurr is the first entry strictly larger than |
660 | | // nMaxY). Using splice here, since we hold |
661 | | // references to the moved list element! |
662 | 0 | io_rEdgeList.splice( aCurr, |
663 | 0 | aNewEdges, |
664 | 0 | aNewEdges.begin() ); |
665 | | // done with insertion, can early-exit here. |
666 | 0 | return; |
667 | 0 | } |
668 | | |
669 | 0 | ++aCurr; |
670 | 0 | } |
671 | | |
672 | | // append remainder of aNewList (might still contain 2 or |
673 | | // 1 elements, depending of the contents of io_rEdgeList). |
674 | 0 | io_rEdgeList.splice( aCurr, |
675 | 0 | aNewEdges ); |
676 | 0 | } |
677 | | |
678 | | bool isSameRect(ActiveEdge const & rEdge, |
679 | | basegfx::B2DRange const & rRect) |
680 | 0 | { |
681 | 0 | return &rEdge.getRect() == &rRect; |
682 | 0 | } |
683 | | |
684 | | // wow what a hack. necessary because stl's list::erase does |
685 | | // not eat reverse_iterator |
686 | | template<typename Cont, typename Iter> Iter eraseFromList(Cont&, const Iter&); |
687 | | template<> ListOfEdges::iterator eraseFromList( |
688 | | ListOfEdges& rList, const ListOfEdges::iterator& aIter) |
689 | 0 | { |
690 | 0 | return rList.erase(aIter); |
691 | 0 | } |
692 | | template<> ListOfEdges::reverse_iterator eraseFromList( |
693 | | ListOfEdges& rList, const ListOfEdges::reverse_iterator& aIter) |
694 | 0 | { |
695 | 0 | return ListOfEdges::reverse_iterator( |
696 | 0 | rList.erase(std::prev(aIter.base()))); |
697 | 0 | } |
698 | | |
699 | | template<int bPerformErase, |
700 | | typename Iterator> void processActiveEdges( |
701 | | Iterator first, |
702 | | Iterator last, |
703 | | ListOfEdges& rActiveEdgeList, |
704 | | SweepLineEvent const & rCurrEvent, |
705 | | VectorOfPolygons& rPolygonPool, |
706 | | B2DPolyPolygon& rRes ) |
707 | 0 | { |
708 | 0 | const basegfx::B2DRange& rCurrRect=rCurrEvent.getRect(); |
709 | | |
710 | | // fast-forward to rCurrEvent's first active edge (holds |
711 | | // for both starting and finishing sweep line events, a |
712 | | // rect is regarded _outside_ any rects whose events have |
713 | | // started earlier |
714 | 0 | first = std::find_if(first, last, |
715 | 0 | [&rCurrRect](ActiveEdge& anEdge) { return isSameRect(anEdge, rCurrRect); }); Unexecuted instantiation: b2drangeclipper.cxx:basegfx::(anonymous namespace)::processActiveEdges<0, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >(std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&)::{lambda(basegfx::(anonymous namespace)::ActiveEdge&)#1}::operator()(basegfx::(anonymous namespace)::ActiveEdge&) const Unexecuted instantiation: b2drangeclipper.cxx:basegfx::(anonymous namespace)::processActiveEdges<0, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> > >(std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&)::{lambda(basegfx::(anonymous namespace)::ActiveEdge&)#1}::operator()(basegfx::(anonymous namespace)::ActiveEdge&) const Unexecuted instantiation: b2drangeclipper.cxx:basegfx::(anonymous namespace)::processActiveEdges<1, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >(std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&)::{lambda(basegfx::(anonymous namespace)::ActiveEdge&)#1}::operator()(basegfx::(anonymous namespace)::ActiveEdge&) const Unexecuted instantiation: b2drangeclipper.cxx:basegfx::(anonymous namespace)::processActiveEdges<1, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> > >(std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&)::{lambda(basegfx::(anonymous namespace)::ActiveEdge&)#1}::operator()(basegfx::(anonymous namespace)::ActiveEdge&) const |
716 | |
|
717 | 0 | if(first == last) |
718 | 0 | return; |
719 | | |
720 | 0 | int nCount=0; |
721 | 0 | std::ptrdiff_t nCurrPolyIdx=-1; |
722 | 0 | while(first != last) |
723 | 0 | { |
724 | 0 | if( nCurrPolyIdx == -1 ) |
725 | 0 | nCurrPolyIdx=first->getTargetPolygonIndex(); |
726 | |
|
727 | 0 | assert(nCurrPolyIdx != -1); |
728 | | |
729 | | // second encounter of my rect -> second edge |
730 | | // encountered, done |
731 | 0 | const bool bExit= |
732 | 0 | nCount && |
733 | 0 | isSameRect(*first, |
734 | 0 | rCurrRect); |
735 | | |
736 | | // deal with current active edge |
737 | 0 | nCurrPolyIdx = |
738 | 0 | rPolygonPool.get(nCurrPolyIdx).intersect( |
739 | 0 | rCurrEvent, |
740 | 0 | *first, |
741 | 0 | rPolygonPool, |
742 | 0 | rRes, |
743 | 0 | bExit); |
744 | | |
745 | | // prune upper & lower active edges, if requested |
746 | 0 | if( bPerformErase && (bExit || !nCount) ) |
747 | 0 | first = eraseFromList(rActiveEdgeList,first); |
748 | 0 | else |
749 | 0 | ++first; |
750 | | |
751 | | // delayed exit, had to prune first |
752 | 0 | if( bExit ) |
753 | 0 | return; |
754 | | |
755 | 0 | ++nCount; |
756 | 0 | } |
757 | 0 | } Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdges<0, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >(std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdges<0, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> > >(std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdges<1, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >(std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*>, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdges<1, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> > >(std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::reverse_iterator<std::__1::__list_iterator<basegfx::(anonymous namespace)::ActiveEdge, void*> >, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, basegfx::(anonymous namespace)::SweepLineEvent const&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) |
758 | | |
759 | | template<int bPerformErase> void processActiveEdgesTopDown( |
760 | | SweepLineEvent& rCurrEvent, |
761 | | ListOfEdges& rActiveEdgeList, |
762 | | VectorOfPolygons& rPolygonPool, |
763 | | B2DPolyPolygon& rRes ) |
764 | 0 | { |
765 | 0 | processActiveEdges<bPerformErase>( |
766 | 0 | rActiveEdgeList. begin(), |
767 | 0 | rActiveEdgeList. end(), |
768 | 0 | rActiveEdgeList, |
769 | 0 | rCurrEvent, |
770 | 0 | rPolygonPool, |
771 | 0 | rRes); |
772 | 0 | } Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdgesTopDown<0>(basegfx::(anonymous namespace)::SweepLineEvent&, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdgesTopDown<1>(basegfx::(anonymous namespace)::SweepLineEvent&, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) |
773 | | |
774 | | template<int bPerformErase> void processActiveEdgesBottomUp( |
775 | | SweepLineEvent& rCurrEvent, |
776 | | ListOfEdges& rActiveEdgeList, |
777 | | VectorOfPolygons& rPolygonPool, |
778 | | B2DPolyPolygon& rRes ) |
779 | 0 | { |
780 | 0 | processActiveEdges<bPerformErase>( |
781 | 0 | rActiveEdgeList. rbegin(), |
782 | 0 | rActiveEdgeList. rend(), |
783 | 0 | rActiveEdgeList, |
784 | 0 | rCurrEvent, |
785 | 0 | rPolygonPool, |
786 | 0 | rRes); |
787 | 0 | } Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdgesBottomUp<0>(basegfx::(anonymous namespace)::SweepLineEvent&, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) Unexecuted instantiation: b2drangeclipper.cxx:void basegfx::(anonymous namespace)::processActiveEdgesBottomUp<1>(basegfx::(anonymous namespace)::SweepLineEvent&, std::__1::list<basegfx::(anonymous namespace)::ActiveEdge, std::__1::allocator<basegfx::(anonymous namespace)::ActiveEdge> >&, o3tl::vector_pool<basegfx::(anonymous namespace)::ImplPolygon>&, basegfx::B2DPolyPolygon&) |
788 | | |
789 | | enum{ NoErase=0, PerformErase=1 }; |
790 | | |
791 | | void handleStartingEdge( SweepLineEvent& rCurrEvent, |
792 | | ListOfEdges& rActiveEdgeList, |
793 | | VectorOfPolygons& rPolygonPool, |
794 | | B2DPolyPolygon& rRes) |
795 | 0 | { |
796 | | // inject two new active edges for rect |
797 | 0 | createActiveEdgesFromStartEvent( rActiveEdgeList, |
798 | 0 | rPolygonPool, |
799 | 0 | rCurrEvent ); |
800 | |
|
801 | 0 | if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN ) |
802 | 0 | processActiveEdgesTopDown<NoErase>( |
803 | 0 | rCurrEvent, rActiveEdgeList, rPolygonPool, rRes); |
804 | 0 | else |
805 | 0 | processActiveEdgesBottomUp<NoErase>( |
806 | 0 | rCurrEvent, rActiveEdgeList, rPolygonPool, rRes); |
807 | 0 | } |
808 | | |
809 | | void handleFinishingEdge( SweepLineEvent& rCurrEvent, |
810 | | ListOfEdges& rActiveEdgeList, |
811 | | VectorOfPolygons& rPolygonPool, |
812 | | B2DPolyPolygon& rRes) |
813 | 0 | { |
814 | 0 | if( rCurrEvent.getEdgeDirection() == SweepLineEvent::PROCEED_DOWN ) |
815 | 0 | processActiveEdgesTopDown<PerformErase>( |
816 | 0 | rCurrEvent, rActiveEdgeList, rPolygonPool, rRes); |
817 | 0 | else |
818 | 0 | processActiveEdgesBottomUp<PerformErase>( |
819 | 0 | rCurrEvent, rActiveEdgeList, rPolygonPool, rRes); |
820 | 0 | } |
821 | | |
822 | | void handleSweepLineEvent( SweepLineEvent& rCurrEvent, |
823 | | ListOfEdges& rActiveEdgeList, |
824 | | VectorOfPolygons& rPolygonPool, |
825 | | B2DPolyPolygon& rRes) |
826 | 0 | { |
827 | 0 | if( rCurrEvent.getEdgeType() == SweepLineEvent::STARTING_EDGE ) |
828 | 0 | handleStartingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes); |
829 | 0 | else |
830 | 0 | handleFinishingEdge(rCurrEvent,rActiveEdgeList,rPolygonPool,rRes); |
831 | 0 | } |
832 | | } |
833 | | |
834 | | namespace utils |
835 | | { |
836 | | B2DPolyPolygon solveCrossovers(const std::vector<B2DRange>& rRanges, |
837 | | const std::vector<B2VectorOrientation>& rOrientations) |
838 | 0 | { |
839 | | // sweep-line algorithm to generate a poly-polygon |
840 | | // from a bunch of rectangles |
841 | | // =============================================== |
842 | | |
843 | | // This algorithm uses the well-known sweep line |
844 | | // concept, explained in every good text book about |
845 | | // computational geometry. |
846 | | |
847 | | // We start with creating two structures for every |
848 | | // rectangle, one representing the left x coordinate, |
849 | | // one representing the right x coordinate (and both |
850 | | // referencing the original rect). These structs are |
851 | | // sorted with increasing x coordinates. |
852 | | |
853 | | // Then, we start processing the resulting list from |
854 | | // the beginning. Every entry in the list defines a |
855 | | // point in time of the line sweeping from left to |
856 | | // right across all rectangles. |
857 | 0 | VectorOfEvents aSweepLineEvents; |
858 | 0 | setupSweepLineEventListFromRanges( aSweepLineEvents, |
859 | 0 | rRanges, |
860 | 0 | rOrientations ); |
861 | |
|
862 | 0 | B2DPolyPolygon aRes; |
863 | 0 | VectorOfPolygons aPolygonPool; |
864 | 0 | ListOfEdges aActiveEdgeList; |
865 | | |
866 | | // sometimes not enough, but a usable compromise |
867 | 0 | aPolygonPool.reserve( rRanges.size() ); |
868 | |
|
869 | 0 | for (auto& aSweepLineEvent : aSweepLineEvents) |
870 | 0 | handleSweepLineEvent(aSweepLineEvent, aActiveEdgeList, aPolygonPool, aRes); |
871 | |
|
872 | 0 | return aRes; |
873 | 0 | } |
874 | | } |
875 | | } |
876 | | |
877 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |