37 | Drag and drop an optimized SVG file onto the page that contains a path.
38 | Clean up your SVG first with
39 |
43 | SVGOMG
44 |
45 | . Alternatively, manually enter path info into the configuration form
46 | below.
47 |
48 |
49 | Resize the container/viewport to see your motion path scale!
50 |
51 |
52 | Path configuration
53 |
108 |
109 |
Interested?
110 |
Check out the Github repo for installation and usage instructions!
111 |
112 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/playground/meanderer.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global = global || self, global.Meanderer = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | function _classCallCheck(instance, Constructor) {
8 | if (!(instance instanceof Constructor)) {
9 | throw new TypeError("Cannot call a class as a function");
10 | }
11 | }
12 |
13 | function _slicedToArray(arr, i) {
14 | return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
15 | }
16 |
17 | function _toConsumableArray(arr) {
18 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
19 | }
20 |
21 | function _arrayWithoutHoles(arr) {
22 | if (Array.isArray(arr)) return _arrayLikeToArray(arr);
23 | }
24 |
25 | function _arrayWithHoles(arr) {
26 | if (Array.isArray(arr)) return arr;
27 | }
28 |
29 | function _iterableToArray(iter) {
30 | if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
31 | }
32 |
33 | function _iterableToArrayLimit(arr, i) {
34 | if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
35 | var _arr = [];
36 | var _n = true;
37 | var _d = false;
38 | var _e = undefined;
39 |
40 | try {
41 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
42 | _arr.push(_s.value);
43 |
44 | if (i && _arr.length === i) break;
45 | }
46 | } catch (err) {
47 | _d = true;
48 | _e = err;
49 | } finally {
50 | try {
51 | if (!_n && _i["return"] != null) _i["return"]();
52 | } finally {
53 | if (_d) throw _e;
54 | }
55 | }
56 |
57 | return _arr;
58 | }
59 |
60 | function _unsupportedIterableToArray(o, minLen) {
61 | if (!o) return;
62 | if (typeof o === "string") return _arrayLikeToArray(o, minLen);
63 | var n = Object.prototype.toString.call(o).slice(8, -1);
64 | if (n === "Object" && o.constructor) n = o.constructor.name;
65 | if (n === "Map" || n === "Set") return Array.from(n);
66 | if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
67 | }
68 |
69 | function _arrayLikeToArray(arr, len) {
70 | if (len == null || len > arr.length) len = arr.length;
71 |
72 | for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
73 |
74 | return arr2;
75 | }
76 |
77 | function _nonIterableSpread() {
78 | throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
79 | }
80 |
81 | function _nonIterableRest() {
82 | throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
83 | }
84 |
85 | var pi = Math.PI,
86 | tau = 2 * pi,
87 | epsilon = 1e-6,
88 | tauEpsilon = tau - epsilon;
89 |
90 | function Path() {
91 | this._x0 = this._y0 = // start of current subpath
92 | this._x1 = this._y1 = null; // end of current subpath
93 | this._ = "";
94 | }
95 |
96 | function path() {
97 | return new Path;
98 | }
99 |
100 | Path.prototype = path.prototype = {
101 | constructor: Path,
102 | moveTo: function(x, y) {
103 | this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
104 | },
105 | closePath: function() {
106 | if (this._x1 !== null) {
107 | this._x1 = this._x0, this._y1 = this._y0;
108 | this._ += "Z";
109 | }
110 | },
111 | lineTo: function(x, y) {
112 | this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
113 | },
114 | quadraticCurveTo: function(x1, y1, x, y) {
115 | this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
116 | },
117 | bezierCurveTo: function(x1, y1, x2, y2, x, y) {
118 | this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
119 | },
120 | arcTo: function(x1, y1, x2, y2, r) {
121 | x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
122 | var x0 = this._x1,
123 | y0 = this._y1,
124 | x21 = x2 - x1,
125 | y21 = y2 - y1,
126 | x01 = x0 - x1,
127 | y01 = y0 - y1,
128 | l01_2 = x01 * x01 + y01 * y01;
129 |
130 | // Is the radius negative? Error.
131 | if (r < 0) throw new Error("negative radius: " + r);
132 |
133 | // Is this path empty? Move to (x1,y1).
134 | if (this._x1 === null) {
135 | this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
136 | }
137 |
138 | // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
139 | else if (!(l01_2 > epsilon));
140 |
141 | // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
142 | // Equivalently, is (x1,y1) coincident with (x2,y2)?
143 | // Or, is the radius zero? Line to (x1,y1).
144 | else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {
145 | this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
146 | }
147 |
148 | // Otherwise, draw an arc!
149 | else {
150 | var x20 = x2 - x0,
151 | y20 = y2 - y0,
152 | l21_2 = x21 * x21 + y21 * y21,
153 | l20_2 = x20 * x20 + y20 * y20,
154 | l21 = Math.sqrt(l21_2),
155 | l01 = Math.sqrt(l01_2),
156 | l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
157 | t01 = l / l01,
158 | t21 = l / l21;
159 |
160 | // If the start tangent is not coincident with (x0,y0), line to.
161 | if (Math.abs(t01 - 1) > epsilon) {
162 | this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
163 | }
164 |
165 | this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
166 | }
167 | },
168 | arc: function(x, y, r, a0, a1, ccw) {
169 | x = +x, y = +y, r = +r, ccw = !!ccw;
170 | var dx = r * Math.cos(a0),
171 | dy = r * Math.sin(a0),
172 | x0 = x + dx,
173 | y0 = y + dy,
174 | cw = 1 ^ ccw,
175 | da = ccw ? a0 - a1 : a1 - a0;
176 |
177 | // Is the radius negative? Error.
178 | if (r < 0) throw new Error("negative radius: " + r);
179 |
180 | // Is this path empty? Move to (x0,y0).
181 | if (this._x1 === null) {
182 | this._ += "M" + x0 + "," + y0;
183 | }
184 |
185 | // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
186 | else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {
187 | this._ += "L" + x0 + "," + y0;
188 | }
189 |
190 | // Is this arc empty? We’re done.
191 | if (!r) return;
192 |
193 | // Does the angle go the wrong way? Flip the direction.
194 | if (da < 0) da = da % tau + tau;
195 |
196 | // Is this a complete circle? Draw two arcs to complete the circle.
197 | if (da > tauEpsilon) {
198 | this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
199 | }
200 |
201 | // Is this arc non-empty? Draw an arc!
202 | else if (da > epsilon) {
203 | this._ += "A" + r + "," + r + ",0," + (+(da >= pi)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
204 | }
205 | },
206 | rect: function(x, y, w, h) {
207 | this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z";
208 | },
209 | toString: function() {
210 | return this._;
211 | }
212 | };
213 |
214 | function constant(x) {
215 | return function constant() {
216 | return x;
217 | };
218 | }
219 |
220 | function Linear(context) {
221 | this._context = context;
222 | }
223 |
224 | Linear.prototype = {
225 | areaStart: function() {
226 | this._line = 0;
227 | },
228 | areaEnd: function() {
229 | this._line = NaN;
230 | },
231 | lineStart: function() {
232 | this._point = 0;
233 | },
234 | lineEnd: function() {
235 | if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
236 | this._line = 1 - this._line;
237 | },
238 | point: function(x, y) {
239 | x = +x, y = +y;
240 | switch (this._point) {
241 | case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
242 | case 1: this._point = 2; // proceed
243 | default: this._context.lineTo(x, y); break;
244 | }
245 | }
246 | };
247 |
248 | function curveLinear(context) {
249 | return new Linear(context);
250 | }
251 |
252 | function x(p) {
253 | return p[0];
254 | }
255 |
256 | function y(p) {
257 | return p[1];
258 | }
259 |
260 | function line() {
261 | var x$1 = x,
262 | y$1 = y,
263 | defined = constant(true),
264 | context = null,
265 | curve = curveLinear,
266 | output = null;
267 |
268 | function line(data) {
269 | var i,
270 | n = data.length,
271 | d,
272 | defined0 = false,
273 | buffer;
274 |
275 | if (context == null) output = curve(buffer = path());
276 |
277 | for (i = 0; i <= n; ++i) {
278 | if (!(i < n && defined(d = data[i], i, data)) === defined0) {
279 | if (defined0 = !defined0) output.lineStart();
280 | else output.lineEnd();
281 | }
282 | if (defined0) output.point(+x$1(d, i, data), +y$1(d, i, data));
283 | }
284 |
285 | if (buffer) return output = null, buffer + "" || null;
286 | }
287 |
288 | line.x = function(_) {
289 | return arguments.length ? (x$1 = typeof _ === "function" ? _ : constant(+_), line) : x$1;
290 | };
291 |
292 | line.y = function(_) {
293 | return arguments.length ? (y$1 = typeof _ === "function" ? _ : constant(+_), line) : y$1;
294 | };
295 |
296 | line.defined = function(_) {
297 | return arguments.length ? (defined = typeof _ === "function" ? _ : constant(!!_), line) : defined;
298 | };
299 |
300 | line.curve = function(_) {
301 | return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
302 | };
303 |
304 | line.context = function(_) {
305 | return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
306 | };
307 |
308 | return line;
309 | }
310 |
311 | function ascending(a, b) {
312 | return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
313 | }
314 |
315 | function bisector(compare) {
316 | if (compare.length === 1) compare = ascendingComparator(compare);
317 | return {
318 | left: function(a, x, lo, hi) {
319 | if (lo == null) lo = 0;
320 | if (hi == null) hi = a.length;
321 | while (lo < hi) {
322 | var mid = lo + hi >>> 1;
323 | if (compare(a[mid], x) < 0) lo = mid + 1;
324 | else hi = mid;
325 | }
326 | return lo;
327 | },
328 | right: function(a, x, lo, hi) {
329 | if (lo == null) lo = 0;
330 | if (hi == null) hi = a.length;
331 | while (lo < hi) {
332 | var mid = lo + hi >>> 1;
333 | if (compare(a[mid], x) > 0) hi = mid;
334 | else lo = mid + 1;
335 | }
336 | return lo;
337 | }
338 | };
339 | }
340 |
341 | function ascendingComparator(f) {
342 | return function(d, x) {
343 | return ascending(f(d), x);
344 | };
345 | }
346 |
347 | var ascendingBisect = bisector(ascending);
348 | var bisectRight = ascendingBisect.right;
349 |
350 | var e10 = Math.sqrt(50),
351 | e5 = Math.sqrt(10),
352 | e2 = Math.sqrt(2);
353 |
354 | function ticks(start, stop, count) {
355 | var reverse,
356 | i = -1,
357 | n,
358 | ticks,
359 | step;
360 |
361 | stop = +stop, start = +start, count = +count;
362 | if (start === stop && count > 0) return [start];
363 | if (reverse = stop < start) n = start, start = stop, stop = n;
364 | if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
365 |
366 | if (step > 0) {
367 | start = Math.ceil(start / step);
368 | stop = Math.floor(stop / step);
369 | ticks = new Array(n = Math.ceil(stop - start + 1));
370 | while (++i < n) ticks[i] = (start + i) * step;
371 | } else {
372 | start = Math.floor(start * step);
373 | stop = Math.ceil(stop * step);
374 | ticks = new Array(n = Math.ceil(start - stop + 1));
375 | while (++i < n) ticks[i] = (start - i) / step;
376 | }
377 |
378 | if (reverse) ticks.reverse();
379 |
380 | return ticks;
381 | }
382 |
383 | function tickIncrement(start, stop, count) {
384 | var step = (stop - start) / Math.max(0, count),
385 | power = Math.floor(Math.log(step) / Math.LN10),
386 | error = step / Math.pow(10, power);
387 | return power >= 0
388 | ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
389 | : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
390 | }
391 |
392 | function tickStep(start, stop, count) {
393 | var step0 = Math.abs(stop - start) / Math.max(0, count),
394 | step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
395 | error = step0 / step1;
396 | if (error >= e10) step1 *= 10;
397 | else if (error >= e5) step1 *= 5;
398 | else if (error >= e2) step1 *= 2;
399 | return stop < start ? -step1 : step1;
400 | }
401 |
402 | function initRange(domain, range) {
403 | switch (arguments.length) {
404 | case 0: break;
405 | case 1: this.range(domain); break;
406 | default: this.range(range).domain(domain); break;
407 | }
408 | return this;
409 | }
410 |
411 | var prefix = "$";
412 |
413 | function Map() {}
414 |
415 | Map.prototype = map.prototype = {
416 | constructor: Map,
417 | has: function(key) {
418 | return (prefix + key) in this;
419 | },
420 | get: function(key) {
421 | return this[prefix + key];
422 | },
423 | set: function(key, value) {
424 | this[prefix + key] = value;
425 | return this;
426 | },
427 | remove: function(key) {
428 | var property = prefix + key;
429 | return property in this && delete this[property];
430 | },
431 | clear: function() {
432 | for (var property in this) if (property[0] === prefix) delete this[property];
433 | },
434 | keys: function() {
435 | var keys = [];
436 | for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
437 | return keys;
438 | },
439 | values: function() {
440 | var values = [];
441 | for (var property in this) if (property[0] === prefix) values.push(this[property]);
442 | return values;
443 | },
444 | entries: function() {
445 | var entries = [];
446 | for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
447 | return entries;
448 | },
449 | size: function() {
450 | var size = 0;
451 | for (var property in this) if (property[0] === prefix) ++size;
452 | return size;
453 | },
454 | empty: function() {
455 | for (var property in this) if (property[0] === prefix) return false;
456 | return true;
457 | },
458 | each: function(f) {
459 | for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
460 | }
461 | };
462 |
463 | function map(object, f) {
464 | var map = new Map;
465 |
466 | // Copy constructor.
467 | if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
468 |
469 | // Index array by numeric index or specified key function.
470 | else if (Array.isArray(object)) {
471 | var i = -1,
472 | n = object.length,
473 | o;
474 |
475 | if (f == null) while (++i < n) map.set(i, object[i]);
476 | else while (++i < n) map.set(f(o = object[i], i, object), o);
477 | }
478 |
479 | // Convert object to map.
480 | else if (object) for (var key in object) map.set(key, object[key]);
481 |
482 | return map;
483 | }
484 |
485 | function Set() {}
486 |
487 | var proto = map.prototype;
488 |
489 | Set.prototype = set.prototype = {
490 | constructor: Set,
491 | has: proto.has,
492 | add: function(value) {
493 | value += "";
494 | this[prefix + value] = value;
495 | return this;
496 | },
497 | remove: proto.remove,
498 | clear: proto.clear,
499 | values: proto.keys,
500 | size: proto.size,
501 | empty: proto.empty,
502 | each: proto.each
503 | };
504 |
505 | function set(object, f) {
506 | var set = new Set;
507 |
508 | // Copy constructor.
509 | if (object instanceof Set) object.each(function(value) { set.add(value); });
510 |
511 | // Otherwise, assume it’s an array.
512 | else if (object) {
513 | var i = -1, n = object.length;
514 | if (f == null) while (++i < n) set.add(object[i]);
515 | else while (++i < n) set.add(f(object[i], i, object));
516 | }
517 |
518 | return set;
519 | }
520 |
521 | var array = Array.prototype;
522 |
523 | var map$1 = array.map;
524 | var slice = array.slice;
525 |
526 | function define(constructor, factory, prototype) {
527 | constructor.prototype = factory.prototype = prototype;
528 | prototype.constructor = constructor;
529 | }
530 |
531 | function extend(parent, definition) {
532 | var prototype = Object.create(parent.prototype);
533 | for (var key in definition) prototype[key] = definition[key];
534 | return prototype;
535 | }
536 |
537 | function Color() {}
538 |
539 | var darker = 0.7;
540 | var brighter = 1 / darker;
541 |
542 | var reI = "\\s*([+-]?\\d+)\\s*",
543 | reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
544 | reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
545 | reHex = /^#([0-9a-f]{3,8})$/,
546 | reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
547 | reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
548 | reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
549 | reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
550 | reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
551 | reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
552 |
553 | var named = {
554 | aliceblue: 0xf0f8ff,
555 | antiquewhite: 0xfaebd7,
556 | aqua: 0x00ffff,
557 | aquamarine: 0x7fffd4,
558 | azure: 0xf0ffff,
559 | beige: 0xf5f5dc,
560 | bisque: 0xffe4c4,
561 | black: 0x000000,
562 | blanchedalmond: 0xffebcd,
563 | blue: 0x0000ff,
564 | blueviolet: 0x8a2be2,
565 | brown: 0xa52a2a,
566 | burlywood: 0xdeb887,
567 | cadetblue: 0x5f9ea0,
568 | chartreuse: 0x7fff00,
569 | chocolate: 0xd2691e,
570 | coral: 0xff7f50,
571 | cornflowerblue: 0x6495ed,
572 | cornsilk: 0xfff8dc,
573 | crimson: 0xdc143c,
574 | cyan: 0x00ffff,
575 | darkblue: 0x00008b,
576 | darkcyan: 0x008b8b,
577 | darkgoldenrod: 0xb8860b,
578 | darkgray: 0xa9a9a9,
579 | darkgreen: 0x006400,
580 | darkgrey: 0xa9a9a9,
581 | darkkhaki: 0xbdb76b,
582 | darkmagenta: 0x8b008b,
583 | darkolivegreen: 0x556b2f,
584 | darkorange: 0xff8c00,
585 | darkorchid: 0x9932cc,
586 | darkred: 0x8b0000,
587 | darksalmon: 0xe9967a,
588 | darkseagreen: 0x8fbc8f,
589 | darkslateblue: 0x483d8b,
590 | darkslategray: 0x2f4f4f,
591 | darkslategrey: 0x2f4f4f,
592 | darkturquoise: 0x00ced1,
593 | darkviolet: 0x9400d3,
594 | deeppink: 0xff1493,
595 | deepskyblue: 0x00bfff,
596 | dimgray: 0x696969,
597 | dimgrey: 0x696969,
598 | dodgerblue: 0x1e90ff,
599 | firebrick: 0xb22222,
600 | floralwhite: 0xfffaf0,
601 | forestgreen: 0x228b22,
602 | fuchsia: 0xff00ff,
603 | gainsboro: 0xdcdcdc,
604 | ghostwhite: 0xf8f8ff,
605 | gold: 0xffd700,
606 | goldenrod: 0xdaa520,
607 | gray: 0x808080,
608 | green: 0x008000,
609 | greenyellow: 0xadff2f,
610 | grey: 0x808080,
611 | honeydew: 0xf0fff0,
612 | hotpink: 0xff69b4,
613 | indianred: 0xcd5c5c,
614 | indigo: 0x4b0082,
615 | ivory: 0xfffff0,
616 | khaki: 0xf0e68c,
617 | lavender: 0xe6e6fa,
618 | lavenderblush: 0xfff0f5,
619 | lawngreen: 0x7cfc00,
620 | lemonchiffon: 0xfffacd,
621 | lightblue: 0xadd8e6,
622 | lightcoral: 0xf08080,
623 | lightcyan: 0xe0ffff,
624 | lightgoldenrodyellow: 0xfafad2,
625 | lightgray: 0xd3d3d3,
626 | lightgreen: 0x90ee90,
627 | lightgrey: 0xd3d3d3,
628 | lightpink: 0xffb6c1,
629 | lightsalmon: 0xffa07a,
630 | lightseagreen: 0x20b2aa,
631 | lightskyblue: 0x87cefa,
632 | lightslategray: 0x778899,
633 | lightslategrey: 0x778899,
634 | lightsteelblue: 0xb0c4de,
635 | lightyellow: 0xffffe0,
636 | lime: 0x00ff00,
637 | limegreen: 0x32cd32,
638 | linen: 0xfaf0e6,
639 | magenta: 0xff00ff,
640 | maroon: 0x800000,
641 | mediumaquamarine: 0x66cdaa,
642 | mediumblue: 0x0000cd,
643 | mediumorchid: 0xba55d3,
644 | mediumpurple: 0x9370db,
645 | mediumseagreen: 0x3cb371,
646 | mediumslateblue: 0x7b68ee,
647 | mediumspringgreen: 0x00fa9a,
648 | mediumturquoise: 0x48d1cc,
649 | mediumvioletred: 0xc71585,
650 | midnightblue: 0x191970,
651 | mintcream: 0xf5fffa,
652 | mistyrose: 0xffe4e1,
653 | moccasin: 0xffe4b5,
654 | navajowhite: 0xffdead,
655 | navy: 0x000080,
656 | oldlace: 0xfdf5e6,
657 | olive: 0x808000,
658 | olivedrab: 0x6b8e23,
659 | orange: 0xffa500,
660 | orangered: 0xff4500,
661 | orchid: 0xda70d6,
662 | palegoldenrod: 0xeee8aa,
663 | palegreen: 0x98fb98,
664 | paleturquoise: 0xafeeee,
665 | palevioletred: 0xdb7093,
666 | papayawhip: 0xffefd5,
667 | peachpuff: 0xffdab9,
668 | peru: 0xcd853f,
669 | pink: 0xffc0cb,
670 | plum: 0xdda0dd,
671 | powderblue: 0xb0e0e6,
672 | purple: 0x800080,
673 | rebeccapurple: 0x663399,
674 | red: 0xff0000,
675 | rosybrown: 0xbc8f8f,
676 | royalblue: 0x4169e1,
677 | saddlebrown: 0x8b4513,
678 | salmon: 0xfa8072,
679 | sandybrown: 0xf4a460,
680 | seagreen: 0x2e8b57,
681 | seashell: 0xfff5ee,
682 | sienna: 0xa0522d,
683 | silver: 0xc0c0c0,
684 | skyblue: 0x87ceeb,
685 | slateblue: 0x6a5acd,
686 | slategray: 0x708090,
687 | slategrey: 0x708090,
688 | snow: 0xfffafa,
689 | springgreen: 0x00ff7f,
690 | steelblue: 0x4682b4,
691 | tan: 0xd2b48c,
692 | teal: 0x008080,
693 | thistle: 0xd8bfd8,
694 | tomato: 0xff6347,
695 | turquoise: 0x40e0d0,
696 | violet: 0xee82ee,
697 | wheat: 0xf5deb3,
698 | white: 0xffffff,
699 | whitesmoke: 0xf5f5f5,
700 | yellow: 0xffff00,
701 | yellowgreen: 0x9acd32
702 | };
703 |
704 | define(Color, color, {
705 | copy: function(channels) {
706 | return Object.assign(new this.constructor, this, channels);
707 | },
708 | displayable: function() {
709 | return this.rgb().displayable();
710 | },
711 | hex: color_formatHex, // Deprecated! Use color.formatHex.
712 | formatHex: color_formatHex,
713 | formatHsl: color_formatHsl,
714 | formatRgb: color_formatRgb,
715 | toString: color_formatRgb
716 | });
717 |
718 | function color_formatHex() {
719 | return this.rgb().formatHex();
720 | }
721 |
722 | function color_formatHsl() {
723 | return hslConvert(this).formatHsl();
724 | }
725 |
726 | function color_formatRgb() {
727 | return this.rgb().formatRgb();
728 | }
729 |
730 | function color(format) {
731 | var m, l;
732 | format = (format + "").trim().toLowerCase();
733 | return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
734 | : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00
735 | : l === 8 ? new Rgb(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
736 | : l === 4 ? new Rgb((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000
737 | : null) // invalid hex
738 | : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
739 | : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
740 | : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
741 | : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
742 | : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
743 | : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
744 | : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
745 | : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
746 | : null;
747 | }
748 |
749 | function rgbn(n) {
750 | return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
751 | }
752 |
753 | function rgba(r, g, b, a) {
754 | if (a <= 0) r = g = b = NaN;
755 | return new Rgb(r, g, b, a);
756 | }
757 |
758 | function rgbConvert(o) {
759 | if (!(o instanceof Color)) o = color(o);
760 | if (!o) return new Rgb;
761 | o = o.rgb();
762 | return new Rgb(o.r, o.g, o.b, o.opacity);
763 | }
764 |
765 | function rgb(r, g, b, opacity) {
766 | return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
767 | }
768 |
769 | function Rgb(r, g, b, opacity) {
770 | this.r = +r;
771 | this.g = +g;
772 | this.b = +b;
773 | this.opacity = +opacity;
774 | }
775 |
776 | define(Rgb, rgb, extend(Color, {
777 | brighter: function(k) {
778 | k = k == null ? brighter : Math.pow(brighter, k);
779 | return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
780 | },
781 | darker: function(k) {
782 | k = k == null ? darker : Math.pow(darker, k);
783 | return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
784 | },
785 | rgb: function() {
786 | return this;
787 | },
788 | displayable: function() {
789 | return (-0.5 <= this.r && this.r < 255.5)
790 | && (-0.5 <= this.g && this.g < 255.5)
791 | && (-0.5 <= this.b && this.b < 255.5)
792 | && (0 <= this.opacity && this.opacity <= 1);
793 | },
794 | hex: rgb_formatHex, // Deprecated! Use color.formatHex.
795 | formatHex: rgb_formatHex,
796 | formatRgb: rgb_formatRgb,
797 | toString: rgb_formatRgb
798 | }));
799 |
800 | function rgb_formatHex() {
801 | return "#" + hex(this.r) + hex(this.g) + hex(this.b);
802 | }
803 |
804 | function rgb_formatRgb() {
805 | var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
806 | return (a === 1 ? "rgb(" : "rgba(")
807 | + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
808 | + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
809 | + Math.max(0, Math.min(255, Math.round(this.b) || 0))
810 | + (a === 1 ? ")" : ", " + a + ")");
811 | }
812 |
813 | function hex(value) {
814 | value = Math.max(0, Math.min(255, Math.round(value) || 0));
815 | return (value < 16 ? "0" : "") + value.toString(16);
816 | }
817 |
818 | function hsla(h, s, l, a) {
819 | if (a <= 0) h = s = l = NaN;
820 | else if (l <= 0 || l >= 1) h = s = NaN;
821 | else if (s <= 0) h = NaN;
822 | return new Hsl(h, s, l, a);
823 | }
824 |
825 | function hslConvert(o) {
826 | if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
827 | if (!(o instanceof Color)) o = color(o);
828 | if (!o) return new Hsl;
829 | if (o instanceof Hsl) return o;
830 | o = o.rgb();
831 | var r = o.r / 255,
832 | g = o.g / 255,
833 | b = o.b / 255,
834 | min = Math.min(r, g, b),
835 | max = Math.max(r, g, b),
836 | h = NaN,
837 | s = max - min,
838 | l = (max + min) / 2;
839 | if (s) {
840 | if (r === max) h = (g - b) / s + (g < b) * 6;
841 | else if (g === max) h = (b - r) / s + 2;
842 | else h = (r - g) / s + 4;
843 | s /= l < 0.5 ? max + min : 2 - max - min;
844 | h *= 60;
845 | } else {
846 | s = l > 0 && l < 1 ? 0 : h;
847 | }
848 | return new Hsl(h, s, l, o.opacity);
849 | }
850 |
851 | function hsl(h, s, l, opacity) {
852 | return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
853 | }
854 |
855 | function Hsl(h, s, l, opacity) {
856 | this.h = +h;
857 | this.s = +s;
858 | this.l = +l;
859 | this.opacity = +opacity;
860 | }
861 |
862 | define(Hsl, hsl, extend(Color, {
863 | brighter: function(k) {
864 | k = k == null ? brighter : Math.pow(brighter, k);
865 | return new Hsl(this.h, this.s, this.l * k, this.opacity);
866 | },
867 | darker: function(k) {
868 | k = k == null ? darker : Math.pow(darker, k);
869 | return new Hsl(this.h, this.s, this.l * k, this.opacity);
870 | },
871 | rgb: function() {
872 | var h = this.h % 360 + (this.h < 0) * 360,
873 | s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
874 | l = this.l,
875 | m2 = l + (l < 0.5 ? l : 1 - l) * s,
876 | m1 = 2 * l - m2;
877 | return new Rgb(
878 | hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
879 | hsl2rgb(h, m1, m2),
880 | hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
881 | this.opacity
882 | );
883 | },
884 | displayable: function() {
885 | return (0 <= this.s && this.s <= 1 || isNaN(this.s))
886 | && (0 <= this.l && this.l <= 1)
887 | && (0 <= this.opacity && this.opacity <= 1);
888 | },
889 | formatHsl: function() {
890 | var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
891 | return (a === 1 ? "hsl(" : "hsla(")
892 | + (this.h || 0) + ", "
893 | + (this.s || 0) * 100 + "%, "
894 | + (this.l || 0) * 100 + "%"
895 | + (a === 1 ? ")" : ", " + a + ")");
896 | }
897 | }));
898 |
899 | /* From FvD 13.37, CSS Color Module Level 3 */
900 | function hsl2rgb(h, m1, m2) {
901 | return (h < 60 ? m1 + (m2 - m1) * h / 60
902 | : h < 180 ? m2
903 | : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
904 | : m1) * 255;
905 | }
906 |
907 | function constant$1(x) {
908 | return function() {
909 | return x;
910 | };
911 | }
912 |
913 | function linear(a, d) {
914 | return function(t) {
915 | return a + t * d;
916 | };
917 | }
918 |
919 | function exponential(a, b, y) {
920 | return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
921 | return Math.pow(a + t * b, y);
922 | };
923 | }
924 |
925 | function gamma(y) {
926 | return (y = +y) === 1 ? nogamma : function(a, b) {
927 | return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
928 | };
929 | }
930 |
931 | function nogamma(a, b) {
932 | var d = b - a;
933 | return d ? linear(a, d) : constant$1(isNaN(a) ? b : a);
934 | }
935 |
936 | var rgb$1 = (function rgbGamma(y) {
937 | var color = gamma(y);
938 |
939 | function rgb$1(start, end) {
940 | var r = color((start = rgb(start)).r, (end = rgb(end)).r),
941 | g = color(start.g, end.g),
942 | b = color(start.b, end.b),
943 | opacity = nogamma(start.opacity, end.opacity);
944 | return function(t) {
945 | start.r = r(t);
946 | start.g = g(t);
947 | start.b = b(t);
948 | start.opacity = opacity(t);
949 | return start + "";
950 | };
951 | }
952 |
953 | rgb$1.gamma = rgbGamma;
954 |
955 | return rgb$1;
956 | })(1);
957 |
958 | function numberArray(a, b) {
959 | if (!b) b = [];
960 | var n = a ? Math.min(b.length, a.length) : 0,
961 | c = b.slice(),
962 | i;
963 | return function(t) {
964 | for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;
965 | return c;
966 | };
967 | }
968 |
969 | function isNumberArray(x) {
970 | return ArrayBuffer.isView(x) && !(x instanceof DataView);
971 | }
972 |
973 | function genericArray(a, b) {
974 | var nb = b ? b.length : 0,
975 | na = a ? Math.min(nb, a.length) : 0,
976 | x = new Array(na),
977 | c = new Array(nb),
978 | i;
979 |
980 | for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
981 | for (; i < nb; ++i) c[i] = b[i];
982 |
983 | return function(t) {
984 | for (i = 0; i < na; ++i) c[i] = x[i](t);
985 | return c;
986 | };
987 | }
988 |
989 | function date(a, b) {
990 | var d = new Date;
991 | return a = +a, b = +b, function(t) {
992 | return d.setTime(a * (1 - t) + b * t), d;
993 | };
994 | }
995 |
996 | function interpolateNumber(a, b) {
997 | return a = +a, b = +b, function(t) {
998 | return a * (1 - t) + b * t;
999 | };
1000 | }
1001 |
1002 | function object(a, b) {
1003 | var i = {},
1004 | c = {},
1005 | k;
1006 |
1007 | if (a === null || typeof a !== "object") a = {};
1008 | if (b === null || typeof b !== "object") b = {};
1009 |
1010 | for (k in b) {
1011 | if (k in a) {
1012 | i[k] = interpolateValue(a[k], b[k]);
1013 | } else {
1014 | c[k] = b[k];
1015 | }
1016 | }
1017 |
1018 | return function(t) {
1019 | for (k in i) c[k] = i[k](t);
1020 | return c;
1021 | };
1022 | }
1023 |
1024 | var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
1025 | reB = new RegExp(reA.source, "g");
1026 |
1027 | function zero(b) {
1028 | return function() {
1029 | return b;
1030 | };
1031 | }
1032 |
1033 | function one(b) {
1034 | return function(t) {
1035 | return b(t) + "";
1036 | };
1037 | }
1038 |
1039 | function string(a, b) {
1040 | var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
1041 | am, // current match in a
1042 | bm, // current match in b
1043 | bs, // string preceding current number in b, if any
1044 | i = -1, // index in s
1045 | s = [], // string constants and placeholders
1046 | q = []; // number interpolators
1047 |
1048 | // Coerce inputs to strings.
1049 | a = a + "", b = b + "";
1050 |
1051 | // Interpolate pairs of numbers in a & b.
1052 | while ((am = reA.exec(a))
1053 | && (bm = reB.exec(b))) {
1054 | if ((bs = bm.index) > bi) { // a string precedes the next number in b
1055 | bs = b.slice(bi, bs);
1056 | if (s[i]) s[i] += bs; // coalesce with previous string
1057 | else s[++i] = bs;
1058 | }
1059 | if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
1060 | if (s[i]) s[i] += bm; // coalesce with previous string
1061 | else s[++i] = bm;
1062 | } else { // interpolate non-matching numbers
1063 | s[++i] = null;
1064 | q.push({i: i, x: interpolateNumber(am, bm)});
1065 | }
1066 | bi = reB.lastIndex;
1067 | }
1068 |
1069 | // Add remains of b.
1070 | if (bi < b.length) {
1071 | bs = b.slice(bi);
1072 | if (s[i]) s[i] += bs; // coalesce with previous string
1073 | else s[++i] = bs;
1074 | }
1075 |
1076 | // Special optimization for only a single match.
1077 | // Otherwise, interpolate each of the numbers and rejoin the string.
1078 | return s.length < 2 ? (q[0]
1079 | ? one(q[0].x)
1080 | : zero(b))
1081 | : (b = q.length, function(t) {
1082 | for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
1083 | return s.join("");
1084 | });
1085 | }
1086 |
1087 | function interpolateValue(a, b) {
1088 | var t = typeof b, c;
1089 | return b == null || t === "boolean" ? constant$1(b)
1090 | : (t === "number" ? interpolateNumber
1091 | : t === "string" ? ((c = color(b)) ? (b = c, rgb$1) : string)
1092 | : b instanceof color ? rgb$1
1093 | : b instanceof Date ? date
1094 | : isNumberArray(b) ? numberArray
1095 | : Array.isArray(b) ? genericArray
1096 | : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
1097 | : interpolateNumber)(a, b);
1098 | }
1099 |
1100 | function interpolateRound(a, b) {
1101 | return a = +a, b = +b, function(t) {
1102 | return Math.round(a * (1 - t) + b * t);
1103 | };
1104 | }
1105 |
1106 | function constant$2(x) {
1107 | return function() {
1108 | return x;
1109 | };
1110 | }
1111 |
1112 | function number(x) {
1113 | return +x;
1114 | }
1115 |
1116 | var unit = [0, 1];
1117 |
1118 | function identity(x) {
1119 | return x;
1120 | }
1121 |
1122 | function normalize(a, b) {
1123 | return (b -= (a = +a))
1124 | ? function(x) { return (x - a) / b; }
1125 | : constant$2(isNaN(b) ? NaN : 0.5);
1126 | }
1127 |
1128 | function clamper(domain) {
1129 | var a = domain[0], b = domain[domain.length - 1], t;
1130 | if (a > b) t = a, a = b, b = t;
1131 | return function(x) { return Math.max(a, Math.min(b, x)); };
1132 | }
1133 |
1134 | // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
1135 | // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
1136 | function bimap(domain, range, interpolate) {
1137 | var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
1138 | if (d1 < d0) d0 = normalize(d1, d0), r0 = interpolate(r1, r0);
1139 | else d0 = normalize(d0, d1), r0 = interpolate(r0, r1);
1140 | return function(x) { return r0(d0(x)); };
1141 | }
1142 |
1143 | function polymap(domain, range, interpolate) {
1144 | var j = Math.min(domain.length, range.length) - 1,
1145 | d = new Array(j),
1146 | r = new Array(j),
1147 | i = -1;
1148 |
1149 | // Reverse descending domains.
1150 | if (domain[j] < domain[0]) {
1151 | domain = domain.slice().reverse();
1152 | range = range.slice().reverse();
1153 | }
1154 |
1155 | while (++i < j) {
1156 | d[i] = normalize(domain[i], domain[i + 1]);
1157 | r[i] = interpolate(range[i], range[i + 1]);
1158 | }
1159 |
1160 | return function(x) {
1161 | var i = bisectRight(domain, x, 1, j) - 1;
1162 | return r[i](d[i](x));
1163 | };
1164 | }
1165 |
1166 | function copy(source, target) {
1167 | return target
1168 | .domain(source.domain())
1169 | .range(source.range())
1170 | .interpolate(source.interpolate())
1171 | .clamp(source.clamp())
1172 | .unknown(source.unknown());
1173 | }
1174 |
1175 | function transformer() {
1176 | var domain = unit,
1177 | range = unit,
1178 | interpolate = interpolateValue,
1179 | transform,
1180 | untransform,
1181 | unknown,
1182 | clamp = identity,
1183 | piecewise,
1184 | output,
1185 | input;
1186 |
1187 | function rescale() {
1188 | piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
1189 | output = input = null;
1190 | return scale;
1191 | }
1192 |
1193 | function scale(x) {
1194 | return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
1195 | }
1196 |
1197 | scale.invert = function(y) {
1198 | return clamp(untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y)));
1199 | };
1200 |
1201 | scale.domain = function(_) {
1202 | return arguments.length ? (domain = map$1.call(_, number), clamp === identity || (clamp = clamper(domain)), rescale()) : domain.slice();
1203 | };
1204 |
1205 | scale.range = function(_) {
1206 | return arguments.length ? (range = slice.call(_), rescale()) : range.slice();
1207 | };
1208 |
1209 | scale.rangeRound = function(_) {
1210 | return range = slice.call(_), interpolate = interpolateRound, rescale();
1211 | };
1212 |
1213 | scale.clamp = function(_) {
1214 | return arguments.length ? (clamp = _ ? clamper(domain) : identity, scale) : clamp !== identity;
1215 | };
1216 |
1217 | scale.interpolate = function(_) {
1218 | return arguments.length ? (interpolate = _, rescale()) : interpolate;
1219 | };
1220 |
1221 | scale.unknown = function(_) {
1222 | return arguments.length ? (unknown = _, scale) : unknown;
1223 | };
1224 |
1225 | return function(t, u) {
1226 | transform = t, untransform = u;
1227 | return rescale();
1228 | };
1229 | }
1230 |
1231 | function continuous(transform, untransform) {
1232 | return transformer()(transform, untransform);
1233 | }
1234 |
1235 | // Computes the decimal coefficient and exponent of the specified number x with
1236 | // significant digits p, where x is positive and p is in [1, 21] or undefined.
1237 | // For example, formatDecimal(1.23) returns ["123", 0].
1238 | function formatDecimal(x, p) {
1239 | if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
1240 | var i, coefficient = x.slice(0, i);
1241 |
1242 | // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
1243 | // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
1244 | return [
1245 | coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
1246 | +x.slice(i + 1)
1247 | ];
1248 | }
1249 |
1250 | function exponent(x) {
1251 | return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
1252 | }
1253 |
1254 | function formatGroup(grouping, thousands) {
1255 | return function(value, width) {
1256 | var i = value.length,
1257 | t = [],
1258 | j = 0,
1259 | g = grouping[0],
1260 | length = 0;
1261 |
1262 | while (i > 0 && g > 0) {
1263 | if (length + g + 1 > width) g = Math.max(1, width - length);
1264 | t.push(value.substring(i -= g, i + g));
1265 | if ((length += g + 1) > width) break;
1266 | g = grouping[j = (j + 1) % grouping.length];
1267 | }
1268 |
1269 | return t.reverse().join(thousands);
1270 | };
1271 | }
1272 |
1273 | function formatNumerals(numerals) {
1274 | return function(value) {
1275 | return value.replace(/[0-9]/g, function(i) {
1276 | return numerals[+i];
1277 | });
1278 | };
1279 | }
1280 |
1281 | // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
1282 | var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
1283 |
1284 | function formatSpecifier(specifier) {
1285 | if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
1286 | var match;
1287 | return new FormatSpecifier({
1288 | fill: match[1],
1289 | align: match[2],
1290 | sign: match[3],
1291 | symbol: match[4],
1292 | zero: match[5],
1293 | width: match[6],
1294 | comma: match[7],
1295 | precision: match[8] && match[8].slice(1),
1296 | trim: match[9],
1297 | type: match[10]
1298 | });
1299 | }
1300 |
1301 | formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
1302 |
1303 | function FormatSpecifier(specifier) {
1304 | this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
1305 | this.align = specifier.align === undefined ? ">" : specifier.align + "";
1306 | this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
1307 | this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
1308 | this.zero = !!specifier.zero;
1309 | this.width = specifier.width === undefined ? undefined : +specifier.width;
1310 | this.comma = !!specifier.comma;
1311 | this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
1312 | this.trim = !!specifier.trim;
1313 | this.type = specifier.type === undefined ? "" : specifier.type + "";
1314 | }
1315 |
1316 | FormatSpecifier.prototype.toString = function() {
1317 | return this.fill
1318 | + this.align
1319 | + this.sign
1320 | + this.symbol
1321 | + (this.zero ? "0" : "")
1322 | + (this.width === undefined ? "" : Math.max(1, this.width | 0))
1323 | + (this.comma ? "," : "")
1324 | + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
1325 | + (this.trim ? "~" : "")
1326 | + this.type;
1327 | };
1328 |
1329 | // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
1330 | function formatTrim(s) {
1331 | out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
1332 | switch (s[i]) {
1333 | case ".": i0 = i1 = i; break;
1334 | case "0": if (i0 === 0) i0 = i; i1 = i; break;
1335 | default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;
1336 | }
1337 | }
1338 | return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
1339 | }
1340 |
1341 | var prefixExponent;
1342 |
1343 | function formatPrefixAuto(x, p) {
1344 | var d = formatDecimal(x, p);
1345 | if (!d) return x + "";
1346 | var coefficient = d[0],
1347 | exponent = d[1],
1348 | i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
1349 | n = coefficient.length;
1350 | return i === n ? coefficient
1351 | : i > n ? coefficient + new Array(i - n + 1).join("0")
1352 | : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
1353 | : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
1354 | }
1355 |
1356 | function formatRounded(x, p) {
1357 | var d = formatDecimal(x, p);
1358 | if (!d) return x + "";
1359 | var coefficient = d[0],
1360 | exponent = d[1];
1361 | return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
1362 | : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
1363 | : coefficient + new Array(exponent - coefficient.length + 2).join("0");
1364 | }
1365 |
1366 | var formatTypes = {
1367 | "%": function(x, p) { return (x * 100).toFixed(p); },
1368 | "b": function(x) { return Math.round(x).toString(2); },
1369 | "c": function(x) { return x + ""; },
1370 | "d": function(x) { return Math.round(x).toString(10); },
1371 | "e": function(x, p) { return x.toExponential(p); },
1372 | "f": function(x, p) { return x.toFixed(p); },
1373 | "g": function(x, p) { return x.toPrecision(p); },
1374 | "o": function(x) { return Math.round(x).toString(8); },
1375 | "p": function(x, p) { return formatRounded(x * 100, p); },
1376 | "r": formatRounded,
1377 | "s": formatPrefixAuto,
1378 | "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
1379 | "x": function(x) { return Math.round(x).toString(16); }
1380 | };
1381 |
1382 | function identity$1(x) {
1383 | return x;
1384 | }
1385 |
1386 | var map$2 = Array.prototype.map,
1387 | prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
1388 |
1389 | function formatLocale(locale) {
1390 | var group = locale.grouping === undefined || locale.thousands === undefined ? identity$1 : formatGroup(map$2.call(locale.grouping, Number), locale.thousands + ""),
1391 | currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
1392 | currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
1393 | decimal = locale.decimal === undefined ? "." : locale.decimal + "",
1394 | numerals = locale.numerals === undefined ? identity$1 : formatNumerals(map$2.call(locale.numerals, String)),
1395 | percent = locale.percent === undefined ? "%" : locale.percent + "",
1396 | minus = locale.minus === undefined ? "-" : locale.minus + "",
1397 | nan = locale.nan === undefined ? "NaN" : locale.nan + "";
1398 |
1399 | function newFormat(specifier) {
1400 | specifier = formatSpecifier(specifier);
1401 |
1402 | var fill = specifier.fill,
1403 | align = specifier.align,
1404 | sign = specifier.sign,
1405 | symbol = specifier.symbol,
1406 | zero = specifier.zero,
1407 | width = specifier.width,
1408 | comma = specifier.comma,
1409 | precision = specifier.precision,
1410 | trim = specifier.trim,
1411 | type = specifier.type;
1412 |
1413 | // The "n" type is an alias for ",g".
1414 | if (type === "n") comma = true, type = "g";
1415 |
1416 | // The "" type, and any invalid type, is an alias for ".12~g".
1417 | else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
1418 |
1419 | // If zero fill is specified, padding goes after sign and before digits.
1420 | if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
1421 |
1422 | // Compute the prefix and suffix.
1423 | // For SI-prefix, the suffix is lazily computed.
1424 | var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
1425 | suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
1426 |
1427 | // What format function should we use?
1428 | // Is this an integer type?
1429 | // Can this type generate exponential notation?
1430 | var formatType = formatTypes[type],
1431 | maybeSuffix = /[defgprs%]/.test(type);
1432 |
1433 | // Set the default precision if not specified,
1434 | // or clamp the specified precision to the supported range.
1435 | // For significant precision, it must be in [1, 21].
1436 | // For fixed precision, it must be in [0, 20].
1437 | precision = precision === undefined ? 6
1438 | : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
1439 | : Math.max(0, Math.min(20, precision));
1440 |
1441 | function format(value) {
1442 | var valuePrefix = prefix,
1443 | valueSuffix = suffix,
1444 | i, n, c;
1445 |
1446 | if (type === "c") {
1447 | valueSuffix = formatType(value) + valueSuffix;
1448 | value = "";
1449 | } else {
1450 | value = +value;
1451 |
1452 | // Perform the initial formatting.
1453 | var valueNegative = value < 0;
1454 | value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
1455 |
1456 | // Trim insignificant zeros.
1457 | if (trim) value = formatTrim(value);
1458 |
1459 | // If a negative value rounds to zero during formatting, treat as positive.
1460 | if (valueNegative && +value === 0) valueNegative = false;
1461 |
1462 | // Compute the prefix and suffix.
1463 | valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
1464 |
1465 | valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
1466 |
1467 | // Break the formatted value into the integer “value” part that can be
1468 | // grouped, and fractional or exponential “suffix” part that is not.
1469 | if (maybeSuffix) {
1470 | i = -1, n = value.length;
1471 | while (++i < n) {
1472 | if (c = value.charCodeAt(i), 48 > c || c > 57) {
1473 | valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
1474 | value = value.slice(0, i);
1475 | break;
1476 | }
1477 | }
1478 | }
1479 | }
1480 |
1481 | // If the fill character is not "0", grouping is applied before padding.
1482 | if (comma && !zero) value = group(value, Infinity);
1483 |
1484 | // Compute the padding.
1485 | var length = valuePrefix.length + value.length + valueSuffix.length,
1486 | padding = length < width ? new Array(width - length + 1).join(fill) : "";
1487 |
1488 | // If the fill character is "0", grouping is applied after padding.
1489 | if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
1490 |
1491 | // Reconstruct the final output based on the desired alignment.
1492 | switch (align) {
1493 | case "<": value = valuePrefix + value + valueSuffix + padding; break;
1494 | case "=": value = valuePrefix + padding + value + valueSuffix; break;
1495 | case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
1496 | default: value = padding + valuePrefix + value + valueSuffix; break;
1497 | }
1498 |
1499 | return numerals(value);
1500 | }
1501 |
1502 | format.toString = function() {
1503 | return specifier + "";
1504 | };
1505 |
1506 | return format;
1507 | }
1508 |
1509 | function formatPrefix(specifier, value) {
1510 | var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
1511 | e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
1512 | k = Math.pow(10, -e),
1513 | prefix = prefixes[8 + e / 3];
1514 | return function(value) {
1515 | return f(k * value) + prefix;
1516 | };
1517 | }
1518 |
1519 | return {
1520 | format: newFormat,
1521 | formatPrefix: formatPrefix
1522 | };
1523 | }
1524 |
1525 | var locale;
1526 | var format;
1527 | var formatPrefix;
1528 |
1529 | defaultLocale({
1530 | decimal: ".",
1531 | thousands: ",",
1532 | grouping: [3],
1533 | currency: ["$", ""],
1534 | minus: "-"
1535 | });
1536 |
1537 | function defaultLocale(definition) {
1538 | locale = formatLocale(definition);
1539 | format = locale.format;
1540 | formatPrefix = locale.formatPrefix;
1541 | return locale;
1542 | }
1543 |
1544 | function precisionFixed(step) {
1545 | return Math.max(0, -exponent(Math.abs(step)));
1546 | }
1547 |
1548 | function precisionPrefix(step, value) {
1549 | return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
1550 | }
1551 |
1552 | function precisionRound(step, max) {
1553 | step = Math.abs(step), max = Math.abs(max) - step;
1554 | return Math.max(0, exponent(max) - exponent(step)) + 1;
1555 | }
1556 |
1557 | function tickFormat(start, stop, count, specifier) {
1558 | var step = tickStep(start, stop, count),
1559 | precision;
1560 | specifier = formatSpecifier(specifier == null ? ",f" : specifier);
1561 | switch (specifier.type) {
1562 | case "s": {
1563 | var value = Math.max(Math.abs(start), Math.abs(stop));
1564 | if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
1565 | return formatPrefix(specifier, value);
1566 | }
1567 | case "":
1568 | case "e":
1569 | case "g":
1570 | case "p":
1571 | case "r": {
1572 | if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
1573 | break;
1574 | }
1575 | case "f":
1576 | case "%": {
1577 | if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
1578 | break;
1579 | }
1580 | }
1581 | return format(specifier);
1582 | }
1583 |
1584 | function linearish(scale) {
1585 | var domain = scale.domain;
1586 |
1587 | scale.ticks = function(count) {
1588 | var d = domain();
1589 | return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
1590 | };
1591 |
1592 | scale.tickFormat = function(count, specifier) {
1593 | var d = domain();
1594 | return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
1595 | };
1596 |
1597 | scale.nice = function(count) {
1598 | if (count == null) count = 10;
1599 |
1600 | var d = domain(),
1601 | i0 = 0,
1602 | i1 = d.length - 1,
1603 | start = d[i0],
1604 | stop = d[i1],
1605 | step;
1606 |
1607 | if (stop < start) {
1608 | step = start, start = stop, stop = step;
1609 | step = i0, i0 = i1, i1 = step;
1610 | }
1611 |
1612 | step = tickIncrement(start, stop, count);
1613 |
1614 | if (step > 0) {
1615 | start = Math.floor(start / step) * step;
1616 | stop = Math.ceil(stop / step) * step;
1617 | step = tickIncrement(start, stop, count);
1618 | } else if (step < 0) {
1619 | start = Math.ceil(start * step) / step;
1620 | stop = Math.floor(stop * step) / step;
1621 | step = tickIncrement(start, stop, count);
1622 | }
1623 |
1624 | if (step > 0) {
1625 | d[i0] = Math.floor(start / step) * step;
1626 | d[i1] = Math.ceil(stop / step) * step;
1627 | domain(d);
1628 | } else if (step < 0) {
1629 | d[i0] = Math.ceil(start * step) / step;
1630 | d[i1] = Math.floor(stop * step) / step;
1631 | domain(d);
1632 | }
1633 |
1634 | return scale;
1635 | };
1636 |
1637 | return scale;
1638 | }
1639 |
1640 | function linear$1() {
1641 | var scale = continuous(identity, identity);
1642 |
1643 | scale.copy = function() {
1644 | return copy(scale, linear$1());
1645 | };
1646 |
1647 | initRange.apply(scale, arguments);
1648 |
1649 | return linearish(scale);
1650 | }
1651 |
1652 | /**
1653 | * Meanderer class. Accepts a path, container, height, width, and change handler.
1654 | * Although it doesn't need a handler. We can just call get path and let it do that.
1655 | * The checks can be handled outside. We don't need to do it inside.
1656 | */
1657 |
1658 | var Meanderer = function Meanderer(_ref) {
1659 | var height = _ref.height,
1660 | path = _ref.path,
1661 | _ref$threshold = _ref.threshold,
1662 | threshold = _ref$threshold === void 0 ? 0.2 : _ref$threshold,
1663 | width = _ref.width;
1664 |
1665 | _classCallCheck(this, Meanderer);
1666 |
1667 | _initialiseProps.call(this);
1668 |
1669 | this.height = height;
1670 | this.path = path;
1671 | this.threshold = threshold;
1672 | this.width = width; // With what we are given create internal references
1673 |
1674 | this.aspect_ratio = width / height; // Convert the path into a data set
1675 |
1676 | this.path_data = this.convertPathToData(path);
1677 | this.maximums = this.getMaximums(this.path_data);
1678 | this.range_ratios = this.getRatios(this.maximums, width, height);
1679 | } // This is relevant for when we want to interpolate points to
1680 | // the container scale. We need the minimum and maximum for both X and Y
1681 | ;
1682 |
1683 | var _initialiseProps = function _initialiseProps() {
1684 | var _this = this;
1685 |
1686 | this.getMaximums = function (data) {
1687 | var X_POINTS = data.map(function (point) {
1688 | return point[0];
1689 | });
1690 | var Y_POINTS = data.map(function (point) {
1691 | return point[1];
1692 | });
1693 | return [Math.max.apply(Math, _toConsumableArray(X_POINTS)), // x2
1694 | Math.max.apply(Math, _toConsumableArray(Y_POINTS)) // y2
1695 | ];
1696 | };
1697 |
1698 | this.getRatios = function (maxs, width, height) {
1699 | return [maxs[0] / width, maxs[1] / height];
1700 | };
1701 |
1702 | this.convertPathToData = function (path) {
1703 | // To convert the path data to points, we need an SVG path element.
1704 | var svgContainer = document.createElement("div"); // To create one though, a quick way is to use innerHTML
1705 |
1706 | svgContainer.innerHTML = "");
1707 | var pathElement = svgContainer.querySelector("path"); // Now to gather up the path points using the SVGGeometryElement API 👍
1708 |
1709 | var DATA = []; // Iterate over the total length of the path pushing the x and y into
1710 | // a data set for d3 to handle 👍
1711 |
1712 | for (var p = 0; p < pathElement.getTotalLength(); p++) {
1713 | var _pathElement$getPoint = pathElement.getPointAtLength(p),
1714 | x = _pathElement$getPoint.x,
1715 | y = _pathElement$getPoint.y;
1716 |
1717 | DATA.push([x, y]);
1718 | }
1719 |
1720 | return DATA;
1721 | };
1722 |
1723 | this.generatePath = function (containerWidth, containerHeight) {
1724 | var height = _this.height,
1725 | width = _this.width,
1726 | aspectRatio = _this.aspect_ratio,
1727 | data = _this.path_data,
1728 | _this$maximums = _slicedToArray(_this.maximums, 2),
1729 | maxWidth = _this$maximums[0],
1730 | maxHeight = _this$maximums[1],
1731 | _this$range_ratios = _slicedToArray(_this.range_ratios, 2),
1732 | widthRatio = _this$range_ratios[0],
1733 | heightRatio = _this$range_ratios[1],
1734 | threshold = _this.threshold;
1735 |
1736 | var OFFSETS = [0, 0]; // Get the aspect ratio defined by the container
1737 |
1738 | var newAspectRatio = containerWidth / containerHeight; // We only need to start applying offsets if the aspect ratio of the container is off 👍
1739 | // In here we need to work out which side needs the offset. It's whichever one is smallest in order to centralize.
1740 | // What if the container matches the aspect ratio...
1741 |
1742 | if (Math.abs(newAspectRatio - aspectRatio) > threshold) {
1743 | // We know the tolerance is off so we need to work out a ratio
1744 | // This works flawlessly. Now we need to check for when the height is less than the width
1745 | if (width < height) {
1746 | var ratio = (height - width) / height;
1747 | OFFSETS[0] = ratio * containerWidth / 2;
1748 | } else {
1749 | var _ratio = (width - height) / width;
1750 |
1751 | OFFSETS[1] = _ratio * containerHeight / 2;
1752 | }
1753 | } // Create two d3 scales for X and Y
1754 |
1755 |
1756 | var xScale = linear$1().domain([0, maxWidth]).range([OFFSETS[0], containerWidth * widthRatio - OFFSETS[0]]);
1757 | var yScale = linear$1().domain([0, maxHeight]).range([OFFSETS[1], containerHeight * heightRatio - OFFSETS[1]]); // Map our data points using the scales
1758 |
1759 | var SCALED_POINTS = data.map(function (POINT) {
1760 | return [xScale(POINT[0]), yScale(POINT[1])];
1761 | });
1762 | return line()(SCALED_POINTS);
1763 | };
1764 | };
1765 |
1766 | return Meanderer;
1767 |
1768 | })));
1769 |
--------------------------------------------------------------------------------