android: Update app-specific/MIME type icons
[LibreOffice.git] / basegfx / test / boxclipper.cxx
blobb65f25bae425fb6937ad68b6dee5622bfe334727
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/range/b2dpolyrange.hxx>
26 #include <basegfx/polygon/b2dpolygon.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/polygon/b2dpolypolygontools.hxx>
29 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
30 #include <basegfx/polygon/b2dpolypolygon.hxx>
31 #include <comphelper/random.hxx>
32 #include <rtl/math.hxx>
34 #include "boxclipper.hxx"
36 using namespace ::basegfx;
38 namespace basegfx2d
40 /// Gets a random ordinal [0,n)
41 double getRandomOrdinal( const std::size_t n )
43 // use this one when displaying polygons in OOo, which still sucks
44 // great rocks when trying to import non-integer svg:d attributes
45 return comphelper::rng::uniform_size_distribution(0, n-1);
48 static bool compare(const B2DPoint& left, const B2DPoint& right)
50 return left.getX()<right.getX()
51 || (rtl::math::approxEqual(left.getX(),right.getX()) && left.getY()<right.getY());
54 class boxclipper : public CppUnit::TestFixture
56 private:
57 B2DPolyRange aDisjunctRanges;
58 B2DPolyRange aEqualRanges;
59 B2DPolyRange aIntersectionN;
60 B2DPolyRange aIntersectionE;
61 B2DPolyRange aIntersectionS;
62 B2DPolyRange aIntersectionW;
63 B2DPolyRange aIntersectionNE;
64 B2DPolyRange aIntersectionSE;
65 B2DPolyRange aIntersectionSW;
66 B2DPolyRange aIntersectionNW;
67 B2DPolyRange aRingIntersection;
68 B2DPolyRange aRingIntersection2;
69 B2DPolyRange aRingIntersectExtraStrip;
70 B2DPolyRange aComplexIntersections;
71 B2DPolyRange aRandomIntersections;
73 public:
74 // initialise your test code values here.
75 void setUp() override
77 B2DRange aCenter(100, 100, -100, -100);
78 B2DRange aOffside(800, 800, 1000, 1000);
79 B2DRange aNorth(100, 0, -100, -200);
80 B2DRange aSouth(100, 200, -100, 0);
81 B2DRange aEast(0, 100, 200, -100);
82 B2DRange aWest(-200, 100, 0, -100);
83 B2DRange aNorthEast(0, 0, 200, -200);
84 B2DRange aSouthEast(0, 0, 200, 200);
85 B2DRange aSouthWest(0, 0, -200, 200);
86 B2DRange aNorthWest(0, 0, -200, -200);
88 B2DRange aNorth2(-150, 50, 150, 350);
89 B2DRange aSouth2(-150, -50, 150, -350);
90 B2DRange aEast2 (50, -150, 350, 150);
91 B2DRange aWest2 (-50, -150,-350, 150);
93 aDisjunctRanges.appendElement( aCenter, B2VectorOrientation::Negative );
94 aDisjunctRanges.appendElement( aOffside, B2VectorOrientation::Negative );
96 aEqualRanges.appendElement( aCenter, B2VectorOrientation::Negative );
97 aEqualRanges.appendElement( aCenter, B2VectorOrientation::Negative );
99 aIntersectionN.appendElement( aCenter, B2VectorOrientation::Negative );
100 aIntersectionN.appendElement( aNorth, B2VectorOrientation::Negative );
102 aIntersectionE.appendElement( aCenter, B2VectorOrientation::Negative );
103 aIntersectionE.appendElement( aEast, B2VectorOrientation::Negative );
105 aIntersectionS.appendElement( aCenter, B2VectorOrientation::Negative );
106 aIntersectionS.appendElement( aSouth, B2VectorOrientation::Negative );
108 aIntersectionW.appendElement( aCenter, B2VectorOrientation::Negative );
109 aIntersectionW.appendElement( aWest, B2VectorOrientation::Negative );
111 aIntersectionNE.appendElement( aCenter, B2VectorOrientation::Negative );
112 aIntersectionNE.appendElement( aNorthEast, B2VectorOrientation::Negative );
114 aIntersectionSE.appendElement( aCenter, B2VectorOrientation::Negative );
115 aIntersectionSE.appendElement( aSouthEast, B2VectorOrientation::Negative );
117 aIntersectionSW.appendElement( aCenter, B2VectorOrientation::Negative );
118 aIntersectionSW.appendElement( aSouthWest, B2VectorOrientation::Negative );
120 aIntersectionNW.appendElement( aCenter, B2VectorOrientation::Negative );
121 aIntersectionNW.appendElement( aNorthWest, B2VectorOrientation::Negative );
123 aRingIntersection.appendElement( aNorth2, B2VectorOrientation::Negative );
124 aRingIntersection.appendElement( aEast2, B2VectorOrientation::Negative );
125 aRingIntersection.appendElement( aSouth2, B2VectorOrientation::Negative );
127 aRingIntersection2 = aRingIntersection;
128 aRingIntersection2.appendElement( aWest2, B2VectorOrientation::Negative );
130 aRingIntersectExtraStrip = aRingIntersection2;
131 aRingIntersectExtraStrip.appendElement( B2DRange(0, -25, 200, 25),
132 B2VectorOrientation::Negative );
134 aComplexIntersections.appendElement( aCenter, B2VectorOrientation::Negative );
135 aComplexIntersections.appendElement( aOffside, B2VectorOrientation::Negative );
136 aComplexIntersections.appendElement( aCenter, B2VectorOrientation::Negative );
137 aComplexIntersections.appendElement( aNorth, B2VectorOrientation::Negative );
138 aComplexIntersections.appendElement( aEast, B2VectorOrientation::Negative );
139 aComplexIntersections.appendElement( aSouth, B2VectorOrientation::Negative );
140 aComplexIntersections.appendElement( aWest, B2VectorOrientation::Negative );
141 aComplexIntersections.appendElement( aNorthEast, B2VectorOrientation::Negative );
142 aComplexIntersections.appendElement( aSouthEast, B2VectorOrientation::Negative );
143 aComplexIntersections.appendElement( aSouthWest, B2VectorOrientation::Negative );
144 aComplexIntersections.appendElement( aNorthWest, B2VectorOrientation::Negative );
146 #ifdef GENERATE_RANDOM
147 for( int i=0; i<800; ++i )
149 B2DRange aRandomRange(
150 getRandomOrdinal( 1000 ),
151 getRandomOrdinal( 1000 ),
152 getRandomOrdinal( 1000 ),
153 getRandomOrdinal( 1000 ) );
155 aRandomIntersections.appendElement( aRandomRange, B2VectorOrientation::Negative );
157 #else
158 static constexpr OUStringLiteral randomSvg=u"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";
159 B2DPolyPolygon randomPoly;
160 CPPUNIT_ASSERT(utils::importFromSvgD(randomPoly, randomSvg, false, nullptr));
161 for (auto const& aPolygon : std::as_const(randomPoly))
162 aRandomIntersections.appendElement(aPolygon.getB2DRange(), B2VectorOrientation::Negative);
163 #endif
166 B2DPolyPolygon normalizePoly( const B2DPolyPolygon& rPoly ) const
168 B2DPolyPolygon aRes;
169 for( sal_uInt32 i=0; i<rPoly.count(); ++i )
171 B2DPolygon aTmp=rPoly.getB2DPolygon(i);
172 if( utils::getOrientation(aTmp) == B2VectorOrientation::Negative )
173 aTmp.flip();
175 aTmp=utils::removeNeutralPoints(aTmp);
176 std::vector<B2DPoint> aTmp2(aTmp.count());
177 for(sal_uInt32 j=0; j<aTmp.count(); ++j)
178 aTmp2[j] = aTmp.getB2DPoint(j);
180 std::vector<B2DPoint>::iterator pSmallest=aTmp2.end();
181 for(std::vector<B2DPoint>::iterator pCurr=aTmp2.begin(); pCurr!=aTmp2.end(); ++pCurr)
183 if( pSmallest == aTmp2.end() || compare(*pCurr, *pSmallest) )
185 pSmallest=pCurr;
189 if( pSmallest != aTmp2.end() )
190 std::rotate(aTmp2.begin(),pSmallest,aTmp2.end());
192 aTmp.clear();
193 for(const auto& rCurr : aTmp2)
194 aTmp.append(rCurr);
196 aRes.append(aTmp);
199 // boxclipper & generic clipper disagree slightly on area-less
200 // polygons (one or two points only)
201 aRes = utils::stripNeutralPolygons(aRes);
203 // now, sort all polygons with increasing 0th point
204 std::sort(aRes.begin(),
205 aRes.end(),
206 [](const B2DPolygon& aPolygon1, const B2DPolygon& aPolygon2) {
207 return compare(aPolygon1.getB2DPoint(0),
208 aPolygon2.getB2DPoint(0)); } );
210 return aRes;
213 void verifyPoly(const char* sName, const char* sSvg, const B2DPolyRange& toTest) const
215 B2DPolyPolygon aTmp1;
216 CPPUNIT_ASSERT_MESSAGE(sName,
217 utils::importFromSvgD(
218 aTmp1, OUString::createFromAscii(sSvg), false, nullptr));
220 const OUString aSvg=
221 utils::exportToSvgD(toTest.solveCrossovers(), true, true, false);
222 B2DPolyPolygon aTmp2;
223 CPPUNIT_ASSERT_MESSAGE(sName,
224 utils::importFromSvgD(
225 aTmp2, aSvg, false, nullptr));
227 CPPUNIT_ASSERT_EQUAL_MESSAGE(
228 sName,
229 normalizePoly(aTmp1), normalizePoly(aTmp2));
232 void verifyPoly()
234 const char* const disjunct="m-100-100v200h200v-200zm900 900v200h200v-200z";
235 const char* const equal="m-100-100v200h200v-200zm200 0h-200v200h200v-200z";
236 const char* const intersectionN="m-100-100v100h200v-100zm200 0v-100h-200v100 200h200v-200z";
237 const char* const intersectionE="m0-100v200h100v-200zm0 0h-100v200h100 200v-200z";
238 const char* const intersectionS="m-100 0v100h200v-100zm0-100v200 100h200v-100-200z";
239 const char* const intersectionW="m-100-100v200h100v-200zm0 0h-100v200h100 200v-200z";
240 const char* const intersectionNE="m0-100v100h100v-100zm0-100v100h-100v200h200v-100h100v-200z";
241 const char* const intersectionSE="m0 0v100h100v-100zm100 0v-100h-200v200h100v100h200v-200z";
242 const char* const intersectionSW="m-100 0v100h100v-100zm0-100v100h-100v200h200v-100h100v-200z";
243 const char* const intersectionNW="m-100-100v100h100v-100zm100 0v-100h-200v200h100v100h200v-200z";
244 const char* const ringIntersection="m50-150v100h100v-100zm0 200v100h100v-100zm100-200v-200h-300v300h200v100h-200v300h300v-200h200v-300z";
245 const char* const ringIntersection2="m-150 50v100h100v-100zm0-200v100h100v-100zm100 200v-100h100v100z"
246 "m100-200v100h100v-100zm0 200v100h100v-100zm100-200v-200h-300v200h-200v300h200v200h300v-200h200v-300z";
247 const char* const ringIntersectExtraStrip="m-150 50v100h100v-100zm0-200v100h100v-100zm100 200v-100h100v25h-50v50h50v25z"
248 "m100-200v100h100v-100zm0 200v100h100v-100zm0-75v50h150v-50z"
249 "m100-125v-200h-300v200h-200v300h200v200h300v-200h200v-300z";
250 const char* const complexIntersections="m0 0zm0 0zm0 0zm0 0v-100 100h-100 100v100-100h100zm0 0v-100 100h-100 100v100-100h100z"
251 "m100 0v-100h-100-100v100 100h100 100v-100zm0 0v-100h-100-100v100 100h100 100v-100z"
252 "m0 0v-100h-100v-100 100h-100v100h-100 100v100h100v100-100h100v-100h100z"
253 "m0-100v-100h-100-100v100h-100v100 100h100v100h100 100v-100h100v-100-100z"
254 "m100 0v-100h-200-100-100v100 200 100h100 100 200v-100-200zm600 900v200h200v-200z";
255 const char* const randomIntersections="m20-4515v393h43v-393zm34-8690v127h18v-127zm24 674v427h56v-427zm126-451v16-16z"
256 "m22 3470v260h50v-260zm55 599v167h26v-167zm-49-1831v455h136v-455z"
257 "m10 8845v19h158v-19zm54-38v25h228v-25zm156-13245v108h100v-108z"
258 "m101 14826v200h75v-200zm-205-3000v365h315v-365zm-309-1877v19h628v-19z"
259 "m549-1398v127h98v-127zm18 5351v215h111v-215zm-362-10061v152h488v-152z"
260 "m488 0v-469h-492v621h4v280h488v-432zm-378 5368v48h384v-48zm274-10182v712h119v-712z"
261 "m-424 3173v-94h-47v110h47v96h551v-112zm-105-2249v157h353v112h100v-112h205v-157z"
262 "m284 5177v203h377v-203zm337 4727v66h40v-66zm-326 6110v57h374v-57zm351-12583v39h48v-39z"
263 "m23 12583v-505h-571v576h571v-14h30v-57zm-368-2682v-8h-292v27h134v102h562v-121z"
264 "m-9-12299v320h428v-320zm364 1216v-410h-598v316h-32v110h32v96h47v280h615v-392z"
265 "m-537 11431v486h388v279h111v-279h122v-486zm112-4621v142h550v-142zm101-2719v494h450v-494z"
266 "m340 6609v33h120v-33zm-85-4349v-479h-98v479h-258v459h-165v247h189v307h384v-307h142v-105h13v-601z"
267 "m-270-3159v36h490v-36zm442 2163v7h52v-7zm-345 7158v588h403v-588zm378-1813v-93h-122v126h2v155h148v-188z"
268 "m19-5345v-259h-96v266h44v20h52v-20h10v-7zm-91-6571v-430h-428v430h-211v589h743v-589z"
269 "m101 6571v-461h-705v468h599v20h44v191h65v-218zm-89-8442v40h94v-40zm-71 10742v-43h-221v109h181v427h211v-493z"
270 "m0-4727v-189h-634v392h257v97h33v351h490v-351h29v-300zm-97 6698v-333h-315v333h-262v456h863v-456z"
271 "m-142-8556v22h429v-22zm238-56v17h208v-17zm91 7234v664h120v-664zm69 2452v-336h-567v524h419v13h201v-201z"
272 "m-42-13332v272h115v-272z";
274 verifyPoly("disjunct", disjunct, aDisjunctRanges);
275 verifyPoly("equal", equal, aEqualRanges);
276 verifyPoly("intersectionN", intersectionN, aIntersectionN);
277 verifyPoly("intersectionE", intersectionE, aIntersectionE);
278 verifyPoly("intersectionS", intersectionS, aIntersectionS);
279 verifyPoly("intersectionW", intersectionW, aIntersectionW);
280 verifyPoly("intersectionNE", intersectionNE, aIntersectionNE);
281 verifyPoly("intersectionSE", intersectionSE, aIntersectionSE);
282 verifyPoly("intersectionSW", intersectionSW, aIntersectionSW);
283 verifyPoly("intersectionNW", intersectionNW, aIntersectionNW);
284 verifyPoly("ringIntersection", ringIntersection, aRingIntersection);
285 verifyPoly("ringIntersection2", ringIntersection2, aRingIntersection2);
286 verifyPoly("ringIntersectExtraStrip", ringIntersectExtraStrip, aRingIntersectExtraStrip);
287 verifyPoly("complexIntersections", complexIntersections, aComplexIntersections);
288 verifyPoly("randomIntersections", randomIntersections, aRandomIntersections);
291 void dumpSvg(const char* pName,
292 const ::basegfx::B2DPolyPolygon& rPoly) const
294 (void)pName; (void)rPoly;
295 #if OSL_DEBUG_LEVEL > 2
296 fprintf(stderr, "%s - svg:d=\"%s\"\n",
297 pName, OUStringToOString(
298 basegfx::utils::exportToSvgD(rPoly, , true, true, false),
299 RTL_TEXTENCODING_UTF8).getStr() );
300 #endif
303 void getPolyPolygon()
305 dumpSvg("disjunct",aDisjunctRanges.solveCrossovers());
306 dumpSvg("equal",aEqualRanges.solveCrossovers());
307 dumpSvg("intersectionN",aIntersectionN.solveCrossovers());
308 dumpSvg("intersectionE",aIntersectionE.solveCrossovers());
309 dumpSvg("intersectionS",aIntersectionS.solveCrossovers());
310 dumpSvg("intersectionW",aIntersectionW.solveCrossovers());
311 dumpSvg("intersectionNE",aIntersectionNE.solveCrossovers());
312 dumpSvg("intersectionSE",aIntersectionSE.solveCrossovers());
313 dumpSvg("intersectionSW",aIntersectionSW.solveCrossovers());
314 dumpSvg("intersectionNW",aIntersectionNW.solveCrossovers());
315 dumpSvg("ringIntersection",aRingIntersection.solveCrossovers());
316 dumpSvg("ringIntersection2",aRingIntersection2.solveCrossovers());
317 dumpSvg("aRingIntersectExtraStrip",aRingIntersectExtraStrip.solveCrossovers());
318 dumpSvg("complexIntersections",aComplexIntersections.solveCrossovers());
319 dumpSvg("randomIntersections",aRandomIntersections.solveCrossovers());
321 CPPUNIT_ASSERT_MESSAGE("getPolyPolygon", true );
324 void validatePoly( const char* pName, const B2DPolyRange& rRange ) const
326 B2DPolyPolygon genericClip;
327 const sal_uInt32 nCount=rRange.count();
328 for( sal_uInt32 i=0; i<nCount; ++i )
330 B2DPolygon aRect=utils::createPolygonFromRect(std::get<0>(rRange.getElement(i)));
331 if( std::get<1>(rRange.getElement(i)) == B2VectorOrientation::Negative )
332 aRect.flip();
334 genericClip.append(aRect);
337 #if OSL_DEBUG_LEVEL > 2
338 fprintf(stderr, "%s input - svg:d=\"%s\"\n",
339 pName, OUStringToOString(
340 basegfx::utils::exportToSvgD(
341 genericClip, , true, true, false),
342 RTL_TEXTENCODING_UTF8).getStr() );
343 #endif
345 const B2DPolyPolygon boxClipResult=rRange.solveCrossovers();
346 const OUString boxClipSvg(
347 basegfx::utils::exportToSvgD(
348 normalizePoly(boxClipResult), true, true, false));
349 #if OSL_DEBUG_LEVEL > 2
350 fprintf(stderr, "%s boxclipper - svg:d=\"%s\"\n",
351 pName, OUStringToOString(
352 boxClipSvg,
353 RTL_TEXTENCODING_UTF8).getStr() );
354 #endif
356 genericClip = utils::solveCrossovers(genericClip);
357 const OUString genericClipSvg(
358 basegfx::utils::exportToSvgD(
359 normalizePoly(genericClip), true, true, false));
360 #if OSL_DEBUG_LEVEL > 2
361 fprintf(stderr, "%s genclipper - svg:d=\"%s\"\n",
362 pName, OUStringToOString(
363 genericClipSvg,
364 RTL_TEXTENCODING_UTF8).getStr() );
365 #endif
367 CPPUNIT_ASSERT_EQUAL_MESSAGE(pName,
368 boxClipSvg, genericClipSvg);
371 void validatePoly()
373 validatePoly("disjunct", aDisjunctRanges);
374 validatePoly("equal", aEqualRanges);
375 validatePoly("intersectionN", aIntersectionN);
376 validatePoly("intersectionE", aIntersectionE);
377 validatePoly("intersectionS", aIntersectionS);
378 validatePoly("intersectionW", aIntersectionW);
379 validatePoly("intersectionNE", aIntersectionNE);
380 validatePoly("intersectionSE", aIntersectionSE);
381 validatePoly("intersectionSW", aIntersectionSW);
382 validatePoly("intersectionNW", aIntersectionNW);
383 // subtle differences on Solaris Intel, comparison not smart enough
384 // (due to floating point inaccuracies)
385 //validatePoly("ringIntersection", aRingIntersection);
386 //validatePoly("ringIntersection2", aRingIntersection2);
387 //validatePoly("ringIntersectExtraStrip", aRingIntersectExtraStrip);
388 // generic clipper buggy here, likely
389 //validatePoly("complexIntersections", aComplexIntersections);
390 //validatePoly("randomIntersections", aRandomIntersections);
393 // Change the following lines only, if you add, remove or rename
394 // member functions of the current class,
395 // because these macros are need by auto register mechanism.
397 CPPUNIT_TEST_SUITE(boxclipper);
398 CPPUNIT_TEST(validatePoly);
399 CPPUNIT_TEST(verifyPoly);
400 CPPUNIT_TEST(getPolyPolygon);
401 CPPUNIT_TEST_SUITE_END();
404 CPPUNIT_TEST_SUITE_REGISTRATION(basegfx2d::boxclipper);
405 } // namespace basegfx2d
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */