Index: src/core/SkGeometry.cpp |
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp |
index ac02afc6334644487cc55cb88aa0c6757203e7ab..7896b0db40d8debb0d682b13767bb8f1d151ad5b 100644 |
--- a/src/core/SkGeometry.cpp |
+++ b/src/core/SkGeometry.cpp |
@@ -1555,3 +1555,89 @@ SkScalar SkConic::TransformW(const SkPoint pts[], SkScalar w, |
w = SkScalarSqrt((w1 * w1) / (w0 * w2)); |
return w; |
} |
+ |
+int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir, |
+ const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) { |
+ // rotate by x,y so that uStart is (1.0) |
+ SkScalar x = SkPoint::DotProduct(uStart, uStop); |
+ SkScalar y = SkPoint::CrossProduct(uStart, uStop); |
+ |
+ SkScalar absY = SkScalarAbs(y); |
+ |
+ // check for (effectively) coincident vectors |
+ // this can happen if our angle is nearly 0 or nearly 180 (y == 0) |
+ // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) |
+ if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) || |
+ (y <= 0 && kCCW_SkRotationDirection == dir))) { |
+ return 0; |
+ } |
+ |
+ if (dir == kCCW_SkRotationDirection) { |
+ y = -y; |
+ } |
+ |
+ // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in? |
+ // 0 == [0 .. 90) |
+ // 1 == [90 ..180) |
+ // 2 == [180..270) |
+ // 3 == [270..360) |
+ // |
+ int quadrant = 0; |
+ if (0 == y) { |
+ quadrant = 2; // 180 |
+ SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero); |
+ } else if (0 == x) { |
+ SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero); |
+ quadrant = y > 0 ? 1 : 3; // 90 : 270 |
+ } else { |
+ if (y < 0) { |
+ quadrant += 2; |
+ } |
+ if ((x < 0) != (y < 0)) { |
+ quadrant += 1; |
+ } |
+ } |
+ |
+ const SkPoint quadrantPts[] = { |
+ { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 } |
+ }; |
+ const SkScalar quadrantWeight = SK_ScalarRoot2Over2; |
+ |
+ int conicCount = quadrant; |
+ for (int i = 0; i < conicCount; ++i) { |
+ dst[i].set(&quadrantPts[i * 2], quadrantWeight); |
+ } |
+ |
+ // Now compute any remaing (sub-90-degree) arc for the last conic |
+ const SkPoint finalP = { x, y }; |
+ const SkPoint& lastQ = quadrantPts[quadrant * 2]; // will already be a unit-vector |
+ const SkScalar dot = SkVector::DotProduct(lastQ, finalP); |
+ SkASSERT(0 <= dot && dot <= SK_Scalar1); |
+ |
+ if (dot < 1 - SK_ScalarNearlyZero) { |
+ SkVector offCurve = { lastQ.x() + x, lastQ.y() + y }; |
+ // compute the bisector vector, and then rescale to be the off-curve point. |
+ // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get |
+ // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot. |
+ // This is nice, since our computed weight is cos(theta/2) as well! |
+ // |
+ const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2); |
+ offCurve.setLength(SkScalarInvert(cosThetaOver2)); |
+ dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2); |
+ conicCount += 1; |
+ } |
+ |
+ // now handle counter-clockwise and the initial unitStart rotation |
+ SkMatrix matrix; |
+ matrix.setSinCos(uStart.fY, uStart.fX); |
+ if (dir == kCCW_SkRotationDirection) { |
+ matrix.preScale(SK_Scalar1, -SK_Scalar1); |
+ } |
+ if (userMatrix) { |
+ matrix.postConcat(*userMatrix); |
+ } |
+ for (int i = 0; i < conicCount; ++i) { |
+ matrix.mapPoints(dst[i].fPts, 3); |
+ } |
+ return conicCount; |
+} |