Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / basegfx / source / tools / b2dclipstate.cxx
blob126235699b4b81249d5136b91d8a2a6fb6d24962
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/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>
29 #include <utility>
32 namespace basegfx::utils
34 class ImplB2DClipState
36 public:
37 enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
39 ImplB2DClipState() :
40 mePendingOps(UNION)
43 explicit ImplB2DClipState( B2DPolyPolygon aPoly ) :
44 maClipPoly(std::move(aPoly)),
45 mePendingOps(UNION)
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();
61 bool isNull() const
63 return !maPendingPolygons.count()
64 && !maPendingRanges.count()
65 && isNullClipPoly();
68 void makeNull()
70 maPendingPolygons.clear();
71 maPendingRanges.clear();
72 maClipPoly.clear();
73 maClipPoly.append(B2DPolygon());
74 mePendingOps = UNION;
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() )
88 return;
90 commitPendingPolygons();
91 if( mePendingOps != eOp )
92 commitPendingRanges();
94 mePendingOps = eOp;
95 maPendingRanges.appendElement(
96 rRange,
97 B2VectorOrientation::Positive);
100 void addPolyPolygon(const B2DPolyPolygon& aPoly, Operation eOp)
102 commitPendingRanges();
103 if( mePendingOps != eOp )
104 commitPendingPolygons();
106 mePendingOps = eOp;
107 maPendingPolygons.append(aPoly);
110 void unionRange(const B2DRange& rRange)
112 if( isCleared() )
113 return;
115 addRange(rRange,UNION);
118 void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
120 if( isCleared() )
121 return;
123 addPolyPolygon(rPolyPoly,UNION);
126 void intersectRange(const B2DRange& rRange)
128 if( isNull() )
129 return;
131 addRange(rRange,INTERSECT);
134 void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
136 if( isNull() )
137 return;
139 addPolyPolygon(rPolyPoly,INTERSECT);
142 void subtractRange(const B2DRange& rRange )
144 if( isNull() )
145 return;
147 addRange(rRange,SUBTRACT);
150 void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
152 if( isNull() )
153 return;
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();
180 return maClipPoly;
183 private:
184 void commitPendingPolygons() const
186 if( !maPendingPolygons.count() )
187 return;
189 // assumption: maClipPoly has kept polygons prepared for
190 // clipping; i.e. no neutral polygons & correct
191 // orientation
192 maPendingPolygons = utils::prepareForPolygonOperation(maPendingPolygons);
193 const bool bIsEmpty=isNullClipPoly();
194 const bool bIsCleared=!maClipPoly.count();
195 switch(mePendingOps)
197 case UNION:
198 assert( !bIsCleared );
200 if( bIsEmpty )
201 maClipPoly = maPendingPolygons;
202 else
203 maClipPoly = utils::solvePolygonOperationOr(
204 maClipPoly,
205 maPendingPolygons);
206 break;
207 case INTERSECT:
208 assert( !bIsEmpty );
210 if( bIsCleared )
211 maClipPoly = maPendingPolygons;
212 else
213 maClipPoly = utils::solvePolygonOperationAnd(
214 maClipPoly,
215 maPendingPolygons);
216 break;
217 case XOR:
218 if( bIsEmpty )
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
228 // 'holes' here)
230 // going for an ugly hack meanwhile
231 maClipPoly = utils::solvePolygonOperationXor(
232 B2DPolyPolygon(
233 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
234 maPendingPolygons);
236 else
237 maClipPoly = utils::solvePolygonOperationXor(
238 maClipPoly,
239 maPendingPolygons);
240 break;
241 case SUBTRACT:
242 assert( !bIsEmpty );
244 // first union all pending ones, subtract en bloc then
245 maPendingPolygons = solveCrossovers(maPendingPolygons);
246 maPendingPolygons = stripNeutralPolygons(maPendingPolygons);
247 maPendingPolygons = stripDispensablePolygons(maPendingPolygons);
249 if( bIsCleared )
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
257 // 'holes' here)
259 // going for an ugly hack meanwhile
260 maClipPoly = utils::solvePolygonOperationDiff(
261 B2DPolyPolygon(
262 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
263 maPendingPolygons);
265 else
266 maClipPoly = utils::solvePolygonOperationDiff(
267 maClipPoly,
268 maPendingPolygons);
269 break;
272 maPendingPolygons.clear();
273 mePendingOps = UNION;
276 void commitPendingRanges() const
278 if( !maPendingRanges.count() )
279 return;
281 // use the specialized range clipper for the win
282 B2DPolyPolygon aCollectedRanges;
283 const bool bIsEmpty=isNullClipPoly();
284 const bool bIsCleared=!maClipPoly.count();
285 switch(mePendingOps)
287 case UNION:
288 assert( !bIsCleared );
290 aCollectedRanges = maPendingRanges.solveCrossovers();
291 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
292 aCollectedRanges = stripDispensablePolygons(aCollectedRanges);
293 if( bIsEmpty )
294 maClipPoly = aCollectedRanges;
295 else
296 maClipPoly = utils::solvePolygonOperationOr(
297 maClipPoly,
298 aCollectedRanges);
299 break;
300 case INTERSECT:
301 assert( !bIsEmpty );
303 aCollectedRanges = maPendingRanges.solveCrossovers();
304 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
305 if( maPendingRanges.count() > 1 )
306 aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
308 if( bIsCleared )
309 maClipPoly = aCollectedRanges;
310 else
311 maClipPoly = utils::solvePolygonOperationAnd(
312 maClipPoly,
313 aCollectedRanges);
314 break;
315 case XOR:
316 aCollectedRanges = maPendingRanges.solveCrossovers();
317 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
318 aCollectedRanges = correctOrientations(aCollectedRanges);
320 if( bIsEmpty )
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
330 // 'holes' here)
332 // going for an ugly hack meanwhile
333 maClipPoly = utils::solvePolygonOperationXor(
334 B2DPolyPolygon(
335 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
336 aCollectedRanges);
338 else
339 maClipPoly = utils::solvePolygonOperationXor(
340 maClipPoly,
341 aCollectedRanges);
342 break;
343 case SUBTRACT:
344 assert( !bIsEmpty );
346 // first union all pending ranges, subtract en bloc then
347 aCollectedRanges = maPendingRanges.solveCrossovers();
348 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
349 aCollectedRanges = stripDispensablePolygons(aCollectedRanges);
351 if( bIsCleared )
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
359 // 'holes' here)
361 // going for an ugly hack meanwhile
362 maClipPoly = utils::solvePolygonOperationDiff(
363 B2DPolyPolygon(
364 utils::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
365 aCollectedRanges);
367 else
368 maClipPoly = utils::solvePolygonOperationDiff(
369 maClipPoly,
370 aCollectedRanges);
371 break;
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()
402 mpImpl->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))
413 return true;
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: */