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>
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
));
44 for(sal_uInt32
b(0); b
< nCount
; b
++)
48 const B2DPolygon
aCompare(rCandidate
.getB2DPolygon(b
));
50 if(utils::isInside(aCompare
, aCandidate
, true))
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
);
64 aRetval
.setB2DPolygon(a
, aFlipped
);
71 B2DPolyPolygon
correctOutmostPolygon(const B2DPolyPolygon
& rCandidate
)
73 const sal_uInt32
nCount(rCandidate
.count());
77 for(sal_uInt32
a(0); a
< nCount
; a
++)
79 const B2DPolygon
aCandidate(rCandidate
.getB2DPolygon(a
));
82 for(sal_uInt32
b(0); b
< nCount
; b
++)
86 const B2DPolygon
aCompare(rCandidate
.getB2DPolygon(b
));
88 if(utils::isInside(aCompare
, aCandidate
, true))
97 B2DPolyPolygon
aRetval(rCandidate
);
101 // exchange polygon a and polygon 0
102 aRetval
.setB2DPolygon(0, aCandidate
);
103 aRetval
.setB2DPolygon(a
, rCandidate
.getB2DPolygon(0));
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
));
132 aRetval
.append(aCandidate
);
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
));
161 aRetval
.append(aCandidate
);
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
);
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
));
196 return (nInsideCount
% 2);
200 B2DRange
getRange(const B2DPolyPolygon
& rCandidate
)
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
));
214 double getSignedArea(const B2DPolyPolygon
& rCandidate
)
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
);
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
));
253 pLineTarget
? &aLineTarget
: nullptr,
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
))
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
));
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
));
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
;
323 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate
, rTestPoint
, nNewEdgeIndex
, fNewCut
));
325 if(fRetval
== DBL_MAX
|| fNewDistance
< fRetval
)
327 fRetval
= fNewDistance
;
329 rEdgeIndex
= nNewEdgeIndex
;
332 if(fTools::equal(fRetval
, fZero
))
334 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
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
));
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
));
374 B2DPolyPolygon
growInNormalDirection(const B2DPolyPolygon
& rCandidate
, double fValue
)
378 B2DPolyPolygon aRetval
;
380 for(sal_uInt32
a(0); a
< rCandidate
.count(); a
++)
382 aRetval
.append(growInNormalDirection(rCandidate
.getB2DPolygon(a
), fValue
));
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
));
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
));
418 bool isRectangle( const B2DPolyPolygon
& rPoly
)
420 // exclude some cheap cases first
421 if( rPoly
.count() != 1 )
424 return isRectangle( rPoly
.getB2DPolygon(0) );
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
)));
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
)));
459 B2DPolyPolygon
createSevenSegmentPolyPolygon(sal_Char nNumber
, bool bLitSegments
)
463 const double fTotalSize
=1.0;
464 const double fPosMiddleSegment
=0.6;
465 const double fSegmentEndChopHoriz
=0.08;
466 const double fSegmentEndChopVert
=0.04;
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:
486 static const double corners
[] =
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):
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
[] =
541 else if( nNumber
== 'E' )
545 else if( nNumber
== '.' )
548 aRes
.append(createPolygonFromCircle(B2DPoint(fTotalSize
/2, fTotalSize
),
549 fSegmentEndChopHoriz
));
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
)) )
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
);
578 start
.setX(start
.getX()+fSegmentEndChopHoriz
);
579 end
.setX(end
.getX()-fSegmentEndChopHoriz
);
582 aCurrSegment
.append(start
);
583 aCurrSegment
.append(end
);
585 aRes
.append(aCurrSegment
);
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
);
609 void B2DPolyPolygonToUnoPointSequenceSequence(
610 const B2DPolyPolygon
& rPolyPolygon
,
611 css::drawing::PointSequenceSequence
& rPointSequenceSequenceRetval
)
613 const sal_uInt32
nCount(rPolyPolygon
.count());
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
);
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()));
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(
657 aRetval
.append(aNewPolygon
);
664 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
665 const B2DPolyPolygon
& rPolyPolygon
,
666 css::drawing::PolyPolygonBezierCoords
& rPolyPolygonBezierCoordsRetval
)
668 const sal_uInt32
nCount(rPolyPolygon
.count());
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(
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: */