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 <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>
29 //////////////////////////////////////////////////////////////////////////////
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
++)
50 const B2DPolygon
aCompare(rCandidate
.getB2DPolygon(b
));
52 if(tools::isInside(aCompare
, aCandidate
, true))
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
);
66 aRetval
.setB2DPolygon(a
, aFlipped
);
73 B2DPolyPolygon
correctOutmostPolygon(const B2DPolyPolygon
& rCandidate
)
75 const sal_uInt32
nCount(rCandidate
.count());
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
++)
88 const B2DPolygon
aCompare(rCandidate
.getB2DPolygon(b
));
90 if(tools::isInside(aCompare
, aCandidate
, true))
99 B2DPolyPolygon
aRetval(rCandidate
);
103 // exchange polygon a and polygon 0L
104 aRetval
.setB2DPolygon(0L, aCandidate
);
105 aRetval
.setB2DPolygon(a
, rCandidate
.getB2DPolygon(0L));
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
));
134 aRetval
.append(aCandidate
);
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
));
163 aRetval
.append(aCandidate
);
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
));
192 aRetval
.append(aCandidate
);
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
);
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
));
227 return (nInsideCount
% 2L);
231 B2DRange
getRange(const B2DPolyPolygon
& rCandidate
)
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
));
245 double getSignedArea(const B2DPolyPolygon
& rCandidate
)
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
);
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
));
284 pLineTarget
? &aLineTarget
: 0,
285 pGapTarget
? &aGapTarget
: 0,
290 pLineTarget
->append(aLineTarget
);
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
))
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
));
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
));
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
;
359 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate
, rTestPoint
, nNewEdgeIndex
, fNewCut
));
361 if(DBL_MAX
== fRetval
|| fNewDistance
< fRetval
)
363 fRetval
= fNewDistance
;
365 rEdgeIndex
= nNewEdgeIndex
;
368 if(fTools::equal(fRetval
, fZero
))
370 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
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
));
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
));
410 B2DPolyPolygon
growInNormalDirection(const B2DPolyPolygon
& rCandidate
, double fValue
)
414 B2DPolyPolygon aRetval
;
416 for(sal_uInt32
a(0L); a
< rCandidate
.count(); a
++)
418 aRetval
.append(growInNormalDirection(rCandidate
.getB2DPolygon(a
), fValue
));
429 void correctGrowShrinkPolygonPair(SAL_UNUSED_PARAMETER B2DPolyPolygon
& /*rOriginal*/, SAL_UNUSED_PARAMETER B2DPolyPolygon
& /*rGrown*/)
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
));
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
));
459 bool isRectangle( const B2DPolyPolygon
& rPoly
)
461 // exclude some cheap cases first
462 if( rPoly
.count() != 1 )
465 return isRectangle( rPoly
.getB2DPolygon(0) );
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
)));
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
)));
500 bool containsOnlyHorizontalAndVerticalEdges(const B2DPolyPolygon
& rCandidate
)
502 if(rCandidate
.areControlPointsUsed())
507 for(sal_uInt32
a(0); a
< rCandidate
.count(); a
++)
509 if(!containsOnlyHorizontalAndVerticalEdges(rCandidate
.getB2DPolygon(a
)))
518 B2DPolyPolygon
createSevenSegmentPolyPolygon(sal_Char nNumber
, bool bLitSegments
)
522 const double fTotalSize
=1.0;
523 const double fPosMiddleSegment
=0.6;
524 const double fSegmentEndChopHoriz
=0.08;
525 const double fSegmentEndChopVert
=0.04;
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:
545 static const double corners
[] =
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):
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
[] =
600 else if( nNumber
== 'E' )
604 else if( nNumber
== '.' )
607 aRes
.append(createPolygonFromCircle(B2DPoint(fTotalSize
/2, fTotalSize
),
608 fSegmentEndChopHoriz
));
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
)) )
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
);
637 start
.setX(start
.getX()+fSegmentEndChopHoriz
);
638 end
.setX(end
.getX()-fSegmentEndChopHoriz
);
641 aCurrSegment
.append(start
);
642 aCurrSegment
.append(end
);
644 aRes
.append(aCurrSegment
);
650 //////////////////////////////////////////////////////////////////////////////
651 // converters for com::sun::star::drawing::PointSequence
653 B2DPolyPolygon
UnoPointSequenceSequenceToB2DPolyPolygon(
654 const com::sun::star::drawing::PointSequenceSequence
& rPointSequenceSequenceSource
,
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
);
670 void B2DPolyPolygonToUnoPointSequenceSequence(
671 const B2DPolyPolygon
& rPolyPolygon
,
672 com::sun::star::drawing::PointSequenceSequence
& rPointSequenceSequenceRetval
)
674 const sal_uInt32
nCount(rPolyPolygon
.count());
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
);
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
,
702 B2DPolyPolygon aRetval
;
703 const sal_uInt32
nSequenceCount((sal_uInt32
)rPolyPolygonBezierCoordsSource
.Coordinates
.getLength());
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(
721 aRetval
.append(aNewPolygon
);
728 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
729 const B2DPolyPolygon
& rPolyPolygon
,
730 com::sun::star::drawing::PolyPolygonBezierCoords
& rPolyPolygonBezierCoordsRetval
)
732 const sal_uInt32
nCount(rPolyPolygon
.count());
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(
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: */