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

Side by Side Diff: charted/lib/core/interpolators/interpolators.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 | « charted/lib/core/interpolators/easing.dart ('k') | charted/lib/core/scales.dart » ('j') | 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 2014 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 //
8
9 part of charted.core.interpolators;
10
11 /// [Interpolator] accepts [t], such that 0.0 < t < 1.0 and returns
12 /// a value in a pre-defined range.
13 typedef Interpolator(num t);
14
15 /// [InterpolatorGenerator] accepts two parameters [a], [b] and returns an
16 /// [Interpolator] for transitioning from [a] to [b]
17 typedef Interpolator InterpolatorGenerator(a, b);
18
19 /// List of registered interpolators - [createInterpolatorFromRegistry]
20 /// iterates through this list from backwards and the first non-null
21 /// interpolate function is returned to the caller.
22 List<InterpolatorGenerator> _interpolators = [ createInterpolatorByType ];
23
24 /// Returns a default interpolator between values [a] and [b]. Unless
25 /// more interpolators are added, one of the internal implementations are
26 /// selected by the type of [a] and [b].
27 Interpolator createInterpolatorFromRegistry(a, b) {
28 var fn, i = _interpolators.length;
29 while (--i >= 0 && fn == null) {
30 fn = _interpolators[i](a, b);
31 }
32 return fn;
33 }
34
35 /// Creates an interpolator based on the type of [a] and [b].
36 ///
37 /// Usage note: Use this method only when type of [a] and [b] are not known.
38 /// When used, this function will prevent tree shaking of all built-in
39 /// interpolators.
40 Interpolator createInterpolatorByType(a, b) =>
41 (a is List && b is List) ? createListInterpolator(a, b) :
42 (a is Map && b is Map) ? createMapInterpolator(a, b) :
43 (a is String && b is String) ? createStringInterpolator(a, b) :
44 (a is num && b is num) ? createNumberInterpolator(a, b) :
45 (a is Color && b is Color) ? createRgbColorInterpolator(a, b) :
46 (t) => (t <= 0.5) ? a : b;
47
48
49 //
50 // Implementations of InterpolatorGenerator
51 //
52
53 /// Generate a numeric interpolator between numbers [a] and [b]
54 Interpolator createNumberInterpolator(num a, num b) {
55 b -= a;
56 return (t) => a + b * t;
57 }
58
59 /// Generate a rounded number interpolator between numbers [a] and [b]
60 Interpolator createRoundedNumberInterpolator(num a, num b) {
61 b -= a;
62 return (t) => (a + b * t).round();
63 }
64
65
66 /// Generate an interpolator between two strings [a] and [b].
67 ///
68 /// The interpolator will interpolate all the number pairs in both strings
69 /// that have same number of numeric parts. The function assumes the non
70 /// number part of the string to be identical and would use string [b] for
71 /// merging the non numeric part of the strings.
72 ///
73 /// Eg: Interpolate between $100.0 and $150.0
74 Interpolator createStringInterpolator(String a, String b) {
75 if (a == null || b == null) return (t) => b;
76
77 // See if both A and B represent colors as RGB or HEX strings.
78 // If yes, use color interpolators
79 if (Color.isRgbColorString(a) && Color.isRgbColorString(b)) {
80 return createRgbColorInterpolator(
81 new Color.fromRgbString(a), new Color.fromRgbString(b));
82 }
83
84 // See if both A and B represent colors as HSL strings.
85 // If yes, use color interpolators.
86 if (Color.isHslColorString(a) && Color.isHslColorString(b)) {
87 return createHslColorInterpolator(
88 new Color.fromHslString(a), new Color.fromHslString(b));
89 }
90
91 var numberRegEx =
92 new RegExp(r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?'),
93 numMatchesInA = numberRegEx.allMatches(a),
94 numMatchesInB = numberRegEx.allMatches(b),
95 stringParts = [],
96 numberPartsInA = [],
97 numberPartsInB = [],
98 interpolators = [],
99 s0 = 0;
100
101 numberPartsInA.addAll(numMatchesInA.map((m) => m.group(0)));
102
103 for (Match m in numMatchesInB) {
104 stringParts.add(b.substring(s0, m.start));
105 numberPartsInB.add(m.group(0));
106 s0 = m.end;
107 }
108
109 if (s0 < b.length) stringParts.add(b.substring(s0));
110
111 int numberLength = math.min(numberPartsInA.length, numberPartsInB.length);
112 int maxLength = math.max(numberPartsInA.length, numberPartsInB.length);
113 for (var i = 0; i < numberLength; i++) {
114 interpolators.add(createNumberInterpolator(num.parse(numberPartsInA[i]),
115 num.parse(numberPartsInB[i])));
116 }
117 if (numberPartsInA.length < numberPartsInB.length) {
118 for (var i = numberLength; i < maxLength; i++) {
119 interpolators.add(createNumberInterpolator(num.parse(numberPartsInB[i]),
120 num.parse(numberPartsInB[i])));
121 }
122 }
123
124 return (t) {
125 StringBuffer sb = new StringBuffer();
126 for (var i = 0; i < stringParts.length; i++) {
127 sb.write(stringParts[i]);
128 if (interpolators.length > i) {
129 sb.write(interpolators[i](t));
130 }
131 }
132 return sb.toString();
133 };
134 }
135
136 /// Generate an interpolator for RGB values.
137 Interpolator createRgbColorInterpolator(Color a, Color b) {
138 if (a == null || b == null) return (t) => b;
139 var ar = a.r,
140 ag = a.g,
141 ab = a.b,
142 br = b.r - ar,
143 bg = b.g - ag,
144 bb = b.b - ab;
145
146 return (t) => new Color.fromRgba((ar + br * t).round(),
147 (ag + bg * t).round(), (ab + bb * t).round(), 1.0).toRgbaString();
148 }
149
150 /// Generate an interpolator using HSL color system converted to Hex string.
151 Interpolator createHslColorInterpolator(Color a, Color b) {
152 if (a == null || b == null) return (t) => b;
153 var ah = a.h,
154 as = a.s,
155 al = a.l,
156 bh = b.h - ah,
157 bs = b.s - as,
158 bl = b.l - al;
159
160 return (t) => new Color.fromHsla((ah + bh * t).round(),
161 (as + bs * t).round(), (al + bl * t).round(), 1.0).toHslaString();
162 }
163
164 /// Generates an interpolator to interpolate each element between lists
165 /// [a] and [b] using registered interpolators.
166 Interpolator createListInterpolator(List a, List b) {
167 if (a == null || b == null) return (t) => b;
168 var x = [],
169 aLength = a.length,
170 numInterpolated = b.length,
171 n0 = math.min(aLength, numInterpolated),
172 output = new List.filled(math.max(aLength, numInterpolated), null),
173 i;
174
175 for (i = 0; i < n0; i++) x.add(createInterpolatorFromRegistry(a[i], b[i]));
176 for (; i < aLength; ++i) output[i] = a[i];
177 for (; i < numInterpolated; ++i) output[i] = b[i];
178
179 return (t) {
180 for (i = 0; i < n0; ++i) output[i] = x[i](t);
181 return output;
182 };
183 }
184
185 /// Generates an interpolator to interpolate each value on [a] to [b] using
186 /// registered interpolators.
187 Interpolator createMapInterpolator(Map a, Map b) {
188 if (a == null || b == null) return (t) => b;
189 var interpolatorsMap = new Map(),
190 output = new Map(),
191 aKeys = a.keys.toList(),
192 bKeys = b.keys.toList();
193
194 aKeys.forEach((k) {
195 if (b[k] != null) {
196 interpolatorsMap[k] = (createInterpolatorFromRegistry(a[k], b[k]));
197 } else {
198 output[k] = a[k];
199 }
200 });
201
202 bKeys.forEach((k) {
203 if (output[k] == null) {
204 output[k] = b[k];
205 }
206 });
207
208 return (t) {
209 interpolatorsMap.forEach((k, v) => output[k] = v(t));
210 return output;
211 };
212 }
213
214 /// Returns the interpolator that interpolators two transform strings
215 /// [a] and [b] by their translate, rotate, scale and skewX parts.
216 Interpolator createTransformInterpolator(String a, String b) {
217 if (a == null || b == null) return (t) => b;
218 var numRegExStr = r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?',
219 numberRegEx = new RegExp(numRegExStr),
220 translateRegEx =
221 new RegExp(r'translate\(' + '$numRegExStr,$numRegExStr' + r'\)'),
222 scaleRegEx =
223 new RegExp(r'scale\(' + numRegExStr + r',' + numRegExStr + r'\)'),
224 rotateRegEx = new RegExp(r'rotate\(' + numRegExStr + r'(deg)?\)'),
225 skewRegEx = new RegExp(r'skewX\(' + numRegExStr + r'(deg)?\)'),
226
227 translateA = translateRegEx.firstMatch(a),
228 scaleA = scaleRegEx.firstMatch(a),
229 rotateA = rotateRegEx.firstMatch(a),
230 skewA = skewRegEx.firstMatch(a),
231
232 translateB = translateRegEx.firstMatch(b),
233 scaleB = scaleRegEx.firstMatch(b),
234 rotateB = rotateRegEx.firstMatch(b),
235 skewB = skewRegEx.firstMatch(b);
236
237 var numSetA = [],
238 numSetB = [],
239 tempStr, match;
240
241 // translate
242 if (translateA != null) {
243 tempStr = a.substring(translateA.start, translateA.end);
244 match = numberRegEx.allMatches(tempStr);
245 for (Match m in match) {
246 numSetA.add(num.parse(m.group(0)));
247 }
248 } else {
249 numSetA.addAll(const[0, 0]);
250 }
251
252 if (translateB != null) {
253 tempStr = b.substring(translateB.start, translateB.end);
254 match = numberRegEx.allMatches(tempStr);
255 for (Match m in match) {
256 numSetB.add(num.parse(m.group(0)));
257 }
258 } else {
259 numSetB.addAll(const[0, 0]);
260 }
261
262 // scale
263 if (scaleA != null) {
264 tempStr = a.substring(scaleA.start, scaleA.end);
265 match = numberRegEx.allMatches(tempStr);
266 for (Match m in match) {
267 numSetA.add(num.parse(m.group(0)));
268 }
269 } else {
270 numSetA.addAll(const[1, 1]);
271 }
272
273 if (scaleB != null) {
274 tempStr = b.substring(scaleB.start, scaleB.end);
275 match = numberRegEx.allMatches(tempStr);
276 for (Match m in match) {
277 numSetB.add(num.parse(m.group(0)));
278 }
279 } else {
280 numSetB.addAll(const[1, 1]);
281 }
282
283 // rotate
284 if (rotateA != null) {
285 tempStr = a.substring(rotateA.start, rotateA.end);
286 match = numberRegEx.firstMatch(tempStr);
287 numSetA.add(num.parse(match.group(0)));
288 } else {
289 numSetA.add(0);
290 }
291
292 if (rotateB != null) {
293 tempStr = b.substring(rotateB.start, rotateB.end);
294 match = numberRegEx.firstMatch(tempStr);
295 numSetB.add(num.parse(match.group(0)));
296 } else {
297 numSetB.add(0);
298 }
299
300 // rotate < 180 degree
301 if (numSetA[4] != numSetB[4]) {
302 if (numSetA[4] - numSetB[4] > 180) {
303 numSetB[4] += 360;
304 } else if (numSetB[4] - numSetA[4] > 180) {
305 numSetA[4] += 360;
306 }
307 }
308
309 // skew
310 if (skewA != null) {
311 tempStr = a.substring(skewA.start, skewA.end);
312 match = numberRegEx.firstMatch(tempStr);
313 numSetA.add(num.parse(match.group(0)));
314 } else {
315 numSetA.add(0);
316 }
317
318 if (skewB != null) {
319 tempStr = b.substring(skewB.start, skewB.end);
320 match = numberRegEx.firstMatch(tempStr);
321 numSetB.add(num.parse(match.group(0)));
322 } else {
323 numSetB.add(0);
324 }
325
326 return (t) {
327 return
328 'translate(${createNumberInterpolator(numSetA[0], numSetB[0])(t)},'
329 '${createNumberInterpolator(numSetA[1], numSetB[1])(t)})'
330 'scale(${createNumberInterpolator(numSetA[2], numSetB[2])(t)},'
331 '${createNumberInterpolator(numSetA[3], numSetB[3])(t)})'
332 'rotate(${createNumberInterpolator(numSetA[4], numSetB[4])(t)})'
333 'skewX(${createNumberInterpolator(numSetA[5], numSetB[5])(t)})';
334 };
335 }
336
337 /// Returns the interpolator that interpolators zoom list [a] to [b]. Zoom
338 /// lists are described by triple elements [ux0, uy0, w0] and [ux1, uy1, w1].
339 Interpolator createZoomInterpolator(List a, List b) {
340 if (a == null || b == null) return (t) => b;
341 assert(a.length == b.length && a.length == 3);
342
343 var sqrt2 = math.SQRT2,
344 param2 = 2,
345 param4 = 4;
346
347 var ux0 = a[0], uy0 = a[1], w0 = a[2],
348 ux1 = b[0], uy1 = b[1], w1 = b[2];
349
350 var dx = ux1 - ux0,
351 dy = uy1 - uy0,
352 d2 = dx * dx + dy * dy,
353 d1 = math.sqrt(d2),
354 b0 = (w1 * w1 - w0 * w0 + param4 * d2) / (2 * w0 * param2 * d1),
355 b1 = (w1 * w1 - w0 * w0 - param4 * d2) / (2 * w1 * param2 * d1),
356 r0 = math.log(math.sqrt(b0 * b0 + 1) - b0),
357 r1 = math.log(math.sqrt(b1 * b1 + 1) - b1),
358 dr = r1 - r0,
359 S = ((!dr.isNaN) ? dr : math.log(w1 / w0)) / sqrt2;
360
361 return (t) {
362 var s = t * S;
363 if (!dr.isNaN) {
364 // General case.
365 var coshr0 = cosh(r0),
366 u = w0 / (param2 * d1) * (coshr0 * tanh(sqrt2 * s + r0) - sinh(r0));
367 return [
368 ux0 + u * dx,
369 uy0 + u * dy,
370 w0 * coshr0 / cosh(sqrt2 * s + r0)
371 ];
372 }
373 // Special case for u0 ~= u1.
374 return [
375 ux0 + t * dx,
376 uy0 + t * dy,
377 w0 * math.exp(sqrt2 * s)
378 ];
379 };
380 }
381
382 /// Reverse interpolator for a number.
383 Interpolator uninterpolateNumber(num a, num b) {
384 b = 1 / (b - a);
385 return (x) => (x - a) * b;
386 }
387
388 /// Reverse interpolator for a clamped number.
389 Interpolator uninterpolateClamp(num a, num b) {
390 b = 1 / (b - a);
391 return (x) => math.max(0, math.min(1, (x - a) * b));
392 }
OLDNEW
« no previous file with comments | « charted/lib/core/interpolators/easing.dart ('k') | charted/lib/core/scales.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698