Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <basegfx/tools/b2dclipstate.hxx>
21 :
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>
30 :
31 : namespace basegfx
32 : {
33 : namespace tools
34 : {
35 318 : class ImplB2DClipState
36 : {
37 : public:
38 : enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
39 :
40 268 : ImplB2DClipState() :
41 : maPendingPolygons(),
42 : maPendingRanges(),
43 : maClipPoly(),
44 268 : mePendingOps(UNION)
45 268 : {}
46 :
47 6 : explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
48 : maPendingPolygons(),
49 : maPendingRanges(),
50 : maClipPoly(rPoly),
51 6 : mePendingOps(UNION)
52 6 : {}
53 :
54 94 : bool isCleared() const
55 : {
56 94 : return !maClipPoly.count()
57 68 : && !maPendingPolygons.count()
58 162 : && !maPendingRanges.count();
59 : }
60 :
61 60 : bool isNullClipPoly() const
62 : {
63 60 : return maClipPoly.count() == 1
64 120 : && !maClipPoly.getB2DPolygon(0).count();
65 : }
66 :
67 62 : bool isNull() const
68 : {
69 62 : return !maPendingPolygons.count()
70 60 : && !maPendingRanges.count()
71 122 : && isNullClipPoly();
72 : }
73 :
74 8 : void makeNull()
75 : {
76 8 : maPendingPolygons.clear();
77 8 : maPendingRanges.clear();
78 8 : maClipPoly.clear();
79 8 : maClipPoly.append(B2DPolygon());
80 8 : mePendingOps = UNION;
81 8 : }
82 :
83 16 : bool operator==(const ImplB2DClipState& rRHS) const
84 : {
85 16 : return maPendingPolygons == rRHS.maPendingPolygons
86 16 : && maPendingRanges == rRHS.maPendingRanges
87 16 : && maClipPoly == rRHS.maClipPoly
88 48 : && mePendingOps == rRHS.mePendingOps;
89 : }
90 :
91 92 : void addRange(const B2DRange& rRange, Operation eOp)
92 : {
93 92 : if( rRange.isEmpty() )
94 92 : return;
95 :
96 92 : commitPendingPolygons();
97 92 : if( mePendingOps != eOp )
98 26 : commitPendingRanges();
99 :
100 92 : mePendingOps = eOp;
101 : maPendingRanges.appendElement(
102 : rRange,
103 92 : ORIENTATION_POSITIVE);
104 : }
105 :
106 : void addPolygon(B2DPolygon aPoly, Operation eOp)
107 : {
108 : commitPendingRanges();
109 : if( mePendingOps != eOp )
110 : commitPendingPolygons();
111 :
112 : mePendingOps = eOp;
113 : maPendingPolygons.append(aPoly);
114 : }
115 :
116 12 : void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp)
117 : {
118 12 : commitPendingRanges();
119 12 : if( mePendingOps != eOp )
120 12 : commitPendingPolygons();
121 :
122 12 : mePendingOps = eOp;
123 12 : maPendingPolygons.append(aPoly);
124 12 : }
125 :
126 40 : void unionRange(const B2DRange& rRange)
127 : {
128 40 : if( isCleared() )
129 60 : return;
130 :
131 20 : addRange(rRange,UNION);
132 : }
133 :
134 0 : void unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
135 : {
136 0 : if( isCleared() )
137 0 : return;
138 :
139 0 : addPolyPolygon(rPolyPoly,UNION);
140 : }
141 :
142 30 : void intersectRange(const B2DRange& rRange)
143 : {
144 30 : if( isNull() )
145 30 : return;
146 :
147 30 : addRange(rRange,INTERSECT);
148 : }
149 :
150 12 : void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
151 : {
152 12 : if( isNull() )
153 12 : return;
154 :
155 12 : addPolyPolygon(rPolyPoly,INTERSECT);
156 : }
157 :
158 20 : void subtractRange(const B2DRange& rRange )
159 : {
160 20 : if( isNull() )
161 20 : return;
162 :
163 20 : addRange(rRange,SUBTRACT);
164 : }
165 :
166 0 : void subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
167 : {
168 0 : if( isNull() )
169 0 : return;
170 :
171 0 : addPolyPolygon(rPolyPoly,SUBTRACT);
172 : }
173 :
174 22 : void xorRange(const B2DRange& rRange)
175 : {
176 22 : addRange(rRange,XOR);
177 22 : }
178 :
179 0 : void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
180 : {
181 0 : addPolyPolygon(rPolyPoly,XOR);
182 0 : }
183 :
184 26 : B2DPolyPolygon getClipPoly() const
185 : {
186 26 : commitPendingRanges();
187 26 : commitPendingPolygons();
188 :
189 26 : return maClipPoly;
190 : }
191 :
192 : private:
193 130 : void commitPendingPolygons() const
194 : {
195 130 : if( !maPendingPolygons.count() )
196 248 : return;
197 :
198 : // assumption: maClipPoly has kept polygons prepared for
199 : // clipping; i.e. no neutral polygons & correct
200 : // orientation
201 12 : maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons);
202 12 : const bool bIsEmpty=isNullClipPoly();
203 12 : const bool bIsCleared=!maClipPoly.count();
204 12 : switch(mePendingOps)
205 : {
206 : case UNION:
207 : OSL_ASSERT( !bIsCleared );
208 :
209 0 : if( bIsEmpty )
210 0 : maClipPoly = maPendingPolygons;
211 : else
212 : maClipPoly = tools::solvePolygonOperationOr(
213 : maClipPoly,
214 0 : maPendingPolygons);
215 0 : break;
216 : case INTERSECT:
217 : OSL_ASSERT( !bIsEmpty );
218 :
219 12 : if( bIsCleared )
220 6 : maClipPoly = maPendingPolygons;
221 : else
222 : maClipPoly = tools::solvePolygonOperationAnd(
223 : maClipPoly,
224 6 : maPendingPolygons);
225 12 : break;
226 : case XOR:
227 0 : if( bIsEmpty )
228 0 : maClipPoly = maPendingPolygons;
229 0 : else if( bIsCleared )
230 : {
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)
238 :
239 : // going for an ugly hack meanwhile
240 : maClipPoly = tools::solvePolygonOperationXor(
241 : B2DPolyPolygon(
242 : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
243 0 : maPendingPolygons);
244 : }
245 : else
246 : maClipPoly = tools::solvePolygonOperationXor(
247 : maClipPoly,
248 0 : maPendingPolygons);
249 0 : break;
250 : case SUBTRACT:
251 : OSL_ASSERT( !bIsEmpty );
252 :
253 : // first union all pending ones, subtract en bloc then
254 0 : maPendingPolygons = solveCrossovers(maPendingPolygons);
255 0 : maPendingPolygons = stripNeutralPolygons(maPendingPolygons);
256 0 : maPendingPolygons = stripDispensablePolygons(maPendingPolygons, false);
257 :
258 0 : if( bIsCleared )
259 : {
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)
267 :
268 : // going for an ugly hack meanwhile
269 : maClipPoly = tools::solvePolygonOperationDiff(
270 : B2DPolyPolygon(
271 : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
272 0 : maPendingPolygons);
273 : }
274 : else
275 : maClipPoly = tools::solvePolygonOperationDiff(
276 : maClipPoly,
277 0 : maPendingPolygons);
278 0 : break;
279 : }
280 :
281 12 : maPendingPolygons.clear();
282 12 : mePendingOps = UNION;
283 : }
284 :
285 64 : void commitPendingRanges() const
286 : {
287 64 : if( !maPendingRanges.count() )
288 64 : return;
289 :
290 : // use the specialized range clipper for the win
291 22 : B2DPolyPolygon aCollectedRanges;
292 22 : const bool bIsEmpty=isNullClipPoly();
293 22 : const bool bIsCleared=!maClipPoly.count();
294 22 : switch(mePendingOps)
295 : {
296 : case UNION:
297 : OSL_ASSERT( !bIsCleared );
298 :
299 2 : aCollectedRanges = maPendingRanges.solveCrossovers();
300 2 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
301 2 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
302 2 : if( bIsEmpty )
303 2 : maClipPoly = aCollectedRanges;
304 : else
305 : maClipPoly = tools::solvePolygonOperationOr(
306 : maClipPoly,
307 0 : aCollectedRanges);
308 2 : break;
309 : case INTERSECT:
310 : OSL_ASSERT( !bIsEmpty );
311 :
312 12 : aCollectedRanges = maPendingRanges.solveCrossovers();
313 12 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
314 12 : if( maPendingRanges.count() > 1 )
315 2 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
316 :
317 12 : if( bIsCleared )
318 12 : maClipPoly = aCollectedRanges;
319 : else
320 : maClipPoly = tools::solvePolygonOperationAnd(
321 : maClipPoly,
322 0 : aCollectedRanges);
323 12 : break;
324 : case XOR:
325 4 : aCollectedRanges = maPendingRanges.solveCrossovers();
326 4 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
327 4 : aCollectedRanges = correctOrientations(aCollectedRanges);
328 :
329 4 : if( bIsEmpty )
330 2 : maClipPoly = aCollectedRanges;
331 2 : else if( bIsCleared )
332 : {
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)
340 :
341 : // going for an ugly hack meanwhile
342 : maClipPoly = tools::solvePolygonOperationXor(
343 : B2DPolyPolygon(
344 : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
345 0 : aCollectedRanges);
346 : }
347 : else
348 : maClipPoly = tools::solvePolygonOperationXor(
349 : maClipPoly,
350 2 : aCollectedRanges);
351 4 : break;
352 : case SUBTRACT:
353 : OSL_ASSERT( !bIsEmpty );
354 :
355 : // first union all pending ranges, subtract en bloc then
356 4 : aCollectedRanges = maPendingRanges.solveCrossovers();
357 4 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
358 4 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
359 :
360 4 : if( bIsCleared )
361 : {
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)
369 :
370 : // going for an ugly hack meanwhile
371 : maClipPoly = tools::solvePolygonOperationDiff(
372 : B2DPolyPolygon(
373 : tools::createPolygonFromRect(B2DRange(-1E20,-1E20,1E20,1E20))),
374 0 : aCollectedRanges);
375 : }
376 : else
377 : maClipPoly = tools::solvePolygonOperationDiff(
378 : maClipPoly,
379 4 : aCollectedRanges);
380 4 : break;
381 : }
382 :
383 22 : maPendingRanges.clear();
384 22 : mePendingOps = UNION;
385 : }
386 :
387 : mutable B2DPolyPolygon maPendingPolygons;
388 : mutable B2DPolyRange maPendingRanges;
389 : mutable B2DPolyPolygon maClipPoly;
390 : mutable Operation mePendingOps;
391 : };
392 :
393 268 : B2DClipState::B2DClipState() :
394 268 : mpImpl()
395 268 : {}
396 :
397 274 : B2DClipState::~B2DClipState()
398 274 : {}
399 :
400 0 : B2DClipState::B2DClipState( const B2DClipState& rOrig ) :
401 0 : mpImpl(rOrig.mpImpl)
402 0 : {}
403 :
404 6 : B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) :
405 6 : mpImpl( ImplB2DClipState(rPolyPoly) )
406 6 : {}
407 :
408 202 : B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS )
409 : {
410 202 : mpImpl = rRHS.mpImpl;
411 202 : return *this;
412 : }
413 :
414 8 : void B2DClipState::makeNull()
415 : {
416 8 : mpImpl->makeNull();
417 8 : }
418 :
419 54 : bool B2DClipState::isCleared() const
420 : {
421 54 : return mpImpl->isCleared();
422 : }
423 :
424 162 : bool B2DClipState::operator==(const B2DClipState& rRHS) const
425 : {
426 162 : if(mpImpl.same_object(rRHS.mpImpl))
427 146 : return true;
428 :
429 16 : return ((*mpImpl) == (*rRHS.mpImpl));
430 : }
431 :
432 0 : bool B2DClipState::operator!=(const B2DClipState& rRHS) const
433 : {
434 0 : return !(*this == rRHS);
435 : }
436 :
437 40 : void B2DClipState::unionRange(const B2DRange& rRange)
438 : {
439 40 : mpImpl->unionRange(rRange);
440 40 : }
441 :
442 0 : void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
443 : {
444 0 : mpImpl->unionPolyPolygon(rPolyPoly);
445 0 : }
446 :
447 30 : void B2DClipState::intersectRange(const B2DRange& rRange)
448 : {
449 30 : mpImpl->intersectRange(rRange);
450 30 : }
451 :
452 12 : void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
453 : {
454 12 : mpImpl->intersectPolyPolygon(rPolyPoly);
455 12 : }
456 :
457 20 : void B2DClipState::subtractRange(const B2DRange& rRange)
458 : {
459 20 : mpImpl->subtractRange(rRange);
460 20 : }
461 :
462 0 : void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
463 : {
464 0 : mpImpl->subtractPolyPolygon(rPolyPoly);
465 0 : }
466 :
467 22 : void B2DClipState::xorRange(const B2DRange& rRange)
468 : {
469 22 : mpImpl->xorRange(rRange);
470 22 : }
471 :
472 0 : void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
473 : {
474 0 : mpImpl->xorPolyPolygon(rPolyPoly);
475 0 : }
476 :
477 26 : B2DPolyPolygon B2DClipState::getClipPoly() const
478 : {
479 26 : return mpImpl->getClipPoly();
480 : }
481 :
482 : } // end of namespace tools
483 : } // end of namespace basegfx
484 :
485 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|