/* -*- 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 .
*/
if(o_rPolyPolygon.count() && !aCurrPoly.count() && aCurrChar != 'm' && aCurrChar != 'M')
{ // we have a new sub-polygon starting, but without a 'moveto' command. // this requires to add the current point as start point to the polygon // (see SVG1.1 8.3.3 The "closepath" command)
aCurrPoly.append(B2DPoint(nLastX, nLastY));
}
// create closed polygon and reset import values if(aCurrPoly.count())
{ if(!bHandleRelativeNextPointCompatible)
{ // SVG defines that "the next subpath starts at the // same initial point as the current subpath", so set the // current point if we do not need to be compatible
nLastX = aCurrPoly.getB2DPoint(0).getX();
nLastY = aCurrPoly.getB2DPoint(0).getY();
}
// ensure existence of start point
sal_uInt32 nCurrPolyCount = aCurrPoly.count(); if (nCurrPolyCount == 0)
{
aCurrPoly.append(B2DPoint(nLastX, nLastY));
nCurrPolyCount = 1;
}
assert(nCurrPolyCount > 0 && "coverity 2023.12.2");
// get first control point. It's the reflection of the PrevControlPoint // of the last point. If not existent, use current point (see SVG)
B2DPoint aPrevControl(nLastX, nLastY); const sal_uInt32 nIndex(nCurrPolyCount - 1);
// ensure existence of start point
sal_uInt32 nCurrPolyCount = aCurrPoly.count(); if (nCurrPolyCount == 0)
{
aCurrPoly.append(B2DPoint(nLastX, nLastY));
nCurrPolyCount = 1;
}
assert(nCurrPolyCount > 0 && "coverity 2023.12.2");
// get first control point. It's the reflection of the PrevControlPoint // of the last point. If not existent, use current point (see SVG)
B2DPoint aPrevControl(nLastX, nLastY); const sal_uInt32 nIndex(nCurrPolyCount - 1); const B2DPoint aPrevPoint(aCurrPoly.getB2DPoint(nIndex));
// use mirrored previous control point
aPrevControl.setX((2.0 * aPrevPoint.getX()) - aPrevControlPoint.getX());
aPrevControl.setY((2.0 * aPrevPoint.getY()) - aPrevControlPoint.getY());
}
if(!aPrevControl.equal(aPrevPoint))
{ // there is a prev control point, and we have the already mirrored one // in aPrevControl. We also need the quadratic control point for this // new quadratic segment to calculate the 2nd cubic control point const B2DPoint aQuadControlPoint(
((3.0 * aPrevControl.getX()) - aPrevPoint.getX()) / 2.0,
((3.0 * aPrevControl.getY()) - aPrevPoint.getY()) / 2.0);
// calculate the cubic bezier coefficients from the quadratic ones. constdouble nX2Prime((aQuadControlPoint.getX() * 2.0 + nX) / 3.0); constdouble nY2Prime((aQuadControlPoint.getY() * 2.0 + nY) / 3.0);
// append curved edge, use mirrored cubic control point directly
aCurrPoly.appendBezierSegment(aPrevControl, B2DPoint(nX2Prime, nY2Prime), B2DPoint(nX, nY));
} else
{ // when no previous control, SVG says to use current point -> straight line. // Just add end point
aCurrPoly.append(B2DPoint(nX, nY));
}
// set last position
nLastX = nX;
nLastY = nY;
} break;
}
// map both angles to [0,2pi)
fTheta1 = fmod(2*M_PI+fTheta1,2*M_PI);
fTheta2 = fmod(2*M_PI+fTheta2,2*M_PI);
// make sure the large arc is taken // (since // createPolygonFromEllipseSegment() // normalizes to e.g. cw arc) if( !bSweepFlag )
std::swap(fTheta1,fTheta2);
// finally, create bezier polygon from this
B2DPolygon aSegment(
utils::createPolygonFromUnitEllipseSegment(
fTheta1, fTheta2 ));
// transform ellipse by rotation & move to final center
aTransform = basegfx::utils::createScaleB2DHomMatrix(fRX, fRY);
aTransform.translate(aCenter_prime.getX(),
aCenter_prime.getY());
aTransform.rotate(deg2rad(fPhi)); const B2DPoint aOffset((p1+p2)/2.0);
aTransform.translate(aOffset.getX(),
aOffset.getY());
aSegment.transform(aTransform);
// createPolygonFromEllipseSegment() // always creates arcs that are // positively oriented - flip polygon // if we swapped angles above if( !bSweepFlag )
aSegment.flip();
// remember PointIndex of evtl. added pure helper points
sal_uInt32 nPointIndex(aCurrPoly.count() + 1);
aCurrPoly.append(aSegment);
// if asked for, mark pure helper points by adding them to the index list of // helper points if(pHelpPointIndexSet && aCurrPoly.count() > 1)
{ const sal_uInt32 nPolyIndex(o_rPolyPolygon.count());
OUStringBuffer aResult(std::max<int>(nCombinedPointCount * 32,512));
B2DPoint aCurrentSVGPosition(0.0, 0.0); // SVG assumes (0,0) as the initial current point
if(nPointCount)
{ constbool bPolyUsesControlPoints(aPolygon.areControlPointsUsed()); const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1);
sal_Unicode aLastSVGCommand(' '); // last SVG command char
B2DPoint aLeft, aRight; // for quadratic bezier test
// handle polygon start point
B2DPoint aEdgeStart(aPolygon.getB2DPoint(0)); bool bUseRelativeCoordinatesForFirstPoint(bUseRelativeCoordinates);
if(bHandleRelativeNextPointCompatible)
{ // To get around the error that the start point for the next polygon is the // start point of the current one (and not the last as it was handled up to now) // do force to write an absolute 'M' command as start for the next polygon
bUseRelativeCoordinatesForFirstPoint = false;
}
// Write 'moveto' and the 1st coordinates, set aLastSVGCommand to 'lineto'
putCommandChar(aResult, aLastSVGCommand, 'M', bUseRelativeCoordinatesForFirstPoint, bOOXMLMotionPath);
putNumberChar(aResult, aEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinatesForFirstPoint, bOOXMLMotionPath);
putNumberChar(aResult, aEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinatesForFirstPoint, bOOXMLMotionPath);
aLastSVGCommand = bUseRelativeCoordinatesForFirstPoint ? 'l' : 'L';
aCurrentSVGPosition = aEdgeStart;
for(sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++)
{ // prepare access to next point const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); const B2DPoint aEdgeEnd(aPolygon.getB2DPoint(nNextIndex));
// handle edge from (aEdgeStart, aEdgeEnd) using indices (nIndex, nNextIndex) constbool bEdgeIsBezier(bPolyUsesControlPoints
&& (aPolygon.isNextControlPointUsed(nIndex) || aPolygon.isPrevControlPointUsed(nNextIndex)));
// check continuity at current edge's start point. For SVG, do NOT use an // existing continuity since no 'S' or 's' statement should be written. At // import, that 'previous' control vector is not available. SVG documentation // says for interpretation:
// "(If there is no previous command or if the previous command was // not a C, c, S or s, assume the first control point is coincident // with the current point.)"
// That's what is done from our import, so avoid exporting it as first statement // is necessary. constbool bSymmetricAtEdgeStart(
!bOOXMLMotionPath && nIndex != 0
&& aPolygon.getContinuityInPoint(nIndex) == B2VectorContinuity::C2);
if(bDetectQuadraticBeziers)
{ // check for quadratic beziers - that's // the case if both control points are in // the same place when they are prolonged // to the common quadratic control point
putNumberChar(aResult, aControlEdgeStart.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates, bOOXMLMotionPath);
putNumberChar(aResult, aControlEdgeStart.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates, bOOXMLMotionPath);
putNumberChar(aResult, aControlEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates, bOOXMLMotionPath);
putNumberChar(aResult, aControlEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates, bOOXMLMotionPath);
putNumberChar(aResult, aEdgeEnd.getX(), aCurrentSVGPosition.getX(), bUseRelativeCoordinates, bOOXMLMotionPath);
putNumberChar(aResult, aEdgeEnd.getY(), aCurrentSVGPosition.getY(), bUseRelativeCoordinates, bOOXMLMotionPath);
aCurrentSVGPosition = aEdgeEnd;
}
}
} else
{ // straight edge if(nNextIndex == 0)
{ // it's a closed polygon's last edge and it's not a bezier edge, so there is // no need to write it
} else
{ constbool bXEqual(rtl::math::approxEqual(aEdgeStart.getX(), aEdgeEnd.getX())); constbool bYEqual(rtl::math::approxEqual(aEdgeStart.getY(), aEdgeEnd.getY()));
if(bXEqual && bYEqual)
{ // point is a double point; do not export at all
} elseif(bXEqual && !bOOXMLMotionPath)
{ // export as vertical line
putCommandChar(aResult, aLastSVGCommand, 'V', bUseRelativeCoordinates, bOOXMLMotionPath);
// prepare edge start for next loop step
aEdgeStart = aEdgeEnd;
}
// close path if closed poly (Z and z are equivalent here, but looks nicer when case is matched) if(aPolygon.isClosed())
{
putCommandChar(aResult, aLastSVGCommand, 'Z', bUseRelativeCoordinates, bOOXMLMotionPath);
} elseif (bOOXMLMotionPath)
{
putCommandChar(aResult, aLastSVGCommand, 'E', bUseRelativeCoordinates, bOOXMLMotionPath);
}
if(!bHandleRelativeNextPointCompatible)
{ // SVG defines that "the next subpath starts at the same initial point as the current subpath", // so set aCurrentSVGPosition to the 1st point of the current, now ended and written path
aCurrentSVGPosition = aPolygon.getB2DPoint(0);
}
}
}
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.