/* -*- 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 .
*/
void ImpSubDivDistance( const B2DPoint& rfPA, // start point const B2DPoint& rfEA, // edge on A const B2DPoint& rfEB, // edge on B const B2DPoint& rfPB, // end point
B2DPolygon& rTarget, // target polygon double fDistanceBound2, // quadratic distance criteria double fLastDistanceError2, // the last quadratic distance error
sal_uInt16 nMaxRecursionDepth) // endless loop protection
{ if(nMaxRecursionDepth)
{ // decide if another recursion is needed. If not, set // nMaxRecursionDepth to zero
// Perform bezier flatness test (lecture notes from R. Schaback, // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
// What is calculated here is an upper bound to the distance from // a line through b_0 and b_3 (rfPA and P4 in our notation) and the // curve. We can drop 0 and n from the running indices, since the // argument of max becomes zero for those cases. constdouble fJ1x(rfEA.getX() - rfPA.getX() - 1.0/3.0*(rfPB.getX() - rfPA.getX())); constdouble fJ1y(rfEA.getY() - rfPA.getY() - 1.0/3.0*(rfPB.getY() - rfPA.getY())); constdouble fJ2x(rfEB.getX() - rfPA.getX() - 2.0/3.0*(rfPB.getX() - rfPA.getX())); constdouble fJ2y(rfEB.getY() - rfPA.getY() - 2.0/3.0*(rfPB.getY() - rfPA.getY())); constdouble fDistanceError2(std::max(fJ1x*fJ1x + fJ1y*fJ1y, fJ2x*fJ2x + fJ2y*fJ2y));
// stop if error measure does not improve anymore. This is a // safety guard against floating point inaccuracies. // stop if distance from line is guaranteed to be bounded by d constbool bFurtherDivision(fLastDistanceError2 > fDistanceError2 && fDistanceError2 >= fDistanceBound2);
// controls parallel to edge can be trivial. No edge -> not parallel -> control can // still not be trivial (e.g. ballon loop) if(aEdge.equalZero()) return;
// get control vectors const B2DVector aVecA(maControlPointA - maStartPoint); const B2DVector aVecB(maControlPointB - maEndPoint);
// check if trivial per se bool bAIsTrivial(aVecA.equalZero()); bool bBIsTrivial(aVecB.equalZero());
// #i102241# prepare inverse edge length to normalize cross values; // else the small compare value used in fTools::equalZero // will be length dependent and this detection will work as less // precise as longer the edge is. In principle, the length of the control // vector would need to be used too, but to be trivial it is assumed to // be of roughly equal length to the edge, so edge length can be used // for both. Only needed when one of both is not trivial per se. constdouble fInverseEdgeLength(bAIsTrivial && bBIsTrivial
? 1.0
: 1.0 / aEdge.getLength());
// if A is not zero, check if it could be if(!bAIsTrivial)
{ // #i102241# parallel to edge? Check aVecA, aEdge. Use cross() which does what // we need here with the precision we need constdouble fCross(aVecA.cross(aEdge) * fInverseEdgeLength);
if(fTools::equalZero(fCross))
{ // get scale to edge. Use bigger distance for numeric quality constdouble fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
? aVecA.getX() / aEdge.getX()
: aVecA.getY() / aEdge.getY());
// relative end point of vector in edge range? if (fTools::betweenOrEqualEither(fScale, 0.0, 1.0))
{
bAIsTrivial = true;
}
}
}
// if B is not zero, check if it could be, but only if A is already trivial; // else solve to trivial will not be possible for whole edge if(bAIsTrivial && !bBIsTrivial)
{ // parallel to edge? Check aVecB, aEdge constdouble fCross(aVecB.cross(aEdge) * fInverseEdgeLength);
if(fTools::equalZero(fCross))
{ // get scale to edge. Use bigger distance for numeric quality constdouble fScale(fabs(aEdge.getX()) > fabs(aEdge.getY())
? aVecB.getX() / aEdge.getX()
: aVecB.getY() / aEdge.getY());
// end point of vector in edge range? Caution: controlB is directed AGAINST edge if (fTools::betweenOrEqualEither(fScale, -1.0, 0.0))
{
bBIsTrivial = true;
}
}
}
// if both are/can be reduced, do it. // Not possible if only one is/can be reduced (!) if(bAIsTrivial && bBIsTrivial)
{
maControlPointA = maStartPoint;
maControlPointB = maEndPoint;
}
}
void B2DCubicBezier::adaptiveSubdivideByAngle(B2DPolygon& rTarget, double fAngleBound) const
{ if(isBezier())
{ // use support method #i37443# and allow unsharpen the criteria
ImpSubDivAngleStart(maStartPoint, maControlPointA, maControlPointB, maEndPoint, rTarget,
deg2rad(fAngleBound));
} else
{
rTarget.append(getEndPoint());
}
}
B2DVector B2DCubicBezier::getTangent(double t) const
{ if(t <= 0.0)
{ // tangent in start point
B2DVector aTangent(getControlPointA() - getStartPoint());
if(!aTangent.equalZero())
{ return aTangent;
}
// start point and control vector are the same, fallback // to implicit start vector to control point B
aTangent = (getControlPointB() - getStartPoint()) * 0.3;
if(!aTangent.equalZero())
{ return aTangent;
}
// not a bezier at all, return edge vector return (getEndPoint() - getStartPoint()) * 0.3;
} elseif(fTools::moreOrEqual(t, 1.0))
{ // tangent in end point
B2DVector aTangent(getEndPoint() - getControlPointB());
if(!aTangent.equalZero())
{ return aTangent;
}
// end point and control vector are the same, fallback // to implicit start vector from control point A
aTangent = (getEndPoint() - getControlPointA()) * 0.3;
if(!aTangent.equalZero())
{ return aTangent;
}
// not a bezier at all, return edge vector return (getEndPoint() - getStartPoint()) * 0.3;
} else
{ // t is in ]0.0 .. 1.0[. Split and extract
B2DCubicBezier aRight;
split(t, nullptr, &aRight);
B2DPoint B2DCubicBezier::interpolatePoint(double t) const
{
OSL_ENSURE(t >= 0.0 && t <= 1.0, "B2DCubicBezier::interpolatePoint: Access out of range (!)");
// as start make a fix division, creates nInitialDivisions + 2 points
aInitialPolygon.append(getStartPoint());
adaptiveSubdivideByCount(aInitialPolygon, nInitialDivisions);
// now look for the closest point const sal_uInt32 nPointCount(aInitialPolygon.count());
B2DVector aVector(rTestPoint - aInitialPolygon.getB2DPoint(0)); double pointDistance(std::hypot(aVector.getX(), aVector.getY())); double newPointDistance;
sal_uInt32 nSmallestIndex(0);
// look right and left for even smaller distances double fStepValue(1.0 / static_cast<double>((nPointCount - 1) * 2)); // half the edge step width double fPosition(static_cast<double>(nSmallestIndex) / static_cast<double>(nPointCount - 1));
while(true)
{ // test left double fPosLeft(fPosition - fStepValue);
if(fEnd <= fStart)
{ // empty or NULL, create single point at center constdouble fSplit((fEnd + fStart) * 0.5); const B2DPoint aPoint(interpolate(getStartPoint(), getEndPoint(), fSplit));
aRetval.setStartPoint(aPoint);
aRetval.setEndPoint(aPoint);
aRetval.setControlPointA(aPoint);
aRetval.setControlPointB(aPoint);
} else
{ if(isBezier())
{ // copy bezier; cut off right, then cut off left. Do not forget to // adapt cut value when both cuts happen constbool bEndIsOne(fTools::equal(fEnd, 1.0)); constbool bStartIsZero(fTools::equalZero(fStart));
aRetval = *this;
namespace
{ void impCheckExtremumResult(double fCandidate, std::vector< double >& rResult)
{ // check for range ]0.0 .. 1.0[ with excluding 1.0 and 0.0 clearly // by using the equalZero test, NOT ::more or ::less which will use the // ApproxEqual() which is too exact here if(fCandidate > 0.0 && !fTools::equalZero(fCandidate))
{ if(fCandidate < 1.0 && !fTools::equalZero(fCandidate - 1.0))
{
rResult.push_back(fCandidate);
}
}
}
}
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.