1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basegfx/utils/b2dclipstate.hxx>
22 #include <basegfx/range/b2drange.hxx>
23 #include <basegfx/range/b2dpolyrange.hxx>
24 #include <basegfx/polygon/b2dpolygon.hxx>
25 #include <basegfx/polygon/b2dpolygontools.hxx>
26 #include <basegfx/polygon/b2dpolypolygon.hxx>
27 #include <basegfx/polygon/b2dpolypolygontools.hxx>
28 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
32 namespace basegfx::utils
34 class ImplB2DClipState
37 enum Operation
{UNION
, INTERSECT
, XOR
, SUBTRACT
};
43 explicit ImplB2DClipState( B2DPolyPolygon aPoly
) :
44 maClipPoly(std::move(aPoly
)),
48 bool isCleared() const
50 return !maClipPoly
.count()
51 && !maPendingPolygons
.count()
52 && !maPendingRanges
.count();
55 bool isNullClipPoly() const
57 return maClipPoly
.count() == 1
58 && !maClipPoly
.getB2DPolygon(0).count();
63 return !maPendingPolygons
.count()
64 && !maPendingRanges
.count()
70 maPendingPolygons
.clear();
71 maPendingRanges
.clear();
73 maClipPoly
.append(B2DPolygon());
77 bool operator==(const ImplB2DClipState
& rRHS
) const
79 return maPendingPolygons
== rRHS
.maPendingPolygons
80 && maPendingRanges
== rRHS
.maPendingRanges
81 && maClipPoly
== rRHS
.maClipPoly
82 && mePendingOps
== rRHS
.mePendingOps
;
85 void addRange(const B2DRange
& rRange
, Operation eOp
)
87 if( rRange
.isEmpty() )
90 commitPendingPolygons();
91 if( mePendingOps
!= eOp
)
92 commitPendingRanges();
95 maPendingRanges
.appendElement(
97 B2VectorOrientation::Positive
);
100 void addPolyPolygon(const B2DPolyPolygon
& aPoly
, Operation eOp
)
102 commitPendingRanges();
103 if( mePendingOps
!= eOp
)
104 commitPendingPolygons();
107 maPendingPolygons
.append(aPoly
);
110 void unionRange(const B2DRange
& rRange
)
115 addRange(rRange
,UNION
);
118 void unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
123 addPolyPolygon(rPolyPoly
,UNION
);
126 void intersectRange(const B2DRange
& rRange
)
131 addRange(rRange
,INTERSECT
);
134 void intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
139 addPolyPolygon(rPolyPoly
,INTERSECT
);
142 void subtractRange(const B2DRange
& rRange
)
147 addRange(rRange
,SUBTRACT
);
150 void subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
155 addPolyPolygon(rPolyPoly
,SUBTRACT
);
158 void xorRange(const B2DRange
& rRange
)
160 addRange(rRange
,XOR
);
163 void xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
165 addPolyPolygon(rPolyPoly
,XOR
);
168 void transform(const basegfx::B2DHomMatrix
& rTranslate
)
170 maPendingRanges
.transform(rTranslate
);
171 maPendingPolygons
.transform(rTranslate
);
172 maClipPoly
.transform(rTranslate
);
175 B2DPolyPolygon
const & getClipPoly() const
177 commitPendingRanges();
178 commitPendingPolygons();
184 void commitPendingPolygons() const
186 if( !maPendingPolygons
.count() )
189 // assumption: maClipPoly has kept polygons prepared for
190 // clipping; i.e. no neutral polygons & correct
192 maPendingPolygons
= utils::prepareForPolygonOperation(maPendingPolygons
);
193 const bool bIsEmpty
=isNullClipPoly();
194 const bool bIsCleared
=!maClipPoly
.count();
198 assert( !bIsCleared
);
201 maClipPoly
= maPendingPolygons
;
203 maClipPoly
= utils::solvePolygonOperationOr(
211 maClipPoly
= maPendingPolygons
;
213 maClipPoly
= utils::solvePolygonOperationAnd(
219 maClipPoly
= maPendingPolygons
;
220 else if( bIsCleared
)
222 // not representable, strictly speaking,
223 // using polygons with the common even/odd
224 // or nonzero winding number fill rule. If
225 // we'd want to represent it, fill rule
226 // would need to be "non-negative winding
227 // number" (and we then would return
230 // going for an ugly hack meanwhile
231 maClipPoly
= utils::solvePolygonOperationXor(
233 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
237 maClipPoly
= utils::solvePolygonOperationXor(
244 // first union all pending ones, subtract en bloc then
245 maPendingPolygons
= solveCrossovers(maPendingPolygons
);
246 maPendingPolygons
= stripNeutralPolygons(maPendingPolygons
);
247 maPendingPolygons
= stripDispensablePolygons(maPendingPolygons
);
251 // not representable, strictly speaking,
252 // using polygons with the common even/odd
253 // or nonzero winding number fill rule. If
254 // we'd want to represent it, fill rule
255 // would need to be "non-negative winding
256 // number" (and we then would return
259 // going for an ugly hack meanwhile
260 maClipPoly
= utils::solvePolygonOperationDiff(
262 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
266 maClipPoly
= utils::solvePolygonOperationDiff(
272 maPendingPolygons
.clear();
273 mePendingOps
= UNION
;
276 void commitPendingRanges() const
278 if( !maPendingRanges
.count() )
281 // use the specialized range clipper for the win
282 B2DPolyPolygon aCollectedRanges
;
283 const bool bIsEmpty
=isNullClipPoly();
284 const bool bIsCleared
=!maClipPoly
.count();
288 assert( !bIsCleared
);
290 aCollectedRanges
= maPendingRanges
.solveCrossovers();
291 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
292 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
294 maClipPoly
= aCollectedRanges
;
296 maClipPoly
= utils::solvePolygonOperationOr(
303 aCollectedRanges
= maPendingRanges
.solveCrossovers();
304 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
305 if( maPendingRanges
.count() > 1 )
306 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, true);
309 maClipPoly
= aCollectedRanges
;
311 maClipPoly
= utils::solvePolygonOperationAnd(
316 aCollectedRanges
= maPendingRanges
.solveCrossovers();
317 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
318 aCollectedRanges
= correctOrientations(aCollectedRanges
);
321 maClipPoly
= aCollectedRanges
;
322 else if( bIsCleared
)
324 // not representable, strictly speaking,
325 // using polygons with the common even/odd
326 // or nonzero winding number fill rule. If
327 // we'd want to represent it, fill rule
328 // would need to be "non-negative winding
329 // number" (and we then would return
332 // going for an ugly hack meanwhile
333 maClipPoly
= utils::solvePolygonOperationXor(
335 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
339 maClipPoly
= utils::solvePolygonOperationXor(
346 // first union all pending ranges, subtract en bloc then
347 aCollectedRanges
= maPendingRanges
.solveCrossovers();
348 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
349 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
353 // not representable, strictly speaking,
354 // using polygons with the common even/odd
355 // or nonzero winding number fill rule. If
356 // we'd want to represent it, fill rule
357 // would need to be "non-negative winding
358 // number" (and we then would return
361 // going for an ugly hack meanwhile
362 maClipPoly
= utils::solvePolygonOperationDiff(
364 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
368 maClipPoly
= utils::solvePolygonOperationDiff(
374 maPendingRanges
.clear();
375 mePendingOps
= UNION
;
378 mutable B2DPolyPolygon maPendingPolygons
;
379 mutable B2DPolyRange maPendingRanges
;
380 mutable B2DPolyPolygon maClipPoly
;
381 mutable Operation mePendingOps
;
384 B2DClipState::B2DClipState() = default;
386 B2DClipState::~B2DClipState() = default;
388 B2DClipState::B2DClipState( const B2DClipState
& ) = default;
390 B2DClipState::B2DClipState( B2DClipState
&& ) = default;
392 B2DClipState::B2DClipState( const B2DPolyPolygon
& rPolyPoly
) :
393 mpImpl( ImplB2DClipState(rPolyPoly
) )
396 B2DClipState
& B2DClipState::operator=( const B2DClipState
& ) = default;
398 B2DClipState
& B2DClipState::operator=( B2DClipState
&& ) = default;
400 void B2DClipState::makeNull()
405 bool B2DClipState::isCleared() const
407 return mpImpl
->isCleared();
410 bool B2DClipState::operator==(const B2DClipState
& rRHS
) const
412 if(mpImpl
.same_object(rRHS
.mpImpl
))
415 return ((*mpImpl
) == (*rRHS
.mpImpl
));
418 bool B2DClipState::operator!=(const B2DClipState
& rRHS
) const
420 return !(*this == rRHS
);
423 void B2DClipState::unionRange(const B2DRange
& rRange
)
425 mpImpl
->unionRange(rRange
);
428 void B2DClipState::unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
430 mpImpl
->unionPolyPolygon(rPolyPoly
);
433 void B2DClipState::intersectRange(const B2DRange
& rRange
)
435 mpImpl
->intersectRange(rRange
);
438 void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
440 mpImpl
->intersectPolyPolygon(rPolyPoly
);
443 void B2DClipState::subtractRange(const B2DRange
& rRange
)
445 mpImpl
->subtractRange(rRange
);
448 void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
450 mpImpl
->subtractPolyPolygon(rPolyPoly
);
453 void B2DClipState::xorRange(const B2DRange
& rRange
)
455 mpImpl
->xorRange(rRange
);
458 void B2DClipState::xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
460 mpImpl
->xorPolyPolygon(rPolyPoly
);
463 B2DPolyPolygon
const & B2DClipState::getClipPoly() const
465 return mpImpl
->getClipPoly();
468 void B2DClipState::transform(const basegfx::B2DHomMatrix
& rTranslate
)
470 return mpImpl
->transform(rTranslate
);
474 } // end of namespace
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */