blob: 823f56adcff9388f8e7f82980afee91bfa1c2e5d [file] [log] [blame]
hstern0446a3c2016-08-08 12:28:13 -07001/*
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"
hstern4ab47e02016-08-10 10:55:09 -07009#include "SkGeometry.h"
hstern0446a3c2016-08-08 12:28:13 -070010
11// for abs
12#include <cmath>
13
hstern4ab47e02016-08-10 10:55:09 -070014#define UNIMPLEMENTED SkDEBUGF(("%s:%d unimplemented\n", __FILE__, __LINE__))
15
16/// Used inside SkCurveMeasure::getTime's Newton's iteration
17static inline SkPoint evaluate(const SkPoint pts[4], SkSegType segType,
18 SkScalar t) {
19 SkPoint pos;
20 switch (segType) {
21 case kQuad_SegType:
22 pos = SkEvalQuadAt(pts, t);
23 break;
24 case kLine_SegType:
25 pos = SkPoint::Make(SkScalarInterp(pts[0].x(), pts[1].x(), t),
26 SkScalarInterp(pts[0].y(), pts[1].y(), t));
27 break;
28 case kCubic_SegType:
29 SkEvalCubicAt(pts, t, &pos, nullptr, nullptr);
30 break;
31 case kConic_SegType: {
32 SkConic conic(pts, pts[3].x());
33 conic.evalAt(t, &pos);
34 }
35 break;
36 default:
37 UNIMPLEMENTED;
38 }
39
40 return pos;
41}
42
43/// Used inside SkCurveMeasure::getTime's Newton's iteration
44static inline SkVector evaluateDerivative(const SkPoint pts[4],
45 SkSegType segType, SkScalar t) {
46 SkVector tan;
47 switch (segType) {
48 case kQuad_SegType:
49 tan = SkEvalQuadTangentAt(pts, t);
50 break;
51 case kLine_SegType:
52 tan = pts[1] - pts[0];
53 break;
54 case kCubic_SegType:
55 SkEvalCubicAt(pts, t, nullptr, &tan, nullptr);
56 break;
57 case kConic_SegType: {
58 SkConic conic(pts, pts[3].x());
59 conic.evalAt(t, nullptr, &tan);
60 }
61 break;
62 default:
63 UNIMPLEMENTED;
64 }
65
66 return tan;
67}
68/// Used in ArcLengthIntegrator::computeLength
hstern0446a3c2016-08-08 12:28:13 -070069static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
70 const Sk8f (&xCoeff)[3],
71 const Sk8f (&yCoeff)[3],
72 const SkSegType segType) {
73 Sk8f x;
74 Sk8f y;
75 switch (segType) {
76 case kQuad_SegType:
77 x = xCoeff[0]*ts + xCoeff[1];
78 y = yCoeff[0]*ts + yCoeff[1];
79 break;
80 case kLine_SegType:
hstern4ab47e02016-08-10 10:55:09 -070081 // length of line derivative is constant
82 // and we precompute it in the constructor
83 return xCoeff[0];
hstern0446a3c2016-08-08 12:28:13 -070084 case kCubic_SegType:
85 x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2];
86 y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2];
87 break;
88 case kConic_SegType:
hstern4ab47e02016-08-10 10:55:09 -070089 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070090 break;
91 default:
hstern4ab47e02016-08-10 10:55:09 -070092 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070093 }
94
95 x = x * x;
96 y = y * y;
97
98 return (x + y).sqrt();
99}
hstern4ab47e02016-08-10 10:55:09 -0700100
hstern0446a3c2016-08-08 12:28:13 -0700101ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
102 : fSegType(segType) {
103 switch (fSegType) {
104 case kQuad_SegType: {
105 float Ax = pts[0].x();
106 float Bx = pts[1].x();
107 float Cx = pts[2].x();
108 float Ay = pts[0].y();
109 float By = pts[1].y();
110 float Cy = pts[2].y();
111
112 // precompute coefficients for derivative
113 xCoeff[0] = Sk8f(2.0f*(Ax - 2*Bx + Cx));
114 xCoeff[1] = Sk8f(2.0f*(Bx - Ax));
115
116 yCoeff[0] = Sk8f(2.0f*(Ay - 2*By + Cy));
117 yCoeff[1] = Sk8f(2.0f*(By - Ay));
118 }
119 break;
hstern4ab47e02016-08-10 10:55:09 -0700120 case kLine_SegType: {
121 // the length of the derivative of a line is constant
122 // we put in in both coeff arrays for consistency's sake
123 SkScalar length = (pts[1] - pts[0]).length();
124 xCoeff[0] = Sk8f(length);
125 yCoeff[0] = Sk8f(length);
126 }
hstern0446a3c2016-08-08 12:28:13 -0700127 break;
128 case kCubic_SegType:
129 {
130 float Ax = pts[0].x();
131 float Bx = pts[1].x();
132 float Cx = pts[2].x();
133 float Dx = pts[3].x();
134 float Ay = pts[0].y();
135 float By = pts[1].y();
136 float Cy = pts[2].y();
137 float Dy = pts[3].y();
138
hstern4ab47e02016-08-10 10:55:09 -0700139 // precompute coefficients for derivative
hstern0446a3c2016-08-08 12:28:13 -0700140 xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx));
141 xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx)));
142 xCoeff[2] = Sk8f(3.0f*(-Ax + Bx));
143
144 yCoeff[0] = Sk8f(3.0f*(-Ay + 3.0f*(By - Cy) + Dy));
145 yCoeff[1] = Sk8f(3.0f * -Ay + By + 2.0f*(Ay - 2.0f*By + Cy));
146 yCoeff[2] = Sk8f(3.0f*(-Ay + By));
147 }
148 break;
149 case kConic_SegType:
hstern4ab47e02016-08-10 10:55:09 -0700150 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700151 break;
152 default:
hstern4ab47e02016-08-10 10:55:09 -0700153 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700154 }
155}
156
157// We use Gaussian quadrature
158// (https://en.wikipedia.org/wiki/Gaussian_quadrature)
159// to approximate the arc length integral here, because it is amenable to SIMD.
160SkScalar ArcLengthIntegrator::computeLength(SkScalar t) {
161 SkScalar length = 0.0f;
162
163 Sk8f lengths = evaluateDerivativeLength(absc*t, xCoeff, yCoeff, fSegType);
164 lengths = weights*lengths;
165 // is it faster or more accurate to sum and then multiply or vice versa?
166 lengths = lengths*(t*0.5f);
167
168 // Why does SkNx index with ints? does negative index mean something?
169 for (int i = 0; i < 8; i++) {
170 length += lengths[i];
171 }
172 return length;
173}
174
175SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
176 : fSegType(segType) {
177 switch (fSegType) {
178 case SkSegType::kQuad_SegType:
179 for (size_t i = 0; i < 3; i++) {
180 fPts[i] = pts[i];
181 }
182 break;
183 case SkSegType::kLine_SegType:
hstern4ab47e02016-08-10 10:55:09 -0700184 fPts[0] = pts[0];
185 fPts[1] = pts[1];
hstern0446a3c2016-08-08 12:28:13 -0700186 break;
187 case SkSegType::kCubic_SegType:
188 for (size_t i = 0; i < 4; i++) {
189 fPts[i] = pts[i];
190 }
191 break;
192 case SkSegType::kConic_SegType:
hstern4ab47e02016-08-10 10:55:09 -0700193 for (size_t i = 0; i < 4; i++) {
194 fPts[i] = pts[i];
195 }
hstern0446a3c2016-08-08 12:28:13 -0700196 break;
197 default:
hstern4ab47e02016-08-10 10:55:09 -0700198 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700199 break;
200 }
201 fIntegrator = ArcLengthIntegrator(fPts, fSegType);
202}
203
204SkScalar SkCurveMeasure::getLength() {
205 if (-1.0f == fLength) {
206 fLength = fIntegrator.computeLength(1.0f);
207 }
208 return fLength;
209}
210
211// Given an arc length targetLength, we want to determine what t
212// gives us the corresponding arc length along the curve.
213// We do this by letting the arc length integral := f(t) and
214// solving for the root of the equation f(t) - targetLength = 0
215// using Newton's method and lerp-bisection.
216// The computationally expensive parts are the integral approximation
217// at each step, and computing the derivative of the arc length integral,
218// which is equal to the length of the tangent (so we have to do a sqrt).
219
220SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
221 if (targetLength == 0.0f) {
222 return 0.0f;
223 }
224
225 SkScalar currentLength = getLength();
226
227 if (SkScalarNearlyEqual(targetLength, currentLength)) {
228 return 1.0f;
229 }
230
231 // initial estimate of t is percentage of total length
232 SkScalar currentT = targetLength / currentLength;
233 SkScalar prevT = -1.0f;
234 SkScalar newT;
235
236 SkScalar minT = 0.0f;
237 SkScalar maxT = 1.0f;
238
239 int iterations = 0;
240 while (iterations < kNewtonIters + kBisectIters) {
241 currentLength = fIntegrator.computeLength(currentT);
242 SkScalar lengthDiff = currentLength - targetLength;
243
244 // Update root bounds.
245 // If lengthDiff is positive, we have overshot the target, so
246 // we know the current t is an upper bound, and similarly
247 // for the lower bound.
248 if (lengthDiff > 0.0f) {
249 if (currentT < maxT) {
250 maxT = currentT;
251 }
252 } else {
253 if (currentT > minT) {
254 minT = currentT;
255 }
256 }
257
258 // We have a tolerance on both the absolute value of the difference and
259 // on the t value
260 // because we may not have enough precision in the t to get close enough
261 // in the length.
262 if ((std::abs(lengthDiff) < kTolerance) ||
263 (std::abs(prevT - currentT) < kTolerance)) {
264 break;
265 }
266
267 prevT = currentT;
268 if (iterations < kNewtonIters) {
hstern0446a3c2016-08-08 12:28:13 -0700269 // This is just newton's formula.
hstern4ab47e02016-08-10 10:55:09 -0700270 SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length();
hstern0446a3c2016-08-08 12:28:13 -0700271 newT = currentT - (lengthDiff / dt);
272
273 // If newT is out of bounds, bisect inside newton.
274 if ((newT < 0.0f) || (newT > 1.0f)) {
275 newT = (minT + maxT) * 0.5f;
276 }
277 } else if (iterations < kNewtonIters + kBisectIters) {
278 if (lengthDiff > 0.0f) {
279 maxT = currentT;
280 } else {
281 minT = currentT;
282 }
283 // TODO(hstern) do a lerp here instead of a bisection
284 newT = (minT + maxT) * 0.5f;
285 } else {
286 SkDEBUGF(("%.7f %.7f didn't get close enough after bisection.\n",
hstern4ab47e02016-08-10 10:55:09 -0700287 currentT, currentLength));
hstern0446a3c2016-08-08 12:28:13 -0700288 break;
289 }
290 currentT = newT;
291
292 SkASSERT(minT <= maxT);
293
294 iterations++;
295 }
296
297 // debug. is there an SKDEBUG or something for ifdefs?
298 fIters = iterations;
299
300 return currentT;
301}
302
hstern80ac5912016-08-10 07:45:31 -0700303void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos,
hstern4ab47e02016-08-10 10:55:09 -0700304 SkVector* tan, SkScalar* time) {
hstern0446a3c2016-08-08 12:28:13 -0700305 SkScalar t = getTime(targetLength);
306
hstern80ac5912016-08-10 07:45:31 -0700307 if (time) {
308 *time = t;
309 }
hstern0446a3c2016-08-08 12:28:13 -0700310 if (pos) {
hstern4ab47e02016-08-10 10:55:09 -0700311 *pos = evaluate(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700312 }
313 if (tan) {
hstern4ab47e02016-08-10 10:55:09 -0700314 *tan = evaluateDerivative(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700315 }
316}