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>
35 class ImplB2DClipState
38 enum Operation
{UNION
, INTERSECT
, XOR
, SUBTRACT
};
47 explicit ImplB2DClipState( const B2DPolyPolygon
& rPoly
) :
54 bool isCleared() const
56 return !maClipPoly
.count()
57 && !maPendingPolygons
.count()
58 && !maPendingRanges
.count();
61 bool isNullClipPoly() const
63 return maClipPoly
.count() == 1
64 && !maClipPoly
.getB2DPolygon(0).count();
69 return !maPendingPolygons
.count()
70 && !maPendingRanges
.count()
76 maPendingPolygons
.clear();
77 maPendingRanges
.clear();
79 maClipPoly
.append(B2DPolygon());
83 bool operator==(const ImplB2DClipState
& rRHS
) const
85 return maPendingPolygons
== rRHS
.maPendingPolygons
86 && maPendingRanges
== rRHS
.maPendingRanges
87 && maClipPoly
== rRHS
.maClipPoly
88 && mePendingOps
== rRHS
.mePendingOps
;
91 void addRange(const B2DRange
& rRange
, Operation eOp
)
93 if( rRange
.isEmpty() )
96 commitPendingPolygons();
97 if( mePendingOps
!= eOp
)
98 commitPendingRanges();
101 maPendingRanges
.appendElement(
103 B2VectorOrientation::Positive
);
106 void addPolyPolygon(const B2DPolyPolygon
& aPoly
, Operation eOp
)
108 commitPendingRanges();
109 if( mePendingOps
!= eOp
)
110 commitPendingPolygons();
113 maPendingPolygons
.append(aPoly
);
116 void unionRange(const B2DRange
& rRange
)
121 addRange(rRange
,UNION
);
124 void unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
129 addPolyPolygon(rPolyPoly
,UNION
);
132 void intersectRange(const B2DRange
& rRange
)
137 addRange(rRange
,INTERSECT
);
140 void intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
145 addPolyPolygon(rPolyPoly
,INTERSECT
);
148 void subtractRange(const B2DRange
& rRange
)
153 addRange(rRange
,SUBTRACT
);
156 void subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
161 addPolyPolygon(rPolyPoly
,SUBTRACT
);
164 void xorRange(const B2DRange
& rRange
)
166 addRange(rRange
,XOR
);
169 void xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
171 addPolyPolygon(rPolyPoly
,XOR
);
174 void transform(const basegfx::B2DHomMatrix
& rTranslate
)
176 maPendingRanges
.transform(rTranslate
);
177 maPendingPolygons
.transform(rTranslate
);
178 maClipPoly
.transform(rTranslate
);
181 B2DPolyPolygon
const & getClipPoly() const
183 commitPendingRanges();
184 commitPendingPolygons();
190 void commitPendingPolygons() const
192 if( !maPendingPolygons
.count() )
195 // assumption: maClipPoly has kept polygons prepared for
196 // clipping; i.e. no neutral polygons & correct
198 maPendingPolygons
= utils::prepareForPolygonOperation(maPendingPolygons
);
199 const bool bIsEmpty
=isNullClipPoly();
200 const bool bIsCleared
=!maClipPoly
.count();
204 assert( !bIsCleared
);
207 maClipPoly
= maPendingPolygons
;
209 maClipPoly
= utils::solvePolygonOperationOr(
217 maClipPoly
= maPendingPolygons
;
219 maClipPoly
= utils::solvePolygonOperationAnd(
225 maClipPoly
= maPendingPolygons
;
226 else if( bIsCleared
)
228 // not representable, strictly speaking,
229 // using polygons with the common even/odd
230 // or nonzero winding number fill rule. If
231 // we'd want to represent it, fill rule
232 // would need to be "non-negative winding
233 // number" (and we then would return
236 // going for an ugly hack meanwhile
237 maClipPoly
= utils::solvePolygonOperationXor(
239 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
243 maClipPoly
= utils::solvePolygonOperationXor(
250 // first union all pending ones, subtract en bloc then
251 maPendingPolygons
= solveCrossovers(maPendingPolygons
);
252 maPendingPolygons
= stripNeutralPolygons(maPendingPolygons
);
253 maPendingPolygons
= stripDispensablePolygons(maPendingPolygons
);
257 // not representable, strictly speaking,
258 // using polygons with the common even/odd
259 // or nonzero winding number fill rule. If
260 // we'd want to represent it, fill rule
261 // would need to be "non-negative winding
262 // number" (and we then would return
265 // going for an ugly hack meanwhile
266 maClipPoly
= utils::solvePolygonOperationDiff(
268 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
272 maClipPoly
= utils::solvePolygonOperationDiff(
278 maPendingPolygons
.clear();
279 mePendingOps
= UNION
;
282 void commitPendingRanges() const
284 if( !maPendingRanges
.count() )
287 // use the specialized range clipper for the win
288 B2DPolyPolygon aCollectedRanges
;
289 const bool bIsEmpty
=isNullClipPoly();
290 const bool bIsCleared
=!maClipPoly
.count();
294 assert( !bIsCleared
);
296 aCollectedRanges
= maPendingRanges
.solveCrossovers();
297 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
298 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
300 maClipPoly
= aCollectedRanges
;
302 maClipPoly
= utils::solvePolygonOperationOr(
309 aCollectedRanges
= maPendingRanges
.solveCrossovers();
310 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
311 if( maPendingRanges
.count() > 1 )
312 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, true);
315 maClipPoly
= aCollectedRanges
;
317 maClipPoly
= utils::solvePolygonOperationAnd(
322 aCollectedRanges
= maPendingRanges
.solveCrossovers();
323 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
324 aCollectedRanges
= correctOrientations(aCollectedRanges
);
327 maClipPoly
= aCollectedRanges
;
328 else if( bIsCleared
)
330 // not representable, strictly speaking,
331 // using polygons with the common even/odd
332 // or nonzero winding number fill rule. If
333 // we'd want to represent it, fill rule
334 // would need to be "non-negative winding
335 // number" (and we then would return
338 // going for an ugly hack meanwhile
339 maClipPoly
= utils::solvePolygonOperationXor(
341 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
345 maClipPoly
= utils::solvePolygonOperationXor(
352 // first union all pending ranges, subtract en bloc then
353 aCollectedRanges
= maPendingRanges
.solveCrossovers();
354 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
355 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
);
359 // not representable, strictly speaking,
360 // using polygons with the common even/odd
361 // or nonzero winding number fill rule. If
362 // we'd want to represent it, fill rule
363 // would need to be "non-negative winding
364 // number" (and we then would return
367 // going for an ugly hack meanwhile
368 maClipPoly
= utils::solvePolygonOperationDiff(
370 utils::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
374 maClipPoly
= utils::solvePolygonOperationDiff(
380 maPendingRanges
.clear();
381 mePendingOps
= UNION
;
384 mutable B2DPolyPolygon maPendingPolygons
;
385 mutable B2DPolyRange maPendingRanges
;
386 mutable B2DPolyPolygon maClipPoly
;
387 mutable Operation mePendingOps
;
390 B2DClipState::B2DClipState() = default;
392 B2DClipState::~B2DClipState() = default;
394 B2DClipState::B2DClipState( const B2DClipState
& ) = default;
396 B2DClipState::B2DClipState( B2DClipState
&& ) = default;
398 B2DClipState::B2DClipState( const B2DPolyPolygon
& rPolyPoly
) :
399 mpImpl( ImplB2DClipState(rPolyPoly
) )
402 B2DClipState
& B2DClipState::operator=( const B2DClipState
& ) = default;
404 B2DClipState
& B2DClipState::operator=( B2DClipState
&& ) = default;
406 void B2DClipState::makeNull()
411 bool B2DClipState::isCleared() const
413 return mpImpl
->isCleared();
416 bool B2DClipState::operator==(const B2DClipState
& rRHS
) const
418 if(mpImpl
.same_object(rRHS
.mpImpl
))
421 return ((*mpImpl
) == (*rRHS
.mpImpl
));
424 bool B2DClipState::operator!=(const B2DClipState
& rRHS
) const
426 return !(*this == rRHS
);
429 void B2DClipState::unionRange(const B2DRange
& rRange
)
431 mpImpl
->unionRange(rRange
);
434 void B2DClipState::unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
436 mpImpl
->unionPolyPolygon(rPolyPoly
);
439 void B2DClipState::intersectRange(const B2DRange
& rRange
)
441 mpImpl
->intersectRange(rRange
);
444 void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
446 mpImpl
->intersectPolyPolygon(rPolyPoly
);
449 void B2DClipState::subtractRange(const B2DRange
& rRange
)
451 mpImpl
->subtractRange(rRange
);
454 void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
456 mpImpl
->subtractPolyPolygon(rPolyPoly
);
459 void B2DClipState::xorRange(const B2DRange
& rRange
)
461 mpImpl
->xorRange(rRange
);
464 void B2DClipState::xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
466 mpImpl
->xorPolyPolygon(rPolyPoly
);
469 B2DPolyPolygon
const & B2DClipState::getClipPoly() const
471 return mpImpl
->getClipPoly();
474 void B2DClipState::transform(const basegfx::B2DHomMatrix
& rTranslate
)
476 return mpImpl
->transform(rTranslate
);
480 } // end of namespace utils
481 } // end of namespace basegfx
483 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */