Created
March 7, 2013 07:15
-
-
Save chenzx/5106148 to your computer and use it in GitHub Desktop.
Convert arc to bezier cubic curves, code from WebKit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This works by converting the SVG arc to "simple" beziers. | |
| // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. | |
| // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter | |
| bool decomposeArcToCubic(float angle, float rx, float ry, FloatPoint& point1, FloatPoint& point2, bool largeArcFlag, bool sweepFlag, | |
| std::vector<FloatPoint>& v1, std::vector<FloatPoint>& v2, std::vector<FloatPoint>& v | |
| ) | |
| { | |
| FloatSize midPointDistance = point1 - point2; | |
| midPointDistance.scale(0.5f); | |
| AffineTransform pointTransform; | |
| pointTransform.rotate(-angle); | |
| FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPointDistance.width(), midPointDistance.height())); | |
| float squareRx = rx * rx; | |
| float squareRy = ry * ry; | |
| float squareX = transformedMidPoint.x() * transformedMidPoint.x(); | |
| float squareY = transformedMidPoint.y() * transformedMidPoint.y(); | |
| // Check if the radii are big enough to draw the arc, scale radii if not. | |
| // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii | |
| float radiiScale = squareX / squareRx + squareY / squareRy; | |
| if (radiiScale > 1) { | |
| rx *= sqrtf(radiiScale); | |
| ry *= sqrtf(radiiScale); | |
| } | |
| pointTransform.makeIdentity(); | |
| pointTransform.scale(1 / rx, 1 / ry); | |
| pointTransform.rotate(-angle); | |
| point1 = pointTransform.mapPoint(point1); | |
| point2 = pointTransform.mapPoint(point2); | |
| FloatSize delta = point2 - point1; | |
| float d = delta.width() * delta.width() + delta.height() * delta.height(); | |
| float scaleFactorSquared = (std::max)(1 / d - 0.25f, 0.f); | |
| float scaleFactor = sqrtf(scaleFactorSquared); | |
| if (sweepFlag == largeArcFlag) | |
| scaleFactor = -scaleFactor; | |
| delta.scale(scaleFactor); | |
| FloatPoint centerPoint = FloatPoint(0.5f * (point1.x() + point2.x()) - delta.height(), | |
| 0.5f * (point1.y() + point2.y()) + delta.width()); | |
| float theta1 = atan2f(point1.y() - centerPoint.y(), point1.x() - centerPoint.x()); | |
| float theta2 = atan2f(point2.y() - centerPoint.y(), point2.x() - centerPoint.x()); | |
| float thetaArc = theta2 - theta1; | |
| if (thetaArc < 0 && sweepFlag) | |
| thetaArc += 2 * piFloat; | |
| else if (thetaArc > 0 && !sweepFlag) | |
| thetaArc -= 2 * piFloat; | |
| pointTransform.makeIdentity(); | |
| pointTransform.rotate(angle); | |
| pointTransform.scale(rx, ry); | |
| // Some results of atan2 on some platform implementations are not exact enough. So that we get more | |
| // cubic curves than expected here. Adding 0.001f reduces the count of sgements to the correct count. | |
| int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); | |
| for (int i = 0; i < segments; ++i) { | |
| float startTheta = theta1 + i * thetaArc / segments; | |
| float endTheta = theta1 + (i + 1) * thetaArc / segments; | |
| float t = (8 / 6.f) * tanf(0.25f * (endTheta - startTheta)); | |
| if (!isfinite(t)) | |
| return false; | |
| float sinStartTheta = sinf(startTheta); | |
| float cosStartTheta = cosf(startTheta); | |
| float sinEndTheta = sinf(endTheta); | |
| float cosEndTheta = cosf(endTheta); | |
| point1 = FloatPoint(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta); | |
| point1.move(centerPoint.x(), centerPoint.y()); | |
| FloatPoint targetPoint = FloatPoint(cosEndTheta, sinEndTheta); | |
| targetPoint.move(centerPoint.x(), centerPoint.y()); | |
| point2 = targetPoint; | |
| point2.move(t * sinEndTheta, -t * cosEndTheta); | |
| /* | |
| m_consumer->curveToCubic(pointTransform.mapPoint(point1), pointTransform.mapPoint(point2), | |
| pointTransform.mapPoint(targetPoint), AbsoluteCoordinates); | |
| */ | |
| FloatPoint p1 = pointTransform.mapPoint(point1); | |
| FloatPoint p2 = pointTransform.mapPoint(point2); | |
| FloatPoint p = pointTransform.mapPoint(targetPoint); | |
| TRACE1("%s: cubicCurveto x1=%f y1=%f x2=%f y2=%f x=%f y=%f\n", __FUNCTION__, p1.x(), p1.y(), p2.x(), p2.y(), p.x(), p.y()); | |
| v1.push_back( p1 ); | |
| v2.push_back( p2 ); | |
| v.push_back( p ); | |
| } | |
| return true; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment