1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <sal/types.h>
21 #include <cppunit/TestAssert.h>
22 #include <cppunit/TestFixture.h>
23 #include <cppunit/extensions/HelperMacros.h>
25 #include <basegfx/matrix/b2dhommatrix.hxx>
26 #include <basegfx/curve/b2dcubicbezier.hxx>
27 #include <basegfx/curve/b2dbeziertools.hxx>
28 #include <basegfx/range/b2dpolyrange.hxx>
29 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <basegfx/polygon/b2dpolygontools.hxx>
31 #include <basegfx/polygon/b2dpolypolygontools.hxx>
32 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
33 #include <basegfx/polygon/b2dpolygonclipper.hxx>
34 #include <basegfx/polygon/b2dpolypolygon.hxx>
35 #include <basegfx/numeric/ftools.hxx>
36 #include <comphelper/random.hxx>
38 #include <boxclipper.hxx>
40 using namespace ::basegfx
;
44 /// Gets a random ordinal [0,n)
45 double getRandomOrdinal( const ::std::size_t n
)
47 // use this one when displaying polygons in OOo, which still sucks
48 // great rocks when trying to import non-integer svg:d attributes
49 return comphelper::rng::uniform_size_distribution(0, n
-1);
52 inline bool compare(const B2DPoint
& left
, const B2DPoint
& right
)
54 return left
.getX()<right
.getX()
55 || (rtl::math::approxEqual(left
.getX(),right
.getX()) && left
.getY()<right
.getY());
58 class boxclipper
: public CppUnit::TestFixture
61 B2DPolyRange aDisjunctRanges
;
62 B2DPolyRange aEqualRanges
;
63 B2DPolyRange aIntersectionN
;
64 B2DPolyRange aIntersectionE
;
65 B2DPolyRange aIntersectionS
;
66 B2DPolyRange aIntersectionW
;
67 B2DPolyRange aIntersectionNE
;
68 B2DPolyRange aIntersectionSE
;
69 B2DPolyRange aIntersectionSW
;
70 B2DPolyRange aIntersectionNW
;
71 B2DPolyRange aRingIntersection
;
72 B2DPolyRange aRingIntersection2
;
73 B2DPolyRange aRingIntersectExtraStrip
;
74 B2DPolyRange aComplexIntersections
;
75 B2DPolyRange aRandomIntersections
;
78 // initialise your test code values here.
81 B2DRange
aCenter(100, 100, -100, -100);
82 B2DRange
aOffside(800, 800, 1000, 1000);
83 B2DRange
aNorth(100, 0, -100, -200);
84 B2DRange
aSouth(100, 200, -100, 0);
85 B2DRange
aEast(0, 100, 200, -100);
86 B2DRange
aWest(-200, 100, 0, -100);
87 B2DRange
aNorthEast(0, 0, 200, -200);
88 B2DRange
aSouthEast(0, 0, 200, 200);
89 B2DRange
aSouthWest(0, 0, -200, 200);
90 B2DRange
aNorthWest(0, 0, -200, -200);
92 B2DRange
aNorth2(-150, 50, 150, 350);
93 B2DRange
aSouth2(-150, -50, 150, -350);
94 B2DRange
aEast2 (50, -150, 350, 150);
95 B2DRange
aWest2 (-50, -150,-350, 150);
97 aDisjunctRanges
.appendElement( aCenter
, B2VectorOrientation::Negative
);
98 aDisjunctRanges
.appendElement( aOffside
, B2VectorOrientation::Negative
);
100 aEqualRanges
.appendElement( aCenter
, B2VectorOrientation::Negative
);
101 aEqualRanges
.appendElement( aCenter
, B2VectorOrientation::Negative
);
103 aIntersectionN
.appendElement( aCenter
, B2VectorOrientation::Negative
);
104 aIntersectionN
.appendElement( aNorth
, B2VectorOrientation::Negative
);
106 aIntersectionE
.appendElement( aCenter
, B2VectorOrientation::Negative
);
107 aIntersectionE
.appendElement( aEast
, B2VectorOrientation::Negative
);
109 aIntersectionS
.appendElement( aCenter
, B2VectorOrientation::Negative
);
110 aIntersectionS
.appendElement( aSouth
, B2VectorOrientation::Negative
);
112 aIntersectionW
.appendElement( aCenter
, B2VectorOrientation::Negative
);
113 aIntersectionW
.appendElement( aWest
, B2VectorOrientation::Negative
);
115 aIntersectionNE
.appendElement( aCenter
, B2VectorOrientation::Negative
);
116 aIntersectionNE
.appendElement( aNorthEast
, B2VectorOrientation::Negative
);
118 aIntersectionSE
.appendElement( aCenter
, B2VectorOrientation::Negative
);
119 aIntersectionSE
.appendElement( aSouthEast
, B2VectorOrientation::Negative
);
121 aIntersectionSW
.appendElement( aCenter
, B2VectorOrientation::Negative
);
122 aIntersectionSW
.appendElement( aSouthWest
, B2VectorOrientation::Negative
);
124 aIntersectionNW
.appendElement( aCenter
, B2VectorOrientation::Negative
);
125 aIntersectionNW
.appendElement( aNorthWest
, B2VectorOrientation::Negative
);
127 aRingIntersection
.appendElement( aNorth2
, B2VectorOrientation::Negative
);
128 aRingIntersection
.appendElement( aEast2
, B2VectorOrientation::Negative
);
129 aRingIntersection
.appendElement( aSouth2
, B2VectorOrientation::Negative
);
131 aRingIntersection2
= aRingIntersection
;
132 aRingIntersection2
.appendElement( aWest2
, B2VectorOrientation::Negative
);
134 aRingIntersectExtraStrip
= aRingIntersection2
;
135 aRingIntersectExtraStrip
.appendElement( B2DRange(0, -25, 200, 25),
136 B2VectorOrientation::Negative
);
138 aComplexIntersections
.appendElement( aCenter
, B2VectorOrientation::Negative
);
139 aComplexIntersections
.appendElement( aOffside
, B2VectorOrientation::Negative
);
140 aComplexIntersections
.appendElement( aCenter
, B2VectorOrientation::Negative
);
141 aComplexIntersections
.appendElement( aNorth
, B2VectorOrientation::Negative
);
142 aComplexIntersections
.appendElement( aEast
, B2VectorOrientation::Negative
);
143 aComplexIntersections
.appendElement( aSouth
, B2VectorOrientation::Negative
);
144 aComplexIntersections
.appendElement( aWest
, B2VectorOrientation::Negative
);
145 aComplexIntersections
.appendElement( aNorthEast
, B2VectorOrientation::Negative
);
146 aComplexIntersections
.appendElement( aSouthEast
, B2VectorOrientation::Negative
);
147 aComplexIntersections
.appendElement( aSouthWest
, B2VectorOrientation::Negative
);
148 aComplexIntersections
.appendElement( aNorthWest
, B2VectorOrientation::Negative
);
150 #ifdef GENERATE_RANDOM
151 for( int i
=0; i
<800; ++i
)
153 B2DRange
aRandomRange(
154 getRandomOrdinal( 1000 ),
155 getRandomOrdinal( 1000 ),
156 getRandomOrdinal( 1000 ),
157 getRandomOrdinal( 1000 ) );
159 aRandomIntersections
.appendElement( aRandomRange
, B2VectorOrientation::Negative
);
162 const char* randomSvg
="m394 783h404v57h-404zm-197-505h571v576h-571zm356-634h75v200h-75zm-40-113h403v588h-403zm93-811h111v494h-111zm-364-619h562v121h-562zm-134-8h292v27h-292zm110 356h621v486h-621zm78-386h228v25h-228zm475-345h201v201h-201zm-2-93h122v126h-122zm-417-243h567v524h-567zm-266-738h863v456h-863zm262-333h315v698h-315zm-328-826h43v393h-43zm830-219h120v664h-120zm-311-636h221v109h-221zm-500 137h628v19h-628zm681-94h211v493h-211zm-366-646h384v355h-384zm-189-199h715v247h-715zm165-459h563v601h-563zm258-479h98v606h-98zm270-517h65v218h-65zm-44-259h96v286h-96zm-599-202h705v468h-705zm216-803h450v494h-450zm-150-22h26v167h-26zm-55-599h50v260h-50zm190-278h490v387h-490zm-290-453h634v392h-634zm257 189h552v300h-552zm-151-690h136v455h-136zm12-597h488v432h-488zm501-459h48v39h-48zm-224-112h429v22h-429zm-281 102h492v621h-492zm519-158h208v17h-208zm-681-563h56v427h-56zm126-451h615v392h-615zm-47-410h598v522h-598zm-32 316h79v110h-79zm-71-129h18v127h-18zm126-993h743v589h-743zm211-430h428v750h-428zm61-554h100v220h-100zm-353-49h658v157h-658zm778-383h115v272h-115zm-249-541h119v712h-119zm203 86h94v40h-94z";
163 B2DPolyPolygon randomPoly
;
164 tools::importFromSvgD(
166 OUString::createFromAscii(randomSvg
), false, nullptr);
167 for (auto const& aPolygon
: randomPoly
)
168 aRandomIntersections
.appendElement(aPolygon
.getB2DRange(), B2VectorOrientation::Negative
);
172 B2DPolyPolygon
normalizePoly( const B2DPolyPolygon
& rPoly
)
175 for( sal_uInt32 i
=0; i
<rPoly
.count(); ++i
)
177 B2DPolygon aTmp
=rPoly
.getB2DPolygon(i
);
178 if( B2VectorOrientation::Negative
== tools::getOrientation(aTmp
) )
181 aTmp
=tools::removeNeutralPoints(aTmp
);
182 std::vector
<B2DPoint
> aTmp2(aTmp
.count());
183 for(sal_uInt32 j
=0; j
<aTmp
.count(); ++j
)
184 aTmp2
[j
] = aTmp
.getB2DPoint(j
);
186 std::vector
<B2DPoint
>::iterator pSmallest
=aTmp2
.end();
187 for(std::vector
<B2DPoint
>::iterator pCurr
=aTmp2
.begin(); pCurr
!=aTmp2
.end(); ++pCurr
)
189 if( pSmallest
== aTmp2
.end() || compare(*pCurr
, *pSmallest
) )
195 if( pSmallest
!= aTmp2
.end() )
196 std::rotate(aTmp2
.begin(),pSmallest
,aTmp2
.end());
199 for(std::vector
<B2DPoint
>::iterator pCurr
=aTmp2
.begin(); pCurr
!=aTmp2
.end(); ++pCurr
)
205 // boxclipper & generic clipper disagree slightly on area-less
206 // polygons (one or two points only)
207 aRes
= tools::stripNeutralPolygons(aRes
);
209 // now, sort all polygons with increasing 0th point
210 std::sort(aRes
.begin(),
212 [](const B2DPolygon
& aPolygon1
, const B2DPolygon
& aPolygon2
) {
213 return compare(aPolygon1
.getB2DPoint(0),
214 aPolygon2
.getB2DPoint(0)); } );
219 void verifyPoly(const char* sName
, const char* sSvg
, const B2DPolyRange
& toTest
)
221 B2DPolyPolygon aTmp1
;
222 CPPUNIT_ASSERT_MESSAGE(sName
,
223 tools::importFromSvgD(
224 aTmp1
, OUString::createFromAscii(sSvg
), false, nullptr));
227 tools::exportToSvgD(toTest
.solveCrossovers(), true, true, false);
228 B2DPolyPolygon aTmp2
;
229 CPPUNIT_ASSERT_MESSAGE(sName
,
230 tools::importFromSvgD(
231 aTmp2
, aSvg
, false, nullptr));
233 CPPUNIT_ASSERT_MESSAGE(
235 normalizePoly(aTmp2
) == normalizePoly(aTmp1
));
240 const char* disjunct
="m-100-100v200h200v-200zm900 900v200h200v-200z";
241 const char* equal
="m-100-100v200h200v-200zm200 0h-200v200h200v-200z";
242 const char* intersectionN
="m-100-100v100h200v-100zm200 0v-100h-200v100 200h200v-200z";
243 const char* intersectionE
="m0-100v200h100v-200zm0 0h-100v200h100 200v-200z";
244 const char* intersectionS
="m-100 0v100h200v-100zm0-100v200 100h200v-100-200z";
245 const char* intersectionW
="m-100-100v200h100v-200zm0 0h-100v200h100 200v-200z";
246 const char* intersectionNE
="m0-100v100h100v-100zm0-100v100h-100v200h200v-100h100v-200z";
247 const char* intersectionSE
="m0 0v100h100v-100zm100 0v-100h-200v200h100v100h200v-200z";
248 const char* intersectionSW
="m-100 0v100h100v-100zm0-100v100h-100v200h200v-100h100v-200z";
249 const char* intersectionNW
="m-100-100v100h100v-100zm100 0v-100h-200v200h100v100h200v-200z";
250 const char* ringIntersection
="m50-150v100h100v-100zm0 200v100h100v-100zm100-200v-200h-300v300h200v100h-200v300h300v-200h200v-300z";
251 const char* ringIntersection2
="m-150 50v100h100v-100zm0-200v100h100v-100zm100 200v-100h100v100z"
252 "m100-200v100h100v-100zm0 200v100h100v-100zm100-200v-200h-300v200h-200v300h200v200h300v-200h200v-300z";
253 const char* ringIntersectExtraStrip
="m-150 50v100h100v-100zm0-200v100h100v-100zm100 200v-100h100v25h-50v50h50v25z"
254 "m100-200v100h100v-100zm0 200v100h100v-100zm0-75v50h150v-50z"
255 "m100-125v-200h-300v200h-200v300h200v200h300v-200h200v-300z";
256 const char* complexIntersections
="m0 0zm0 0zm0 0zm0 0v-100 100h-100 100v100-100h100zm0 0v-100 100h-100 100v100-100h100z"
257 "m100 0v-100h-100-100v100 100h100 100v-100zm0 0v-100h-100-100v100 100h100 100v-100z"
258 "m0 0v-100h-100v-100 100h-100v100h-100 100v100h100v100-100h100v-100h100z"
259 "m0-100v-100h-100-100v100h-100v100 100h100v100h100 100v-100h100v-100-100z"
260 "m100 0v-100h-200-100-100v100 200 100h100 100 200v-100-200zm600 900v200h200v-200z";
261 const char* randomIntersections
="m20-4515v393h43v-393zm34-8690v127h18v-127zm24 674v427h56v-427zm126-451v16-16z"
262 "m22 3470v260h50v-260zm55 599v167h26v-167zm-49-1831v455h136v-455z"
263 "m10 8845v19h158v-19zm54-38v25h228v-25zm156-13245v108h100v-108z"
264 "m101 14826v200h75v-200zm-205-3000v365h315v-365zm-309-1877v19h628v-19z"
265 "m549-1398v127h98v-127zm18 5351v215h111v-215zm-362-10061v152h488v-152z"
266 "m488 0v-469h-492v621h4v280h488v-432zm-378 5368v48h384v-48zm274-10182v712h119v-712z"
267 "m-424 3173v-94h-47v110h47v96h551v-112zm-105-2249v157h353v112h100v-112h205v-157z"
268 "m284 5177v203h377v-203zm337 4727v66h40v-66zm-326 6110v57h374v-57zm351-12583v39h48v-39z"
269 "m23 12583v-505h-571v576h571v-14h30v-57zm-368-2682v-8h-292v27h134v102h562v-121z"
270 "m-9-12299v320h428v-320zm364 1216v-410h-598v316h-32v110h32v96h47v280h615v-392z"
271 "m-537 11431v486h388v279h111v-279h122v-486zm112-4621v142h550v-142zm101-2719v494h450v-494z"
272 "m340 6609v33h120v-33zm-85-4349v-479h-98v479h-258v459h-165v247h189v307h384v-307h142v-105h13v-601z"
273 "m-270-3159v36h490v-36zm442 2163v7h52v-7zm-345 7158v588h403v-588zm378-1813v-93h-122v126h2v155h148v-188z"
274 "m19-5345v-259h-96v266h44v20h52v-20h10v-7zm-91-6571v-430h-428v430h-211v589h743v-589z"
275 "m101 6571v-461h-705v468h599v20h44v191h65v-218zm-89-8442v40h94v-40zm-71 10742v-43h-221v109h181v427h211v-493z"
276 "m0-4727v-189h-634v392h257v97h33v351h490v-351h29v-300zm-97 6698v-333h-315v333h-262v456h863v-456z"
277 "m-142-8556v22h429v-22zm238-56v17h208v-17zm91 7234v664h120v-664zm69 2452v-336h-567v524h419v13h201v-201z"
278 "m-42-13332v272h115v-272z";
280 verifyPoly("disjunct", disjunct
, aDisjunctRanges
);
281 verifyPoly("equal", equal
, aEqualRanges
);
282 verifyPoly("intersectionN", intersectionN
, aIntersectionN
);
283 verifyPoly("intersectionE", intersectionE
, aIntersectionE
);
284 verifyPoly("intersectionS", intersectionS
, aIntersectionS
);
285 verifyPoly("intersectionW", intersectionW
, aIntersectionW
);
286 verifyPoly("intersectionNE", intersectionNE
, aIntersectionNE
);
287 verifyPoly("intersectionSE", intersectionSE
, aIntersectionSE
);
288 verifyPoly("intersectionSW", intersectionSW
, aIntersectionSW
);
289 verifyPoly("intersectionNW", intersectionNW
, aIntersectionNW
);
290 verifyPoly("ringIntersection", ringIntersection
, aRingIntersection
);
291 verifyPoly("ringIntersection2", ringIntersection2
, aRingIntersection2
);
292 verifyPoly("ringIntersectExtraStrip", ringIntersectExtraStrip
, aRingIntersectExtraStrip
);
293 verifyPoly("complexIntersections", complexIntersections
, aComplexIntersections
);
294 verifyPoly("randomIntersections", randomIntersections
, aRandomIntersections
);
297 void dumpSvg(const char* pName
,
298 const ::basegfx::B2DPolyPolygon
& rPoly
)
300 (void)pName
; (void)rPoly
;
301 #if OSL_DEBUG_LEVEL > 2
302 fprintf(stderr
, "%s - svg:d=\"%s\"\n",
303 pName
, OUStringToOString(
304 basegfx::tools::exportToSvgD(rPoly
, , true, true, false),
305 RTL_TEXTENCODING_UTF8
).getStr() );
309 void getPolyPolygon()
311 dumpSvg("disjunct",aDisjunctRanges
.solveCrossovers());
312 dumpSvg("equal",aEqualRanges
.solveCrossovers());
313 dumpSvg("intersectionN",aIntersectionN
.solveCrossovers());
314 dumpSvg("intersectionE",aIntersectionE
.solveCrossovers());
315 dumpSvg("intersectionS",aIntersectionS
.solveCrossovers());
316 dumpSvg("intersectionW",aIntersectionW
.solveCrossovers());
317 dumpSvg("intersectionNE",aIntersectionNE
.solveCrossovers());
318 dumpSvg("intersectionSE",aIntersectionSE
.solveCrossovers());
319 dumpSvg("intersectionSW",aIntersectionSW
.solveCrossovers());
320 dumpSvg("intersectionNW",aIntersectionNW
.solveCrossovers());
321 dumpSvg("ringIntersection",aRingIntersection
.solveCrossovers());
322 dumpSvg("ringIntersection2",aRingIntersection2
.solveCrossovers());
323 dumpSvg("aRingIntersectExtraStrip",aRingIntersectExtraStrip
.solveCrossovers());
324 dumpSvg("complexIntersections",aComplexIntersections
.solveCrossovers());
325 dumpSvg("randomIntersections",aRandomIntersections
.solveCrossovers());
327 CPPUNIT_ASSERT_MESSAGE("getPolyPolygon", true );
330 void validatePoly( const char* pName
, const B2DPolyRange
& rRange
)
332 B2DPolyPolygon genericClip
;
333 const sal_uInt32 nCount
=rRange
.count();
334 for( sal_uInt32 i
=0; i
<nCount
; ++i
)
336 B2DPolygon aRect
=tools::createPolygonFromRect(std::get
<0>(rRange
.getElement(i
)));
337 if( std::get
<1>(rRange
.getElement(i
)) == B2VectorOrientation::Negative
)
340 genericClip
.append(aRect
);
343 #if OSL_DEBUG_LEVEL > 2
344 fprintf(stderr
, "%s input - svg:d=\"%s\"\n",
345 pName
, OUStringToOString(
346 basegfx::tools::exportToSvgD(
347 genericClip
, , true, true, false),
348 RTL_TEXTENCODING_UTF8
).getStr() );
351 const B2DPolyPolygon boxClipResult
=rRange
.solveCrossovers();
352 const OUString
boxClipSvg(
353 basegfx::tools::exportToSvgD(
354 normalizePoly(boxClipResult
), true, true, false));
355 #if OSL_DEBUG_LEVEL > 2
356 fprintf(stderr
, "%s boxclipper - svg:d=\"%s\"\n",
357 pName
, OUStringToOString(
359 RTL_TEXTENCODING_UTF8
).getStr() );
362 genericClip
= tools::solveCrossovers(genericClip
);
363 const OUString
genericClipSvg(
364 basegfx::tools::exportToSvgD(
365 normalizePoly(genericClip
), true, true, false));
366 #if OSL_DEBUG_LEVEL > 2
367 fprintf(stderr
, "%s genclipper - svg:d=\"%s\"\n",
368 pName
, OUStringToOString(
370 RTL_TEXTENCODING_UTF8
).getStr() );
373 CPPUNIT_ASSERT_MESSAGE(pName
,
374 genericClipSvg
== boxClipSvg
);
379 validatePoly("disjunct", aDisjunctRanges
);
380 validatePoly("equal", aEqualRanges
);
381 validatePoly("intersectionN", aIntersectionN
);
382 validatePoly("intersectionE", aIntersectionE
);
383 validatePoly("intersectionS", aIntersectionS
);
384 validatePoly("intersectionW", aIntersectionW
);
385 validatePoly("intersectionNE", aIntersectionNE
);
386 validatePoly("intersectionSE", aIntersectionSE
);
387 validatePoly("intersectionSW", aIntersectionSW
);
388 validatePoly("intersectionNW", aIntersectionNW
);
389 // subtle differences on Solaris Intel, comparison not smart enough
390 // (due to floating point inaccuracies)
391 //validatePoly("ringIntersection", aRingIntersection);
392 //validatePoly("ringIntersection2", aRingIntersection2);
393 //validatePoly("ringIntersectExtraStrip", aRingIntersectExtraStrip);
394 // generic clipper buggy here, likely
395 //validatePoly("complexIntersections", aComplexIntersections);
396 //validatePoly("randomIntersections", aRandomIntersections);
399 // Change the following lines only, if you add, remove or rename
400 // member functions of the current class,
401 // because these macros are need by auto register mechanism.
403 CPPUNIT_TEST_SUITE(boxclipper
);
404 CPPUNIT_TEST(validatePoly
);
405 CPPUNIT_TEST(verifyPoly
);
406 CPPUNIT_TEST(getPolyPolygon
);
407 CPPUNIT_TEST_SUITE_END();
410 CPPUNIT_TEST_SUITE_REGISTRATION(basegfx2d::boxclipper
);
411 } // namespace basegfx2d
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */