/* -*- 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 .
*/
// init return value from arrow
aRetval.append(rArrow);
// get size of the arrow const B2DRange aArrowSize(getRange(rArrow));
// build ArrowTransform; center in X, align with axis in Y
B2DHomMatrix aArrowTransform(basegfx::utils::createTranslateB2DHomMatrix(
-aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
// get arrow size in Y
B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
aUpperCenter *= aArrowTransform; constdouble fArrowYLength(B2DVector(aUpperCenter).getLength());
// move arrow to have docking position centered
aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition + fShift);
// get the polygon vector we want to plant this arrow on constdouble fConsumedLength(fArrowYLength * (1.0 - fDockingPosition) - fShift); const B2DVector aHead(rCandidate.getB2DPoint(bStart ? 0 : rCandidate.count() - 1)); const B2DVector aTail(getPositionAbsolute(rCandidate,
bStart ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
// from that vector, take the needed rotation and add rotate for arrow to transformation const B2DVector aTargetDirection(aHead - aTail); constdouble fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + M_PI_2);
// rotate around docking position
aArrowTransform.rotate(fRotation);
// move arrow docking position to polygon head
aArrowTransform.translate(aHead.getX(), aHead.getY());
// transform retval and close
aRetval.transform(aArrowTransform);
aRetval.setClosed(true);
// if pConsumedLength is asked for, fill it if(pConsumedLength)
{
*pConsumedLength = fConsumedLength;
}
}
return aRetval;
}
} // end of namespace
namespace basegfx
{ // anonymous namespace for local helpers namespace
{ bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
{ // isBezier() is true, already tested by caller const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
if(aEdge.equalZero())
{ // start and end point the same, but control vectors used -> balloon curve loop // is not a simple edge returnfalse;
}
// get tangentA and scalar with edge const B2DVector aTangentA(rCandidate.getTangent(0.0)); constdouble fScalarAE(aEdge.scalar(aTangentA));
if(fScalarAE <= 0.0)
{ // angle between TangentA and Edge is bigger or equal 90 degrees returnfalse;
}
// get self-scalars for E and A constdouble fScalarE(aEdge.scalar(aEdge)); constdouble fScalarA(aTangentA.scalar(aTangentA)); constdouble fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
{ // length of TangentA is more than fMaxPartOfEdge of length of edge returnfalse;
}
if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
{ // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos returnfalse;
}
// get tangentB and scalar with edge const B2DVector aTangentB(rCandidate.getTangent(1.0)); constdouble fScalarBE(aEdge.scalar(aTangentB));
if(fScalarBE <= 0.0)
{ // angle between TangentB and Edge is bigger or equal 90 degrees returnfalse;
}
// get self-scalar for B constdouble fScalarB(aTangentB.scalar(aTangentB));
if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
{ // length of TangentB is more than fMaxPartOfEdge of length of edge returnfalse;
}
if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
{ // angle between TangentB and Edge is bigger or equal defined by fMaxCos returnfalse;
}
// prepare edge for loop
aEdge.setStartPoint(rCandidate.getB2DPoint(0));
aRetval.append(aEdge.getStartPoint());
for(sal_uInt32 a(0); a < nEdgeCount; a++)
{ // fill B2DCubicBezier const sal_uInt32 nNextIndex((a + 1) % nPointCount);
aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
// get rid of unnecessary bezier segments
aEdge.testAndSolveTrivialBezier();
if(aEdge.isBezier())
{ // before splitting recursively with internal simple criteria, use // ExtremumPosFinder to remove those
std::vector< double > aExtremumPositions;
// prepare edge for next step
aEdge.setStartPoint(aEdge.getEndPoint());
}
// copy closed flag and check for double points
aRetval.setClosed(rCandidate.isClosed());
aRetval.removeDoublePoints();
return aRetval;
} else
{ return rCandidate;
}
}
B2DPolygon createAreaGeometryForEdge( const B2DCubicBezier& rEdge, double fHalfLineWidth, bool bStartRound, bool bEndRound, bool bStartSquare, bool bEndSquare)
{ // create polygon for edge // Unfortunately, while it would be geometrically correct to not add // the in-between points EdgeEnd and EdgeStart, it leads to rounding // errors when converting to integer polygon coordinates for painting if(rEdge.isBezier())
{ // prepare target and data common for upper and lower
B2DPolygon aBezierPolygon; const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint()); constdouble fEdgeLength(aPureEdgeVector.getLength()); constbool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize(); const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA)); const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB));
if(bStartRound || bEndRound)
{ // double points possible when round caps are used at start or end
aBezierPolygon.removeDoublePoints();
}
if(bCut && ((bStartRound || bStartSquare) && (bEndRound || bEndSquare)))
{ // When cut exists and both ends are extended with caps, a self-intersecting polygon // is created; one cut point is known, but there is a 2nd one in the caps geometry. // Solve by using tooling. // Remark: This nearly never happens due to curve preparations to extreme points // and maximum angle turning, but I constructed a test case and checked that it is // working properly. const B2DPolyPolygon aTemp(utils::solveCrossovers(aBezierPolygon)); const sal_uInt32 nTempCount(aTemp.count());
if(nTempCount)
{ if(nTempCount > 1)
{ // as expected, multiple polygons (with same orientation). Remove // the one which contains aCutPoint, or better take the one without for (sal_uInt32 a(0); a < nTempCount; a++)
{
aBezierPolygon = aTemp.getB2DPolygon(a);
OSL_ENSURE(aBezierPolygon.count(), "Error in line geometry creation, could not solve self-intersection (!)");
} else
{ // none found, use result
aBezierPolygon = aTemp.getB2DPolygon(0);
}
} else
{
OSL_ENSURE(false, "Error in line geometry creation, could not solve self-intersection (!)");
}
}
// return return aBezierPolygon;
} else
{ // Get start and end point, create tangent and set to needed length
B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
aTangent.setLength(fHalfLineWidth);
// LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
B2DPolygon aEdgePolygon; const B2DPoint aStartPoint(rPoint + rPerpendPrev); const B2DPoint aEndPoint(rPoint + rPerpendEdge);
// test if for Miter, the angle is too small and the fallback // to bevel needs to be used if(eJoin == B2DLineJoin::Miter)
{ constdouble fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
switch(eJoin)
{ case B2DLineJoin::Miter :
{
aEdgePolygon.append(aEndPoint);
aEdgePolygon.append(rPoint);
aEdgePolygon.append(aStartPoint);
// Look for the cut point between start point along rTangentPrev and // end point along rTangentEdge. -rTangentEdge should be used, but since // the cut value is used for interpolating along the first edge, the negation // is not needed since the same fCut will be found on the first edge. // If it exists, insert it to complete the mitered fill polygon. double fCutPos(0.0);
utils::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CutFlagValue::ALL, &fCutPos);
if(aBow.count() > 1)
{ // #i101491# // use the original start/end positions; the ones from bow creation may be numerically // different due to their different creation. To guarantee good merging quality with edges // and edge roundings (and to reduce point count)
aEdgePolygon = aBow;
aEdgePolygon.setB2DPoint(0, aStartPoint);
aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
aEdgePolygon.append(rPoint);
if(aOrientation == B2VectorOrientation::Neutral)
{ // they are parallel or empty; if they are both not zero and point // in opposite direction, a half-circle is needed if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
{ constdouble fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
if(fTools::equal(fAngle, M_PI))
{ // for half-circle production, fallback to positive // orientation
aOrientation = B2VectorOrientation::Positive;
}
}
}
aEdge.setStartPoint(aEdge.getEndPoint());
}
}
} else
{ // point count, but no edge count -> single point const basegfx::B2DPolygon aCircle(
createPolygonFromCircle(
aCandidate.getB2DPoint(0),
fHalfLineWidth));
aRetval.append(aCircle);
}
return aRetval;
} else
{ return B2DPolyPolygon(rCandidate);
}
}
} // end of namespace utils
} // end of namespace basegfx
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.