/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_basegfx.hxx" #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// namespace basegfx { namespace tools { B2DPolyPolygon correctOrientations(const B2DPolyPolygon& rCandidate) { B2DPolyPolygon aRetval(rCandidate); const sal_uInt32 nCount(aRetval.count()); for(sal_uInt32 a(0L); a < nCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); const B2VectorOrientation aOrientation(tools::getOrientation(aCandidate)); sal_uInt32 nDepth(0L); for(sal_uInt32 b(0L); b < nCount; b++) { if(b != a) { const B2DPolygon aCompare(rCandidate.getB2DPolygon(b)); if(tools::isInside(aCompare, aCandidate, true)) { nDepth++; } } } const bool bShallBeHole(1L == (nDepth & 0x00000001)); const bool bIsHole(ORIENTATION_NEGATIVE == aOrientation); if(bShallBeHole != bIsHole && ORIENTATION_NEUTRAL != aOrientation) { B2DPolygon aFlipped(aCandidate); aFlipped.flip(); aRetval.setB2DPolygon(a, aFlipped); } } return aRetval; } B2DPolyPolygon correctOutmostPolygon(const B2DPolyPolygon& rCandidate) { const sal_uInt32 nCount(rCandidate.count()); if(nCount > 1L) { for(sal_uInt32 a(0L); a < nCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); sal_uInt32 nDepth(0L); for(sal_uInt32 b(0L); b < nCount; b++) { if(b != a) { const B2DPolygon aCompare(rCandidate.getB2DPolygon(b)); if(tools::isInside(aCompare, aCandidate, true)) { nDepth++; } } } if(!nDepth) { B2DPolyPolygon aRetval(rCandidate); if(a != 0L) { // exchange polygon a and polygon 0L aRetval.setB2DPolygon(0L, aCandidate); aRetval.setB2DPolygon(a, rCandidate.getB2DPolygon(0L)); } // exit return aRetval; } } } return rCandidate; } B2DPolyPolygon adaptiveSubdivideByDistance(const B2DPolyPolygon& rCandidate, double fDistanceBound) { if(rCandidate.areControlPointsUsed()) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); if(aCandidate.areControlPointsUsed()) { aRetval.append(tools::adaptiveSubdivideByDistance(aCandidate, fDistanceBound)); } else { aRetval.append(aCandidate); } } return aRetval; } else { return rCandidate; } } B2DPolyPolygon adaptiveSubdivideByAngle(const B2DPolyPolygon& rCandidate, double fAngleBound) { if(rCandidate.areControlPointsUsed()) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); if(aCandidate.areControlPointsUsed()) { aRetval.append(tools::adaptiveSubdivideByAngle(aCandidate, fAngleBound)); } else { aRetval.append(aCandidate); } } return aRetval; } else { return rCandidate; } } B2DPolyPolygon adaptiveSubdivideByCount(const B2DPolyPolygon& rCandidate, sal_uInt32 nCount) { if(rCandidate.areControlPointsUsed()) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); if(aCandidate.areControlPointsUsed()) { aRetval.append(tools::adaptiveSubdivideByCount(aCandidate, nCount)); } else { aRetval.append(aCandidate); } } return aRetval; } else { return rCandidate; } } bool isInside(const B2DPolyPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder) { const sal_uInt32 nPolygonCount(rCandidate.count()); if(1L == nPolygonCount) { return isInside(rCandidate.getB2DPolygon(0L), rPoint, bWithBorder); } else { sal_Int32 nInsideCount(0L); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aPolygon(rCandidate.getB2DPolygon(a)); const bool bInside(isInside(aPolygon, rPoint, bWithBorder)); if(bInside) { nInsideCount++; } } return (nInsideCount % 2L); } } B2DRange getRangeWithControlPoints(const B2DPolyPolygon& rCandidate) { B2DRange aRetval; const sal_uInt32 nPolygonCount(rCandidate.count()); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { B2DPolygon aCandidate = rCandidate.getB2DPolygon(a); aRetval.expand(tools::getRangeWithControlPoints(aCandidate)); } return aRetval; } B2DRange getRange(const B2DPolyPolygon& rCandidate) { B2DRange aRetval; const sal_uInt32 nPolygonCount(rCandidate.count()); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { B2DPolygon aCandidate = rCandidate.getB2DPolygon(a); aRetval.expand(tools::getRange(aCandidate)); } return aRetval; } double getSignedArea(const B2DPolyPolygon& rCandidate) { double fRetval(0.0); const sal_uInt32 nPolygonCount(rCandidate.count()); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); fRetval += tools::getSignedArea(aCandidate); } return fRetval; } double getArea(const B2DPolyPolygon& rCandidate) { return fabs(getSignedArea(rCandidate)); } void applyLineDashing(const B2DPolyPolygon& rCandidate, const ::std::vector& rDotDashArray, B2DPolyPolygon* pLineTarget, B2DPolyPolygon* pGapTarget, double fFullDashDotLen) { if(0.0 == fFullDashDotLen && rDotDashArray.size()) { // calculate fFullDashDotLen from rDotDashArray fFullDashDotLen = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0); } if(rCandidate.count() && fFullDashDotLen > 0.0) { B2DPolyPolygon aLineTarget, aGapTarget; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); applyLineDashing( aCandidate, rDotDashArray, pLineTarget ? &aLineTarget : 0, pGapTarget ? &aGapTarget : 0, fFullDashDotLen); if(pLineTarget) { pLineTarget->append(aLineTarget); } if(pGapTarget) { pGapTarget->append(aGapTarget); } } } } bool isInEpsilonRange(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance) { const sal_uInt32 nPolygonCount(rCandidate.count()); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); if(isInEpsilonRange(aCandidate, rTestPosition, fDistance)) { return true; } } return false; } B3DPolyPolygon createB3DPolyPolygonFromB2DPolyPolygon(const B2DPolyPolygon& rCandidate, double fZCoordinate) { const sal_uInt32 nPolygonCount(rCandidate.count()); B3DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); aRetval.append(createB3DPolygonFromB2DPolygon(aCandidate, fZCoordinate)); } return aRetval; } B2DPolyPolygon createB2DPolyPolygonFromB3DPolyPolygon(const B3DPolyPolygon& rCandidate, const B3DHomMatrix& rMat) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { B3DPolygon aCandidate(rCandidate.getB3DPolygon(a)); aRetval.append(createB2DPolygonFromB3DPolygon(aCandidate, rMat)); } return aRetval; } double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPoint& rTestPoint, sal_uInt32& rPolygonIndex, sal_uInt32& rEdgeIndex, double& rCut) { double fRetval(DBL_MAX); const double fZero(0.0); const sal_uInt32 nPolygonCount(rCandidate.count()); for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); sal_uInt32 nNewEdgeIndex; double fNewCut = 0.0; const double fNewDistance(getSmallestDistancePointToPolygon(aCandidate, rTestPoint, nNewEdgeIndex, fNewCut)); if(DBL_MAX == fRetval || fNewDistance < fRetval) { fRetval = fNewDistance; rPolygonIndex = a; rEdgeIndex = nNewEdgeIndex; rCut = fNewCut; if(fTools::equal(fRetval, fZero)) { // already found zero distance, cannot get better. Ensure numerical zero value and end loop. fRetval = 0.0; break; } } } return fRetval; } B2DPolyPolygon distort(const B2DPolyPolygon& rCandidate, const B2DRange& rOriginal, const B2DPoint& rTopLeft, const B2DPoint& rTopRight, const B2DPoint& rBottomLeft, const B2DPoint& rBottomRight) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); aRetval.append(distort(aCandidate, rOriginal, rTopLeft, rTopRight, rBottomLeft, rBottomRight)); } return aRetval; } B2DPolyPolygon rotateAroundPoint(const B2DPolyPolygon& rCandidate, const B2DPoint& rCenter, double fAngle) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); aRetval.append(rotateAroundPoint(aCandidate, rCenter, fAngle)); } return aRetval; } B2DPolyPolygon expandToCurve(const B2DPolyPolygon& rCandidate) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); aRetval.append(expandToCurve(aCandidate)); } return aRetval; } B2DPolyPolygon setContinuity(const B2DPolyPolygon& rCandidate, B2VectorContinuity eContinuity) { if(rCandidate.areControlPointsUsed()) { const sal_uInt32 nPolygonCount(rCandidate.count()); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidate.getB2DPolygon(a)); aRetval.append(setContinuity(aCandidate, eContinuity)); } return aRetval; } else { return rCandidate; } } B2DPolyPolygon growInNormalDirection(const B2DPolyPolygon& rCandidate, double fValue) { if(0.0 != fValue) { B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(growInNormalDirection(rCandidate.getB2DPolygon(a), fValue)); } return aRetval; } else { return rCandidate; } } void correctGrowShrinkPolygonPair(B2DPolyPolygon& /*rOriginal*/, B2DPolyPolygon& /*rGrown*/) { } B2DPolyPolygon reSegmentPolyPolygon(const B2DPolyPolygon& rCandidate, sal_uInt32 nSegments) { B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(reSegmentPolygon(rCandidate.getB2DPolygon(a), nSegments)); } return aRetval; } B2DPolyPolygon interpolate(const B2DPolyPolygon& rOld1, const B2DPolyPolygon& rOld2, double t) { OSL_ENSURE(rOld1.count() == rOld2.count(), "B2DPolyPolygon interpolate: Different geometry (!)"); B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rOld1.count(); a++) { aRetval.append(interpolate(rOld1.getB2DPolygon(a), rOld2.getB2DPolygon(a), t)); } return aRetval; } bool isRectangle( const B2DPolyPolygon& rPoly ) { // exclude some cheap cases first if( rPoly.count() != 1 ) return false; return isRectangle( rPoly.getB2DPolygon(0) ); } // #i76891# B2DPolyPolygon simplifyCurveSegments(const B2DPolyPolygon& rCandidate) { if(rCandidate.areControlPointsUsed()) { B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(simplifyCurveSegments(rCandidate.getB2DPolygon(a))); } return aRetval; } else { return rCandidate; } } B2DPolyPolygon reSegmentPolyPolygonEdges(const B2DPolyPolygon& rCandidate, sal_uInt32 nSubEdges, bool bHandleCurvedEdges, bool bHandleStraightEdges) { B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(reSegmentPolygonEdges(rCandidate.getB2DPolygon(a), nSubEdges, bHandleCurvedEdges, bHandleStraightEdges)); } return aRetval; } ////////////////////////////////////////////////////////////////////// // comparators with tolerance for 2D PolyPolygons bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB, const double& rfSmallValue) { const sal_uInt32 nPolygonCount(rCandidateA.count()); if(nPolygonCount != rCandidateB.count()) return false; for(sal_uInt32 a(0); a < nPolygonCount; a++) { const B2DPolygon aCandidate(rCandidateA.getB2DPolygon(a)); if(!equal(aCandidate, rCandidateB.getB2DPolygon(a), rfSmallValue)) return false; } return true; } bool equal(const B2DPolyPolygon& rCandidateA, const B2DPolyPolygon& rCandidateB) { const double fSmallValue(fTools::getSmallValue()); return equal(rCandidateA, rCandidateB, fSmallValue); } B2DPolyPolygon snapPointsOfHorizontalOrVerticalEdges(const B2DPolyPolygon& rCandidate) { B2DPolyPolygon aRetval; for(sal_uInt32 a(0L); a < rCandidate.count(); a++) { aRetval.append(snapPointsOfHorizontalOrVerticalEdges(rCandidate.getB2DPolygon(a))); } return aRetval; } bool containsOnlyHorizontalAndVerticalEdges(const B2DPolyPolygon& rCandidate) { if(rCandidate.areControlPointsUsed()) { return false; } for(sal_uInt32 a(0); a < rCandidate.count(); a++) { if(!containsOnlyHorizontalAndVerticalEdges(rCandidate.getB2DPolygon(a))) { return false; } } return true; } ////////////////////////////////////////////////////////////////////////////// // converters for com::sun::star::drawing::PointSequence B2DPolyPolygon UnoPointSequenceSequenceToB2DPolyPolygon( const com::sun::star::drawing::PointSequenceSequence& rPointSequenceSequenceSource, bool bCheckClosed) { B2DPolyPolygon aRetval; const com::sun::star::drawing::PointSequence* pPointSequence = rPointSequenceSequenceSource.getConstArray(); const com::sun::star::drawing::PointSequence* pPointSeqEnd = pPointSequence + rPointSequenceSequenceSource.getLength(); for(;pPointSequence != pPointSeqEnd; pPointSequence++) { const B2DPolygon aNewPolygon = UnoPointSequenceToB2DPolygon(*pPointSequence, bCheckClosed); aRetval.append(aNewPolygon); } return aRetval; } void B2DPolyPolygonToUnoPointSequenceSequence( const B2DPolyPolygon& rPolyPolygon, com::sun::star::drawing::PointSequenceSequence& rPointSequenceSequenceRetval) { const sal_uInt32 nCount(rPolyPolygon.count()); if(nCount) { rPointSequenceSequenceRetval.realloc(nCount); com::sun::star::drawing::PointSequence* pPointSequence = rPointSequenceSequenceRetval.getArray(); for(sal_uInt32 a(0); a < nCount; a++) { const B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(a)); B2DPolygonToUnoPointSequence(aPolygon, *pPointSequence); pPointSequence++; } } else { rPointSequenceSequenceRetval.realloc(0); } } ////////////////////////////////////////////////////////////////////////////// // converters for com::sun::star::drawing::PolyPolygonBezierCoords (curved polygons) B2DPolyPolygon UnoPolyPolygonBezierCoordsToB2DPolyPolygon( const com::sun::star::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsSource, bool bCheckClosed) { B2DPolyPolygon aRetval; const sal_uInt32 nSequenceCount((sal_uInt32)rPolyPolygonBezierCoordsSource.Coordinates.getLength()); if(nSequenceCount) { OSL_ENSURE(nSequenceCount == (sal_uInt32)rPolyPolygonBezierCoordsSource.Flags.getLength(), "UnoPolyPolygonBezierCoordsToB2DPolyPolygon: unequal number of Points and Flags (!)"); const com::sun::star::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsSource.Coordinates.getConstArray(); const com::sun::star::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsSource.Flags.getConstArray(); for(sal_uInt32 a(0); a < nSequenceCount; a++) { const B2DPolygon aNewPolygon(UnoPolygonBezierCoordsToB2DPolygon( *pPointSequence, *pFlagSequence, bCheckClosed)); pPointSequence++; pFlagSequence++; aRetval.append(aNewPolygon); } } return aRetval; } void B2DPolyPolygonToUnoPolyPolygonBezierCoords( const B2DPolyPolygon& rPolyPolygon, com::sun::star::drawing::PolyPolygonBezierCoords& rPolyPolygonBezierCoordsRetval) { const sal_uInt32 nCount(rPolyPolygon.count()); if(nCount) { // prepare return value memory rPolyPolygonBezierCoordsRetval.Coordinates.realloc((sal_Int32)nCount); rPolyPolygonBezierCoordsRetval.Flags.realloc((sal_Int32)nCount); // get pointers to arrays com::sun::star::drawing::PointSequence* pPointSequence = rPolyPolygonBezierCoordsRetval.Coordinates.getArray(); com::sun::star::drawing::FlagSequence* pFlagSequence = rPolyPolygonBezierCoordsRetval.Flags.getArray(); for(sal_uInt32 a(0); a < nCount; a++) { const B2DPolygon aSource(rPolyPolygon.getB2DPolygon(a)); B2DPolygonToUnoPolygonBezierCoords( aSource, *pPointSequence, *pFlagSequence); pPointSequence++; pFlagSequence++; } } else { rPolyPolygonBezierCoordsRetval.Coordinates.realloc(0); rPolyPolygonBezierCoordsRetval.Flags.realloc(0); } } } // end of namespace tools } // end of namespace basegfx ////////////////////////////////////////////////////////////////////////////// // eof