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 <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>
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
));
46 for(sal_uInt32
b(0); b
< nCount
; b
++)
50 const B2DPolygon
& aCompare(rCandidate
.getB2DPolygon(b
));
52 if(utils::isInside(aCompare
, aCandidate
, true))
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
);
66 aRetval
.setB2DPolygon(a
, aFlipped
);
73 B2DPolyPolygon
correctOutmostPolygon(const B2DPolyPolygon
& rCandidate
)
75 const sal_uInt32
nCount(rCandidate
.count());
79 for(sal_uInt32
a(0); a
< nCount
; a
++)
81 const B2DPolygon
& aCandidate(rCandidate
.getB2DPolygon(a
));
84 for(sal_uInt32
b(0); b
< nCount
; b
++)
88 const B2DPolygon
& aCompare(rCandidate
.getB2DPolygon(b
));
90 if(utils::isInside(aCompare
, aCandidate
, true))
99 B2DPolyPolygon
aRetval(rCandidate
);
103 // exchange polygon a and polygon 0
104 aRetval
.setB2DPolygon(0, aCandidate
);
105 aRetval
.setB2DPolygon(a
, rCandidate
.getB2DPolygon(0));
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
));
131 aRetval
.append(rPolygon
);
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
));
157 aRetval
.append(rPolygon
);
169 bool isInside(const B2DPolyPolygon
& rCandidate
, const B2DPoint
& rPoint
, bool bWithBorder
)
171 if(rCandidate
.count() == 1)
173 return isInside(rCandidate
.getB2DPolygon(0), rPoint
, bWithBorder
);
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
)
187 for(auto const& rPolygon
: rCandidate
)
189 aRetval
.expand(utils::getRange(rPolygon
));
195 double getSignedArea(const B2DPolyPolygon
& rCandidate
)
199 for(auto const& rPolygon
: rCandidate
)
201 fRetval
+= utils::getSignedArea(rPolygon
);
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
)
229 pLineTarget
? &aLineTarget
: nullptr,
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
))
254 B3DPolyPolygon
createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon
& rCandidate
, double fZCoordinate
)
256 B3DPolyPolygon aRetval
;
258 for(auto const& rPolygon
: rCandidate
)
260 aRetval
.append(createB3DPolygonFromB2DPolygon(rPolygon
, fZCoordinate
));
266 B2DPolyPolygon
createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon
& rCandidate
, const B3DHomMatrix
& rMat
)
268 B2DPolyPolygon aRetval
;
270 for(auto const& rPolygon
: rCandidate
)
272 aRetval
.append(createB2DPolygonFromB3DPolygon(rPolygon
, rMat
));
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
;
289 const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate
, rTestPoint
, nNewEdgeIndex
, fNewCut
));
291 if(fRetval
== DBL_MAX
|| fNewDistance
< fRetval
)
293 fRetval
= fNewDistance
;
295 rEdgeIndex
= nNewEdgeIndex
;
298 if(fTools::equal(fRetval
, fZero
))
300 // already found zero distance, cannot get better. Ensure numerical zero value and end loop.
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
));
322 B2DPolyPolygon
expandToCurve(const B2DPolyPolygon
& rCandidate
)
324 B2DPolyPolygon aRetval
;
326 for(auto const& rPolygon
: rCandidate
)
328 aRetval
.append(expandToCurve(rPolygon
));
334 B2DPolyPolygon
growInNormalDirection(const B2DPolyPolygon
& rCandidate
, double fValue
)
338 B2DPolyPolygon aRetval
;
340 for(auto const& rPolygon
: rCandidate
)
342 aRetval
.append(growInNormalDirection(rPolygon
, fValue
));
353 B2DPolyPolygon
reSegmentPolyPolygon(const B2DPolyPolygon
& rCandidate
, sal_uInt32 nSegments
)
355 B2DPolyPolygon aRetval
;
357 for(auto const& rPolygon
: rCandidate
)
359 aRetval
.append(reSegmentPolygon(rPolygon
, nSegments
));
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
));
378 bool isRectangle( const B2DPolyPolygon
& rPoly
)
380 // exclude some cheap cases first
381 if( rPoly
.count() != 1 )
384 return isRectangle( rPoly
.getB2DPolygon(0) );
388 B2DPolyPolygon
simplifyCurveSegments(const B2DPolyPolygon
& rCandidate
)
390 if(rCandidate
.areControlPointsUsed())
392 B2DPolyPolygon aRetval
;
394 for(auto const& rPolygon
: rCandidate
)
396 aRetval
.append(simplifyCurveSegments(rPolygon
));
407 B2DPolyPolygon
snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon
& rCandidate
)
409 B2DPolyPolygon aRetval
;
411 for(auto const& rPolygon
: rCandidate
)
413 aRetval
.append(snapPointsOfHorizontalOrVerticalEdges(rPolygon
));
419 B2DPolyPolygon
createSevenSegmentPolyPolygon(sal_Char nNumber
, bool bLitSegments
)
423 const double fTotalSize
=1.0;
424 const double fPosMiddleSegment
=0.6;
425 const double fSegmentEndChopHoriz
=0.08;
426 const double fSegmentEndChopVert
=0.04;
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:
446 static const double corners
[] =
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):
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
[] =
501 else if( nNumber
== 'E' )
505 else if( nNumber
== '.' )
508 aRes
.append(createPolygonFromCircle(B2DPoint(fTotalSize
/2, fTotalSize
),
509 fSegmentEndChopHoriz
));
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
)) )
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
);
538 start
.setX(start
.getX()+fSegmentEndChopHoriz
);
539 end
.setX(end
.getX()-fSegmentEndChopHoriz
);
542 aCurrSegment
.append(start
);
543 aCurrSegment
.append(end
);
545 aRes
.append(aCurrSegment
);
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
);
569 void B2DPolyPolygonToUnoPointSequenceSequence(
570 const B2DPolyPolygon
& rPolyPolygon
,
571 css::drawing::PointSequenceSequence
& rPointSequenceSequenceRetval
)
573 const sal_uInt32
nCount(rPolyPolygon
.count());
577 rPointSequenceSequenceRetval
.realloc(nCount
);
578 css::drawing::PointSequence
* pPointSequence
= rPointSequenceSequenceRetval
.getArray();
580 for(auto const& rPolygon
: rPolyPolygon
)
582 B2DPolygonToUnoPointSequence(rPolygon
, *pPointSequence
);
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()));
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(
615 aRetval
.append(aNewPolygon
);
622 void B2DPolyPolygonToUnoPolyPolygonBezierCoords(
623 const B2DPolyPolygon
& rPolyPolygon
,
624 css::drawing::PolyPolygonBezierCoords
& rPolyPolygonBezierCoordsRetval
)
626 const sal_uInt32
nCount(rPolyPolygon
.count());
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(
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: */