Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / basegfx / source / polygon / b2dpolypolygontools.cxx
blob062e37da9dae900a87b1bccf8faa816436a1b413
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 <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
23 #include <basegfx/polygon/b2dpolypolygon.hxx>
24 #include <basegfx/polygon/b2dpolygon.hxx>
25 #include <basegfx/polygon/b2dpolygontools.hxx>
26 #include <basegfx/numeric/ftools.hxx>
28 #include <algorithm>
29 #include <numeric>
31 namespace basegfx
33 namespace utils
35 B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate)
37 B2DPolyPolygon aRetval(rCandidate);
38 const sal_uInt32 nCount(aRetval.count());
40 for(sal_uInt32 a(0); a < nCount; a++)
42 const B2DPolygon& aCandidate(rCandidate.getB2DPolygon(a));
43 const B2VectorOrientation aOrientation(utils::getOrientation(aCandidate));
44 sal_uInt32 nDepth(0);
46 for(sal_uInt32 b(0); b < nCount; b++)
48 if(b != a)
50 const B2DPolygon& aCompare(rCandidate.getB2DPolygon(b));
52 if(utils::isInside(aCompare, aCandidate, true))
54 nDepth++;
59 const bool bShallBeHole((nDepth & 0x00000001) == 1);
60 const bool bIsHole(aOrientation == B2VectorOrientation::Negative);
62 if(bShallBeHole != bIsHole && aOrientation != B2VectorOrientation::Neutral)
64 B2DPolygon aFlipped(aCandidate);
65 aFlipped.flip();
66 aRetval.setB2DPolygon(a, aFlipped);
70 return aRetval;
73 B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon& rCandidate)
75 const sal_uInt32 nCount(rCandidate.count());
77 if(nCount > 1)
79 for(sal_uInt32 a(0); a < nCount; a++)
81 const B2DPolygon& aCandidate(rCandidate.getB2DPolygon(a));
82 sal_uInt32 nDepth(0);
84 for(sal_uInt32 b(0); b < nCount; b++)
86 if(b != a)
88 const B2DPolygon& aCompare(rCandidate.getB2DPolygon(b));
90 if(utils::isInside(aCompare, aCandidate, true))
92 nDepth++;
97 if(!nDepth)
99 B2DPolyPolygon aRetval(rCandidate);
101 if(a != 0)
103 // exchange polygon a and polygon 0
104 aRetval.setB2DPolygon(0, aCandidate);
105 aRetval.setB2DPolygon(a, rCandidate.getB2DPolygon(0));
108 // exit
109 return aRetval;
114 return rCandidate;
117 B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound)
119 if(rCandidate.areControlPointsUsed())
121 B2DPolyPolygon aRetval;
123 for(auto const& rPolygon : rCandidate)
125 if(rPolygon.areControlPointsUsed())
127 aRetval.append(utils::adaptiveSubdivideByDistance(rPolygon, fDistanceBound));
129 else
131 aRetval.append(rPolygon);
135 return aRetval;
137 else
139 return rCandidate;
143 B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound)
145 if(rCandidate.areControlPointsUsed())
147 B2DPolyPolygon aRetval;
149 for(auto const& rPolygon : rCandidate)
151 if(rPolygon.areControlPointsUsed())
153 aRetval.append(utils::adaptiveSubdivideByAngle(rPolygon, fAngleBound));
155 else
157 aRetval.append(rPolygon);
161 return aRetval;
163 else
165 return rCandidate;
169 bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
171 if(rCandidate.count() == 1)
173 return isInside(rCandidate.getB2DPolygon(0), rPoint, bWithBorder);
175 else
177 sal_Int32 nInsideCount = std::count_if(rCandidate.begin(), rCandidate.end(), [rPoint, bWithBorder](B2DPolygon polygon){ return isInside(polygon, rPoint, bWithBorder); });
179 return (nInsideCount % 2);
183 B2DRange getRange(const B2DPolyPolygon& rCandidate)
185 B2DRange aRetval;
187 for(auto const& rPolygon : rCandidate)
189 aRetval.expand(utils::getRange(rPolygon));
192 return aRetval;
195 double getSignedArea(const B2DPolyPolygon& rCandidate)
197 double fRetval(0.0);
199 for(auto const& rPolygon : rCandidate)
201 fRetval += utils::getSignedArea(rPolygon);
204 return fRetval;
207 double getArea(const B2DPolyPolygon& rCandidate)
209 return fabs(getSignedArea(rCandidate));
212 void applyLineDashing(const B2DPolyPolygon& rCandidate, const std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, double fFullDashDotLen)
214 if(fFullDashDotLen == 0.0 && !rDotDashArray.empty())
216 // calculate fFullDashDotLen from rDotDashArray
217 fFullDashDotLen = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
220 if(rCandidate.count() && fFullDashDotLen > 0.0)
222 B2DPolyPolygon aLineTarget;
224 for(auto const& rPolygon : rCandidate)
226 applyLineDashing(
227 rPolygon,
228 rDotDashArray,
229 pLineTarget ? &aLineTarget : nullptr,
230 nullptr,
231 fFullDashDotLen);
233 if(pLineTarget)
235 pLineTarget->append(aLineTarget);
241 bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
243 for(auto const& rPolygon : rCandidate)
245 if(isInEpsilonRange(rPolygon, rTestPosition, fDistance))
247 return true;
251 return false;
254 B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate)
256 B3DPolyPolygon aRetval;
258 for(auto const& rPolygon : rCandidate)
260 aRetval.append(createB3DPolygonFromB2DPolygon(rPolygon, fZCoordinate));
263 return aRetval;
266 B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat)
268 B2DPolyPolygon aRetval;
270 for(auto const& rPolygon : rCandidate)
272 aRetval.append(createB2DPolygonFromB3DPolygon(rPolygon, rMat));
275 return aRetval;
278 double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut)
280 double fRetval(DBL_MAX);
281 const double fZero(0.0);
282 const sal_uInt32 nPolygonCount(rCandidate.count());
284 for(sal_uInt32 a(0); a < nPolygonCount; a++)
286 const B2DPolygon& aCandidate(rCandidate.getB2DPolygon(a));
287 sal_uInt32 nNewEdgeIndex;
288 double fNewCut(0.0);
289 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate, rTestPoint, nNewEdgeIndex, fNewCut));
291 if(fRetval == DBL_MAX || fNewDistance < fRetval)
293 fRetval = fNewDistance;
294 rPolygonIndex = a;
295 rEdgeIndex = nNewEdgeIndex;
296 rCut = fNewCut;
298 if(fTools::equal(fRetval, fZero))
300 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
301 fRetval = 0.0;
302 break;
307 return fRetval;
310 B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
312 B2DPolyPolygon aRetval;
314 for(auto const& rPolygon : rCandidate)
316 aRetval.append(distort(rPolygon, rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
319 return aRetval;
322 B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate)
324 B2DPolyPolygon aRetval;
326 for(auto const& rPolygon : rCandidate)
328 aRetval.append(expandToCurve(rPolygon));
331 return aRetval;
334 B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue)
336 if(fValue != 0.0)
338 B2DPolyPolygon aRetval;
340 for(auto const& rPolygon : rCandidate)
342 aRetval.append(growInNormalDirection(rPolygon, fValue));
345 return aRetval;
347 else
349 return rCandidate;
353 B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments)
355 B2DPolyPolygon aRetval;
357 for(auto const& rPolygon : rCandidate)
359 aRetval.append(reSegmentPolygon(rPolygon, nSegments));
362 return aRetval;
365 B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t)
367 OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolyPolygon interpolate: Different geometry (!)");
368 B2DPolyPolygon aRetval;
370 for(sal_uInt32 a(0); a < rOld1.count(); a++)
372 aRetval.append(interpolate(rOld1.getB2DPolygon(a), rOld2.getB2DPolygon(a), t));
375 return aRetval;
378 bool isRectangle( const B2DPolyPolygon& rPoly )
380 // exclude some cheap cases first
381 if( rPoly.count() != 1 )
382 return false;
384 return isRectangle( rPoly.getB2DPolygon(0) );
387 // #i76891#
388 B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate)
390 if(rCandidate.areControlPointsUsed())
392 B2DPolyPolygon aRetval;
394 for(auto const& rPolygon : rCandidate)
396 aRetval.append(simplifyCurveSegments(rPolygon));
399 return aRetval;
401 else
403 return rCandidate;
407 B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate)
409 B2DPolyPolygon aRetval;
411 for(auto const& rPolygon : rCandidate)
413 aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rPolygon));
416 return aRetval;
419 B2DPolyPolygon createSevenSegmentPolyPolygon(sal_Char nNumber, bool bLitSegments)
421 // config here
422 // {
423 const double fTotalSize=1.0;
424 const double fPosMiddleSegment=0.6;
425 const double fSegmentEndChopHoriz=0.08;
426 const double fSegmentEndChopVert =0.04;
427 // }
428 // config here
430 const double fLeft=0.0;
431 const double fRight=fTotalSize;
432 const double fTop=0.0;
433 const double fMiddle=fPosMiddleSegment;
434 const double fBottom=fTotalSize;
436 // from 0 to 5: pair of segment corner coordinates
438 // segment corner indices are these:
440 // 0 - 1
441 // | |
442 // 2 - 3
443 // | |
444 // 4 - 5
446 static const double corners[] =
448 fLeft, fTop,
449 fRight, fTop,
450 fLeft, fMiddle,
451 fRight, fMiddle,
452 fLeft, fBottom,
453 fRight, fBottom
456 // from 0 to 9: which segments are 'lit' for this number?
458 // array denotes graph edges to traverse, with -1 means
459 // stop (the vertices are the corner indices from above):
460 // 0
461 // -
462 // 1 | | 2
463 // - 3
464 // 4 | | 5
465 // -
466 // 6
468 static const int numbers[] =
470 1, 1, 1, 0, 1, 1, 1, // 0
471 0, 0, 1, 0, 0, 1, 0, // 1
472 1, 0, 1, 1, 1, 0, 1, // 2
473 1, 0, 1, 1, 0, 1, 1, // 3
474 0, 1, 1, 1, 0, 1, 0, // 4
475 1, 1, 0, 1, 0, 1, 1, // 5
476 1, 1, 0, 1, 1, 1, 1, // 6
477 1, 0, 1, 0, 0, 1, 0, // 1
478 1, 1, 1, 1, 1, 1, 1, // 8
479 1, 1, 1, 1, 0, 1, 1, // 9
480 0, 0, 0, 1, 0, 0, 0, // '-'
481 1, 1, 0, 1, 1, 0, 1, // 'E'
484 // maps segment index to two corner ids:
485 static const int index2corner[] =
487 0, 2, // 0
488 0, 4, // 1
489 2, 6, // 2
490 4, 6, // 3
491 4, 8, // 4
492 6, 10, // 5
493 8, 10, // 6
496 B2DPolyPolygon aRes;
497 if( nNumber == '-' )
499 nNumber = 10;
501 else if( nNumber == 'E' )
503 nNumber = 11;
505 else if( nNumber == '.' )
507 if( bLitSegments )
508 aRes.append(createPolygonFromCircle(B2DPoint(fTotalSize/2, fTotalSize),
509 fSegmentEndChopHoriz));
510 return aRes;
512 else
514 nNumber=std::clamp<sal_uInt32>(nNumber,'0','9') - '0';
517 B2DPolygon aCurrSegment;
518 const size_t sliceSize=SAL_N_ELEMENTS(numbers)/12;
519 const int* pCurrSegment=numbers + nNumber*sliceSize;
520 for( size_t i=0; i<sliceSize; i++, pCurrSegment++)
522 if( !(*pCurrSegment ^ int(bLitSegments)) )
524 const size_t j=2*i;
525 aCurrSegment.clear();
526 B2DPoint start(corners[index2corner[j]],
527 corners[index2corner[j]+1] );
528 B2DPoint end (corners[index2corner[j+1]],
529 corners[index2corner[j+1]+1]);
531 if( rtl::math::approxEqual(start.getX(), end.getX()) )
533 start.setY(start.getY()+fSegmentEndChopVert);
534 end.setY(end.getY()-fSegmentEndChopVert);
536 else
538 start.setX(start.getX()+fSegmentEndChopHoriz);
539 end.setX(end.getX()-fSegmentEndChopHoriz);
542 aCurrSegment.append(start);
543 aCurrSegment.append(end);
545 aRes.append(aCurrSegment);
548 return aRes;
551 // converters for css::drawing::PointSequence
553 B2DPolyPolygon UnoPointSequenceSequenceToB2DPolyPolygon(
554 const css::drawing::PointSequenceSequence& rPointSequenceSequenceSource)
556 B2DPolyPolygon aRetval;
557 const css::drawing::PointSequence* pPointSequence = rPointSequenceSequenceSource.getConstArray();
558 const css::drawing::PointSequence* pPointSeqEnd = pPointSequence + rPointSequenceSequenceSource.getLength();
560 for(;pPointSequence != pPointSeqEnd; pPointSequence++)
562 const B2DPolygon aNewPolygon = UnoPointSequenceToB2DPolygon(*pPointSequence);
563 aRetval.append(aNewPolygon);
566 return aRetval;
569 void B2DPolyPolygonToUnoPointSequenceSequence(
570 const B2DPolyPolygon& rPolyPolygon,
571 css::drawing::PointSequenceSequence& rPointSequenceSequenceRetval)
573 const sal_uInt32 nCount(rPolyPolygon.count());
575 if(nCount)
577 rPointSequenceSequenceRetval.realloc(nCount);
578 css::drawing::PointSequence* pPointSequence = rPointSequenceSequenceRetval.getArray();
580 for(auto const& rPolygon : rPolyPolygon)
582 B2DPolygonToUnoPointSequence(rPolygon, *pPointSequence);
583 pPointSequence++;
586 else
588 rPointSequenceSequenceRetval.realloc(0);
592 // converters for css::drawing::PolyPolygonBezierCoords (curved polygons)
594 B2DPolyPolygon UnoPolyPolygonBezierCoordsToB2DPolyPolygon(
595 const css::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsSource)
597 B2DPolyPolygon aRetval;
598 const sal_uInt32 nSequenceCount(static_cast<sal_uInt32>(rPolyPolygonBezierCoordsSource.Coordinates.getLength()));
600 if(nSequenceCount)
602 OSL_ENSURE(nSequenceCount == static_cast<sal_uInt32>(rPolyPolygonBezierCoordsSource.Flags.getLength()),
603 "UnoPolyPolygonBezierCoordsToB2DPolyPolygon: unequal number of Points and Flags (!)");
604 const css::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsSource.Coordinates.getConstArray();
605 const css::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsSource.Flags.getConstArray();
607 for(sal_uInt32 a(0); a < nSequenceCount; a++)
609 const B2DPolygon aNewPolygon(UnoPolygonBezierCoordsToB2DPolygon(
610 *pPointSequence,
611 *pFlagSequence));
613 pPointSequence++;
614 pFlagSequence++;
615 aRetval.append(aNewPolygon);
619 return aRetval;
622 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
623 const B2DPolyPolygon& rPolyPolygon,
624 css::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsRetval)
626 const sal_uInt32 nCount(rPolyPolygon.count());
628 if(nCount)
630 // prepare return value memory
631 rPolyPolygonBezierCoordsRetval.Coordinates.realloc(static_cast<sal_Int32>(nCount));
632 rPolyPolygonBezierCoordsRetval.Flags.realloc(static_cast<sal_Int32>(nCount));
634 // get pointers to arrays
635 css::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsRetval.Coordinates.getArray();
636 css::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsRetval.Flags.getArray();
638 for(auto const& rSource : rPolyPolygon)
640 B2DPolygonToUnoPolygonBezierCoords(
641 rSource,
642 *pPointSequence,
643 *pFlagSequence);
644 pPointSequence++;
645 pFlagSequence++;
648 else
650 rPolyPolygonBezierCoordsRetval.Coordinates.realloc(0);
651 rPolyPolygonBezierCoordsRetval.Flags.realloc(0);
655 } // end of namespace utils
656 } // end of namespace basegfx
658 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */