Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / basegfx / source / tools / b2dclipstate.cxx
blobbf77c2536aaa34ffc86402e25cfaf6918610ba8f
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/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>
31 namespace basegfx
33 namespace tools
35 class ImplB2DClipState
37 public:
38 enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
40 ImplB2DClipState() :
41 maPendingPolygons(),
42 maPendingRanges(),
43 maClipPoly(),
44 mePendingOps(UNION)
47 explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
48 maPendingPolygons(),
49 maPendingRanges(),
50 maClipPoly(rPoly),
51 mePendingOps(UNION)
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();
67 bool isNull() const
69 return !maPendingPolygons.count()
70 && !maPendingRanges.count()
71 && isNullClipPoly();
74 void makeNull()
76 maPendingPolygons.clear();
77 maPendingRanges.clear();
78 maClipPoly.clear();
79 maClipPoly.append(B2DPolygon());
80 mePendingOps = UNION;
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() )
94 return;
96 commitPendingPolygons();
97 if( mePendingOps != eOp )
98 commitPendingRanges();
100 mePendingOps = eOp;
101 maPendingRanges.appendElement(
102 rRange,
103 ORIENTATION_POSITIVE);
106 void addPolygon(B2DPolygon aPoly, Operation eOp)
108 commitPendingRanges();
109 if( mePendingOps != eOp )
110 commitPendingPolygons();
112 mePendingOps = eOp;
113 maPendingPolygons.append(aPoly);
116 void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp)
118 commitPendingRanges();
119 if( mePendingOps != eOp )
120 commitPendingPolygons();
122 mePendingOps = eOp;
123 maPendingPolygons.append(aPoly);
126 void unionRange(const B2DRange& rRange)
128 if( isCleared() )
129 return;
131 addRange(rRange,UNION);
134 void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
136 if( isCleared() )
137 return;
139 addPolyPolygon(rPolyPoly,UNION);
142 void intersectRange(const B2DRange& rRange)
144 if( isNull() )
145 return;
147 addRange(rRange,INTERSECT);
150 void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
152 if( isNull() )
153 return;
155 addPolyPolygon(rPolyPoly,INTERSECT);
158 void subtractRange(const B2DRange& rRange )
160 if( isNull() )
161 return;
163 addRange(rRange,SUBTRACT);
166 void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
168 if( isNull() )
169 return;
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();
189 return maClipPoly;
192 private:
193 void commitPendingPolygons() const
195 if( !maPendingPolygons.count() )
196 return;
198 // assumption: maClipPoly has kept polygons prepared for
199 // clipping; i.e. no neutral polygons & correct
200 // orientation
201 maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons);
202 const bool bIsEmpty=isNullClipPoly();
203 const bool bIsCleared=!maClipPoly.count();
204 switch(mePendingOps)
206 case UNION:
207 OSL_ASSERT( !bIsCleared );
209 if( bIsEmpty )
210 maClipPoly = maPendingPolygons;
211 else
212 maClipPoly = tools::solvePolygonOperationOr(
213 maClipPoly,
214 maPendingPolygons);
215 break;
216 case INTERSECT:
217 OSL_ASSERT( !bIsEmpty );
219 if( bIsCleared )
220 maClipPoly = maPendingPolygons;
221 else
222 maClipPoly = tools::solvePolygonOperationAnd(
223 maClipPoly,
224 maPendingPolygons);
225 break;
226 case XOR:
227 if( bIsEmpty )
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
237 // 'holes' here)
239 // going for an ugly hack meanwhile
240 maClipPoly = tools::solvePolygonOperationXor(
241 B2DPolyPolygon(
242 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
243 maPendingPolygons);
245 else
246 maClipPoly = tools::solvePolygonOperationXor(
247 maClipPoly,
248 maPendingPolygons);
249 break;
250 case SUBTRACT:
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);
258 if( bIsCleared )
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
266 // 'holes' here)
268 // going for an ugly hack meanwhile
269 maClipPoly = tools::solvePolygonOperationDiff(
270 B2DPolyPolygon(
271 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
272 maPendingPolygons);
274 else
275 maClipPoly = tools::solvePolygonOperationDiff(
276 maClipPoly,
277 maPendingPolygons);
278 break;
281 maPendingPolygons.clear();
282 mePendingOps = UNION;
285 void commitPendingRanges() const
287 if( !maPendingRanges.count() )
288 return;
290 // use the specialized range clipper for the win
291 B2DPolyPolygon aCollectedRanges;
292 const bool bIsEmpty=isNullClipPoly();
293 const bool bIsCleared=!maClipPoly.count();
294 switch(mePendingOps)
296 case UNION:
297 OSL_ASSERT( !bIsCleared );
299 aCollectedRanges = maPendingRanges.solveCrossovers();
300 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
301 aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
302 if( bIsEmpty )
303 maClipPoly = aCollectedRanges;
304 else
305 maClipPoly = tools::solvePolygonOperationOr(
306 maClipPoly,
307 aCollectedRanges);
308 break;
309 case INTERSECT:
310 OSL_ASSERT( !bIsEmpty );
312 aCollectedRanges = maPendingRanges.solveCrossovers();
313 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
314 if( maPendingRanges.count() > 1 )
315 aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
317 if( bIsCleared )
318 maClipPoly = aCollectedRanges;
319 else
320 maClipPoly = tools::solvePolygonOperationAnd(
321 maClipPoly,
322 aCollectedRanges);
323 break;
324 case XOR:
325 aCollectedRanges = maPendingRanges.solveCrossovers();
326 aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
327 aCollectedRanges = correctOrientations(aCollectedRanges);
329 if( bIsEmpty )
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
339 // 'holes' here)
341 // going for an ugly hack meanwhile
342 maClipPoly = tools::solvePolygonOperationXor(
343 B2DPolyPolygon(
344 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
345 aCollectedRanges);
347 else
348 maClipPoly = tools::solvePolygonOperationXor(
349 maClipPoly,
350 aCollectedRanges);
351 break;
352 case SUBTRACT:
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);
360 if( bIsCleared )
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
368 // 'holes' here)
370 // going for an ugly hack meanwhile
371 maClipPoly = tools::solvePolygonOperationDiff(
372 B2DPolyPolygon(
373 tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
374 aCollectedRanges);
376 else
377 maClipPoly = tools::solvePolygonOperationDiff(
378 maClipPoly,
379 aCollectedRanges);
380 break;
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() :
394 mpImpl()
397 B2DClipState::~B2DClipState()
400 B2DClipState::B2DClipState( const B2DClipState& rOrig ) :
401 mpImpl(rOrig.mpImpl)
404 B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) :
405 mpImpl( ImplB2DClipState(rPolyPoly) )
408 B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS )
410 mpImpl = rRHS.mpImpl;
411 return *this;
414 void B2DClipState::makeNull()
416 mpImpl->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))
427 return true;
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: */