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 |