blob: 140228dbfae8cfa6d1a85cda6610c44829792421 [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,
Mike Klein1e764642016-10-14 17:09:03 -040070 const float (&xCoeff)[3][8],
71 const float (&yCoeff)[3][8],
hstern0446a3c2016-08-08 12:28:13 -070072 const SkSegType segType) {
73 Sk8f x;
74 Sk8f y;
Mike Klein1e764642016-10-14 17:09:03 -040075
76 Sk8f x0 = Sk8f::Load(&xCoeff[0]),
77 x1 = Sk8f::Load(&xCoeff[1]),
78 x2 = Sk8f::Load(&xCoeff[2]);
79
80 Sk8f y0 = Sk8f::Load(&yCoeff[0]),
81 y1 = Sk8f::Load(&yCoeff[1]),
82 y2 = Sk8f::Load(&yCoeff[2]);
83
hstern0446a3c2016-08-08 12:28:13 -070084 switch (segType) {
85 case kQuad_SegType:
Mike Klein1e764642016-10-14 17:09:03 -040086 x = x0*ts + x1;
87 y = y0*ts + y1;
hstern0446a3c2016-08-08 12:28:13 -070088 break;
hstern0446a3c2016-08-08 12:28:13 -070089 case kCubic_SegType:
Mike Klein1e764642016-10-14 17:09:03 -040090 x = (x0*ts + x1)*ts + x2;
91 y = (y0*ts + y1)*ts + y2;
hstern0446a3c2016-08-08 12:28:13 -070092 break;
93 case kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -070094 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070095 break;
96 default:
hstern5a4b18c2016-08-10 16:31:10 -070097 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -070098 }
99
100 x = x * x;
101 y = y * y;
102
103 return (x + y).sqrt();
104}
hstern5a4b18c2016-08-10 16:31:10 -0700105
hstern0446a3c2016-08-08 12:28:13 -0700106ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
107 : fSegType(segType) {
108 switch (fSegType) {
109 case kQuad_SegType: {
110 float Ax = pts[0].x();
111 float Bx = pts[1].x();
112 float Cx = pts[2].x();
113 float Ay = pts[0].y();
114 float By = pts[1].y();
115 float Cy = pts[2].y();
116
117 // precompute coefficients for derivative
Mike Klein1e764642016-10-14 17:09:03 -0400118 Sk8f(2*(Ax - 2*Bx + Cx)).store(&xCoeff[0]);
119 Sk8f(2*(Bx - Ax)).store(&xCoeff[1]);
hstern0446a3c2016-08-08 12:28:13 -0700120
Mike Klein1e764642016-10-14 17:09:03 -0400121 Sk8f(2*(Ay - 2*By + Cy)).store(&yCoeff[0]);
122 Sk8f(2*(By - Ay)).store(&yCoeff[1]);
hstern0446a3c2016-08-08 12:28:13 -0700123 }
124 break;
hstern0446a3c2016-08-08 12:28:13 -0700125 case kCubic_SegType:
126 {
127 float Ax = pts[0].x();
128 float Bx = pts[1].x();
129 float Cx = pts[2].x();
130 float Dx = pts[3].x();
131 float Ay = pts[0].y();
132 float By = pts[1].y();
133 float Cy = pts[2].y();
134 float Dy = pts[3].y();
135
hstern5a4b18c2016-08-10 16:31:10 -0700136 // precompute coefficients for derivative
Mike Klein1e764642016-10-14 17:09:03 -0400137 Sk8f(3*(-Ax + 3*(Bx - Cx) + Dx)).store(&xCoeff[0]);
138 Sk8f(6*(Ax - 2*Bx + Cx)).store(&xCoeff[1]);
139 Sk8f(3*(-Ax + Bx)).store(&xCoeff[2]);
hstern0446a3c2016-08-08 12:28:13 -0700140
Mike Klein1e764642016-10-14 17:09:03 -0400141 Sk8f(3*(-Ay + 3*(By - Cy) + Dy)).store(&yCoeff[0]);
142 Sk8f(6*(Ay - 2*By + Cy)).store(&yCoeff[1]);
143 Sk8f(3*(-Ay + By)).store(&yCoeff[2]);
hstern0446a3c2016-08-08 12:28:13 -0700144 }
145 break;
146 case kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700147 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700148 break;
149 default:
hstern5a4b18c2016-08-10 16:31:10 -0700150 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700151 }
152}
153
154// We use Gaussian quadrature
155// (https://en.wikipedia.org/wiki/Gaussian_quadrature)
156// to approximate the arc length integral here, because it is amenable to SIMD.
157SkScalar ArcLengthIntegrator::computeLength(SkScalar t) {
158 SkScalar length = 0.0f;
159
160 Sk8f lengths = evaluateDerivativeLength(absc*t, xCoeff, yCoeff, fSegType);
161 lengths = weights*lengths;
162 // is it faster or more accurate to sum and then multiply or vice versa?
163 lengths = lengths*(t*0.5f);
164
165 // Why does SkNx index with ints? does negative index mean something?
166 for (int i = 0; i < 8; i++) {
167 length += lengths[i];
168 }
169 return length;
170}
171
172SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
173 : fSegType(segType) {
174 switch (fSegType) {
175 case SkSegType::kQuad_SegType:
176 for (size_t i = 0; i < 3; i++) {
177 fPts[i] = pts[i];
178 }
179 break;
180 case SkSegType::kLine_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700181 fPts[0] = pts[0];
182 fPts[1] = pts[1];
183 fLength = (fPts[1] - fPts[0]).length();
hstern0446a3c2016-08-08 12:28:13 -0700184 break;
185 case SkSegType::kCubic_SegType:
186 for (size_t i = 0; i < 4; i++) {
187 fPts[i] = pts[i];
188 }
189 break;
190 case SkSegType::kConic_SegType:
hstern5a4b18c2016-08-10 16:31:10 -0700191 for (size_t i = 0; i < 4; i++) {
192 fPts[i] = pts[i];
193 }
hstern0446a3c2016-08-08 12:28:13 -0700194 break;
195 default:
hstern5a4b18c2016-08-10 16:31:10 -0700196 UNIMPLEMENTED;
hstern0446a3c2016-08-08 12:28:13 -0700197 break;
198 }
hstern5a4b18c2016-08-10 16:31:10 -0700199 if (kLine_SegType != segType) {
200 fIntegrator = ArcLengthIntegrator(fPts, fSegType);
201 }
hstern0446a3c2016-08-08 12:28:13 -0700202}
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) {
hstern5a4b18c2016-08-10 16:31:10 -0700221 if (targetLength <= 0.0f) {
hstern0446a3c2016-08-08 12:28:13 -0700222 return 0.0f;
223 }
224
225 SkScalar currentLength = getLength();
226
hstern5a4b18c2016-08-10 16:31:10 -0700227 if (targetLength > currentLength || (SkScalarNearlyEqual(targetLength, currentLength))) {
hstern0446a3c2016-08-08 12:28:13 -0700228 return 1.0f;
229 }
hstern5a4b18c2016-08-10 16:31:10 -0700230 if (kLine_SegType == fSegType) {
231 return targetLength / currentLength;
232 }
hstern0446a3c2016-08-08 12:28:13 -0700233
234 // initial estimate of t is percentage of total length
235 SkScalar currentT = targetLength / currentLength;
236 SkScalar prevT = -1.0f;
237 SkScalar newT;
238
239 SkScalar minT = 0.0f;
240 SkScalar maxT = 1.0f;
241
242 int iterations = 0;
243 while (iterations < kNewtonIters + kBisectIters) {
244 currentLength = fIntegrator.computeLength(currentT);
245 SkScalar lengthDiff = currentLength - targetLength;
246
247 // Update root bounds.
248 // If lengthDiff is positive, we have overshot the target, so
249 // we know the current t is an upper bound, and similarly
250 // for the lower bound.
251 if (lengthDiff > 0.0f) {
252 if (currentT < maxT) {
253 maxT = currentT;
254 }
255 } else {
256 if (currentT > minT) {
257 minT = currentT;
258 }
259 }
260
261 // We have a tolerance on both the absolute value of the difference and
262 // on the t value
263 // because we may not have enough precision in the t to get close enough
264 // in the length.
265 if ((std::abs(lengthDiff) < kTolerance) ||
266 (std::abs(prevT - currentT) < kTolerance)) {
267 break;
268 }
269
270 prevT = currentT;
271 if (iterations < kNewtonIters) {
hstern0446a3c2016-08-08 12:28:13 -0700272 // This is just newton's formula.
hstern5a4b18c2016-08-10 16:31:10 -0700273 SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length();
hstern0446a3c2016-08-08 12:28:13 -0700274 newT = currentT - (lengthDiff / dt);
275
276 // If newT is out of bounds, bisect inside newton.
277 if ((newT < 0.0f) || (newT > 1.0f)) {
278 newT = (minT + maxT) * 0.5f;
279 }
280 } else if (iterations < kNewtonIters + kBisectIters) {
281 if (lengthDiff > 0.0f) {
282 maxT = currentT;
283 } else {
284 minT = currentT;
285 }
286 // TODO(hstern) do a lerp here instead of a bisection
287 newT = (minT + maxT) * 0.5f;
288 } else {
289 SkDEBUGF(("%.7f %.7f didn't get close enough after bisection.\n",
hstern5a4b18c2016-08-10 16:31:10 -0700290 currentT, currentLength));
hstern0446a3c2016-08-08 12:28:13 -0700291 break;
292 }
293 currentT = newT;
294
295 SkASSERT(minT <= maxT);
296
297 iterations++;
298 }
299
300 // debug. is there an SKDEBUG or something for ifdefs?
301 fIters = iterations;
302
303 return currentT;
304}
305
hstern80ac5912016-08-10 07:45:31 -0700306void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos,
hstern5a4b18c2016-08-10 16:31:10 -0700307 SkVector* tan, SkScalar* time) {
hstern0446a3c2016-08-08 12:28:13 -0700308 SkScalar t = getTime(targetLength);
309
hstern80ac5912016-08-10 07:45:31 -0700310 if (time) {
311 *time = t;
312 }
hstern0446a3c2016-08-08 12:28:13 -0700313 if (pos) {
hstern5a4b18c2016-08-10 16:31:10 -0700314 *pos = evaluate(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700315 }
316 if (tan) {
hstern5a4b18c2016-08-10 16:31:10 -0700317 *tan = evaluateDerivative(fPts, fSegType, t);
hstern0446a3c2016-08-08 12:28:13 -0700318 }
319}