Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / basegfx / source / polygon / b2dpolypolygontools.cxx
blobf1ba12b67663fc59c10935540aa9e44e95fd96ae
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 //////////////////////////////////////////////////////////////////////////////
31 namespace basegfx
33 namespace tools
35 B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate)
37 B2DPolyPolygon aRetval(rCandidate);
38 const sal_uInt32 nCount(aRetval.count());
40 for(sal_uInt32 a(0L); a < nCount; a++)
42 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
43 const B2VectorOrientation aOrientation(tools::getOrientation(aCandidate));
44 sal_uInt32 nDepth(0L);
46 for(sal_uInt32 b(0L); b < nCount; b++)
48 if(b != a)
50 const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
52 if(tools::isInside(aCompare, aCandidate, true))
54 nDepth++;
59 const bool bShallBeHole(1L == (nDepth & 0x00000001));
60 const bool bIsHole(ORIENTATION_NEGATIVE == aOrientation);
62 if(bShallBeHole != bIsHole && ORIENTATION_NEUTRAL != aOrientation)
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 > 1L)
79 for(sal_uInt32 a(0L); a < nCount; a++)
81 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
82 sal_uInt32 nDepth(0L);
84 for(sal_uInt32 b(0L); b < nCount; b++)
86 if(b != a)
88 const B2DPolygon aCompare(rCandidate.getB2DPolygon(b));
90 if(tools::isInside(aCompare, aCandidate, true))
92 nDepth++;
97 if(!nDepth)
99 B2DPolyPolygon aRetval(rCandidate);
101 if(a != 0L)
103 // exchange polygon a and polygon 0L
104 aRetval.setB2DPolygon(0L, aCandidate);
105 aRetval.setB2DPolygon(a, rCandidate.getB2DPolygon(0L));
108 // exit
109 return aRetval;
114 return rCandidate;
117 B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound)
119 if(rCandidate.areControlPointsUsed())
121 const sal_uInt32 nPolygonCount(rCandidate.count());
122 B2DPolyPolygon aRetval;
124 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
126 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
128 if(aCandidate.areControlPointsUsed())
130 aRetval.append(tools::adaptiveSubdivideByDistance(aCandidate, fDistanceBound));
132 else
134 aRetval.append(aCandidate);
138 return aRetval;
140 else
142 return rCandidate;
146 B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound)
148 if(rCandidate.areControlPointsUsed())
150 const sal_uInt32 nPolygonCount(rCandidate.count());
151 B2DPolyPolygon aRetval;
153 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
155 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
157 if(aCandidate.areControlPointsUsed())
159 aRetval.append(tools::adaptiveSubdivideByAngle(aCandidate, fAngleBound));
161 else
163 aRetval.append(aCandidate);
167 return aRetval;
169 else
171 return rCandidate;
175 B2DPolyPolygon adaptiveSubdivideByCount(const B2DPolyPolygon& rCandidate, sal_uInt32 nCount)
177 if(rCandidate.areControlPointsUsed())
179 const sal_uInt32 nPolygonCount(rCandidate.count());
180 B2DPolyPolygon aRetval;
182 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
184 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
186 if(aCandidate.areControlPointsUsed())
188 aRetval.append(tools::adaptiveSubdivideByCount(aCandidate, nCount));
190 else
192 aRetval.append(aCandidate);
196 return aRetval;
198 else
200 return rCandidate;
204 bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
206 const sal_uInt32 nPolygonCount(rCandidate.count());
208 if(1L == nPolygonCount)
210 return isInside(rCandidate.getB2DPolygon(0L), rPoint, bWithBorder);
212 else
214 sal_Int32 nInsideCount(0L);
216 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
218 const B2DPolygon aPolygon(rCandidate.getB2DPolygon(a));
219 const bool bInside(isInside(aPolygon, rPoint, bWithBorder));
221 if(bInside)
223 nInsideCount++;
227 return (nInsideCount % 2L);
231 B2DRange getRange(const B2DPolyPolygon& rCandidate)
233 B2DRange aRetval;
234 const sal_uInt32 nPolygonCount(rCandidate.count());
236 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
238 B2DPolygon aCandidate = rCandidate.getB2DPolygon(a);
239 aRetval.expand(tools::getRange(aCandidate));
242 return aRetval;
245 double getSignedArea(const B2DPolyPolygon& rCandidate)
247 double fRetval(0.0);
248 const sal_uInt32 nPolygonCount(rCandidate.count());
250 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
252 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
254 fRetval += tools::getSignedArea(aCandidate);
257 return fRetval;
260 double getArea(const B2DPolyPolygon& rCandidate)
262 return fabs(getSignedArea(rCandidate));
265 void applyLineDashing(const B2DPolyPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fFullDashDotLen)
267 if(0.0 == fFullDashDotLen && rDotDashArray.size())
269 // calculate fFullDashDotLen from rDotDashArray
270 fFullDashDotLen = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
273 if(rCandidate.count() && fFullDashDotLen > 0.0)
275 B2DPolyPolygon aLineTarget, aGapTarget;
277 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
279 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
281 applyLineDashing(
282 aCandidate,
283 rDotDashArray,
284 pLineTarget ? &aLineTarget : 0,
285 pGapTarget ? &aGapTarget : 0,
286 fFullDashDotLen);
288 if(pLineTarget)
290 pLineTarget->append(aLineTarget);
293 if(pGapTarget)
295 pGapTarget->append(aGapTarget);
301 bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
303 const sal_uInt32 nPolygonCount(rCandidate.count());
305 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
307 B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
309 if(isInEpsilonRange(aCandidate, rTestPosition, fDistance))
311 return true;
315 return false;
318 B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate)
320 const sal_uInt32 nPolygonCount(rCandidate.count());
321 B3DPolyPolygon aRetval;
323 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
325 B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
327 aRetval.append(createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate));
330 return aRetval;
333 B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat)
335 const sal_uInt32 nPolygonCount(rCandidate.count());
336 B2DPolyPolygon aRetval;
338 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
340 B3DPolygon aCandidate(rCandidate.getB3DPolygon(a));
342 aRetval.append(createB2DPolygonFromB3DPolygon(aCandidate, rMat));
345 return aRetval;
348 double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut)
350 double fRetval(DBL_MAX);
351 const double fZero(0.0);
352 const sal_uInt32 nPolygonCount(rCandidate.count());
354 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
356 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
357 sal_uInt32 nNewEdgeIndex;
358 double fNewCut;
359 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate, rTestPoint, nNewEdgeIndex, fNewCut));
361 if(DBL_MAX == fRetval || fNewDistance < fRetval)
363 fRetval = fNewDistance;
364 rPolygonIndex = a;
365 rEdgeIndex = nNewEdgeIndex;
366 rCut = fNewCut;
368 if(fTools::equal(fRetval, fZero))
370 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
371 fRetval = 0.0;
372 break;
377 return fRetval;
380 B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight)
382 const sal_uInt32 nPolygonCount(rCandidate.count());
383 B2DPolyPolygon aRetval;
385 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
387 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
389 aRetval.append(distort(aCandidate, rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight));
392 return aRetval;
395 B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate)
397 const sal_uInt32 nPolygonCount(rCandidate.count());
398 B2DPolyPolygon aRetval;
400 for(sal_uInt32 a(0L); a < nPolygonCount; a++)
402 const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a));
404 aRetval.append(expandToCurve(aCandidate));
407 return aRetval;
410 B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue)
412 if(0.0 != fValue)
414 B2DPolyPolygon aRetval;
416 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
418 aRetval.append(growInNormalDirection(rCandidate.getB2DPolygon(a), fValue));
421 return aRetval;
423 else
425 return rCandidate;
429 void correctGrowShrinkPolygonPair(SAL_UNUSED_PARAMETER B2DPolyPolygon& /*rOriginal*/, SAL_UNUSED_PARAMETER B2DPolyPolygon& /*rGrown*/)
431 //TODO!
434 B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments)
436 B2DPolyPolygon aRetval;
438 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
440 aRetval.append(reSegmentPolygon(rCandidate.getB2DPolygon(a), nSegments));
443 return aRetval;
446 B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t)
448 OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolyPolygon interpolate: Different geometry (!)");
449 B2DPolyPolygon aRetval;
451 for(sal_uInt32 a(0L); a < rOld1.count(); a++)
453 aRetval.append(interpolate(rOld1.getB2DPolygon(a), rOld2.getB2DPolygon(a), t));
456 return aRetval;
459 bool isRectangle( const B2DPolyPolygon& rPoly )
461 // exclude some cheap cases first
462 if( rPoly.count() != 1 )
463 return false;
465 return isRectangle( rPoly.getB2DPolygon(0) );
468 // #i76891#
469 B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate)
471 if(rCandidate.areControlPointsUsed())
473 B2DPolyPolygon aRetval;
475 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
477 aRetval.append(simplifyCurveSegments(rCandidate.getB2DPolygon(a)));
480 return aRetval;
482 else
484 return rCandidate;
488 B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate)
490 B2DPolyPolygon aRetval;
492 for(sal_uInt32 a(0L); a < rCandidate.count(); a++)
494 aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rCandidate.getB2DPolygon(a)));
497 return aRetval;
500 bool containsOnlyHorizontalAndVerticalEdges(const B2DPolyPolygon& rCandidate)
502 if(rCandidate.areControlPointsUsed())
504 return false;
507 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
509 if(!containsOnlyHorizontalAndVerticalEdges(rCandidate.getB2DPolygon(a)))
511 return false;
515 return true;
518 B2DPolyPolygon createSevenSegmentPolyPolygon(sal_Char nNumber, bool bLitSegments)
520 // config here
521 // {
522 const double fTotalSize=1.0;
523 const double fPosMiddleSegment=0.6;
524 const double fSegmentEndChopHoriz=0.08;
525 const double fSegmentEndChopVert =0.04;
526 // }
527 // config here
529 const double fLeft=0.0;
530 const double fRight=fTotalSize;
531 const double fTop=0.0;
532 const double fMiddle=fPosMiddleSegment;
533 const double fBottom=fTotalSize;
535 // from 0 to 5: pair of segment corner coordinates
537 // segment corner indices are these:
539 // 0 - 1
540 // | |
541 // 2 - 3
542 // | |
543 // 4 - 5
545 static const double corners[] =
547 fLeft, fTop,
548 fRight, fTop,
549 fLeft, fMiddle,
550 fRight, fMiddle,
551 fLeft, fBottom,
552 fRight, fBottom
555 // from 0 to 9: which segments are 'lit' for this number?
557 // array denotes graph edges to traverse, with -1 means
558 // stop (the vertices are the corner indices from above):
559 // 0
560 // -
561 // 1 | | 2
562 // - 3
563 // 4 | | 5
564 // -
565 // 6
567 static const int numbers[] =
569 1, 1, 1, 0, 1, 1, 1, // 0
570 0, 0, 1, 0, 0, 1, 0, // 1
571 1, 0, 1, 1, 1, 0, 1, // 2
572 1, 0, 1, 1, 0, 1, 1, // 3
573 0, 1, 1, 1, 0, 1, 0, // 4
574 1, 1, 0, 1, 0, 1, 1, // 5
575 1, 1, 0, 1, 1, 1, 1, // 6
576 1, 0, 1, 0, 0, 1, 0, // 1
577 1, 1, 1, 1, 1, 1, 1, // 8
578 1, 1, 1, 1, 0, 1, 1, // 9
579 0, 0, 0, 1, 0, 0, 0, // '-'
580 1, 1, 0, 1, 1, 0, 1, // 'E'
583 // maps segment index to two corner ids:
584 static const int index2corner[] =
586 0, 2, // 0
587 0, 4, // 1
588 2, 6, // 2
589 4, 6, // 3
590 4, 8, // 4
591 6, 10, // 5
592 8, 10, // 6
595 B2DPolyPolygon aRes;
596 if( nNumber == '-' )
598 nNumber = 10;
600 else if( nNumber == 'E' )
602 nNumber = 11;
604 else if( nNumber == '.' )
606 if( bLitSegments )
607 aRes.append(createPolygonFromCircle(B2DPoint(fTotalSize/2, fTotalSize),
608 fSegmentEndChopHoriz));
609 return aRes;
611 else
613 nNumber=clamp<sal_uInt32>(nNumber,'0','9') - '0';
616 B2DPolygon aCurrSegment;
617 const size_t sliceSize=SAL_N_ELEMENTS(numbers)/12;
618 const int* pCurrSegment=numbers + nNumber*sliceSize;
619 for( size_t i=0; i<sliceSize; i++, pCurrSegment++)
621 if( !(*pCurrSegment ^ int(bLitSegments)) )
623 const size_t j=2*i;
624 aCurrSegment.clear();
625 B2DPoint start(corners[index2corner[j]],
626 corners[index2corner[j]+1] );
627 B2DPoint end (corners[index2corner[j+1]],
628 corners[index2corner[j+1]+1]);
630 if( start.getX() == end.getX() )
632 start.setY(start.getY()+fSegmentEndChopVert);
633 end.setY(end.getY()-fSegmentEndChopVert);
635 else
637 start.setX(start.getX()+fSegmentEndChopHoriz);
638 end.setX(end.getX()-fSegmentEndChopHoriz);
641 aCurrSegment.append(start);
642 aCurrSegment.append(end);
644 aRes.append(aCurrSegment);
647 return aRes;
650 //////////////////////////////////////////////////////////////////////////////
651 // converters for com::sun::star::drawing::PointSequence
653 B2DPolyPolygon UnoPointSequenceSequenceToB2DPolyPolygon(
654 const com::sun::star::drawing::PointSequenceSequence& rPointSequenceSequenceSource,
655 bool bCheckClosed)
657 B2DPolyPolygon aRetval;
658 const com::sun::star::drawing::PointSequence* pPointSequence = rPointSequenceSequenceSource.getConstArray();
659 const com::sun::star::drawing::PointSequence* pPointSeqEnd = pPointSequence + rPointSequenceSequenceSource.getLength();
661 for(;pPointSequence != pPointSeqEnd; pPointSequence++)
663 const B2DPolygon aNewPolygon = UnoPointSequenceToB2DPolygon(*pPointSequence, bCheckClosed);
664 aRetval.append(aNewPolygon);
667 return aRetval;
670 void B2DPolyPolygonToUnoPointSequenceSequence(
671 const B2DPolyPolygon& rPolyPolygon,
672 com::sun::star::drawing::PointSequenceSequence& rPointSequenceSequenceRetval)
674 const sal_uInt32 nCount(rPolyPolygon.count());
676 if(nCount)
678 rPointSequenceSequenceRetval.realloc(nCount);
679 com::sun::star::drawing::PointSequence* pPointSequence = rPointSequenceSequenceRetval.getArray();
681 for(sal_uInt32 a(0); a < nCount; a++)
683 const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(a));
685 B2DPolygonToUnoPointSequence(aPolygon, *pPointSequence);
686 pPointSequence++;
689 else
691 rPointSequenceSequenceRetval.realloc(0);
695 //////////////////////////////////////////////////////////////////////////////
696 // converters for com::sun::star::drawing::PolyPolygonBezierCoords (curved polygons)
698 B2DPolyPolygon UnoPolyPolygonBezierCoordsToB2DPolyPolygon(
699 const com::sun::star::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsSource,
700 bool bCheckClosed)
702 B2DPolyPolygon aRetval;
703 const sal_uInt32 nSequenceCount((sal_uInt32)rPolyPolygonBezierCoordsSource.Coordinates.getLength());
705 if(nSequenceCount)
707 OSL_ENSURE(nSequenceCount == (sal_uInt32)rPolyPolygonBezierCoordsSource.Flags.getLength(),
708 "UnoPolyPolygonBezierCoordsToB2DPolyPolygon: unequal number of Points and Flags (!)");
709 const com::sun::star::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsSource.Coordinates.getConstArray();
710 const com::sun::star::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsSource.Flags.getConstArray();
712 for(sal_uInt32 a(0); a < nSequenceCount; a++)
714 const B2DPolygon aNewPolygon(UnoPolygonBezierCoordsToB2DPolygon(
715 *pPointSequence,
716 *pFlagSequence,
717 bCheckClosed));
719 pPointSequence++;
720 pFlagSequence++;
721 aRetval.append(aNewPolygon);
725 return aRetval;
728 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
729 const B2DPolyPolygon& rPolyPolygon,
730 com::sun::star::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsRetval)
732 const sal_uInt32 nCount(rPolyPolygon.count());
734 if(nCount)
736 // prepare return value memory
737 rPolyPolygonBezierCoordsRetval.Coordinates.realloc((sal_Int32)nCount);
738 rPolyPolygonBezierCoordsRetval.Flags.realloc((sal_Int32)nCount);
740 // get pointers to arrays
741 com::sun::star::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsRetval.Coordinates.getArray();
742 com::sun::star::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsRetval.Flags.getArray();
744 for(sal_uInt32 a(0); a < nCount; a++)
746 const B2DPolygon aSource(rPolyPolygon.getB2DPolygon(a));
748 B2DPolygonToUnoPolygonBezierCoords(
749 aSource,
750 *pPointSequence,
751 *pFlagSequence);
752 pPointSequence++;
753 pFlagSequence++;
756 else
758 rPolyPolygonBezierCoordsRetval.Coordinates.realloc(0);
759 rPolyPolygonBezierCoordsRetval.Flags.realloc(0);
763 } // end of namespace tools
764 } // end of namespace basegfx
766 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */