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/tools/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>
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 ORIENTATION_POSITIVE
);
106 void addPolygon(B2DPolygon aPoly
, Operation eOp
)
108 commitPendingRanges();
109 if( mePendingOps
!= eOp
)
110 commitPendingPolygons();
113 maPendingPolygons
.append(aPoly
);
116 void addPolyPolygon(B2DPolyPolygon aPoly
, Operation eOp
)
118 commitPendingRanges();
119 if( mePendingOps
!= eOp
)
120 commitPendingPolygons();
123 maPendingPolygons
.append(aPoly
);
126 void unionRange(const B2DRange
& rRange
)
131 addRange(rRange
,UNION
);
134 void unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
139 addPolyPolygon(rPolyPoly
,UNION
);
142 void intersectRange(const B2DRange
& rRange
)
147 addRange(rRange
,INTERSECT
);
150 void intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
155 addPolyPolygon(rPolyPoly
,INTERSECT
);
158 void subtractRange(const B2DRange
& rRange
)
163 addRange(rRange
,SUBTRACT
);
166 void subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
171 addPolyPolygon(rPolyPoly
,SUBTRACT
);
174 void xorRange(const B2DRange
& rRange
)
176 addRange(rRange
,XOR
);
179 void xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
181 addPolyPolygon(rPolyPoly
,XOR
);
184 B2DPolyPolygon
getClipPoly() const
186 commitPendingRanges();
187 commitPendingPolygons();
193 void commitPendingPolygons() const
195 if( !maPendingPolygons
.count() )
198 // assumption: maClipPoly has kept polygons prepared for
199 // clipping; i.e. no neutral polygons & correct
201 maPendingPolygons
= tools::prepareForPolygonOperation(maPendingPolygons
);
202 const bool bIsEmpty
=isNullClipPoly();
203 const bool bIsCleared
=!maClipPoly
.count();
207 OSL_ASSERT( !bIsCleared
);
210 maClipPoly
= maPendingPolygons
;
212 maClipPoly
= tools::solvePolygonOperationOr(
217 OSL_ASSERT( !bIsEmpty
);
220 maClipPoly
= maPendingPolygons
;
222 maClipPoly
= tools::solvePolygonOperationAnd(
228 maClipPoly
= maPendingPolygons
;
229 else if( bIsCleared
)
231 // not representable, strictly speaking,
232 // using polygons with the common even/odd
233 // or nonzero winding number fill rule. If
234 // we'd want to represent it, fill rule
235 // would need to be "non-negative winding
236 // number" (and we then would return
239 // going for an ugly hack meanwhile
240 maClipPoly
= tools::solvePolygonOperationXor(
242 tools::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
246 maClipPoly
= tools::solvePolygonOperationXor(
251 OSL_ASSERT( !bIsEmpty
);
253 // first union all pending ones, subtract en bloc then
254 maPendingPolygons
= solveCrossovers(maPendingPolygons
);
255 maPendingPolygons
= stripNeutralPolygons(maPendingPolygons
);
256 maPendingPolygons
= stripDispensablePolygons(maPendingPolygons
, false);
260 // not representable, strictly speaking,
261 // using polygons with the common even/odd
262 // or nonzero winding number fill rule. If
263 // we'd want to represent it, fill rule
264 // would need to be "non-negative winding
265 // number" (and we then would return
268 // going for an ugly hack meanwhile
269 maClipPoly
= tools::solvePolygonOperationDiff(
271 tools::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
275 maClipPoly
= tools::solvePolygonOperationDiff(
281 maPendingPolygons
.clear();
282 mePendingOps
= UNION
;
285 void commitPendingRanges() const
287 if( !maPendingRanges
.count() )
290 // use the specialized range clipper for the win
291 B2DPolyPolygon aCollectedRanges
;
292 const bool bIsEmpty
=isNullClipPoly();
293 const bool bIsCleared
=!maClipPoly
.count();
297 OSL_ASSERT( !bIsCleared
);
299 aCollectedRanges
= maPendingRanges
.solveCrossovers();
300 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
301 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, false);
303 maClipPoly
= aCollectedRanges
;
305 maClipPoly
= tools::solvePolygonOperationOr(
310 OSL_ASSERT( !bIsEmpty
);
312 aCollectedRanges
= maPendingRanges
.solveCrossovers();
313 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
314 if( maPendingRanges
.count() > 1 )
315 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, true);
318 maClipPoly
= aCollectedRanges
;
320 maClipPoly
= tools::solvePolygonOperationAnd(
325 aCollectedRanges
= maPendingRanges
.solveCrossovers();
326 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
327 aCollectedRanges
= correctOrientations(aCollectedRanges
);
330 maClipPoly
= aCollectedRanges
;
331 else if( bIsCleared
)
333 // not representable, strictly speaking,
334 // using polygons with the common even/odd
335 // or nonzero winding number fill rule. If
336 // we'd want to represent it, fill rule
337 // would need to be "non-negative winding
338 // number" (and we then would return
341 // going for an ugly hack meanwhile
342 maClipPoly
= tools::solvePolygonOperationXor(
344 tools::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
348 maClipPoly
= tools::solvePolygonOperationXor(
353 OSL_ASSERT( !bIsEmpty
);
355 // first union all pending ranges, subtract en bloc then
356 aCollectedRanges
= maPendingRanges
.solveCrossovers();
357 aCollectedRanges
= stripNeutralPolygons(aCollectedRanges
);
358 aCollectedRanges
= stripDispensablePolygons(aCollectedRanges
, false);
362 // not representable, strictly speaking,
363 // using polygons with the common even/odd
364 // or nonzero winding number fill rule. If
365 // we'd want to represent it, fill rule
366 // would need to be "non-negative winding
367 // number" (and we then would return
370 // going for an ugly hack meanwhile
371 maClipPoly
= tools::solvePolygonOperationDiff(
373 tools::createPolygonFromRect(B2DRange(-1E20
,-1E20
,1E20
,1E20
))),
377 maClipPoly
= tools::solvePolygonOperationDiff(
383 maPendingRanges
.clear();
384 mePendingOps
= UNION
;
387 mutable B2DPolyPolygon maPendingPolygons
;
388 mutable B2DPolyRange maPendingRanges
;
389 mutable B2DPolyPolygon maClipPoly
;
390 mutable Operation mePendingOps
;
393 B2DClipState::B2DClipState() :
397 B2DClipState::~B2DClipState()
400 B2DClipState::B2DClipState( const B2DClipState
& rOrig
) :
404 B2DClipState::B2DClipState( const B2DPolyPolygon
& rPolyPoly
) :
405 mpImpl( ImplB2DClipState(rPolyPoly
) )
408 B2DClipState
& B2DClipState::operator=( const B2DClipState
& rRHS
)
410 mpImpl
= rRHS
.mpImpl
;
414 void B2DClipState::makeNull()
419 bool B2DClipState::isCleared() const
421 return mpImpl
->isCleared();
424 bool B2DClipState::operator==(const B2DClipState
& rRHS
) const
426 if(mpImpl
.same_object(rRHS
.mpImpl
))
429 return ((*mpImpl
) == (*rRHS
.mpImpl
));
432 bool B2DClipState::operator!=(const B2DClipState
& rRHS
) const
434 return !(*this == rRHS
);
437 void B2DClipState::unionRange(const B2DRange
& rRange
)
439 mpImpl
->unionRange(rRange
);
442 void B2DClipState::unionPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
444 mpImpl
->unionPolyPolygon(rPolyPoly
);
447 void B2DClipState::intersectRange(const B2DRange
& rRange
)
449 mpImpl
->intersectRange(rRange
);
452 void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
454 mpImpl
->intersectPolyPolygon(rPolyPoly
);
457 void B2DClipState::subtractRange(const B2DRange
& rRange
)
459 mpImpl
->subtractRange(rRange
);
462 void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
464 mpImpl
->subtractPolyPolygon(rPolyPoly
);
467 void B2DClipState::xorRange(const B2DRange
& rRange
)
469 mpImpl
->xorRange(rRange
);
472 void B2DClipState::xorPolyPolygon(const B2DPolyPolygon
& rPolyPoly
)
474 mpImpl
->xorPolyPolygon(rPolyPoly
);
477 B2DPolyPolygon
B2DClipState::getClipPoly() const
479 return mpImpl
->getClipPoly();
482 } // end of namespace tools
483 } // end of namespace basegfx
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */