Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / basegfx / source / polygon / b2dpolypolygontools.cxx
bloba02373616a507c6bcc2c0c6bee7a4a6f431a4b55
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 <basegfx/polygon/b2dpolypolygontools.hxx>
21 #include <osl/diagnose.h>
22 #include <basegfx/polygon/b2dpolypolygon.hxx>
23 #include <basegfx/polygon/b2dpolygon.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <basegfx/numeric/ftools.hxx>
26 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
27 #include <numeric>
29 namespace basegfx
31 namespace utils
33 B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate)
35 B2DPolyPolygon aRetval(rCandidate);
36 const sal_uInt32 nCount(aRetval.count());
38 for(sal_uInt32 a(0); a < nCount; a++)
40 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
41 const B2VectorOrientation aOrientation(utils::getOrientation(aCandidate));
42 sal_uInt32 nDepth(0);
44 for(sal_uInt32 b(0); b < nCount; b++)
46 if(b != a)
48 const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
50 if(utils::isInside(aCompare, aCandidate, true))
52 nDepth++;
57 const bool bShallBeHole((nDepth & 0x00000001) == 1);
58 const bool bIsHole(aOrientation == B2VectorOrientation::Negative);
60 if(bShallBeHole != bIsHole && aOrientation != B2VectorOrientation::Neutral)
62 B2DPolygon aFlipped(aCandidate);
63 aFlipped.flip();
64 aRetval.setB2DPolygon(a, aFlipped);
68 return aRetval;
71 B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon& rCandidate)
73 const sal_uInt32 nCount(rCandidate.count());
75 if(nCount > 1)
77 for(sal_uInt32 a(0); a < nCount; a++)
79 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
80 sal_uInt32 nDepth(0);
82 for(sal_uInt32 b(0); b < nCount; b++)
84 if(b != a)
86 const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
88 if(utils::isInside(aCompare, aCandidate, true))
90 nDepth++;
95 if(!nDepth)
97 B2DPolyPolygon aRetval(rCandidate);
99 if(a != 0)
101 // exchange polygon a and polygon 0
102 aRetval.setB2DPolygon(0, aCandidate);
103 aRetval.setB2DPolygon(a, rCandidate.getB2DPolygon(0));
106 // exit
107 return aRetval;
112 return rCandidate;
115 B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound)
117 if(rCandidate.areControlPointsUsed())
119 const sal_uInt32 nPolygonCount(rCandidate.count());
120 B2DPolyPolygon aRetval;
122 for(sal_uInt32 a(0); a < nPolygonCount; a++)
124 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
126 if(aCandidate.areControlPointsUsed())
128 aRetval.append(utils::adaptiveSubdivideByDistance(aCandidate, fDistanceBound));
130 else
132 aRetval.append(aCandidate);
136 return aRetval;
138 else
140 return rCandidate;
144 B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound)
146 if(rCandidate.areControlPointsUsed())
148 const sal_uInt32 nPolygonCount(rCandidate.count());
149 B2DPolyPolygon aRetval;
151 for(sal_uInt32 a(0); a < nPolygonCount; a++)
153 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
155 if(aCandidate.areControlPointsUsed())
157 aRetval.append(utils::adaptiveSubdivideByAngle(aCandidate, fAngleBound));
159 else
161 aRetval.append(aCandidate);
165 return aRetval;
167 else
169 return rCandidate;
173 bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
175 const sal_uInt32 nPolygonCount(rCandidate.count());
177 if(nPolygonCount == 1)
179 return isInside(rCandidate.getB2DPolygon(0), rPoint, bWithBorder);
181 else
183 sal_Int32 nInsideCount(0);
185 for(sal_uInt32 a(0); a < nPolygonCount; a++)
187 const B2DPolygon aPolygon(rCandidate.getB2DPolygon(a));
188 const bool bInside(isInside(aPolygon, rPoint, bWithBorder));
190 if(bInside)
192 nInsideCount++;
196 return (nInsideCount % 2);
200 B2DRange getRange(const B2DPolyPolygon& rCandidate)
202 B2DRange aRetval;
203 const sal_uInt32 nPolygonCount(rCandidate.count());
205 for(sal_uInt32 a(0); a < nPolygonCount; a++)
207 B2DPolygon aCandidate = rCandidate.getB2DPolygon(a);
208 aRetval.expand(utils::getRange(aCandidate));
211 return aRetval;
214 double getSignedArea(const B2DPolyPolygon& rCandidate)
216 double fRetval(0.0);
217 const sal_uInt32 nPolygonCount(rCandidate.count());
219 for(sal_uInt32 a(0); a < nPolygonCount; a++)
221 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
223 fRetval += utils::getSignedArea(aCandidate);
226 return fRetval;
229 double getArea(const B2DPolyPolygon& rCandidate)
231 return fabs(getSignedArea(rCandidate));
234 void applyLineDashing(const B2DPolyPolygon& rCandidate, const std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, double fFullDashDotLen)
236 if(fFullDashDotLen == 0.0 && rDotDashArray.size())
238 // calculate fFullDashDotLen from rDotDashArray
239 fFullDashDotLen = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
242 if(rCandidate.count() && fFullDashDotLen > 0.0)
244 B2DPolyPolygon aLineTarget;
246 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
248 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
250 applyLineDashing(
251 aCandidate,
252 rDotDashArray,
253 pLineTarget ? &aLineTarget : nullptr,
254 nullptr,
255 fFullDashDotLen);
257 if(pLineTarget)
259 pLineTarget->append(aLineTarget);
265 bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
267 const sal_uInt32 nPolygonCount(rCandidate.count());
269 for(sal_uInt32 a(0); a < nPolygonCount; a++)
271 B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
273 if(isInEpsilonRange(aCandidate, rTestPosition, fDistance))
275 return true;
279 return false;
282 B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate)
284 const sal_uInt32 nPolygonCount(rCandidate.count());
285 B3DPolyPolygon aRetval;
287 for(sal_uInt32 a(0); a < nPolygonCount; a++)
289 B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
291 aRetval.append(createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate));
294 return aRetval;
297 B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat)
299 const sal_uInt32 nPolygonCount(rCandidate.count());
300 B2DPolyPolygon aRetval;
302 for(sal_uInt32 a(0); a < nPolygonCount; a++)
304 B3DPolygon aCandidate(rCandidate.getB3DPolygon(a));
306 aRetval.append(createB2DPolygonFromB3DPolygon(aCandidate, rMat));
309 return aRetval;
312 double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut)
314 double fRetval(DBL_MAX);
315 const double fZero(0.0);
316 const sal_uInt32 nPolygonCount(rCandidate.count());
318 for(sal_uInt32 a(0); a < nPolygonCount; a++)
320 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
321 sal_uInt32 nNewEdgeIndex;
322 double fNewCut(0.0);
323 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate, rTestPoint, nNewEdgeIndex, fNewCut));
325 if(fRetval == DBL_MAX || fNewDistance < fRetval)
327 fRetval = fNewDistance;
328 rPolygonIndex = a;
329 rEdgeIndex = nNewEdgeIndex;
330 rCut = fNewCut;
332 if(fTools::equal(fRetval, fZero))
334 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
335 fRetval = 0.0;
336 break;
341 return fRetval;
344 B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
346 const sal_uInt32 nPolygonCount(rCandidate.count());
347 B2DPolyPolygon aRetval;
349 for(sal_uInt32 a(0); a < nPolygonCount; a++)
351 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
353 aRetval.append(distort(aCandidate, rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
356 return aRetval;
359 B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate)
361 const sal_uInt32 nPolygonCount(rCandidate.count());
362 B2DPolyPolygon aRetval;
364 for(sal_uInt32 a(0); a < nPolygonCount; a++)
366 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
368 aRetval.append(expandToCurve(aCandidate));
371 return aRetval;
374 B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue)
376 if(fValue != 0.0)
378 B2DPolyPolygon aRetval;
380 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
382 aRetval.append(growInNormalDirection(rCandidate.getB2DPolygon(a), fValue));
385 return aRetval;
387 else
389 return rCandidate;
393 B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments)
395 B2DPolyPolygon aRetval;
397 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
399 aRetval.append(reSegmentPolygon(rCandidate.getB2DPolygon(a), nSegments));
402 return aRetval;
405 B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t)
407 OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolyPolygon interpolate: Different geometry (!)");
408 B2DPolyPolygon aRetval;
410 for(sal_uInt32 a(0); a < rOld1.count(); a++)
412 aRetval.append(interpolate(rOld1.getB2DPolygon(a), rOld2.getB2DPolygon(a), t));
415 return aRetval;
418 bool isRectangle( const B2DPolyPolygon& rPoly )
420 // exclude some cheap cases first
421 if( rPoly.count() != 1 )
422 return false;
424 return isRectangle( rPoly.getB2DPolygon(0) );
427 // #i76891#
428 B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate)
430 if(rCandidate.areControlPointsUsed())
432 B2DPolyPolygon aRetval;
434 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
436 aRetval.append(simplifyCurveSegments(rCandidate.getB2DPolygon(a)));
439 return aRetval;
441 else
443 return rCandidate;
447 B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate)
449 B2DPolyPolygon aRetval;
451 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
453 aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rCandidate.getB2DPolygon(a)));
456 return aRetval;
459 B2DPolyPolygon createSevenSegmentPolyPolygon(sal_Char nNumber, bool bLitSegments)
461 // config here
462 // {
463 const double fTotalSize=1.0;
464 const double fPosMiddleSegment=0.6;
465 const double fSegmentEndChopHoriz=0.08;
466 const double fSegmentEndChopVert =0.04;
467 // }
468 // config here
470 const double fLeft=0.0;
471 const double fRight=fTotalSize;
472 const double fTop=0.0;
473 const double fMiddle=fPosMiddleSegment;
474 const double fBottom=fTotalSize;
476 // from 0 to 5: pair of segment corner coordinates
478 // segment corner indices are these:
480 // 0 - 1
481 // | |
482 // 2 - 3
483 // | |
484 // 4 - 5
486 static const double corners[] =
488 fLeft, fTop,
489 fRight, fTop,
490 fLeft, fMiddle,
491 fRight, fMiddle,
492 fLeft, fBottom,
493 fRight, fBottom
496 // from 0 to 9: which segments are 'lit' for this number?
498 // array denotes graph edges to traverse, with -1 means
499 // stop (the vertices are the corner indices from above):
500 // 0
501 // -
502 // 1 | | 2
503 // - 3
504 // 4 | | 5
505 // -
506 // 6
508 static const int numbers[] =
510 1, 1, 1, 0, 1, 1, 1, // 0
511 0, 0, 1, 0, 0, 1, 0, // 1
512 1, 0, 1, 1, 1, 0, 1, // 2
513 1, 0, 1, 1, 0, 1, 1, // 3
514 0, 1, 1, 1, 0, 1, 0, // 4
515 1, 1, 0, 1, 0, 1, 1, // 5
516 1, 1, 0, 1, 1, 1, 1, // 6
517 1, 0, 1, 0, 0, 1, 0, // 1
518 1, 1, 1, 1, 1, 1, 1, // 8
519 1, 1, 1, 1, 0, 1, 1, // 9
520 0, 0, 0, 1, 0, 0, 0, // '-'
521 1, 1, 0, 1, 1, 0, 1, // 'E'
524 // maps segment index to two corner ids:
525 static const int index2corner[] =
527 0, 2, // 0
528 0, 4, // 1
529 2, 6, // 2
530 4, 6, // 3
531 4, 8, // 4
532 6, 10, // 5
533 8, 10, // 6
536 B2DPolyPolygon aRes;
537 if( nNumber == '-' )
539 nNumber = 10;
541 else if( nNumber == 'E' )
543 nNumber = 11;
545 else if( nNumber == '.' )
547 if( bLitSegments )
548 aRes.append(createPolygonFromCircle(B2DPoint(fTotalSize/2, fTotalSize),
549 fSegmentEndChopHoriz));
550 return aRes;
552 else
554 nNumber=clamp<sal_uInt32>(nNumber,'0','9') - '0';
557 B2DPolygon aCurrSegment;
558 const size_t sliceSize=SAL_N_ELEMENTS(numbers)/12;
559 const int* pCurrSegment=numbers + nNumber*sliceSize;
560 for( size_t i=0; i<sliceSize; i++, pCurrSegment++)
562 if( !(*pCurrSegment ^ int(bLitSegments)) )
564 const size_t j=2*i;
565 aCurrSegment.clear();
566 B2DPoint start(corners[index2corner[j]],
567 corners[index2corner[j]+1] );
568 B2DPoint end (corners[index2corner[j+1]],
569 corners[index2corner[j+1]+1]);
571 if( rtl::math::approxEqual(start.getX(), end.getX()) )
573 start.setY(start.getY()+fSegmentEndChopVert);
574 end.setY(end.getY()-fSegmentEndChopVert);
576 else
578 start.setX(start.getX()+fSegmentEndChopHoriz);
579 end.setX(end.getX()-fSegmentEndChopHoriz);
582 aCurrSegment.append(start);
583 aCurrSegment.append(end);
585 aRes.append(aCurrSegment);
588 return aRes;
591 // converters for css::drawing::PointSequence
593 B2DPolyPolygon UnoPointSequenceSequenceToB2DPolyPolygon(
594 const css::drawing::PointSequenceSequence& rPointSequenceSequenceSource)
596 B2DPolyPolygon aRetval;
597 const css::drawing::PointSequence* pPointSequence = rPointSequenceSequenceSource.getConstArray();
598 const css::drawing::PointSequence* pPointSeqEnd = pPointSequence + rPointSequenceSequenceSource.getLength();
600 for(;pPointSequence != pPointSeqEnd; pPointSequence++)
602 const B2DPolygon aNewPolygon = UnoPointSequenceToB2DPolygon(*pPointSequence);
603 aRetval.append(aNewPolygon);
606 return aRetval;
609 void B2DPolyPolygonToUnoPointSequenceSequence(
610 const B2DPolyPolygon& rPolyPolygon,
611 css::drawing::PointSequenceSequence& rPointSequenceSequenceRetval)
613 const sal_uInt32 nCount(rPolyPolygon.count());
615 if(nCount)
617 rPointSequenceSequenceRetval.realloc(nCount);
618 css::drawing::PointSequence* pPointSequence = rPointSequenceSequenceRetval.getArray();
620 for(sal_uInt32 a(0); a < nCount; a++)
622 const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(a));
624 B2DPolygonToUnoPointSequence(aPolygon, *pPointSequence);
625 pPointSequence++;
628 else
630 rPointSequenceSequenceRetval.realloc(0);
634 // converters for css::drawing::PolyPolygonBezierCoords (curved polygons)
636 B2DPolyPolygon UnoPolyPolygonBezierCoordsToB2DPolyPolygon(
637 const css::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsSource)
639 B2DPolyPolygon aRetval;
640 const sal_uInt32 nSequenceCount(static_cast<sal_uInt32>(rPolyPolygonBezierCoordsSource.Coordinates.getLength()));
642 if(nSequenceCount)
644 OSL_ENSURE(nSequenceCount == static_cast<sal_uInt32>(rPolyPolygonBezierCoordsSource.Flags.getLength()),
645 "UnoPolyPolygonBezierCoordsToB2DPolyPolygon: unequal number of Points and Flags (!)");
646 const css::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsSource.Coordinates.getConstArray();
647 const css::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsSource.Flags.getConstArray();
649 for(sal_uInt32 a(0); a < nSequenceCount; a++)
651 const B2DPolygon aNewPolygon(UnoPolygonBezierCoordsToB2DPolygon(
652 *pPointSequence,
653 *pFlagSequence));
655 pPointSequence++;
656 pFlagSequence++;
657 aRetval.append(aNewPolygon);
661 return aRetval;
664 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
665 const B2DPolyPolygon& rPolyPolygon,
666 css::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsRetval)
668 const sal_uInt32 nCount(rPolyPolygon.count());
670 if(nCount)
672 // prepare return value memory
673 rPolyPolygonBezierCoordsRetval.Coordinates.realloc(static_cast<sal_Int32>(nCount));
674 rPolyPolygonBezierCoordsRetval.Flags.realloc(static_cast<sal_Int32>(nCount));
676 // get pointers to arrays
677 css::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsRetval.Coordinates.getArray();
678 css::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsRetval.Flags.getArray();
680 for(sal_uInt32 a(0); a < nCount; a++)
682 const B2DPolygon aSource(rPolyPolygon.getB2DPolygon(a));
684 B2DPolygonToUnoPolygonBezierCoords(
685 aSource,
686 *pPointSequence,
687 *pFlagSequence);
688 pPointSequence++;
689 pFlagSequence++;
692 else
694 rPolyPolygonBezierCoordsRetval.Coordinates.realloc(0);
695 rPolyPolygonBezierCoordsRetval.Flags.realloc(0);
699 } // end of namespace utils
700 } // end of namespace basegfx
702 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */