Coverage Report

Created: 2025-07-07 10:01

/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: */