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/range/b2drangeclipper.hxx>
25 #include <basegfx/polygon/b2dpolygon.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolypolygon.hxx>
28 #include <basegfx/polygon/b2dpolypolygontools.hxx>
29 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
36 class ImplB2DClipState
39 enum Operation
{UNION
, INTERSECT
, XOR
, SUBTRACT
};
48 explicit ImplB2DClipState( const B2DPolyPolygon
& rPoly
) :
55 bool isCleared() const
57 return !maClipPoly
.count()
58 && !maPendingPolygons
.count()
59 && !maPendingRanges
.count();
62 bool isNullClipPoly() const
64 return maClipPoly
.count() == 1
65 && !maClipPoly
.getB2DPolygon(0).count();
70 return !maPendingPolygons
.count()
71 && !maPendingRanges
.count()
77 maPendingPolygons
.clear();
78 maPendingRanges
.clear();
80 maClipPoly
.append(B2DPolygon());
84 bool operator==(const ImplB2DClipState
& rRHS
) const
86 return maPendingPolygons
== rRHS
.maPendingPolygons
87 && maPendingRanges
== rRHS
.maPendingRanges
88 && maClipPoly
== rRHS
.maClipPoly
89 && mePendingOps
== rRHS
.mePendingOps
;
92 void addRange(const B2DRange
& rRange
, Operation eOp
)
94 if( rRange
.isEmpty() )
97 commitPendingPolygons();
98 if( mePendingOps
!= eOp
)
99 commitPendingRanges();
102 maPendingRanges
.appendElement(
104 B2VectorOrientation::Positive
);
107 void addPolyPolygon(const B2DPolyPolygon
& aPoly
, Operation eOp
)
109 commitPendingRanges();
110 if( mePendingOps
!= eOp
)
111 commitPendingPolygons();
114 maPendingPolygons
.append(aPoly
);
117 void unionRange(const B2DRange
& rRange
)
122 addRange(rRange
,UNION
);
125 void unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
130 addPolyPolygon(rPolyPoly
,UNION
);
133 void intersectRange(const B2DRange
& rRange
)
138 addRange(rRange
,INTERSECT
);
141 void intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
146 addPolyPolygon(rPolyPoly
,INTERSECT
);
149 void subtractRange(const B2DRange
& rRange
)
154 addRange(rRange
,SUBTRACT
);
157 void subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
162 addPolyPolygon(rPolyPoly
,SUBTRACT
);
165 void xorRange(const B2DRange
& rRange
)
167 addRange(rRange
,XOR
);
170 void xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
172 addPolyPolygon(rPolyPoly
,XOR
);
175 void transform(const basegfx::B2DHomMatrix
& rTranslate
)
177 maPendingRanges
.transform(rTranslate
);
178 maPendingPolygons
.transform(rTranslate
);
179 maClipPoly
.transform(rTranslate
);
182 B2DPolyPolygon
const & getClipPoly() const
184 commitPendingRanges();
185 commitPendingPolygons();
191 void commitPendingPolygons() const
193 if( !maPendingPolygons
.count() )
196 // assumption: maClipPoly has kept polygons prepared for
197 // clipping; i.e. no neutral polygons & correct
199 maPendingPolygons
= utils::prepareForPolygonOperation(maPendingPolygons
);
200 const bool bIsEmpty
=isNullClipPoly();
201 const bool bIsCleared
=!maClipPoly
.count();
205 assert( !bIsCleared
);
208 maClipPoly
= maPendingPolygons
;
210 maClipPoly
= utils::solvePolygonOperationOr(
218 maClipPoly
= maPendingPolygons
;
220 maClipPoly
= utils::solvePolygonOperationAnd(
226 maClipPoly
= maPendingPolygons
;
227 else if( bIsCleared
)
229 // not representable, strictly speaking,
230 // using polygons with the common even/odd
231 // or nonzero winding number fill rule. If
232 // we'd want to represent it, fill rule
233 // would need to be "non-negative winding
234 // number" (and we then would return
237 // going for an ugly hack meanwhile
238 maClipPoly
= utils::solvePolygonOperationXor(
240 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
244 maClipPoly
= utils::solvePolygonOperationXor(
251 // first union all pending ones, subtract en bloc then
252 maPendingPolygons
= solveCrossovers(maPendingPolygons
);
253 maPendingPolygons
= stripNeutralPolygons(maPendingPolygons
);
254 maPendingPolygons
= stripDispensablePolygons(maPendingPolygons
);
258 // not representable, strictly speaking,
259 // using polygons with the common even/odd
260 // or nonzero winding number fill rule. If
261 // we'd want to represent it, fill rule
262 // would need to be "non-negative winding
263 // number" (and we then would return
266 // going for an ugly hack meanwhile
267 maClipPoly
= utils::solvePolygonOperationDiff(
269 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
273 maClipPoly
= utils::solvePolygonOperationDiff(
279 maPendingPolygons
.clear();
280 mePendingOps
= UNION
;
283 void commitPendingRanges() const
285 if( !maPendingRanges
.count() )
288 // use the specialized range clipper for the win
289 B2DPolyPolygon aCollectedRanges
;
290 const bool bIsEmpty
=isNullClipPoly();
291 const bool bIsCleared
=!maClipPoly
.count();
295 assert( !bIsCleared
);
297 aCollectedRanges
= maPendingRanges
.solveCrossovers();
298 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
299 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
301 maClipPoly
= aCollectedRanges
;
303 maClipPoly
= utils::solvePolygonOperationOr(
310 aCollectedRanges
= maPendingRanges
.solveCrossovers();
311 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
312 if( maPendingRanges
.count() > 1 )
313 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, true);
316 maClipPoly
= aCollectedRanges
;
318 maClipPoly
= utils::solvePolygonOperationAnd(
323 aCollectedRanges
= maPendingRanges
.solveCrossovers();
324 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
325 aCollectedRanges
= correctOrientations(aCollectedRanges
);
328 maClipPoly
= aCollectedRanges
;
329 else if( bIsCleared
)
331 // not representable, strictly speaking,
332 // using polygons with the common even/odd
333 // or nonzero winding number fill rule. If
334 // we'd want to represent it, fill rule
335 // would need to be "non-negative winding
336 // number" (and we then would return
339 // going for an ugly hack meanwhile
340 maClipPoly
= utils::solvePolygonOperationXor(
342 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
346 maClipPoly
= utils::solvePolygonOperationXor(
353 // first union all pending ranges, subtract en bloc then
354 aCollectedRanges
= maPendingRanges
.solveCrossovers();
355 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
356 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
360 // not representable, strictly speaking,
361 // using polygons with the common even/odd
362 // or nonzero winding number fill rule. If
363 // we'd want to represent it, fill rule
364 // would need to be "non-negative winding
365 // number" (and we then would return
368 // going for an ugly hack meanwhile
369 maClipPoly
= utils::solvePolygonOperationDiff(
371 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
375 maClipPoly
= utils::solvePolygonOperationDiff(
381 maPendingRanges
.clear();
382 mePendingOps
= UNION
;
385 mutable B2DPolyPolygon maPendingPolygons
;
386 mutable B2DPolyRange maPendingRanges
;
387 mutable B2DPolyPolygon maClipPoly
;
388 mutable Operation mePendingOps
;
391 B2DClipState::B2DClipState() = default;
393 B2DClipState::~B2DClipState() = default;
395 B2DClipState::B2DClipState( const B2DClipState
& ) = default;
397 B2DClipState::B2DClipState( B2DClipState
&& ) = default;
399 B2DClipState::B2DClipState( const B2DPolyPolygon
& rPolyPoly
) :
400 mpImpl( ImplB2DClipState(rPolyPoly
) )
403 B2DClipState
& B2DClipState::operator=( const B2DClipState
& ) = default;
405 B2DClipState
& B2DClipState::operator=( B2DClipState
&& ) = default;
407 void B2DClipState::makeNull()
412 bool B2DClipState::isCleared() const
414 return mpImpl
->isCleared();
417 bool B2DClipState::operator==(const B2DClipState
& rRHS
) const
419 if(mpImpl
.same_object(rRHS
.mpImpl
))
422 return ((*mpImpl
) == (*rRHS
.mpImpl
));
425 bool B2DClipState::operator!=(const B2DClipState
& rRHS
) const
427 return !(*this == rRHS
);
430 void B2DClipState::unionRange(const B2DRange
& rRange
)
432 mpImpl
->unionRange(rRange
);
435 void B2DClipState::unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
437 mpImpl
->unionPolyPolygon(rPolyPoly
);
440 void B2DClipState::intersectRange(const B2DRange
& rRange
)
442 mpImpl
->intersectRange(rRange
);
445 void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
447 mpImpl
->intersectPolyPolygon(rPolyPoly
);
450 void B2DClipState::subtractRange(const B2DRange
& rRange
)
452 mpImpl
->subtractRange(rRange
);
455 void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
457 mpImpl
->subtractPolyPolygon(rPolyPoly
);
460 void B2DClipState::xorRange(const B2DRange
& rRange
)
462 mpImpl
->xorRange(rRange
);
465 void B2DClipState::xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
467 mpImpl
->xorPolyPolygon(rPolyPoly
);
470 B2DPolyPolygon
const & B2DClipState::getClipPoly() const
472 return mpImpl
->getClipPoly();
475 void B2DClipState::transform(const basegfx::B2DHomMatrix
& rTranslate
)
477 return mpImpl
->transform(rTranslate
);
481 } // end of namespace utils
482 } // end of namespace basegfx
484 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */