Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / basegfx / source / tools / b2dclipstate.cxx
blob9459e60d03f7016bc5958a7d5214f7963f13adfc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
32 namespace basegfx
34 namespace utils
36 class ImplB2DClipState
38 public:
39 enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
41 ImplB2DClipState() :
42 maPendingPolygons(),
43 maPendingRanges(),
44 maClipPoly(),
45 mePendingOps(UNION)
48 explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
49 maPendingPolygons(),
50 maPendingRanges(),
51 maClipPoly(rPoly),
52 mePendingOps(UNION)
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();
68 bool isNull() const
70 return !maPendingPolygons.count()
71 && !maPendingRanges.count()
72 && isNullClipPoly();
75 void makeNull()
77 maPendingPolygons.clear();
78 maPendingRanges.clear();
79 maClipPoly.clear();
80 maClipPoly.append(B2DPolygon());
81 mePendingOps = UNION;
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() )
95 return;
97 commitPendingPolygons();
98 if( mePendingOps != eOp )
99 commitPendingRanges();
101 mePendingOps = eOp;
102 maPendingRanges.appendElement(
103 rRange,
104 B2VectorOrientation::Positive);
107 void addPolyPolygon(const B2DPolyPolygon& aPoly, Operation eOp)
109 commitPendingRanges();
110 if( mePendingOps != eOp )
111 commitPendingPolygons();
113 mePendingOps = eOp;
114 maPendingPolygons.append(aPoly);
117 void unionRange(const B2DRange& rRange)
119 if( isCleared() )
120 return;
122 addRange(rRange,UNION);
125 void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
127 if( isCleared() )
128 return;
130 addPolyPolygon(rPolyPoly,UNION);
133 void intersectRange(const B2DRange& rRange)
135 if( isNull() )
136 return;
138 addRange(rRange,INTERSECT);
141 void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
143 if( isNull() )
144 return;
146 addPolyPolygon(rPolyPoly,INTERSECT);
149 void subtractRange(const B2DRange& rRange )
151 if( isNull() )
152 return;
154 addRange(rRange,SUBTRACT);
157 void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
159 if( isNull() )
160 return;
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();
187 return maClipPoly;
190 private:
191 void commitPendingPolygons() const
193 if( !maPendingPolygons.count() )
194 return;
196 // assumption: maClipPoly has kept polygons prepared for
197 // clipping; i.e. no neutral polygons & correct
198 // orientation
199 maPendingPolygons = utils::prepareForPolygonOperation(maPendingPolygons);
200 const bool bIsEmpty=isNullClipPoly();
201 const bool bIsCleared=!maClipPoly.count();
202 switch(mePendingOps)
204 case UNION:
205 assert( !bIsCleared );
207 if( bIsEmpty )
208 maClipPoly = maPendingPolygons;
209 else
210 maClipPoly = utils::solvePolygonOperationOr(
211 maClipPoly,
212 maPendingPolygons);
213 break;
214 case INTERSECT:
215 assert( !bIsEmpty );
217 if( bIsCleared )
218 maClipPoly = maPendingPolygons;
219 else
220 maClipPoly = utils::solvePolygonOperationAnd(
221 maClipPoly,
222 maPendingPolygons);
223 break;
224 case XOR:
225 if( bIsEmpty )
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
235 // 'holes' here)
237 // going for an ugly hack meanwhile
238 maClipPoly = utils::solvePolygonOperationXor(
239 B2DPolyPolygon(
240 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
241 maPendingPolygons);
243 else
244 maClipPoly = utils::solvePolygonOperationXor(
245 maClipPoly,
246 maPendingPolygons);
247 break;
248 case SUBTRACT:
249 assert( !bIsEmpty );
251 // first union all pending ones, subtract en bloc then
252 maPendingPolygons = solveCrossovers(maPendingPolygons);
253 maPendingPolygons = stripNeutralPolygons(maPendingPolygons);
254 maPendingPolygons = stripDispensablePolygons(maPendingPolygons);
256 if( bIsCleared )
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
264 // 'holes' here)
266 // going for an ugly hack meanwhile
267 maClipPoly = utils::solvePolygonOperationDiff(
268 B2DPolyPolygon(
269 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
270 maPendingPolygons);
272 else
273 maClipPoly = utils::solvePolygonOperationDiff(
274 maClipPoly,
275 maPendingPolygons);
276 break;
279 maPendingPolygons.clear();
280 mePendingOps = UNION;
283 void commitPendingRanges() const
285 if( !maPendingRanges.count() )
286 return;
288 // use the specialized range clipper for the win
289 B2DPolyPolygon aCollectedRanges;
290 const bool bIsEmpty=isNullClipPoly();
291 const bool bIsCleared=!maClipPoly.count();
292 switch(mePendingOps)
294 case UNION:
295 assert( !bIsCleared );
297 aCollectedRanges = maPendingRanges.solveCrossovers();
298 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
299 aCollectedRanges = stripDispensablePolygons(aCollectedRanges);
300 if( bIsEmpty )
301 maClipPoly = aCollectedRanges;
302 else
303 maClipPoly = utils::solvePolygonOperationOr(
304 maClipPoly,
305 aCollectedRanges);
306 break;
307 case INTERSECT:
308 assert( !bIsEmpty );
310 aCollectedRanges = maPendingRanges.solveCrossovers();
311 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
312 if( maPendingRanges.count() > 1 )
313 aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
315 if( bIsCleared )
316 maClipPoly = aCollectedRanges;
317 else
318 maClipPoly = utils::solvePolygonOperationAnd(
319 maClipPoly,
320 aCollectedRanges);
321 break;
322 case XOR:
323 aCollectedRanges = maPendingRanges.solveCrossovers();
324 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
325 aCollectedRanges = correctOrientations(aCollectedRanges);
327 if( bIsEmpty )
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
337 // 'holes' here)
339 // going for an ugly hack meanwhile
340 maClipPoly = utils::solvePolygonOperationXor(
341 B2DPolyPolygon(
342 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
343 aCollectedRanges);
345 else
346 maClipPoly = utils::solvePolygonOperationXor(
347 maClipPoly,
348 aCollectedRanges);
349 break;
350 case SUBTRACT:
351 assert( !bIsEmpty );
353 // first union all pending ranges, subtract en bloc then
354 aCollectedRanges = maPendingRanges.solveCrossovers();
355 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
356 aCollectedRanges = stripDispensablePolygons(aCollectedRanges);
358 if( bIsCleared )
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
366 // 'holes' here)
368 // going for an ugly hack meanwhile
369 maClipPoly = utils::solvePolygonOperationDiff(
370 B2DPolyPolygon(
371 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
372 aCollectedRanges);
374 else
375 maClipPoly = utils::solvePolygonOperationDiff(
376 maClipPoly,
377 aCollectedRanges);
378 break;
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()
409 mpImpl->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))
420 return true;
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: */