Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2008 by Sun Microsystems, Inc.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <basegfx/tools/b2dclipstate.hxx>
30 : :
31 : : #include <basegfx/range/b2drange.hxx>
32 : : #include <basegfx/range/b2dpolyrange.hxx>
33 : : #include <basegfx/range/b2drangeclipper.hxx>
34 : : #include <basegfx/polygon/b2dpolygon.hxx>
35 : : #include <basegfx/polygon/b2dpolygontools.hxx>
36 : : #include <basegfx/polygon/b2dpolypolygon.hxx>
37 : : #include <basegfx/polygon/b2dpolypolygontools.hxx>
38 : : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
39 : :
40 : : namespace basegfx
41 : : {
42 : : namespace tools
43 : : {
44 [ + - ][ + - ]: 659 : class ImplB2DClipState
[ + - ][ + - ]
45 : : {
46 : : public:
47 : : enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
48 : :
49 : 584 : ImplB2DClipState() :
50 : : maPendingPolygons(),
51 : : maPendingRanges(),
52 : : maClipPoly(),
53 [ + - ][ + - ]: 584 : mePendingOps(UNION)
54 : 584 : {}
55 : :
56 : 15 : explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
57 : : maPendingPolygons(),
58 : : maPendingRanges(),
59 : : maClipPoly(rPoly),
60 [ + - ][ + - ]: 15 : mePendingOps(UNION)
61 : 15 : {}
62 : :
63 : 187 : bool isCleared() const
64 : : {
65 : 187 : return !maClipPoly.count()
66 : 137 : && !maPendingPolygons.count()
67 [ + + ]: 324 : && !maPendingRanges.count();
[ + + + - ]
68 : : }
69 : :
70 : 100 : bool isNullClipPoly() const
71 : : {
72 [ + - ]: 100 : return maClipPoly.count() == 1
73 [ + + ][ + - ]: 100 : && !maClipPoly.getB2DPolygon(0).count();
[ + - ][ + + ]
[ + + ][ # # ]
74 : : }
75 : :
76 : 130 : bool isNull() const
77 : : {
78 : 130 : return !maPendingPolygons.count()
79 : 125 : && !maPendingRanges.count()
80 [ - + ]: 255 : && isNullClipPoly();
[ + + + + ]
81 : : }
82 : :
83 : 20 : void makeNull()
84 : : {
85 : 20 : maPendingPolygons.clear();
86 : 20 : maPendingRanges.clear();
87 : 20 : maClipPoly.clear();
88 [ + - ]: 20 : maClipPoly.append(B2DPolygon());
89 : 20 : mePendingOps = UNION;
90 : 20 : }
91 : :
92 : 15 : bool operator==(const ImplB2DClipState& rRHS) const
93 : : {
94 : 15 : return maPendingPolygons == rRHS.maPendingPolygons
95 : 15 : && maPendingRanges == rRHS.maPendingRanges
96 : 15 : && maClipPoly == rRHS.maClipPoly
97 [ # # ][ + - : 45 : && mePendingOps == rRHS.mePendingOps;
+ - - + ]
98 : : }
99 : :
100 : 230 : void addRange(const B2DRange& rRange, Operation eOp)
101 : : {
102 [ - + ]: 230 : if( rRange.isEmpty() )
103 : 230 : return;
104 : :
105 : 230 : commitPendingPolygons();
106 [ + + ]: 230 : if( mePendingOps != eOp )
107 : 65 : commitPendingRanges();
108 : :
109 : 230 : mePendingOps = eOp;
110 : : maPendingRanges.appendElement(
111 : : rRange,
112 : 230 : ORIENTATION_POSITIVE);
113 : : }
114 : :
115 : : void addPolygon(B2DPolygon aPoly, Operation eOp)
116 : : {
117 : : commitPendingRanges();
118 : : if( mePendingOps != eOp )
119 : : commitPendingPolygons();
120 : :
121 : : mePendingOps = eOp;
122 : : maPendingPolygons.append(aPoly);
123 : : }
124 : :
125 : 5 : void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp)
126 : : {
127 : 5 : commitPendingRanges();
128 [ + - ]: 5 : if( mePendingOps != eOp )
129 : 5 : commitPendingPolygons();
130 : :
131 : 5 : mePendingOps = eOp;
132 : 5 : maPendingPolygons.append(aPoly);
133 : 5 : }
134 : :
135 : 100 : void unionRange(const B2DRange& rRange)
136 : : {
137 [ + + ]: 100 : if( isCleared() )
138 : 100 : return;
139 : :
140 : 50 : addRange(rRange,UNION);
141 : : }
142 : :
143 : 0 : void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
144 : : {
145 [ # # ]: 0 : if( isCleared() )
146 : 0 : return;
147 : :
148 [ # # ]: 0 : addPolyPolygon(rPolyPoly,UNION);
149 : : }
150 : :
151 : 75 : void intersectRange(const B2DRange& rRange)
152 : : {
153 [ - + ]: 75 : if( isNull() )
154 : 75 : return;
155 : :
156 : 75 : addRange(rRange,INTERSECT);
157 : : }
158 : :
159 : 5 : void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
160 : : {
161 [ - + ]: 5 : if( isNull() )
162 : 5 : return;
163 : :
164 [ + - ]: 5 : addPolyPolygon(rPolyPoly,INTERSECT);
165 : : }
166 : :
167 : 50 : void subtractRange(const B2DRange& rRange )
168 : : {
169 [ - + ]: 50 : if( isNull() )
170 : 50 : return;
171 : :
172 : 50 : addRange(rRange,SUBTRACT);
173 : : }
174 : :
175 : 0 : void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
176 : : {
177 [ # # ]: 0 : if( isNull() )
178 : 0 : return;
179 : :
180 [ # # ]: 0 : addPolyPolygon(rPolyPoly,SUBTRACT);
181 : : }
182 : :
183 : 55 : void xorRange(const B2DRange& rRange)
184 : : {
185 : 55 : addRange(rRange,XOR);
186 : 55 : }
187 : :
188 : 0 : void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
189 : : {
190 [ # # ]: 0 : addPolyPolygon(rPolyPoly,XOR);
191 : 0 : }
192 : :
193 : 40 : B2DPolyPolygon getClipPoly() const
194 : : {
195 : 40 : commitPendingRanges();
196 : 40 : commitPendingPolygons();
197 : :
198 : 40 : return maClipPoly;
199 : : }
200 : :
201 : : private:
202 : 275 : void commitPendingPolygons() const
203 : : {
204 [ + + ]: 275 : if( !maPendingPolygons.count() )
205 : 275 : return;
206 : :
207 : : // assumption: maClipPoly has kept polygons prepared for
208 : : // clipping; i.e. no neutral polygons & correct
209 : : // orientation
210 [ + - ]: 5 : maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons);
211 : 5 : const bool bIsEmpty=isNullClipPoly();
212 : 5 : const bool bIsCleared=!maClipPoly.count();
213 [ - + - - : 5 : switch(mePendingOps)
- ]
214 : : {
215 : : case UNION:
216 : : OSL_ASSERT( !bIsCleared );
217 : :
218 [ # # ]: 0 : if( bIsEmpty )
219 : 0 : maClipPoly = maPendingPolygons;
220 : : else
221 : : maClipPoly = tools::solvePolygonOperationOr(
222 : : maClipPoly,
223 [ # # ]: 0 : maPendingPolygons);
224 : 0 : break;
225 : : case INTERSECT:
226 : : OSL_ASSERT( !bIsEmpty );
227 : :
228 [ + - ]: 5 : if( bIsCleared )
229 : 5 : maClipPoly = maPendingPolygons;
230 : : else
231 : : maClipPoly = tools::solvePolygonOperationAnd(
232 : : maClipPoly,
233 [ # # ]: 0 : maPendingPolygons);
234 : 5 : break;
235 : : case XOR:
236 [ # # ]: 0 : if( bIsEmpty )
237 : 0 : maClipPoly = maPendingPolygons;
238 [ # # ]: 0 : else if( bIsCleared )
239 : : {
240 : : // not representable, strictly speaking,
241 : : // using polygons with the common even/odd
242 : : // or nonzero winding number fill rule. If
243 : : // we'd want to represent it, fill rule
244 : : // would need to be "non-negative winding
245 : : // number" (and we then would return
246 : : // 'holes' here)
247 : :
248 : : // going for an ugly hack meanwhile
249 : : maClipPoly = tools::solvePolygonOperationXor(
250 : : B2DPolyPolygon(
251 : : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
252 [ # # ][ # # ]: 0 : maPendingPolygons);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
253 : : }
254 : : else
255 : : maClipPoly = tools::solvePolygonOperationXor(
256 : : maClipPoly,
257 [ # # ]: 0 : maPendingPolygons);
258 : 0 : break;
259 : : case SUBTRACT:
260 : : OSL_ASSERT( !bIsEmpty );
261 : :
262 : : // first union all pending ones, subtract en bloc then
263 [ # # ]: 0 : maPendingPolygons = solveCrossovers(maPendingPolygons);
264 [ # # ]: 0 : maPendingPolygons = stripNeutralPolygons(maPendingPolygons);
265 [ # # ]: 0 : maPendingPolygons = stripDispensablePolygons(maPendingPolygons, false);
266 : :
267 [ # # ]: 0 : if( bIsCleared )
268 : : {
269 : : // not representable, strictly speaking,
270 : : // using polygons with the common even/odd
271 : : // or nonzero winding number fill rule. If
272 : : // we'd want to represent it, fill rule
273 : : // would need to be "non-negative winding
274 : : // number" (and we then would return
275 : : // 'holes' here)
276 : :
277 : : // going for an ugly hack meanwhile
278 : : maClipPoly = tools::solvePolygonOperationDiff(
279 : : B2DPolyPolygon(
280 : : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
281 [ # # ][ # # ]: 0 : maPendingPolygons);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
282 : : }
283 : : else
284 : : maClipPoly = tools::solvePolygonOperationDiff(
285 : : maClipPoly,
286 [ # # ]: 0 : maPendingPolygons);
287 : 0 : break;
288 : : }
289 : :
290 : 5 : maPendingPolygons.clear();
291 : 5 : mePendingOps = UNION;
292 : : }
293 : :
294 : 110 : void commitPendingRanges() const
295 : : {
296 [ + - ][ + + ]: 110 : if( !maPendingRanges.count() )
297 : 110 : return;
298 : :
299 : : // use the specialized range clipper for the win
300 [ + - ]: 55 : B2DPolyPolygon aCollectedRanges;
301 [ + - ]: 55 : const bool bIsEmpty=isNullClipPoly();
302 [ + - ]: 55 : const bool bIsCleared=!maClipPoly.count();
303 [ + + + + : 55 : switch(mePendingOps)
- ]
304 : : {
305 : : case UNION:
306 : : OSL_ASSERT( !bIsCleared );
307 : :
308 [ + - ][ + - ]: 5 : aCollectedRanges = maPendingRanges.solveCrossovers();
[ + - ]
309 [ + - ][ + - ]: 5 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
[ + - ]
310 [ + - ][ + - ]: 5 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
[ + - ]
311 [ + - ]: 5 : if( bIsEmpty )
312 [ + - ]: 5 : maClipPoly = aCollectedRanges;
313 : : else
314 : : maClipPoly = tools::solvePolygonOperationOr(
315 : : maClipPoly,
316 [ # # ][ # # ]: 0 : aCollectedRanges);
[ # # ]
317 : 5 : break;
318 : : case INTERSECT:
319 : : OSL_ASSERT( !bIsEmpty );
320 : :
321 [ + - ][ + - ]: 30 : aCollectedRanges = maPendingRanges.solveCrossovers();
[ + - ]
322 [ + - ][ + - ]: 30 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
[ + - ]
323 [ + - ][ + + ]: 30 : if( maPendingRanges.count() > 1 )
324 [ + - ][ + - ]: 5 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
[ + - ]
325 : :
326 [ + - ]: 30 : if( bIsCleared )
327 [ + - ]: 30 : maClipPoly = aCollectedRanges;
328 : : else
329 : : maClipPoly = tools::solvePolygonOperationAnd(
330 : : maClipPoly,
331 [ # # ][ # # ]: 0 : aCollectedRanges);
[ # # ]
332 : 30 : break;
333 : : case XOR:
334 [ + - ][ + - ]: 10 : aCollectedRanges = maPendingRanges.solveCrossovers();
[ + - ]
335 [ + - ][ + - ]: 10 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
[ + - ]
336 [ + - ][ + - ]: 10 : aCollectedRanges = correctOrientations(aCollectedRanges);
[ + - ]
337 : :
338 [ + + ]: 10 : if( bIsEmpty )
339 [ + - ]: 5 : maClipPoly = aCollectedRanges;
340 [ - + ]: 5 : else if( bIsCleared )
341 : : {
342 : : // not representable, strictly speaking,
343 : : // using polygons with the common even/odd
344 : : // or nonzero winding number fill rule. If
345 : : // we'd want to represent it, fill rule
346 : : // would need to be "non-negative winding
347 : : // number" (and we then would return
348 : : // 'holes' here)
349 : :
350 : : // going for an ugly hack meanwhile
351 : : maClipPoly = tools::solvePolygonOperationXor(
352 : : B2DPolyPolygon(
353 : : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
354 [ # # ][ # # ]: 0 : aCollectedRanges);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
355 : : }
356 : : else
357 : : maClipPoly = tools::solvePolygonOperationXor(
358 : : maClipPoly,
359 [ + - ][ + - ]: 5 : aCollectedRanges);
[ + - ]
360 : 10 : break;
361 : : case SUBTRACT:
362 : : OSL_ASSERT( !bIsEmpty );
363 : :
364 : : // first union all pending ranges, subtract en bloc then
365 [ + - ][ + - ]: 10 : aCollectedRanges = maPendingRanges.solveCrossovers();
[ + - ]
366 [ + - ][ + - ]: 10 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
[ + - ]
367 [ + - ][ + - ]: 10 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
[ + - ]
368 : :
369 [ - + ]: 10 : if( bIsCleared )
370 : : {
371 : : // not representable, strictly speaking,
372 : : // using polygons with the common even/odd
373 : : // or nonzero winding number fill rule. If
374 : : // we'd want to represent it, fill rule
375 : : // would need to be "non-negative winding
376 : : // number" (and we then would return
377 : : // 'holes' here)
378 : :
379 : : // going for an ugly hack meanwhile
380 : : maClipPoly = tools::solvePolygonOperationDiff(
381 : : B2DPolyPolygon(
382 : : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
383 [ # # ][ # # ]: 0 : aCollectedRanges);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
384 : : }
385 : : else
386 : : maClipPoly = tools::solvePolygonOperationDiff(
387 : : maClipPoly,
388 [ + - ][ + - ]: 10 : aCollectedRanges);
[ + - ]
389 : 10 : break;
390 : : }
391 : :
392 [ + - ]: 55 : maPendingRanges.clear();
393 [ + - ]: 110 : mePendingOps = UNION;
394 : : }
395 : :
396 : : mutable B2DPolyPolygon maPendingPolygons;
397 : : mutable B2DPolyRange maPendingRanges;
398 : : mutable B2DPolyPolygon maClipPoly;
399 : : mutable Operation mePendingOps;
400 : : };
401 : :
402 : 584 : B2DClipState::B2DClipState() :
403 : 584 : mpImpl()
404 : 584 : {}
405 : :
406 : 599 : B2DClipState::~B2DClipState()
407 : 599 : {}
408 : :
409 : 0 : B2DClipState::B2DClipState( const B2DClipState& rOrig ) :
410 : 0 : mpImpl(rOrig.mpImpl)
411 : 0 : {}
412 : :
413 : 15 : B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) :
414 [ + - ]: 15 : mpImpl( ImplB2DClipState(rPolyPoly) )
415 : 15 : {}
416 : :
417 : 437 : B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS )
418 : : {
419 : 437 : mpImpl = rRHS.mpImpl;
420 : 437 : return *this;
421 : : }
422 : :
423 : 20 : void B2DClipState::makeNull()
424 : : {
425 : 20 : mpImpl->makeNull();
426 : 20 : }
427 : :
428 : 87 : bool B2DClipState::isCleared() const
429 : : {
430 : 87 : return mpImpl->isCleared();
431 : : }
432 : :
433 : 347 : bool B2DClipState::operator==(const B2DClipState& rRHS) const
434 : : {
435 [ + + ]: 347 : if(mpImpl.same_object(rRHS.mpImpl))
436 : 332 : return true;
437 : :
438 : 347 : return ((*mpImpl) == (*rRHS.mpImpl));
439 : : }
440 : :
441 : 0 : bool B2DClipState::operator!=(const B2DClipState& rRHS) const
442 : : {
443 : 0 : return !(*this == rRHS);
444 : : }
445 : :
446 : 100 : void B2DClipState::unionRange(const B2DRange& rRange)
447 : : {
448 : 100 : mpImpl->unionRange(rRange);
449 : 100 : }
450 : :
451 : 0 : void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
452 : : {
453 : 0 : mpImpl->unionPolyPolygon(rPolyPoly);
454 : 0 : }
455 : :
456 : 75 : void B2DClipState::intersectRange(const B2DRange& rRange)
457 : : {
458 : 75 : mpImpl->intersectRange(rRange);
459 : 75 : }
460 : :
461 : 5 : void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
462 : : {
463 : 5 : mpImpl->intersectPolyPolygon(rPolyPoly);
464 : 5 : }
465 : :
466 : 50 : void B2DClipState::subtractRange(const B2DRange& rRange)
467 : : {
468 : 50 : mpImpl->subtractRange(rRange);
469 : 50 : }
470 : :
471 : 0 : void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
472 : : {
473 : 0 : mpImpl->subtractPolyPolygon(rPolyPoly);
474 : 0 : }
475 : :
476 : 55 : void B2DClipState::xorRange(const B2DRange& rRange)
477 : : {
478 : 55 : mpImpl->xorRange(rRange);
479 : 55 : }
480 : :
481 : 0 : void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
482 : : {
483 : 0 : mpImpl->xorPolyPolygon(rPolyPoly);
484 : 0 : }
485 : :
486 : 40 : B2DPolyPolygon B2DClipState::getClipPoly() const
487 : : {
488 : 40 : return mpImpl->getClipPoly();
489 : : }
490 : :
491 : : } // end of namespace tools
492 : : } // end of namespace basegfx
493 : :
494 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|