Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: src/utils/SkCurveMeasure.cpp

Issue 2187083002: Add initial CurveMeasure code (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Remove unused variable Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/utils/SkCurveMeasure.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « src/utils/SkCurveMeasure.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698