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

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: Implement evaluateQuad and evaluateQuadDerivative 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
« src/utils/SkCurveMeasure.h ('K') | « 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 SkCurveMeasure::SkCurveMeasure(SkPoint pts[3]) {
14 for (size_t i = 0; i < 3; i++) {
15 fPts[i] = pts[i];
16 }
17 // need to switch on segment type and template for the integrator or somethi ng
18 fIntegrator = new QuadArcLengthIntegrator(pts);
19 }
20
21 // this will be removed when switch to templated integrator or something
22 SkCurveMeasure::~SkCurveMeasure() {
23 delete fIntegrator;
24 }
25
26 SkScalar SkCurveMeasure::getLength() {
27 if (-1.0 == fLength) {
28 fLength = fIntegrator->computeLength(1.0f);
29 }
30 return fLength;
31 }
32
33 // Given an arc length targetLength, we want to determine what t
34 // gives us the corresponding arc length along the curve.
35 // We do this by letting the arc length integral := f(t) and
36 // solving for the root of the equation f(t) - targetLength = 0
37 // using Newton's method and lerp-bisection.
38 // The computationally expensive parts are the integral approximation
39 // at each step, and computing the derivative of the arc length integral,
40 // which is equal to the length of the tangent (so we have to do a sqrt).
41
42 SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
43 if (targetLength == 0.0f) {
44 return 0.0f;
45 }
46
47 SkScalar currentLength = getLength();
48
49 if (SkScalarNearlyEqual(targetLength, currentLength)) {
50 return 1.0f;
51 }
52
53 // initial estimate of t is percentage of total length
54 SkScalar currentT = targetLength / currentLength;
55 SkScalar prevT = -1.0f;
56 SkScalar newT;
57
58 SkScalar minT = 0.0f;
59 SkScalar maxT = 1.0f;
60
61 int iterations = 0;
62 while (iterations < kNewtonIters + kBisectIters) {
63 currentLength = fIntegrator->computeLength(currentT);
64 SkScalar lengthDiff = currentLength - targetLength;
65
66 // Update root bounds.
67 // If lengthDiff is positive, we have overshot the target, so
68 // we know the current t is an upper bound, and similarly
69 // for the lower bound.
70 if (lengthDiff > 0.0f) {
71 if (currentT < maxT) {
72 maxT = currentT;
73 }
74 } else {
75 if (currentT > minT) {
76 minT = currentT;
77 }
78 }
79
80 // We have a tolerance on both the absolute value of the difference and
81 // on the t value
82 // because we may not have enough precision in the t to get close enough
83 // in the length.
84 if ((std::abs(lengthDiff) < kTolerance) ||
85 (std::abs(prevT - currentT) < kTolerance)) {
86 break;
87 }
88
89 prevT = currentT;
90 if (iterations < kNewtonIters) {
91 // TODO(hstern) switch here on curve type. or template on an
92 // evaluator.
93 // This is just newton's formula.
94 SkScalar dt = evaluateQuadDerivative(currentT).length();
95 newT = currentT - (lengthDiff / dt);
96
97 // If newT is out of bounds, bisect inside newton.
98 if ((newT < 0.0f) || (newT > 1.0f)) {
99 newT = (minT + maxT) * 0.5f;
100 }
101 } else if (iterations < kNewtonIters + kBisectIters) {
102 if (lengthDiff > 0.0f) {
103 maxT = currentT;
104 } else {
105 minT = currentT;
106 }
107 // TODO(hstern) do a lerp here instead of a bisection
108 newT = (minT + maxT) * 0.5f;
109 } else {
110 SkDebugf("%.7f %.7f didn't get close enough after bisection.\n",
111 currentT, currentLength);
112 break;
113 }
114 currentT = newT;
115
116 SkASSERT(minT <= maxT);
117
118 iterations++;
119 }
120
121 // debug. is there an SKDEBUG or something for ifdefs?
122 fIters = iterations;
123
124 return currentT;
125 }
126
127 void SkCurveMeasure::getPosTan(SkScalar targetLength, SkPoint* pos,
128 SkVector* tan) {
129 SkScalar t = getTime(targetLength);
130
131 if (pos) {
132 // TODO(hstern) switch here on curve type. or template on evaluator.
133 *pos = evaluateQuad(t);
134 }
135 if (tan) {
136 // TODO(hstern) switch here on curve type. or template on evaluator.
137 *tan = evaluateQuadDerivative(t);
138 }
139 }
140
141 // this is why I feel that the ArcLengthIntegrators should be combined
142 // with some sort of evaluator that caches the constants computed from the
143 // control points. this is basically the same code in QuadArcLengthIntegrator
144 SkPoint SkCurveMeasure::evaluateQuad(SkScalar t) {
145 SkScalar ti = 1.0f - t;
146
147 SkScalar Ax = fPts[0].x();
148 SkScalar Bx = fPts[1].x();
149 SkScalar Cx = fPts[2].x();
150 SkScalar Ay = fPts[0].y();
151 SkScalar By = fPts[1].y();
152 SkScalar Cy = fPts[2].y();
153
154 SkScalar x = Ax*ti*ti + 2.0f*Bx*t*ti + Cx*t*t;
155 SkScalar y = Ay*ti*ti + 2.0f*By*t*ti + Cy*t*t;
156 return SkPoint::Make(x, y);
157 }
158
159 SkVector SkCurveMeasure::evaluateQuadDerivative(SkScalar t) {
160 SkScalar Ax = fPts[0].x();
161 SkScalar Bx = fPts[1].x();
162 SkScalar Cx = fPts[2].x();
163 SkScalar Ay = fPts[0].y();
164 SkScalar By = fPts[1].y();
165 SkScalar Cy = fPts[2].y();
166
167 SkScalar A2BCx = 2.0f*(Ax - 2*Bx + Cx);
168 SkScalar A2BCy = 2.0f*(Ay - 2*By + Cy);
169 SkScalar ABx = 2.0f*(Bx - Ax);
170 SkScalar ABy = 2.0f*(By - Ay);
171
172 return SkPoint::Make(A2BCx*t + ABx, A2BCy*t + ABy);
173 }
OLDNEW
« src/utils/SkCurveMeasure.h ('K') | « src/utils/SkCurveMeasure.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698