blob: d0d2e3633de6b7c838fc89fc51497adf30ac5033 [file] [log] [blame]
/**************************************************************
*
* 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 <basegfx/curve/b2dbeziertools.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <algorithm>
//////////////////////////////////////////////////////////////////////////////
namespace basegfx
{
B2DCubicBezierHelper::B2DCubicBezierHelper(const B2DCubicBezier& rBase, sal_uInt32 nDivisions)
: maLengthArray(),
mnEdgeCount(0)
{
const bool bIsBezier(rBase.isBezier());
if(bIsBezier)
{
// check nDivisions; at least one is needed, but also prevent too big values
if(nDivisions < 1)
{
nDivisions = 1;
}
else if(nDivisions > 1000)
{
nDivisions = 1000;
}
// set nEdgeCount
mnEdgeCount = nDivisions + 1;
// fill in maLengthArray
maLengthArray.clear();
maLengthArray.reserve(mnEdgeCount);
B2DPoint aCurrent(rBase.getStartPoint());
double fLength(0.0);
for(sal_uInt32 a(1);;)
{
const B2DPoint aNext(rBase.interpolatePoint((double)a / (double)mnEdgeCount));
const B2DVector aEdge(aNext - aCurrent);
fLength += aEdge.getLength();
maLengthArray.push_back(fLength);
if(++a < mnEdgeCount)
{
aCurrent = aNext;
}
else
{
const B2DPoint aLastNext(rBase.getEndPoint());
const B2DVector aLastEdge(aLastNext - aNext);
fLength += aLastEdge.getLength();
maLengthArray.push_back(fLength);
break;
}
}
}
else
{
maLengthArray.clear();
maLengthArray.push_back(rBase.getEdgeLength());
mnEdgeCount = 1;
}
}
double B2DCubicBezierHelper::distanceToRelative(double fDistance) const
{
if(fDistance <= 0.0)
{
return 0.0;
}
const double fLength(getLength());
if(fTools::moreOrEqual(fDistance, fLength))
{
return 1.0;
}
// fDistance is in ]0.0 .. fLength[
if(1 == mnEdgeCount)
{
// not a bezier, linear edge
return fDistance / fLength;
}
// it is a bezier
::std::vector< double >::const_iterator aIter = ::std::lower_bound(maLengthArray.begin(), maLengthArray.end(), fDistance);
const sal_uInt32 nIndex(aIter - maLengthArray.begin());
const double fHighBound(maLengthArray[nIndex]);
const double fLowBound(nIndex ? maLengthArray[nIndex - 1] : 0.0);
const double fLinearInterpolatedLength((fDistance - fLowBound) / (fHighBound - fLowBound));
return (static_cast< double >(nIndex) + fLinearInterpolatedLength) / static_cast< double >(mnEdgeCount);
}
double B2DCubicBezierHelper::relativeToDistance(double fRelative) const
{
if(fRelative <= 0.0)
{
return 0.0;
}
const double fLength(getLength());
if(fTools::moreOrEqual(fRelative, 1.0))
{
return fLength;
}
// fRelative is in ]0.0 .. 1.0[
if(1 == mnEdgeCount)
{
// not a bezier, linear edge
return fRelative * fLength;
}
// fRelative is in ]0.0 .. 1.0[
const double fIndex(fRelative * static_cast< double >(mnEdgeCount));
double fIntIndex;
const double fFractIndex(modf(fIndex, &fIntIndex));
const sal_uInt32 nIntIndex(static_cast< sal_uInt32 >(fIntIndex));
const double fStartDistance(nIntIndex ? maLengthArray[nIntIndex - 1] : 0.0);
return fStartDistance + ((maLengthArray[nIntIndex] - fStartDistance) * fFractIndex);
}
} // end of namespace basegfx
//////////////////////////////////////////////////////////////////////////////
// eof