blob: 60fbf34ff24be9d78085f7333e4a74db0ae2b07b [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"
hstern5a4b18c2016-08-10 16:31:10 -07009#include "SkGeometry.h"
hstern0446a3c2016-08-08 12:28:13 -070010
11// for abs
12#include <cmath>
13
hstern5a4b18c2016-08-10 16:31:10 -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;
hstern0446a3c2016-08-08 12:28:13 -070080 case kCubic_SegType:
81 x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2];
82 y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2];
83 break;
84 case kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -070085 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070086 break;
87 default:
hstern5a4b18c2016-08-10 16:31:10 -070088 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070089 }
90
91 x = x * x;
92 y = y * y;
93
94 return (x + y).sqrt();
95}
hstern5a4b18c2016-08-10 16:31:10 -070096
hstern0446a3c2016-08-08 12:28:13 -070097ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
98 : fSegType(segType) {
99 switch (fSegType) {
100 case kQuad_SegType: {
101 float Ax = pts[0].x();
102 float Bx = pts[1].x();
103 float Cx = pts[2].x();
104 float Ay = pts[0].y();
105 float By = pts[1].y();
106 float Cy = pts[2].y();
107
108 // precompute coefficients for derivative
109 xCoeff[0] = Sk8f(2.0f*(Ax - 2*Bx + Cx));
110 xCoeff[1] = Sk8f(2.0f*(Bx - Ax));
111
112 yCoeff[0] = Sk8f(2.0f*(Ay - 2*By + Cy));
113 yCoeff[1] = Sk8f(2.0f*(By - Ay));
114 }
115 break;
hstern0446a3c2016-08-08 12:28:13 -0700116 case kCubic_SegType:
117 {
118 float Ax = pts[0].x();
119 float Bx = pts[1].x();
120 float Cx = pts[2].x();
121 float Dx = pts[3].x();
122 float Ay = pts[0].y();
123 float By = pts[1].y();
124 float Cy = pts[2].y();
125 float Dy = pts[3].y();
126
hstern5a4b18c2016-08-10 16:31:10 -0700127 // precompute coefficients for derivative
hstern0446a3c2016-08-08 12:28:13 -0700128 xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx));
129 xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx)));
130 xCoeff[2] = Sk8f(3.0f*(-Ax + Bx));
131
132 yCoeff[0] = Sk8f(3.0f*(-Ay + 3.0f*(By - Cy) + Dy));
133 yCoeff[1] = Sk8f(3.0f * -Ay + By + 2.0f*(Ay - 2.0f*By + Cy));
134 yCoeff[2] = Sk8f(3.0f*(-Ay + By));
135 }
136 break;
137 case kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700138 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700139 break;
140 default:
hstern5a4b18c2016-08-10 16:31:10 -0700141 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700142 }
143}
144
145// We use Gaussian quadrature
146// (https://en.wikipedia.org/wiki/Gaussian_quadrature)
147// to approximate the arc length integral here, because it is amenable to SIMD.
148SkScalar ArcLengthIntegrator::computeLength(SkScalar t) {
149 SkScalar length = 0.0f;
150
151 Sk8f lengths = evaluateDerivativeLength(absc*t, xCoeff, yCoeff, fSegType);
152 lengths = weights*lengths;
153 // is it faster or more accurate to sum and then multiply or vice versa?
154 lengths = lengths*(t*0.5f);
155
156 // Why does SkNx index with ints? does negative index mean something?
157 for (int i = 0; i < 8; i++) {
158 length += lengths[i];
159 }
160 return length;
161}
162
163SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
164 : fSegType(segType) {
165 switch (fSegType) {
166 case SkSegType::kQuad_SegType:
167 for (size_t i = 0; i < 3; i++) {
168 fPts[i] = pts[i];
169 }
170 break;
171 case SkSegType::kLine_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700172 fPts[0] = pts[0];
173 fPts[1] = pts[1];
174 fLength = (fPts[1] - fPts[0]).length();
hstern0446a3c2016-08-08 12:28:13 -0700175 break;
176 case SkSegType::kCubic_SegType:
177 for (size_t i = 0; i < 4; i++) {
178 fPts[i] = pts[i];
179 }
180 break;
181 case SkSegType::kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700182 for (size_t i = 0; i < 4; i++) {
183 fPts[i] = pts[i];
184 }
hstern0446a3c2016-08-08 12:28:13 -0700185 break;
186 default:
hstern5a4b18c2016-08-10 16:31:10 -0700187 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700188 break;
189 }
hstern5a4b18c2016-08-10 16:31:10 -0700190 if (kLine_SegType != segType) {
191 fIntegrator = ArcLengthIntegrator(fPts, fSegType);
192 }
hstern0446a3c2016-08-08 12:28:13 -0700193}
194
195SkScalar SkCurveMeasure::getLength() {
196 if (-1.0f == fLength) {
197 fLength = fIntegrator.computeLength(1.0f);
198 }
199 return fLength;
200}
201
202// Given an arc length targetLength, we want to determine what t
203// gives us the corresponding arc length along the curve.
204// We do this by letting the arc length integral := f(t) and
205// solving for the root of the equation f(t) - targetLength = 0
206// using Newton's method and lerp-bisection.
207// The computationally expensive parts are the integral approximation
208// at each step, and computing the derivative of the arc length integral,
209// which is equal to the length of the tangent (so we have to do a sqrt).
210
211SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
hstern5a4b18c2016-08-10 16:31:10 -0700212 if (targetLength <= 0.0f) {
hstern0446a3c2016-08-08 12:28:13 -0700213 return 0.0f;
214 }
215
216 SkScalar currentLength = getLength();
217
hstern5a4b18c2016-08-10 16:31:10 -0700218 if (targetLength > currentLength || (SkScalarNearlyEqual(targetLength, currentLength))) {
hstern0446a3c2016-08-08 12:28:13 -0700219 return 1.0f;
220 }
hstern5a4b18c2016-08-10 16:31:10 -0700221 if (kLine_SegType == fSegType) {
222 return targetLength / currentLength;
223 }
hstern0446a3c2016-08-08 12:28:13 -0700224
225 // initial estimate of t is percentage of total length
226 SkScalar currentT = targetLength / currentLength;
227 SkScalar prevT = -1.0f;
228 SkScalar newT;
229
230 SkScalar minT = 0.0f;
231 SkScalar maxT = 1.0f;
232
233 int iterations = 0;
234 while (iterations < kNewtonIters + kBisectIters) {
235 currentLength = fIntegrator.computeLength(currentT);
236 SkScalar lengthDiff = currentLength - targetLength;
237
238 // Update root bounds.
239 // If lengthDiff is positive, we have overshot the target, so
240 // we know the current t is an upper bound, and similarly
241 // for the lower bound.
242 if (lengthDiff > 0.0f) {
243 if (currentT < maxT) {
244 maxT = currentT;
245 }
246 } else {
247 if (currentT > minT) {
248 minT = currentT;
249 }
250 }
251
252 // We have a tolerance on both the absolute value of the difference and
253 // on the t value
254 // because we may not have enough precision in the t to get close enough
255 // in the length.
256 if ((std::abs(lengthDiff) < kTolerance) ||
257 (std::abs(prevT - currentT) < kTolerance)) {
258 break;
259 }
260
261 prevT = currentT;
262 if (iterations < kNewtonIters) {
hstern0446a3c2016-08-08 12:28:13 -0700263 // This is just newton's formula.
hstern5a4b18c2016-08-10 16:31:10 -0700264 SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length();
hstern0446a3c2016-08-08 12:28:13 -0700265 newT = currentT - (lengthDiff / dt);
266
267 // If newT is out of bounds, bisect inside newton.
268 if ((newT < 0.0f) || (newT > 1.0f)) {
269 newT = (minT + maxT) * 0.5f;
270 }
271 } else if (iterations < kNewtonIters + kBisectIters) {
272 if (lengthDiff > 0.0f) {
273 maxT = currentT;
274 } else {
275 minT = currentT;
276 }
277 // TODO(hstern) do a lerp here instead of a bisection
278 newT = (minT + maxT) * 0.5f;
279 } else {
280 SkDEBUGF(("%.7f %.7f didn't get close enough after bisection.\n",
hstern5a4b18c2016-08-10 16:31:10 -0700281 currentT, currentLength));
hstern0446a3c2016-08-08 12:28:13 -0700282 break;
283 }
284 currentT = newT;
285
286 SkASSERT(minT <= maxT);
287
288 iterations++;
289 }
290
291 // debug. is there an SKDEBUG or something for ifdefs?
292 fIters = iterations;
293
294 return currentT;
295}
296
hstern80ac5912016-08-10 07:45:31 -0700297void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos,
hstern5a4b18c2016-08-10 16:31:10 -0700298 SkVector* tan, SkScalar* time) {
hstern0446a3c2016-08-08 12:28:13 -0700299 SkScalar t = getTime(targetLength);
300
hstern80ac5912016-08-10 07:45:31 -0700301 if (time) {
302 *time = t;
303 }
hstern0446a3c2016-08-08 12:28:13 -0700304 if (pos) {
hstern5a4b18c2016-08-10 16:31:10 -0700305 *pos = evaluate(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700306 }
307 if (tan) {
hstern5a4b18c2016-08-10 16:31:10 -0700308 *tan = evaluateDerivative(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700309 }
310}