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