OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2016 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "SkCurveMeasure.h" |
| 9 |
| 10 // for abs |
| 11 #include <cmath> |
| 12 |
| 13 SkCurveMeasure::SkCurveMeasure(SkPoint pts[3]) { |
| 14 for (size_t i = 0; i < 3; i++) { |
| 15 fPts[i] = pts[i]; |
| 16 } |
| 17 // need to switch on segment type and template for the integrator or somethi
ng |
| 18 fIntegrator = new QuadArcLengthIntegrator(pts); |
| 19 } |
| 20 |
| 21 // this will be removed when switch to templated integrator or something |
| 22 SkCurveMeasure::~SkCurveMeasure() { |
| 23 delete fIntegrator; |
| 24 } |
| 25 |
| 26 SkScalar SkCurveMeasure::getLength() { |
| 27 if (-1.0 == fLength) { |
| 28 fLength = fIntegrator->computeLength(1.0f); |
| 29 } |
| 30 return fLength; |
| 31 } |
| 32 |
| 33 // Given an arc length targetLength, we want to determine what t |
| 34 // gives us the corresponding arc length along the curve. |
| 35 // We do this by letting the arc length integral := f(t) and |
| 36 // solving for the root of the equation f(t) - targetLength = 0 |
| 37 // using Newton's method and lerp-bisection. |
| 38 // The computationally expensive parts are the integral approximation |
| 39 // at each step, and computing the derivative of the arc length integral, |
| 40 // which is equal to the length of the tangent (so we have to do a sqrt). |
| 41 |
| 42 SkScalar SkCurveMeasure::getTime(SkScalar targetLength) { |
| 43 if (targetLength == 0.0f) { |
| 44 return 0.0f; |
| 45 } |
| 46 |
| 47 SkScalar currentLength = getLength(); |
| 48 |
| 49 if (SkScalarNearlyEqual(targetLength, currentLength)) { |
| 50 return 1.0f; |
| 51 } |
| 52 |
| 53 // initial estimate of t is percentage of total length |
| 54 SkScalar currentT = targetLength / currentLength; |
| 55 SkScalar prevT = -1.0f; |
| 56 SkScalar newT; |
| 57 |
| 58 SkScalar minT = 0.0f; |
| 59 SkScalar maxT = 1.0f; |
| 60 |
| 61 int iterations = 0; |
| 62 while (iterations < kNewtonIters + kBisectIters) { |
| 63 currentLength = fIntegrator->computeLength(currentT); |
| 64 SkScalar lengthDiff = currentLength - targetLength; |
| 65 |
| 66 // Update root bounds. |
| 67 // If lengthDiff is positive, we have overshot the target, so |
| 68 // we know the current t is an upper bound, and similarly |
| 69 // for the lower bound. |
| 70 if (lengthDiff > 0.0f) { |
| 71 if (currentT < maxT) { |
| 72 maxT = currentT; |
| 73 } |
| 74 } else { |
| 75 if (currentT > minT) { |
| 76 minT = currentT; |
| 77 } |
| 78 } |
| 79 |
| 80 // We have a tolerance on both the absolute value of the difference and |
| 81 // on the t value |
| 82 // because we may not have enough precision in the t to get close enough |
| 83 // in the length. |
| 84 if ((std::abs(lengthDiff) < kTolerance) || |
| 85 (std::abs(prevT - currentT) < kTolerance)) { |
| 86 break; |
| 87 } |
| 88 |
| 89 prevT = currentT; |
| 90 if (iterations < kNewtonIters) { |
| 91 // TODO(hstern) switch here on curve type. or template on an |
| 92 // evaluator. |
| 93 // This is just newton's formula. |
| 94 SkScalar dt = evaluateQuadDerivative(currentT).length(); |
| 95 newT = currentT - (lengthDiff / dt); |
| 96 |
| 97 // If newT is out of bounds, bisect inside newton. |
| 98 if ((newT < 0.0f) || (newT > 1.0f)) { |
| 99 newT = (minT + maxT) * 0.5f; |
| 100 } |
| 101 } else if (iterations < kNewtonIters + kBisectIters) { |
| 102 if (lengthDiff > 0.0f) { |
| 103 maxT = currentT; |
| 104 } else { |
| 105 minT = currentT; |
| 106 } |
| 107 // TODO(hstern) do a lerp here instead of a bisection |
| 108 newT = (minT + maxT) * 0.5f; |
| 109 } else { |
| 110 SkDebugf("%.7f %.7f didn't get close enough after bisection.\n", |
| 111 currentT, currentLength); |
| 112 break; |
| 113 } |
| 114 currentT = newT; |
| 115 |
| 116 SkASSERT(minT <= maxT); |
| 117 |
| 118 iterations++; |
| 119 } |
| 120 |
| 121 // debug. is there an SKDEBUG or something for ifdefs? |
| 122 fIters = iterations; |
| 123 |
| 124 return currentT; |
| 125 } |
| 126 |
| 127 void SkCurveMeasure::getPosTan(SkScalar targetLength, SkPoint* pos, |
| 128 SkVector* tan) { |
| 129 SkScalar t = getTime(targetLength); |
| 130 |
| 131 if (pos) { |
| 132 // TODO(hstern) switch here on curve type. or template on evaluator. |
| 133 *pos = evaluateQuad(t); |
| 134 } |
| 135 if (tan) { |
| 136 // TODO(hstern) switch here on curve type. or template on evaluator. |
| 137 *tan = evaluateQuadDerivative(t); |
| 138 } |
| 139 } |
| 140 |
| 141 // this is why I feel that the ArcLengthIntegrators should be combined |
| 142 // with some sort of evaluator that caches the constants computed from the |
| 143 // control points. this is basically the same code in QuadArcLengthIntegrator |
| 144 SkPoint SkCurveMeasure::evaluateQuad(SkScalar t) { |
| 145 SkScalar ti = 1.0f - t; |
| 146 |
| 147 SkScalar Ax = fPts[0].x(); |
| 148 SkScalar Bx = fPts[1].x(); |
| 149 SkScalar Cx = fPts[2].x(); |
| 150 SkScalar Ay = fPts[0].y(); |
| 151 SkScalar By = fPts[1].y(); |
| 152 SkScalar Cy = fPts[2].y(); |
| 153 |
| 154 SkScalar x = Ax*ti*ti + 2.0f*Bx*t*ti + Cx*t*t; |
| 155 SkScalar y = Ay*ti*ti + 2.0f*By*t*ti + Cy*t*t; |
| 156 return SkPoint::Make(x, y); |
| 157 } |
| 158 |
| 159 SkVector SkCurveMeasure::evaluateQuadDerivative(SkScalar t) { |
| 160 SkScalar Ax = fPts[0].x(); |
| 161 SkScalar Bx = fPts[1].x(); |
| 162 SkScalar Cx = fPts[2].x(); |
| 163 SkScalar Ay = fPts[0].y(); |
| 164 SkScalar By = fPts[1].y(); |
| 165 SkScalar Cy = fPts[2].y(); |
| 166 |
| 167 SkScalar A2BCx = 2.0f*(Ax - 2*Bx + Cx); |
| 168 SkScalar A2BCy = 2.0f*(Ay - 2*By + Cy); |
| 169 SkScalar ABx = 2.0f*(Bx - Ax); |
| 170 SkScalar ABy = 2.0f*(By - Ay); |
| 171 |
| 172 return SkPoint::Make(A2BCx*t + ABx, A2BCy*t + ABy); |
| 173 } |
OLD | NEW |