Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/basegfx/source/polygon/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 149 kB image not shown  

Quelle  b2dpolygontools.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <numeric>
#include <algorithm>
#include <stack>

#include <basegfx/numeric/ftools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <osl/diagnose.h>
#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/range/b2drange.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/point/b3dpoint.hxx>
#include <basegfx/matrix/b3dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/curve/b2dbeziertools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>

// #i37443#
#define ANGLE_BOUND_START_VALUE     (2.25)
#define ANGLE_BOUND_MINIMUM_VALUE   (0.1)
#define STEPSPERQUARTER     (3)

namespace basegfx::utils
{
        void openWithGeometryChange(B2DPolygon& rCandidate)
        {
            if(!rCandidate.isClosed())
                return;

            if(rCandidate.count())
            {
                rCandidate.append(rCandidate.getB2DPoint(0));

                if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(0))
                {
                    rCandidate.setPrevControlPoint(rCandidate.count() - 1, rCandidate.getPrevControlPoint(0));
                    rCandidate.resetPrevControlPoint(0);
                }
            }

            rCandidate.setClosed(false);
        }

        void closeWithGeometryChange(B2DPolygon& rCandidate)
        {
            if(rCandidate.isClosed())
                return;

            while(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
            {
                if(rCandidate.areControlPointsUsed() && rCandidate.isPrevControlPointUsed(rCandidate.count() - 1))
                {
                    rCandidate.setPrevControlPoint(0, rCandidate.getPrevControlPoint(rCandidate.count() - 1));
                }

                rCandidate.remove(rCandidate.count() - 1);
            }

            rCandidate.setClosed(true);
        }

        void checkClosed(B2DPolygon& rCandidate)
        {
            // #i80172# Removed unnecessary assertion
            // OSL_ENSURE(!rCandidate.isClosed(), "checkClosed: already closed (!)");

            if(rCandidate.count() > 1 && rCandidate.getB2DPoint(0) == rCandidate.getB2DPoint(rCandidate.count() - 1))
            {
                closeWithGeometryChange(rCandidate);
            }
        }

        // Get successor and predecessor indices. Returning the same index means there
        // is none. Same for successor.
        sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
        {
            OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");

            if(nIndex)
            {
                return nIndex - 1;
            }
            else if(rCandidate.count())
            {
                return rCandidate.count() - 1;
            }
            else
            {
                return nIndex;
            }
        }

        sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B2DPolygon& rCandidate)
        {
            OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");

            if(nIndex + 1 < rCandidate.count())
            {
                return nIndex + 1;
            }
            else if(nIndex + 1 == rCandidate.count())
            {
                return 0;
            }
            else
            {
                return nIndex;
            }
        }

        B2VectorOrientation getOrientation(const B2DPolygon& rCandidate)
        {
            B2VectorOrientation eRetval(B2VectorOrientation::Neutral);

            if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
            {
                const double fSignedArea(getSignedArea(rCandidate));

                if(fTools::equalZero(fSignedArea))
                {
                    // B2VectorOrientation::Neutral, already set
                }
                if(fSignedArea > 0.0)
                {
                    eRetval = B2VectorOrientation::Positive;
                }
                else if(fSignedArea < 0.0)
                {
                    eRetval = B2VectorOrientation::Negative;
                }
            }

            return eRetval;
        }

        B2VectorContinuity getContinuityInPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
        {
            return rCandidate.getContinuityInPoint(nIndex);
        }

        B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon& rCandidate, double fDistanceBound, int nRecurseLimit)
        {
            if(rCandidate.areControlPointsUsed())
            {
                const sal_uInt32 nPointCount(rCandidate.count());
                B2DPolygon aRetval;

                if(nPointCount)
                {
                    // prepare edge-oriented loop
                    const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
                    B2DCubicBezier aBezier;
                    aBezier.setStartPoint(rCandidate.getB2DPoint(0));

                    // perf: try to avoid too many reallocations by guessing the result's pointcount
                    aRetval.reserve(nPointCount*4);

                    // add start point (always)
                    aRetval.append(aBezier.getStartPoint());

                    for(sal_uInt32 a(0); a < nEdgeCount; a++)
                    {
                        // get next and control points
                        const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                        aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
                        aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
                        aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
                        aBezier.testAndSolveTrivialBezier();

                        if(aBezier.isBezier())
                        {
                            // add curved edge and generate DistanceBound
                            double fBound(0.0);

                            if(fDistanceBound == 0.0)
                            {
                                // If not set, use B2DCubicBezier functionality to guess a rough value
                                const double fRoughLength((aBezier.getEdgeLength() + aBezier.getControlPolygonLength()) / 2.0);

                                // take 1/100th of the rough curve length
                                fBound = fRoughLength * 0.01;
                            }
                            else
                            {
                                // use given bound value
                                fBound = fDistanceBound;
                            }

                            // make sure bound value is not too small. The base units are 1/100th mm, thus
                            // just make sure it's not smaller then 1/100th of that
                            if(fBound < 0.01)
                            {
                                fBound = 0.01;
                            }

                            // call adaptive subdivide which adds edges to aRetval accordingly
                            aBezier.adaptiveSubdivideByDistance(aRetval, fBound, nRecurseLimit);
                        }
                        else
                        {
                            // add non-curved edge
                            aRetval.append(aBezier.getEndPoint());
                        }

                        // prepare next step
                        aBezier.setStartPoint(aBezier.getEndPoint());
                    }

                    if(rCandidate.isClosed())
                    {
                        // set closed flag and correct last point (which is added double now).
                        closeWithGeometryChange(aRetval);
                    }
                }

                return aRetval;
            }
            else
            {
                return rCandidate;
            }
        }

        B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon& rCandidate, double fAngleBound)
        {
            if(rCandidate.areControlPointsUsed())
            {
                const sal_uInt32 nPointCount(rCandidate.count());
                B2DPolygon aRetval;

                if(nPointCount)
                {
                    // prepare edge-oriented loop
                    const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
                    B2DCubicBezier aBezier;
                    aBezier.setStartPoint(rCandidate.getB2DPoint(0));

                    // perf: try to avoid too many reallocations by guessing the result's pointcount
                    aRetval.reserve(nPointCount*4);

                    // add start point (always)
                    aRetval.append(aBezier.getStartPoint());

                    // #i37443# prepare convenient AngleBound if none was given
                    if(fAngleBound == 0.0)
                    {
                        fAngleBound = ANGLE_BOUND_START_VALUE;
                    }
                    else if(fTools::less(fAngleBound, ANGLE_BOUND_MINIMUM_VALUE))
                    {
                        fAngleBound = 0.1;
                    }

                    for(sal_uInt32 a(0); a < nEdgeCount; a++)
                    {
                        // get next and control points
                        const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                        aBezier.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
                        aBezier.setControlPointA(rCandidate.getNextControlPoint(a));
                        aBezier.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
                        aBezier.testAndSolveTrivialBezier();

                        if(aBezier.isBezier())
                        {
                            // call adaptive subdivide
                            aBezier.adaptiveSubdivideByAngle(aRetval, fAngleBound);
                        }
                        else
                        {
                            // add non-curved edge
                            aRetval.append(aBezier.getEndPoint());
                        }

                        // prepare next step
                        aBezier.setStartPoint(aBezier.getEndPoint());
                    }

                    if(rCandidate.isClosed())
                    {
                        // set closed flag and correct last point (which is added double now).
                        closeWithGeometryChange(aRetval);
                    }
                }

                return aRetval;
            }
            else
            {
                return rCandidate;
            }
        }

        bool isInside(const B2DPolygon& rCandidate, const B2DPoint& rPoint, bool bWithBorder)
        {
            const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);

            if(bWithBorder && isPointOnPolygon(aCandidate, rPoint))
            {
                return true;
            }
            else
            {
                bool bRetval(false);
                const sal_uInt32 nPointCount(aCandidate.count());

                if(nPointCount)
                {
                    B2DPoint aCurrentPoint(aCandidate.getB2DPoint(nPointCount - 1));

                    for(sal_uInt32 a(0); a < nPointCount; a++)
                    {
                        const B2DPoint aPreviousPoint(aCurrentPoint);
                        aCurrentPoint = aCandidate.getB2DPoint(a);

                        // cross-over in Y? tdf#130150 use full precision, no need for epsilon
                        const bool bCompYA(aPreviousPoint.getY() > rPoint.getY());
                        const bool bCompYB(aCurrentPoint.getY() > rPoint.getY());

                        if(bCompYA != bCompYB)
                        {
                            // cross-over in X? tdf#130150 use full precision, no need for epsilon
                            const bool bCompXA(aPreviousPoint.getX() > rPoint.getX());
                            const bool bCompXB(aCurrentPoint.getX() > rPoint.getX());

                            if(bCompXA == bCompXB)
                            {
                                if(bCompXA)
                                {
                                    bRetval = !bRetval;
                                }
                            }
                            else
                            {
                                const double fCompare(
                                    aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
                                    (aPreviousPoint.getX() - aCurrentPoint.getX()) /
                                    (aPreviousPoint.getY() - aCurrentPoint.getY()));

                                // tdf#130150 use full precision, no need for epsilon
                                if(fCompare > rPoint.getX())
                                {
                                    bRetval = !bRetval;
                                }
                            }
                        }
                    }
                }

                return bRetval;
            }
        }

        bool isInside(const B2DPolygon& rCandidate, const B2DPolygon& rPolygon, bool bWithBorder)
        {
            const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
            const B2DPolygon aPolygon(rPolygon.areControlPointsUsed() ? rPolygon.getDefaultAdaptiveSubdivision() : rPolygon);
            const sal_uInt32 nPointCount(aPolygon.count());

            for(sal_uInt32 a(0); a < nPointCount; a++)
            {
                const B2DPoint aTestPoint(aPolygon.getB2DPoint(a));

                if(!isInside(aCandidate, aTestPoint, bWithBorder))
                {
                    return false;
                }
            }

            return true;
        }

        B2DRange getRange(const B2DPolygon& rCandidate)
        {
            // changed to use internally buffered version at B2DPolygon
            return rCandidate.getB2DRange();
        }

        double getSignedArea(const B2DPolygon& rCandidate)
        {
            const B2DPolygon aCandidate(rCandidate.areControlPointsUsed() ? rCandidate.getDefaultAdaptiveSubdivision() : rCandidate);
            double fRetval(0.0);
            const sal_uInt32 nPointCount(aCandidate.count());

            if(nPointCount > 2)
            {
                for(sal_uInt32 a(0); a < nPointCount; a++)
                {
                    const B2DPoint aPreviousPoint(aCandidate.getB2DPoint((!a) ? nPointCount - 1 : a - 1));
                    const B2DPoint aCurrentPoint(aCandidate.getB2DPoint(a));

                    fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
                    fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
                }

                // correct to zero if small enough. Also test the quadratic
                // of the result since the precision is near quadratic due to
                // the algorithm
                if(fTools::equalZero(fRetval) || fTools::equalZero(fRetval * fRetval))
                {
                    fRetval = 0.0;
                }
            }

            return fRetval;
        }

        double getArea(const B2DPolygon& rCandidate)
        {
            double fRetval(0.0);

            if(rCandidate.count() > 2 || rCandidate.areControlPointsUsed())
            {
                fRetval = getSignedArea(rCandidate);
                const double fZero(0.0);

                if(fTools::less(fRetval, fZero))
                {
                    fRetval = -fRetval;
                }
            }

            return fRetval;
        }

        double getEdgeLength(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
        {
            const sal_uInt32 nPointCount(rCandidate.count());
            OSL_ENSURE(nIndex < nPointCount, "getEdgeLength: Access to polygon out of range (!)");
            double fRetval(0.0);

            if(nPointCount)
            {
                const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);

                if(rCandidate.areControlPointsUsed())
                {
                    B2DCubicBezier aEdge;

                    aEdge.setStartPoint(rCandidate.getB2DPoint(nIndex));
                    aEdge.setControlPointA(rCandidate.getNextControlPoint(nIndex));
                    aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
                    aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));

                    fRetval = aEdge.getLength();
                }
                else
                {
                    const B2DPoint aCurrent(rCandidate.getB2DPoint(nIndex));
                    const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));

                    fRetval = B2DVector(aNext - aCurrent).getLength();
                }
            }

            return fRetval;
        }

        double getLength(const B2DPolygon& rCandidate)
        {
            double fRetval(0.0);
            const sal_uInt32 nPointCount(rCandidate.count());

            if(nPointCount)
            {
                const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);

                if(rCandidate.areControlPointsUsed())
                {
                    B2DCubicBezier aEdge;
                    aEdge.setStartPoint(rCandidate.getB2DPoint(0));

                    for(sal_uInt32 a(0); a < nEdgeCount; a++)
                    {
                        const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                        aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
                        aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
                        aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));

                        fRetval += aEdge.getLength();
                        aEdge.setStartPoint(aEdge.getEndPoint());
                    }
                }
                else
                {
                    B2DPoint aCurrent(rCandidate.getB2DPoint(0));

                    for(sal_uInt32 a(0); a < nEdgeCount; a++)
                    {
                        const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                        const B2DPoint aNext(rCandidate.getB2DPoint(nNextIndex));

                        fRetval += B2DVector(aNext - aCurrent).getLength();
                        aCurrent = aNext;
                    }
                }
            }

            return fRetval;
        }

        B2DPoint getPositionAbsolute(const B2DPolygon& rCandidate, double fDistance, double fLength)
        {
            B2DPoint aRetval;
            const sal_uInt32 nPointCount(rCandidate.count());

            if( nPointCount == 1 )
            {
                // only one point (i.e. no edge) - simply take that point
                aRetval = rCandidate.getB2DPoint(0);
            }
            else if(nPointCount > 1)
            {
                const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
                sal_uInt32 nIndex(0);
                bool bIndexDone(false);

                // get length if not given
                if(fTools::equalZero(fLength))
                {
                    fLength = getLength(rCandidate);
                }

                if (fDistance < 0.0)
                {
                    // handle fDistance < 0.0
                    if(rCandidate.isClosed())
                    {
                        // if fDistance < 0.0 increment with multiple of fLength
                        sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
                        fDistance += double(nCount + 1) * fLength;
                    }
                    else
                    {
                        // crop to polygon start
                        fDistance = 0.0;
                        bIndexDone = true;
                    }
                }
                else if(fTools::moreOrEqual(fDistance, fLength))
                {
                    // handle fDistance >= fLength
                    if(rCandidate.isClosed())
                    {
                        // if fDistance >= fLength decrement with multiple of fLength
                        sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
                        fDistance -= static_cast<double>(nCount) * fLength;
                    }
                    else
                    {
                        // crop to polygon end
                        fDistance = 0.0;
                        nIndex = nEdgeCount;
                        bIndexDone = true;
                    }
                }

                // look for correct index. fDistance is now [0.0 .. fLength[
                double fEdgeLength(getEdgeLength(rCandidate, nIndex));

                while(!bIndexDone)
                {
                    // edge found must be on the half-open range
                    // [0,fEdgeLength).
                    // Note that in theory, we cannot move beyond
                    // the last polygon point, since fDistance>=fLength
                    // is checked above. Unfortunately, with floating-
                    // point calculations, this case might happen.
                    // Handled by nIndex check below
                    if (nIndex+1 < nEdgeCount && fTools::moreOrEqual(fDistance, fEdgeLength))
                    {
                        // go to next edge
                        fDistance -= fEdgeLength;
                        fEdgeLength = getEdgeLength(rCandidate, ++nIndex);
                    }
                    else
                    {
                        // it's on this edge, stop
                        bIndexDone = true;
                    }
                }

                // get the point using nIndex
                aRetval = rCandidate.getB2DPoint(nIndex);

                // if fDistance != 0.0, move that length on the edge. The edge
                // length is in fEdgeLength.
                if(!fTools::equalZero(fDistance))
                {
                    if(fTools::moreOrEqual(fDistance, fEdgeLength))
                    {
                        // end point of chosen edge
                        const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
                        aRetval = rCandidate.getB2DPoint(nNextIndex);
                    }
                    else if(fTools::equalZero(fDistance))
                    {
                        // start point of chosen edge
                    }
                    else
                    {
                        // inside edge
                        const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount);
                        const B2DPoint aNextPoint(rCandidate.getB2DPoint(nNextIndex));
                        bool bDone(false);

                        // add calculated average value to the return value
                        if(rCandidate.areControlPointsUsed())
                        {
                            // get as bezier segment
                            const B2DCubicBezier aBezierSegment(
                                aRetval, rCandidate.getNextControlPoint(nIndex),
                                rCandidate.getPrevControlPoint(nNextIndex), aNextPoint);

                            if(aBezierSegment.isBezier())
                            {
                                // use B2DCubicBezierHelper to bridge the non-linear gap between
                                // length and bezier distances
                                const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
                                const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fDistance));

                                aRetval = aBezierSegment.interpolatePoint(fBezierDistance);
                                bDone = true;
                            }
                        }

                        if(!bDone)
                        {
                            const double fRelativeInEdge(fDistance / fEdgeLength);
                            aRetval = interpolate(aRetval, aNextPoint, fRelativeInEdge);
                        }
                    }
                }
            }

            return aRetval;
        }

        B2DPoint getPositionRelative(const B2DPolygon& rCandidate, double fDistance, double fLength)
        {
            // get length if not given
            if(fTools::equalZero(fLength))
            {
                fLength = getLength(rCandidate);
            }

            // multiply fDistance with real length to get absolute position and
            // use getPositionAbsolute
            return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
        }

        B2DPolygon getSnippetAbsolute(const B2DPolygon& rCandidate, double fFrom, double fTo, double fLength)
        {
            const sal_uInt32 nPointCount(rCandidate.count());

            if(nPointCount)
            {
                // get length if not given
                if(fTools::equalZero(fLength))
                {
                    fLength = getLength(rCandidate);
                }

                // test and correct fFrom
                if (fFrom < 0.0)
                {
                    fFrom = 0.0;
                }

                // test and correct fTo
                if(fTools::more(fTo, fLength))
                {
                    fTo = fLength;
                }

                // test and correct relationship of fFrom, fTo
                if(fTools::more(fFrom, fTo))
                {
                    fFrom = fTo = (fFrom + fTo) / 2.0;
                }

                if(fTools::equalZero(fFrom) && fTools::equal(fTo, fLength))
                {
                    // no change, result is the whole polygon
                    return rCandidate;
                }
                else
                {
                    B2DPolygon aRetval;
                    const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
                    double fPositionOfStart(0.0);
                    bool bStartDone(false);
                    bool bEndDone(false);

                    for(sal_uInt32 a(0); !(bStartDone && bEndDone) && a < nEdgeCount; a++)
                    {
                        const double fEdgeLength(getEdgeLength(rCandidate, a));

                        if(!bStartDone)
                        {
                            if(fTools::equalZero(fFrom))
                            {
                                aRetval.append(rCandidate.getB2DPoint(a));

                                if(rCandidate.areControlPointsUsed())
                                {
                                    aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
                                }

                                bStartDone = true;
                            }
                            else if(fTools::moreOrEqual(fFrom, fPositionOfStart) && fTools::less(fFrom, fPositionOfStart + fEdgeLength))
                            {
                                // calculate and add start point
                                if(fTools::equalZero(fEdgeLength))
                                {
                                    aRetval.append(rCandidate.getB2DPoint(a));

                                    if(rCandidate.areControlPointsUsed())
                                    {
                                        aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(a));
                                    }
                                }
                                else
                                {
                                    const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                                    const B2DPoint aStart(rCandidate.getB2DPoint(a));
                                    const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
                                    bool bDone(false);

                                    if(rCandidate.areControlPointsUsed())
                                    {
                                        const B2DCubicBezier aBezierSegment(
                                            aStart, rCandidate.getNextControlPoint(a),
                                            rCandidate.getPrevControlPoint(nNextIndex), aEnd);

                                        if(aBezierSegment.isBezier())
                                        {
                                            // use B2DCubicBezierHelper to bridge the non-linear gap between
                                            // length and bezier distances
                                            const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
                                            const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fFrom - fPositionOfStart));
                                            B2DCubicBezier aRight;

                                            aBezierSegment.split(fBezierDistance, nullptr, &aRight);
                                            aRetval.append(aRight.getStartPoint());
                                            aRetval.setNextControlPoint(aRetval.count() - 1, aRight.getControlPointA());
                                            bDone = true;
                                        }
                                    }

                                    if(!bDone)
                                    {
                                        const double fRelValue((fFrom - fPositionOfStart) / fEdgeLength);
                                        aRetval.append(interpolate(aStart, aEnd, fRelValue));
                                    }
                                }

                                bStartDone = true;

                                // if same point, end is done, too.
                                if(rtl::math::approxEqual(fFrom, fTo))
                                {
                                    bEndDone = true;
                                }
                            }
                        }

                        if(!bEndDone && fTools::moreOrEqual(fTo, fPositionOfStart) && fTools::less(fTo, fPositionOfStart + fEdgeLength))
                        {
                            // calculate and add end point
                            if(fTools::equalZero(fEdgeLength))
                            {
                                const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                                aRetval.append(rCandidate.getB2DPoint(nNextIndex));

                                if(rCandidate.areControlPointsUsed())
                                {
                                    aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
                                }
                            }
                            else
                            {
                                const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                                const B2DPoint aStart(rCandidate.getB2DPoint(a));
                                const B2DPoint aEnd(rCandidate.getB2DPoint(nNextIndex));
                                bool bDone(false);

                                if(rCandidate.areControlPointsUsed())
                                {
                                    const B2DCubicBezier aBezierSegment(
                                        aStart, rCandidate.getNextControlPoint(a),
                                        rCandidate.getPrevControlPoint(nNextIndex), aEnd);

                                    if(aBezierSegment.isBezier())
                                    {
                                        // use B2DCubicBezierHelper to bridge the non-linear gap between
                                        // length and bezier distances
                                        const B2DCubicBezierHelper aBezierSegmentHelper(aBezierSegment);
                                        const double fBezierDistance(aBezierSegmentHelper.distanceToRelative(fTo - fPositionOfStart));
                                        B2DCubicBezier aLeft;

                                        aBezierSegment.split(fBezierDistance, &aLeft, nullptr);
                                        aRetval.append(aLeft.getEndPoint());
                                        aRetval.setPrevControlPoint(aRetval.count() - 1, aLeft.getControlPointB());
                                        bDone = true;
                                    }
                                }

                                if(!bDone)
                                {
                                    const double fRelValue((fTo - fPositionOfStart) / fEdgeLength);
                                    aRetval.append(interpolate(aStart, aEnd, fRelValue));
                                }
                            }

                            bEndDone = true;
                        }

                        if(!bEndDone)
                        {
                            if(bStartDone)
                            {
                                // add segments end point
                                const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                                aRetval.append(rCandidate.getB2DPoint(nNextIndex));

                                if(rCandidate.areControlPointsUsed())
                                {
                                    aRetval.setPrevControlPoint(aRetval.count() - 1, rCandidate.getPrevControlPoint(nNextIndex));
                                    aRetval.setNextControlPoint(aRetval.count() - 1, rCandidate.getNextControlPoint(nNextIndex));
                                }
                            }

                            // increment fPositionOfStart
                            fPositionOfStart += fEdgeLength;
                        }
                    }
                    return aRetval;
                }
            }
            else
            {
                return rCandidate;
            }
        }

        CutFlagValue findCut(
            const B2DPoint& rEdge1Start, const B2DVector& rEdge1Delta,
            const B2DPoint& rEdge2Start, const B2DVector& rEdge2Delta,
            CutFlagValue aCutFlags,
            double* pCut1, double* pCut2)
        {
            CutFlagValue aRetval(CutFlagValue::NONE);
            double fCut1(0.0);
            double fCut2(0.0);
            bool bFinished(!static_cast<bool>(aCutFlags & CutFlagValue::ALL));

            // test for same points?
            if(!bFinished
                && (aCutFlags & (CutFlagValue::START1|CutFlagValue::END1))
                && (aCutFlags & (CutFlagValue::START2|CutFlagValue::END2)))
            {
                // same startpoint?
                if((aCutFlags & (CutFlagValue::START1|CutFlagValue::START2)) == (CutFlagValue::START1|CutFlagValue::START2))
                {
                    if(rEdge1Start.equal(rEdge2Start))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::START1|CutFlagValue::START2);
                    }
                }

                // same endpoint?
                if(!bFinished && (aCutFlags & (CutFlagValue::END1|CutFlagValue::END2)) == (CutFlagValue::END1|CutFlagValue::END2))
                {
                    const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);
                    const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);

                    if(aEnd1.equal(aEnd2))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::END1|CutFlagValue::END2);
                        fCut1 = fCut2 = 1.0;
                    }
                }

                // startpoint1 == endpoint2?
                if(!bFinished && (aCutFlags & (CutFlagValue::START1|CutFlagValue::END2)) == (CutFlagValue::START1|CutFlagValue::END2))
                {
                    const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);

                    if(rEdge1Start.equal(aEnd2))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::START1|CutFlagValue::END2);
                        fCut1 = 0.0;
                        fCut2 = 1.0;
                    }
                }

                // startpoint2 == endpoint1?
                if(!bFinished&& (aCutFlags & (CutFlagValue::START2|CutFlagValue::END1)) == (CutFlagValue::START2|CutFlagValue::END1))
                {
                    const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);

                    if(rEdge2Start.equal(aEnd1))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::START2|CutFlagValue::END1);
                        fCut1 = 1.0;
                        fCut2 = 0.0;
                    }
                }
            }

            if(!bFinished && (aCutFlags & CutFlagValue::LINE))
            {
                if(aCutFlags & CutFlagValue::START1)
                {
                    // start1 on line 2 ?
                    if(isPointOnEdge(rEdge1Start, rEdge2Start, rEdge2Delta, &fCut2))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::LINE|CutFlagValue::START1);
                    }
                }

                if(!bFinished && (aCutFlags & CutFlagValue::START2))
                {
                    // start2 on line 1 ?
                    if(isPointOnEdge(rEdge2Start, rEdge1Start, rEdge1Delta, &fCut1))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::LINE|CutFlagValue::START2);
                    }
                }

                if(!bFinished && (aCutFlags & CutFlagValue::END1))
                {
                    // end1 on line 2 ?
                    const B2DPoint aEnd1(rEdge1Start + rEdge1Delta);

                    if(isPointOnEdge(aEnd1, rEdge2Start, rEdge2Delta, &fCut2))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::LINE|CutFlagValue::END1);
                    }
                }

                if(!bFinished && (aCutFlags & CutFlagValue::END2))
                {
                    // end2 on line 1 ?
                    const B2DPoint aEnd2(rEdge2Start + rEdge2Delta);

                    if(isPointOnEdge(aEnd2, rEdge1Start, rEdge1Delta, &fCut1))
                    {
                        bFinished = true;
                        aRetval = (CutFlagValue::LINE|CutFlagValue::END2);
                    }
                }

                if(!bFinished)
                {
                    // cut in line1, line2 ?
                    fCut1 = (rEdge1Delta.getX() * rEdge2Delta.getY()) - (rEdge1Delta.getY() * rEdge2Delta.getX());

                    if(!fTools::equalZero(fCut1))
                    {
                        fCut1 = (rEdge2Delta.getY() * (rEdge2Start.getX() - rEdge1Start.getX())
                            + rEdge2Delta.getX() * (rEdge1Start.getY() - rEdge2Start.getY())) / fCut1;

                        const double fZero(0.0);
                        const double fOne(1.0);

                        // inside parameter range edge1 AND fCut2 is calculable
                        if(fTools::more(fCut1, fZero) && fTools::less(fCut1, fOne)
                            && (!fTools::equalZero(rEdge2Delta.getX()) || !fTools::equalZero(rEdge2Delta.getY())))
                        {
                            // take the more precise calculation of the two possible
                            if(fabs(rEdge2Delta.getX()) > fabs(rEdge2Delta.getY()))
                            {
                                fCut2 = (rEdge1Start.getX() + fCut1
                                    * rEdge1Delta.getX() - rEdge2Start.getX()) / rEdge2Delta.getX();
                            }
                            else
                            {
                                fCut2 = (rEdge1Start.getY() + fCut1
                                    * rEdge1Delta.getY() - rEdge2Start.getY()) / rEdge2Delta.getY();
                            }

                            // inside parameter range edge2, too
                            if(fTools::more(fCut2, fZero) && fTools::less(fCut2, fOne))
                            {
                                aRetval = CutFlagValue::LINE;
                            }
                        }
                    }
                }
            }

            // copy values if wanted
            if(pCut1)
            {
                *pCut1 = fCut1;
            }

            if(pCut2)
            {
                *pCut2 = fCut2;
            }

            return aRetval;
        }

        bool isPointOnEdge(
            const B2DPoint& rPoint,
            const B2DPoint& rEdgeStart,
            const B2DVector& rEdgeDelta,
            double* pCut)
        {
            bool bDeltaXIsZero(fTools::equalZero(rEdgeDelta.getX()));
            bool bDeltaYIsZero(fTools::equalZero(rEdgeDelta.getY()));
            const double fZero(0.0);
            const double fOne(1.0);

            if(bDeltaXIsZero && bDeltaYIsZero)
            {
                // no line, just a point
                return false;
            }
            else if(bDeltaXIsZero)
            {
                // vertical line
                if(fTools::equal(rPoint.getX(), rEdgeStart.getX()))
                {
                    double fValue = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();

                    if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
                    {
                        if(pCut)
                        {
                            *pCut = fValue;
                        }

                        return true;
                    }
                }
            }
            else if(bDeltaYIsZero)
            {
                // horizontal line
                if(fTools::equal(rPoint.getY(), rEdgeStart.getY()))
                {
                    double fValue = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();

                    if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
                    {
                        if(pCut)
                        {
                            *pCut = fValue;
                        }

                        return true;
                    }
                }
            }
            else
            {
                // any angle line
                double fTOne = (rPoint.getX() - rEdgeStart.getX()) / rEdgeDelta.getX();
                double fTTwo = (rPoint.getY() - rEdgeStart.getY()) / rEdgeDelta.getY();

                if(fTools::equal(fTOne, fTTwo))
                {
                    // same parameter representation, point is on line. Take
                    // middle value for better results
                    double fValue = (fTOne + fTTwo) / 2.0;

                    if(fTools::more(fValue, fZero) && fTools::less(fValue, fOne))
                    {
                        // point is inside line bounds, too
                        if(pCut)
                        {
                            *pCut = fValue;
                        }

                        return true;
                    }
                }
            }

            return false;
        }

        void applyLineDashing(
            const B2DPolygon& rCandidate,
            const std::vector<double>& rDotDashArray,
            B2DPolyPolygon* pLineTarget,
            B2DPolyPolygon* pGapTarget,
            double fDotDashLength)
        {
            // clear targets in any case
            if(pLineTarget)
            {
                pLineTarget->clear();
            }

            if(pGapTarget)
            {
                pGapTarget->clear();
            }

            // call version that uses callbacks
            applyLineDashing(
                rCandidate,
                rDotDashArray,
                // provide callbacks as lambdas
                (!pLineTarget
                    ? std::function<void(const basegfx::B2DPolygon&)>()
                    : [&pLineTarget](const basegfx::B2DPolygon& rSnippet){ pLineTarget->append(rSnippet); }),
                (!pGapTarget
                    ? std::function<void(const basegfx::B2DPolygon&)>()
                    : [&pGapTarget](const basegfx::B2DPolygon& rSnippet){ pGapTarget->append(rSnippet); }),
                fDotDashLength);
        }

        static void implHandleSnippet(
            const B2DPolygon& rSnippet,
            const std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback,
            B2DPolygon& rFirst,
            B2DPolygon& rLast)
        {
            if(rSnippet.isClosed())
            {
                if(!rFirst.count())
                {
                    rFirst = rSnippet;
                }
                else
                {
                    if(rLast.count())
                    {
                        rTargetCallback(rLast);
                    }

                    rLast = rSnippet;
                }
            }
            else
            {
                rTargetCallback(rSnippet);
            }
        }

        static void implHandleFirstLast(
            const std::function<void(const basegfx::B2DPolygon& rSnippet)>& rTargetCallback,
            B2DPolygon& rFirst,
            B2DPolygon& rLast)
        {
            if(rFirst.count() && rLast.count()
                && rFirst.getB2DPoint(0).equal(rLast.getB2DPoint(rLast.count() - 1)))
            {
                // start of first and end of last are the same -> merge them
                rLast.append(rFirst);
                rLast.removeDoublePoints();
                rFirst.clear();
            }

            if(rLast.count())
            {
                rTargetCallback(rLast);
            }

            if(rFirst.count())
            {
                rTargetCallback(rFirst);
            }
        }

        void applyLineDashing(
            const B2DPolygon& rCandidate,
            const std::vector<double>& rDotDashArray,
            const std::function<void(const basegfx::B2DPolygon& rSnippet)>& rLineTargetCallback,
            const std::function<void(const basegfx::B2DPolygon& rSnippet)>& rGapTargetCallback,
            double fDotDashLength)
        {
            const sal_uInt32 nPointCount(rCandidate.count());
            const sal_uInt32 nDotDashCount(rDotDashArray.size());

            if(fDotDashLength <= 0.0)
            {
                fDotDashLength = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
            }

            if(fDotDashLength <= 0.0 || (!rLineTargetCallback && !rGapTargetCallback) || !nPointCount)
            {
                // parameters make no sense, just add source to targets
                if (rLineTargetCallback)
                    rLineTargetCallback(rCandidate);

                if (rGapTargetCallback)
                    rGapTargetCallback(rCandidate);

                return;
            }

            // precalculate maximal acceptable length of candidate polygon assuming
            // we want to create a maximum of fNumberOfAllowedSnippets. For
            // fNumberOfAllowedSnippets use ca. 65536, double due to line & gap.
            static const double fNumberOfAllowedSnippets(65535.0 * 2.0);
            const double fAllowedLength((fNumberOfAllowedSnippets * fDotDashLength) / double(rDotDashArray.size()));
            const double fCandidateLength(basegfx::utils::getLength(rCandidate));
            std::vector<double> aDotDashArray(rDotDashArray);

            if(fCandidateLength > fAllowedLength)
            {
                // we would produce more than fNumberOfAllowedSnippets, so
                // adapt aDotDashArray to exactly produce assumed number. Also
                // assert this to let the caller know about it.
                // If this asserts: Please think about checking your DotDashArray
                // before calling this function or evtl. use the callback version
                // to *not* produce that much of data. Even then, you may still
                // think about producing too much runtime (!)
                assert(true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)");

                // calculate correcting factor, apply to aDotDashArray and fDotDashLength
                // to enlarge these as needed
                const double fFactor(fCandidateLength / fAllowedLength);
                std::for_each(aDotDashArray.begin(), aDotDashArray.end(), [&fFactor](double &f){ f *= fFactor; });
            }

            // prepare current edge's start
            B2DCubicBezier aCurrentEdge;
            const bool bIsClosed(rCandidate.isClosed());
            const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
            aCurrentEdge.setStartPoint(rCandidate.getB2DPoint(0));

            // prepare DotDashArray iteration and the line/gap switching bool
            sal_uInt32 nDotDashIndex(0);
            bool bIsLine(true);
            double fDotDashMovingLength(aDotDashArray[0]);
            B2DPolygon aSnippet;

            // remember 1st and last snippets to try to merge after execution
            // is complete and hand to callback
            B2DPolygon aFirstLine, aLastLine;
            B2DPolygon aFirstGap, aLastGap;

            // iterate over all edges
            for(sal_uInt32 a(0); a < nEdgeCount; a++)
            {
                // update current edge (fill in C1, C2 and end point)
                double fLastDotDashMovingLength(0.0);
                const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                aCurrentEdge.setControlPointA(rCandidate.getNextControlPoint(a));
                aCurrentEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
                aCurrentEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));

                // check if we have a trivial bezier segment -> possible fallback to edge
                aCurrentEdge.testAndSolveTrivialBezier();

                if(aCurrentEdge.isBezier())
                {
                    // bezier segment
                    const B2DCubicBezierHelper aCubicBezierHelper(aCurrentEdge);
                    const double fEdgeLength(aCubicBezierHelper.getLength());

                    if(!fTools::equalZero(fEdgeLength))
                    {
                        while(fTools::less(fDotDashMovingLength, fEdgeLength))
                        {
                            // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
                            const bool bHandleLine(bIsLine && rLineTargetCallback);
                            const bool bHandleGap(!bIsLine && rGapTargetCallback);

                            if(bHandleLine || bHandleGap)
                            {
                                const double fBezierSplitStart(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));
                                const double fBezierSplitEnd(aCubicBezierHelper.distanceToRelative(fDotDashMovingLength));
                                B2DCubicBezier aBezierSnippet(aCurrentEdge.snippet(fBezierSplitStart, fBezierSplitEnd));

                                if(!aSnippet.count())
                                {
                                    aSnippet.append(aBezierSnippet.getStartPoint());
                                }

                                aSnippet.appendBezierSegment(aBezierSnippet.getControlPointA(), aBezierSnippet.getControlPointB(), aBezierSnippet.getEndPoint());

                                if(bHandleLine)
                                {
                                    implHandleSnippet(aSnippet, rLineTargetCallback, aFirstLine, aLastLine);
                                }

                                if(bHandleGap)
                                {
                                    implHandleSnippet(aSnippet, rGapTargetCallback, aFirstGap, aLastGap);
                                }

                                aSnippet.clear();
                            }

                            // prepare next DotDashArray step and flip line/gap flag
                            fLastDotDashMovingLength = fDotDashMovingLength;
                            fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount];
                            bIsLine = !bIsLine;
                        }

                        // append closing snippet [fLastDotDashMovingLength, fEdgeLength]
                        const bool bHandleLine(bIsLine && rLineTargetCallback);
                        const bool bHandleGap(!bIsLine && rGapTargetCallback);

                        if(bHandleLine || bHandleGap)
                        {
                            B2DCubicBezier aRight;
                            const double fBezierSplit(aCubicBezierHelper.distanceToRelative(fLastDotDashMovingLength));

                            aCurrentEdge.split(fBezierSplit, nullptr, &aRight);

                            if(!aSnippet.count())
                            {
                                aSnippet.append(aRight.getStartPoint());
                            }

                            aSnippet.appendBezierSegment(aRight.getControlPointA(), aRight.getControlPointB(), aRight.getEndPoint());
                        }

                        // prepare move to next edge
                        fDotDashMovingLength -= fEdgeLength;
                    }
                }
                else
                {
                    // simple edge
                    const double fEdgeLength(aCurrentEdge.getEdgeLength());

                    if(!fTools::equalZero(fEdgeLength))
                    {
                        while(fTools::less(fDotDashMovingLength, fEdgeLength))
                        {
                            // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
                            const bool bHandleLine(bIsLine && rLineTargetCallback);
                            const bool bHandleGap(!bIsLine && rGapTargetCallback);

                            if(bHandleLine || bHandleGap)
                            {
                                if(!aSnippet.count())
                                {
                                    aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
                                }

                                aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fDotDashMovingLength / fEdgeLength));

                                if(bHandleLine)
                                {
                                    implHandleSnippet(aSnippet, rLineTargetCallback, aFirstLine, aLastLine);
                                }

                                if(bHandleGap)
                                {
                                    implHandleSnippet(aSnippet, rGapTargetCallback, aFirstGap, aLastGap);
                                }

                                aSnippet.clear();
                            }

                            // prepare next DotDashArray step and flip line/gap flag
                            fLastDotDashMovingLength = fDotDashMovingLength;
                            fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount];
                            bIsLine = !bIsLine;
                        }

                        // append snippet [fLastDotDashMovingLength, fEdgeLength]
                        const bool bHandleLine(bIsLine && rLineTargetCallback);
                        const bool bHandleGap(!bIsLine && rGapTargetCallback);

                        if(bHandleLine || bHandleGap)
                        {
                            if(!aSnippet.count())
                            {
                                aSnippet.append(interpolate(aCurrentEdge.getStartPoint(), aCurrentEdge.getEndPoint(), fLastDotDashMovingLength / fEdgeLength));
                            }

                            aSnippet.append(aCurrentEdge.getEndPoint());
                        }

                        // prepare move to next edge
                        fDotDashMovingLength -= fEdgeLength;
                    }
                }

                // prepare next edge step (end point gets new start point)
                aCurrentEdge.setStartPoint(aCurrentEdge.getEndPoint());
            }

            // append last intermediate results (if exists)
            if(aSnippet.count())
            {
                const bool bHandleLine(bIsLine && rLineTargetCallback);
                const bool bHandleGap(!bIsLine && rGapTargetCallback);

                if(bHandleLine)
                {
                    implHandleSnippet(aSnippet, rLineTargetCallback, aFirstLine, aLastLine);
                }

                if(bHandleGap)
                {
                    implHandleSnippet(aSnippet, rGapTargetCallback, aFirstGap, aLastGap);
                }
            }

            if(bIsClosed && rLineTargetCallback)
            {
                implHandleFirstLast(rLineTargetCallback, aFirstLine, aLastLine);
            }

            if(bIsClosed && rGapTargetCallback)
            {
                implHandleFirstLast(rGapTargetCallback, aFirstGap, aLastGap);
            }
        }

        // test if point is inside epsilon-range around an edge defined
        // by the two given points. Can be used for HitTesting. The epsilon-range
        // is defined to be the rectangle centered to the given edge, using height
        // 2 x fDistance, and the circle around both points with radius fDistance.
        bool isInEpsilonRange(const B2DPoint& rEdgeStart, const B2DPoint& rEdgeEnd, const B2DPoint& rTestPosition, double fDistance)
        {
            // build edge vector
            const B2DVector aEdge(rEdgeEnd - rEdgeStart);
            bool bDoDistanceTestStart(false);
            bool bDoDistanceTestEnd(false);

            if(aEdge.equalZero())
            {
                // no edge, just a point. Do one of the distance tests.
                bDoDistanceTestStart = true;
            }
            else
            {
                // edge has a length. Create perpendicular vector.
                const B2DVector aPerpend(getPerpendicular(aEdge));
                double fCut(
                    (aPerpend.getY() * (rTestPosition.getX() - rEdgeStart.getX())
                    + aPerpend.getX() * (rEdgeStart.getY() - rTestPosition.getY())) /
                    (aEdge.getX() * aEdge.getX() + aEdge.getY() * aEdge.getY()));
                const double fZero(0.0);
                const double fOne(1.0);

                if(fTools::less(fCut, fZero))
                {
                    // left of rEdgeStart
                    bDoDistanceTestStart = true;
                }
                else if(fTools::more(fCut, fOne))
                {
                    // right of rEdgeEnd
                    bDoDistanceTestEnd = true;
                }
                else
                {
                    // inside line [0.0 .. 1.0]
                    const B2DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
                    const B2DVector aDelta(rTestPosition - aCutPoint);
                    const double fDistanceSquare(aDelta.scalar(aDelta));

                    return fDistanceSquare <= fDistance * fDistance;
                }
            }

            if(bDoDistanceTestStart)
            {
                const B2DVector aDelta(rTestPosition - rEdgeStart);
                const double fDistanceSquare(aDelta.scalar(aDelta));

                if(fDistanceSquare <= fDistance * fDistance)
                {
                    return true;
                }
            }
            else if(bDoDistanceTestEnd)
            {
                const B2DVector aDelta(rTestPosition - rEdgeEnd);
                const double fDistanceSquare(aDelta.scalar(aDelta));

                if(fDistanceSquare <= fDistance * fDistance)
                {
                    return true;
                }
            }

            return false;
        }

        // test if point is inside epsilon-range around the given Polygon. Can be used
        // for HitTesting. The epsilon-range is defined to be the tube around the polygon
        // with distance fDistance and rounded edges (start and end point).
        bool isInEpsilonRange(const B2DPolygon& rCandidate, const B2DPoint& rTestPosition, double fDistance)
        {
            // force to non-bezier polygon
            const B2DPolygon& aCandidate(rCandidate.getDefaultAdaptiveSubdivision());
            const sal_uInt32 nPointCount(aCandidate.count());

            if(!nPointCount)
                return false;

            const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1);
            B2DPoint aCurrent(aCandidate.getB2DPoint(0));

            if(nEdgeCount)
            {
                // edges
                for(sal_uInt32 a(0); a < nEdgeCount; a++)
                {
                    const sal_uInt32 nNextIndex((a + 1) % nPointCount);
                    const B2DPoint aNext(aCandidate.getB2DPoint(nNextIndex));

                    if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
                    {
                        return true;
                    }

                    // prepare next step
                    aCurrent = aNext;
                }
            }
            else
            {
                // no edges, but points -> not closed. Check single point. Just
                // use isInEpsilonRange with twice the same point, it handles those well
                if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
                {
                    return true;
                }
            }

            return false;
        }

        // Calculates distance of curve point to its control point for a Bézier curve, that
        // approximates a unit circle arc. fAngle is the center angle of the circle arc. The
        // constrain 0<=fAngle<=pi/2 must not be violated to give a useful accuracy. For details
        // and alternatives read document "ApproxCircleInfo.odt", attachment of bug tdf#121425.
        static double impDistanceBezierPointToControl(double fAngle)
        {
            SAL_WARN_IF(fAngle < 0 || fAngle > M_PI_2,"basegfx","angle not suitable for approximate circle");
            if (0 <= fAngle && fAngle <= M_PI_2)
            {
                return 4.0/3.0 * ( tan(fAngle/4.0));
            }
            else
                return 0;
        }

        B2DPolygon createPolygonFromRect( const B2DRectangle& rRect, double fRadiusX, double fRadiusY )
        {
            const double fZero(0.0);
            const double fOne(1.0);

            fRadiusX = std::clamp(fRadiusX, 0.0, 1.0);
            fRadiusY = std::clamp(fRadiusY, 0.0, 1.0);

            if(rtl::math::approxEqual(fZero, fRadiusX) || rtl::math::approxEqual(fZero, fRadiusY))
            {
                // at least in one direction no radius, use rectangle.
                // Do not use createPolygonFromRect() here since original
                // creator (historical reasons) still creates a start point at the
                // bottom center, so do the same here to get the same line patterns.
                // Due to this the order of points is different, too.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.31 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.