Chromium Code Reviews| 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 ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) : fSegType(segType) { | |
|
reed1
2016/08/02 21:46:23
exceed 100cols
Harry Stern
2016/08/02 22:05:07
Done, and various other formatting stuff.
| |
| 14 switch (fSegType) { | |
| 15 case kQuad_SegType: { | |
| 16 float Ax = pts[0].x(); | |
| 17 float Bx = pts[1].x(); | |
| 18 float Cx = pts[2].x(); | |
| 19 float Ay = pts[0].y(); | |
| 20 float By = pts[1].y(); | |
| 21 float Cy = pts[2].y(); | |
| 22 | |
| 23 // precompute coefficients for derivative | |
| 24 A2BC_x = Sk8f(2.0f*(Ax - 2*Bx + Cx)); | |
| 25 A2BC_y = Sk8f(2.0f*(Ay - 2*By + Cy)); | |
| 26 | |
| 27 AB_x = Sk8f(2.0f*(Bx - Ax)); | |
| 28 AB_y = Sk8f(2.0f*(By - Ay)); | |
| 29 } | |
| 30 break; | |
| 31 case kLine_SegType: | |
| 32 SkDebugf("Unimplemented"); | |
| 33 break; | |
| 34 case kCubic_SegType: | |
| 35 SkDebugf("Unimplemented"); | |
| 36 break; | |
| 37 case kConic_SegType: | |
| 38 SkDebugf("Unimplemented"); | |
| 39 break; | |
| 40 default: | |
| 41 SkDebugf("Unimplemented"); | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 // We use Gaussian quadrature (https://en.wikipedia.org/wiki/Gaussian_quadrature ) | |
| 46 // to approximate the arc length integral here, because it is amenable to SIMD. | |
| 47 SkScalar ArcLengthIntegrator::computeLength(SkScalar t) { | |
| 48 SkScalar length = 0.0f; | |
| 49 | |
| 50 Sk8f lengths = evaluateDerivativeLength(absc*t); | |
| 51 lengths = weights*lengths; | |
| 52 // is it faster or more accurate to sum and then multiply or vice versa? | |
| 53 lengths = lengths*(t*0.5f); | |
| 54 | |
| 55 // Why does SkNx index with ints? does negative index mean something? | |
| 56 for (int i = 0; i < 8; i++) { | |
| 57 length += lengths[i]; | |
| 58 } | |
| 59 return length; | |
| 60 } | |
| 61 | |
| 62 Sk8f ArcLengthIntegrator::evaluateDerivativeLength(Sk8f ts) { | |
|
Harry Stern
2016/08/02 21:33:21
Windows buildbots are giving me
"error C2719: 'ts'
reed1
2016/08/02 21:46:23
Can you pass ts by const& ?
Harry Stern
2016/08/02 22:05:08
Mike Klein told me I might be able to use SK_VECTO
mtklein
2016/08/02 22:32:35
Actually I think I had a rather more nuanced respo
| |
| 63 Sk8f x; | |
| 64 Sk8f y; | |
| 65 switch (fSegType) { | |
| 66 case kQuad_SegType: | |
| 67 x = A2BC_x*ts + AB_x; | |
| 68 y = A2BC_y*ts + AB_y; | |
| 69 break; | |
| 70 case kLine_SegType: | |
| 71 SkDebugf("Unimplemented"); | |
| 72 break; | |
| 73 case kCubic_SegType: | |
| 74 SkDebugf("Unimplemented"); | |
| 75 break; | |
| 76 case kConic_SegType: | |
| 77 SkDebugf("Unimplemented"); | |
| 78 break; | |
| 79 default: | |
| 80 SkDebugf("Unimplemented"); | |
| 81 } | |
| 82 | |
| 83 x = x * x; | |
| 84 y = y * y; | |
| 85 | |
| 86 return (x + y).sqrt(); | |
| 87 } | |
| 88 | |
| 89 | |
| 90 SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType) : fSegType (segType) { | |
| 91 switch (fSegType) { | |
| 92 case SkSegType::kQuad_SegType: | |
| 93 for (size_t i = 0; i < 3; i++) { | |
| 94 fPts[i] = pts[i]; | |
| 95 } | |
| 96 fIntegrator = ArcLengthIntegrator(fPts, fSegType); | |
| 97 break; | |
| 98 default: | |
| 99 SkDebugf("Unimplemented"); | |
| 100 break; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 SkScalar SkCurveMeasure::getLength() { | |
| 105 if (-1.0f == fLength) { | |
| 106 fLength = fIntegrator.computeLength(1.0f); | |
| 107 } | |
| 108 return fLength; | |
| 109 } | |
| 110 | |
| 111 // Given an arc length targetLength, we want to determine what t | |
| 112 // gives us the corresponding arc length along the curve. | |
| 113 // We do this by letting the arc length integral := f(t) and | |
| 114 // solving for the root of the equation f(t) - targetLength = 0 | |
| 115 // using Newton's method and lerp-bisection. | |
| 116 // The computationally expensive parts are the integral approximation | |
| 117 // at each step, and computing the derivative of the arc length integral, | |
| 118 // which is equal to the length of the tangent (so we have to do a sqrt). | |
| 119 | |
| 120 SkScalar SkCurveMeasure::getTime(SkScalar targetLength) { | |
| 121 if (targetLength == 0.0f) { | |
| 122 return 0.0f; | |
| 123 } | |
| 124 | |
| 125 SkScalar currentLength = getLength(); | |
| 126 | |
| 127 if (SkScalarNearlyEqual(targetLength, currentLength)) { | |
| 128 return 1.0f; | |
| 129 } | |
| 130 | |
| 131 // initial estimate of t is percentage of total length | |
| 132 SkScalar currentT = targetLength / currentLength; | |
| 133 SkScalar prevT = -1.0f; | |
| 134 SkScalar newT; | |
| 135 | |
| 136 SkScalar minT = 0.0f; | |
| 137 SkScalar maxT = 1.0f; | |
| 138 | |
| 139 int iterations = 0; | |
| 140 while (iterations < kNewtonIters + kBisectIters) { | |
| 141 currentLength = fIntegrator.computeLength(currentT); | |
| 142 SkScalar lengthDiff = currentLength - targetLength; | |
| 143 | |
| 144 // Update root bounds. | |
| 145 // If lengthDiff is positive, we have overshot the target, so | |
| 146 // we know the current t is an upper bound, and similarly | |
| 147 // for the lower bound. | |
| 148 if (lengthDiff > 0.0f) { | |
| 149 if (currentT < maxT) { | |
| 150 maxT = currentT; | |
| 151 } | |
| 152 } else { | |
| 153 if (currentT > minT) { | |
| 154 minT = currentT; | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 // We have a tolerance on both the absolute value of the difference and | |
| 159 // on the t value | |
| 160 // because we may not have enough precision in the t to get close enough | |
| 161 // in the length. | |
| 162 if ((std::abs(lengthDiff) < kTolerance) || | |
| 163 (std::abs(prevT - currentT) < kTolerance)) { | |
| 164 break; | |
| 165 } | |
| 166 | |
| 167 prevT = currentT; | |
| 168 if (iterations < kNewtonIters) { | |
| 169 // TODO(hstern) switch here on curve type. | |
| 170 // This is just newton's formula. | |
| 171 SkScalar dt = evaluateQuadDerivative(currentT).length(); | |
| 172 newT = currentT - (lengthDiff / dt); | |
| 173 | |
| 174 // If newT is out of bounds, bisect inside newton. | |
| 175 if ((newT < 0.0f) || (newT > 1.0f)) { | |
| 176 newT = (minT + maxT) * 0.5f; | |
| 177 } | |
| 178 } else if (iterations < kNewtonIters + kBisectIters) { | |
| 179 if (lengthDiff > 0.0f) { | |
| 180 maxT = currentT; | |
| 181 } else { | |
| 182 minT = currentT; | |
| 183 } | |
| 184 // TODO(hstern) do a lerp here instead of a bisection | |
| 185 newT = (minT + maxT) * 0.5f; | |
| 186 } else { | |
| 187 SkDebugf("%.7f %.7f didn't get close enough after bisection.\n", | |
| 188 currentT, currentLength); | |
| 189 break; | |
| 190 } | |
| 191 currentT = newT; | |
| 192 | |
| 193 SkASSERT(minT <= maxT); | |
| 194 | |
| 195 iterations++; | |
| 196 } | |
| 197 | |
| 198 // debug. is there an SKDEBUG or something for ifdefs? | |
| 199 fIters = iterations; | |
| 200 | |
| 201 return currentT; | |
| 202 } | |
| 203 | |
| 204 void SkCurveMeasure::getPosTan(SkScalar targetLength, SkPoint* pos, | |
| 205 SkVector* tan) { | |
| 206 SkScalar t = getTime(targetLength); | |
| 207 | |
| 208 if (pos) { | |
| 209 // TODO(hstern) switch here on curve type. | |
| 210 *pos = evaluateQuad(t); | |
| 211 } | |
| 212 if (tan) { | |
| 213 // TODO(hstern) switch here on curve type. | |
| 214 *tan = evaluateQuadDerivative(t); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 // this is why I feel that the ArcLengthIntegrator should be combined | |
| 219 // with some sort of evaluator that caches the constants computed from the | |
| 220 // control points. this is basically the same code in ArcLengthIntegrator | |
| 221 SkPoint SkCurveMeasure::evaluateQuad(SkScalar t) { | |
| 222 SkScalar ti = 1.0f - t; | |
| 223 | |
| 224 SkScalar Ax = fPts[0].x(); | |
| 225 SkScalar Bx = fPts[1].x(); | |
| 226 SkScalar Cx = fPts[2].x(); | |
| 227 SkScalar Ay = fPts[0].y(); | |
| 228 SkScalar By = fPts[1].y(); | |
| 229 SkScalar Cy = fPts[2].y(); | |
| 230 | |
| 231 SkScalar x = Ax*ti*ti + 2.0f*Bx*t*ti + Cx*t*t; | |
| 232 SkScalar y = Ay*ti*ti + 2.0f*By*t*ti + Cy*t*t; | |
| 233 return SkPoint::Make(x, y); | |
| 234 } | |
| 235 | |
| 236 SkVector SkCurveMeasure::evaluateQuadDerivative(SkScalar t) { | |
| 237 SkScalar Ax = fPts[0].x(); | |
| 238 SkScalar Bx = fPts[1].x(); | |
| 239 SkScalar Cx = fPts[2].x(); | |
| 240 SkScalar Ay = fPts[0].y(); | |
| 241 SkScalar By = fPts[1].y(); | |
| 242 SkScalar Cy = fPts[2].y(); | |
| 243 | |
| 244 SkScalar A2BCx = 2.0f*(Ax - 2*Bx + Cx); | |
| 245 SkScalar A2BCy = 2.0f*(Ay - 2*By + Cy); | |
| 246 SkScalar ABx = 2.0f*(Bx - Ax); | |
| 247 SkScalar ABy = 2.0f*(By - Ay); | |
| 248 | |
| 249 return SkPoint::Make(A2BCx*t + ABx, A2BCy*t + ABy); | |
| 250 } | |
| OLD | NEW |