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 156 : class ImplB2DClipState
36 : {
37 : public:
38 : enum Operation {UNION, INTERSECT, XOR, SUBTRACT};
39 :
40 131 : ImplB2DClipState() :
41 : maPendingPolygons(),
42 : maPendingRanges(),
43 : maClipPoly(),
44 131 : mePendingOps(UNION)
45 131 : {}
46 :
47 3 : explicit ImplB2DClipState( const B2DPolyPolygon& rPoly ) :
48 : maPendingPolygons(),
49 : maPendingRanges(),
50 : maClipPoly(rPoly),
51 3 : mePendingOps(UNION)
52 3 : {}
53 :
54 45 : bool isCleared() const
55 : {
56 45 : return !maClipPoly.count()
57 32 : && !maPendingPolygons.count()
58 77 : && !maPendingRanges.count();
59 : }
60 :
61 30 : bool isNullClipPoly() const
62 : {
63 30 : return maClipPoly.count() == 1
64 60 : && !maClipPoly.getB2DPolygon(0).count();
65 : }
66 :
67 31 : bool isNull() const
68 : {
69 31 : return !maPendingPolygons.count()
70 30 : && !maPendingRanges.count()
71 61 : && isNullClipPoly();
72 : }
73 :
74 4 : void makeNull()
75 : {
76 4 : maPendingPolygons.clear();
77 4 : maPendingRanges.clear();
78 4 : maClipPoly.clear();
79 4 : maClipPoly.append(B2DPolygon());
80 4 : mePendingOps = UNION;
81 4 : }
82 :
83 8 : bool operator==(const ImplB2DClipState& rRHS) const
84 : {
85 8 : return maPendingPolygons == rRHS.maPendingPolygons
86 8 : && maPendingRanges == rRHS.maPendingRanges
87 8 : && maClipPoly == rRHS.maClipPoly
88 24 : && mePendingOps == rRHS.mePendingOps;
89 : }
90 :
91 46 : void addRange(const B2DRange& rRange, Operation eOp)
92 : {
93 46 : if( rRange.isEmpty() )
94 46 : return;
95 :
96 46 : commitPendingPolygons();
97 46 : if( mePendingOps != eOp )
98 13 : commitPendingRanges();
99 :
100 46 : mePendingOps = eOp;
101 : maPendingRanges.appendElement(
102 : rRange,
103 46 : 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 6 : void addPolyPolygon(B2DPolyPolygon aPoly, Operation eOp)
117 : {
118 6 : commitPendingRanges();
119 6 : if( mePendingOps != eOp )
120 6 : commitPendingPolygons();
121 :
122 6 : mePendingOps = eOp;
123 6 : maPendingPolygons.append(aPoly);
124 6 : }
125 :
126 20 : void unionRange(const B2DRange& rRange)
127 : {
128 20 : if( isCleared() )
129 30 : return;
130 :
131 10 : 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 15 : void intersectRange(const B2DRange& rRange)
143 : {
144 15 : if( isNull() )
145 15 : return;
146 :
147 15 : addRange(rRange,INTERSECT);
148 : }
149 :
150 6 : void intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
151 : {
152 6 : if( isNull() )
153 6 : return;
154 :
155 6 : addPolyPolygon(rPolyPoly,INTERSECT);
156 : }
157 :
158 10 : void subtractRange(const B2DRange& rRange )
159 : {
160 10 : if( isNull() )
161 10 : return;
162 :
163 10 : 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 11 : void xorRange(const B2DRange& rRange)
175 : {
176 11 : addRange(rRange,XOR);
177 11 : }
178 :
179 0 : void xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
180 : {
181 0 : addPolyPolygon(rPolyPoly,XOR);
182 0 : }
183 :
184 13 : B2DPolyPolygon getClipPoly() const
185 : {
186 13 : commitPendingRanges();
187 13 : commitPendingPolygons();
188 :
189 13 : return maClipPoly;
190 : }
191 :
192 : private:
193 65 : void commitPendingPolygons() const
194 : {
195 65 : if( !maPendingPolygons.count() )
196 124 : return;
197 :
198 : // assumption: maClipPoly has kept polygons prepared for
199 : // clipping; i.e. no neutral polygons & correct
200 : // orientation
201 6 : maPendingPolygons = tools::prepareForPolygonOperation(maPendingPolygons);
202 6 : const bool bIsEmpty=isNullClipPoly();
203 6 : const bool bIsCleared=!maClipPoly.count();
204 6 : 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 6 : if( bIsCleared )
220 3 : maClipPoly = maPendingPolygons;
221 : else
222 : maClipPoly = tools::solvePolygonOperationAnd(
223 : maClipPoly,
224 3 : maPendingPolygons);
225 6 : 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 6 : maPendingPolygons.clear();
282 6 : mePendingOps = UNION;
283 : }
284 :
285 32 : void commitPendingRanges() const
286 : {
287 32 : if( !maPendingRanges.count() )
288 32 : return;
289 :
290 : // use the specialized range clipper for the win
291 11 : B2DPolyPolygon aCollectedRanges;
292 11 : const bool bIsEmpty=isNullClipPoly();
293 11 : const bool bIsCleared=!maClipPoly.count();
294 11 : switch(mePendingOps)
295 : {
296 : case UNION:
297 : OSL_ASSERT( !bIsCleared );
298 :
299 1 : aCollectedRanges = maPendingRanges.solveCrossovers();
300 1 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
301 1 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
302 1 : if( bIsEmpty )
303 1 : maClipPoly = aCollectedRanges;
304 : else
305 : maClipPoly = tools::solvePolygonOperationOr(
306 : maClipPoly,
307 0 : aCollectedRanges);
308 1 : break;
309 : case INTERSECT:
310 : OSL_ASSERT( !bIsEmpty );
311 :
312 6 : aCollectedRanges = maPendingRanges.solveCrossovers();
313 6 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
314 6 : if( maPendingRanges.count() > 1 )
315 1 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, true);
316 :
317 6 : if( bIsCleared )
318 6 : maClipPoly = aCollectedRanges;
319 : else
320 : maClipPoly = tools::solvePolygonOperationAnd(
321 : maClipPoly,
322 0 : aCollectedRanges);
323 6 : break;
324 : case XOR:
325 2 : aCollectedRanges = maPendingRanges.solveCrossovers();
326 2 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
327 2 : aCollectedRanges = correctOrientations(aCollectedRanges);
328 :
329 2 : if( bIsEmpty )
330 1 : maClipPoly = aCollectedRanges;
331 1 : 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 1 : aCollectedRanges);
351 2 : break;
352 : case SUBTRACT:
353 : OSL_ASSERT( !bIsEmpty );
354 :
355 : // first union all pending ranges, subtract en bloc then
356 2 : aCollectedRanges = maPendingRanges.solveCrossovers();
357 2 : aCollectedRanges = stripNeutralPolygons(aCollectedRanges);
358 2 : aCollectedRanges = stripDispensablePolygons(aCollectedRanges, false);
359 :
360 2 : 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 2 : aCollectedRanges);
380 2 : break;
381 : }
382 :
383 11 : maPendingRanges.clear();
384 11 : mePendingOps = UNION;
385 : }
386 :
387 : mutable B2DPolyPolygon maPendingPolygons;
388 : mutable B2DPolyRange maPendingRanges;
389 : mutable B2DPolyPolygon maClipPoly;
390 : mutable Operation mePendingOps;
391 : };
392 :
393 131 : B2DClipState::B2DClipState() :
394 131 : mpImpl()
395 131 : {}
396 :
397 134 : B2DClipState::~B2DClipState()
398 134 : {}
399 :
400 0 : B2DClipState::B2DClipState( const B2DClipState& rOrig ) :
401 0 : mpImpl(rOrig.mpImpl)
402 0 : {}
403 :
404 3 : B2DClipState::B2DClipState( const B2DPolyPolygon& rPolyPoly ) :
405 3 : mpImpl( ImplB2DClipState(rPolyPoly) )
406 3 : {}
407 :
408 100 : B2DClipState& B2DClipState::operator=( const B2DClipState& rRHS )
409 : {
410 100 : mpImpl = rRHS.mpImpl;
411 100 : return *this;
412 : }
413 :
414 4 : void B2DClipState::makeNull()
415 : {
416 4 : mpImpl->makeNull();
417 4 : }
418 :
419 25 : bool B2DClipState::isCleared() const
420 : {
421 25 : return mpImpl->isCleared();
422 : }
423 :
424 80 : bool B2DClipState::operator==(const B2DClipState& rRHS) const
425 : {
426 80 : if(mpImpl.same_object(rRHS.mpImpl))
427 72 : return true;
428 :
429 8 : return ((*mpImpl) == (*rRHS.mpImpl));
430 : }
431 :
432 0 : bool B2DClipState::operator!=(const B2DClipState& rRHS) const
433 : {
434 0 : return !(*this == rRHS);
435 : }
436 :
437 20 : void B2DClipState::unionRange(const B2DRange& rRange)
438 : {
439 20 : mpImpl->unionRange(rRange);
440 20 : }
441 :
442 0 : void B2DClipState::unionPolyPolygon(const B2DPolyPolygon& rPolyPoly)
443 : {
444 0 : mpImpl->unionPolyPolygon(rPolyPoly);
445 0 : }
446 :
447 15 : void B2DClipState::intersectRange(const B2DRange& rRange)
448 : {
449 15 : mpImpl->intersectRange(rRange);
450 15 : }
451 :
452 6 : void B2DClipState::intersectPolyPolygon(const B2DPolyPolygon& rPolyPoly)
453 : {
454 6 : mpImpl->intersectPolyPolygon(rPolyPoly);
455 6 : }
456 :
457 10 : void B2DClipState::subtractRange(const B2DRange& rRange)
458 : {
459 10 : mpImpl->subtractRange(rRange);
460 10 : }
461 :
462 0 : void B2DClipState::subtractPolyPolygon(const B2DPolyPolygon& rPolyPoly)
463 : {
464 0 : mpImpl->subtractPolyPolygon(rPolyPoly);
465 0 : }
466 :
467 11 : void B2DClipState::xorRange(const B2DRange& rRange)
468 : {
469 11 : mpImpl->xorRange(rRange);
470 11 : }
471 :
472 0 : void B2DClipState::xorPolyPolygon(const B2DPolyPolygon& rPolyPoly)
473 : {
474 0 : mpImpl->xorPolyPolygon(rPolyPoly);
475 0 : }
476 :
477 13 : B2DPolyPolygon B2DClipState::getClipPoly() const
478 : {
479 13 : return mpImpl->getClipPoly();
480 : }
481 :
482 : } // end of namespace tools
483 : } // end of namespace basegfx
484 :
485 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|