vendor/assets/javascripts/vega.js in vega-0.3.1 vs vendor/assets/javascripts/vega.js in vega-0.3.2
- old
+ new
@@ -7,65 +7,53 @@
function accessor(fn, fields, name) {
fn.fields = fields || [];
fn.fname = name;
return fn;
}
-
function accessorName(fn) {
return fn == null ? null : fn.fname;
}
-
function accessorFields(fn) {
return fn == null ? null : fn.fields;
}
-
- function getter(path) {
+ function getter$1(path) {
return path.length === 1 ? get1(path[0]) : getN(path);
}
-
const get1 = field => function (obj) {
return obj[field];
};
-
const getN = path => {
const len = path.length;
return function (obj) {
for (let i = 0; i < len; ++i) {
obj = obj[path[i]];
}
-
return obj;
};
};
-
function error(message) {
throw Error(message);
}
-
function splitAccessPath(p) {
const path = [],
- n = p.length;
+ n = p.length;
let q = null,
- b = 0,
- s = '',
- i,
- j,
- c;
+ b = 0,
+ s = '',
+ i,
+ j,
+ c;
p = p + '';
-
function push() {
path.push(s + p.substring(i, j));
s = '';
i = j + 1;
}
-
for (i = j = 0; j < n; ++j) {
c = p[j];
-
if (c === '\\') {
- s += p.substring(i, j);
- s += p.substring(++j, ++j);
+ s += p.substring(i, j++);
i = j;
} else if (c === q) {
push();
q = null;
b = -1;
@@ -91,46 +79,38 @@
if (b > 0) push();
b = 0;
i = j + 1;
}
}
-
if (b) error('Access path missing closing bracket: ' + p);
if (q) error('Access path missing closing quote: ' + p);
-
if (j > i) {
j++;
push();
}
-
return path;
}
-
function field$1(field, name, opt) {
const path = splitAccessPath(field);
field = path.length === 1 ? path[0] : field;
- return accessor((opt && opt.get || getter)(path), [field], name || field);
+ return accessor((opt && opt.get || getter$1)(path), [field], name || field);
}
-
const id = field$1('id');
const identity$6 = accessor(_ => _, [], 'identity');
- const zero$2 = accessor(() => 0, [], 'zero');
+ const zero$3 = accessor(() => 0, [], 'zero');
const one$2 = accessor(() => 1, [], 'one');
const truthy = accessor(() => true, [], 'true');
const falsy = accessor(() => false, [], 'false');
-
function log$1$1(method, level, input) {
const args = [level].concat([].slice.call(input));
console[method].apply(console, args); // eslint-disable-line no-console
}
-
const None$2 = 0;
const Error$1 = 1;
const Warn = 2;
const Info = 3;
const Debug = 4;
-
function logger(_, method) {
let handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : log$1$1;
let level = _ || None$2;
return {
level(_) {
@@ -139,47 +119,37 @@
return this;
} else {
return level;
}
},
-
error() {
if (level >= Error$1) handler(method || 'error', 'ERROR', arguments);
return this;
},
-
warn() {
if (level >= Warn) handler(method || 'warn', 'WARN', arguments);
return this;
},
-
info() {
if (level >= Info) handler(method || 'log', 'INFO', arguments);
return this;
},
-
debug() {
if (level >= Debug) handler(method || 'log', 'DEBUG', arguments);
return this;
}
-
};
}
-
var isArray = Array.isArray;
-
function isObject(_) {
return _ === Object(_);
}
-
const isLegalKey = key => key !== '__proto__';
-
function mergeConfig() {
for (var _len = arguments.length, configs = new Array(_len), _key = 0; _key < _len; _key++) {
configs[_key] = arguments[_key];
}
-
return configs.reduce((out, source) => {
for (const key in source) {
if (key === 'signals') {
// for signals, we merge the signals arrays
// source signals take precedence over
@@ -194,22 +164,18 @@
layout: 1
} : key === 'style' ? true : null;
writeConfig(out, key, source[key], r);
}
}
-
return out;
}, {});
}
-
function writeConfig(output, key, value, recurse) {
if (!isLegalKey(key)) return;
let k, o;
-
if (isObject(value) && !isArray(value)) {
o = isObject(output[key]) ? output[key] : output[key] = {};
-
for (k in value) {
if (recurse && (recurse === true || recurse[k])) {
writeConfig(o, k, value[k]);
} else if (isLegalKey(k)) {
o[k] = value[k];
@@ -217,646 +183,528 @@
}
} else {
output[key] = value;
}
}
-
function mergeNamed(a, b) {
if (a == null) return b;
const map = {},
- out = [];
-
+ out = [];
function add(_) {
if (!map[_.name]) {
map[_.name] = 1;
out.push(_);
}
}
-
b.forEach(add);
a.forEach(add);
return out;
}
-
function peek$1(array) {
return array[array.length - 1];
}
-
function toNumber(_) {
return _ == null || _ === '' ? null : +_;
}
-
const exp$2 = sign => x => sign * Math.exp(x);
-
const log$4 = sign => x => Math.log(sign * x);
-
const symlog$1 = c => x => Math.sign(x) * Math.log1p(Math.abs(x / c));
-
const symexp = c => x => Math.sign(x) * Math.expm1(Math.abs(x)) * c;
-
const pow$4 = exponent => x => x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
-
function pan(domain, delta, lift, ground) {
const d0 = lift(domain[0]),
- d1 = lift(peek$1(domain)),
- dd = (d1 - d0) * delta;
+ d1 = lift(peek$1(domain)),
+ dd = (d1 - d0) * delta;
return [ground(d0 - dd), ground(d1 - dd)];
}
-
function panLinear(domain, delta) {
return pan(domain, delta, toNumber, identity$6);
}
-
function panLog(domain, delta) {
var sign = Math.sign(domain[0]);
return pan(domain, delta, log$4(sign), exp$2(sign));
}
-
function panPow(domain, delta, exponent) {
return pan(domain, delta, pow$4(exponent), pow$4(1 / exponent));
}
-
function panSymlog(domain, delta, constant) {
return pan(domain, delta, symlog$1(constant), symexp(constant));
}
-
function zoom$1(domain, anchor, scale, lift, ground) {
const d0 = lift(domain[0]),
- d1 = lift(peek$1(domain)),
- da = anchor != null ? lift(anchor) : (d0 + d1) / 2;
+ d1 = lift(peek$1(domain)),
+ da = anchor != null ? lift(anchor) : (d0 + d1) / 2;
return [ground(da + (d0 - da) * scale), ground(da + (d1 - da) * scale)];
}
-
function zoomLinear(domain, anchor, scale) {
return zoom$1(domain, anchor, scale, toNumber, identity$6);
}
-
function zoomLog(domain, anchor, scale) {
const sign = Math.sign(domain[0]);
return zoom$1(domain, anchor, scale, log$4(sign), exp$2(sign));
}
-
function zoomPow(domain, anchor, scale, exponent) {
return zoom$1(domain, anchor, scale, pow$4(exponent), pow$4(1 / exponent));
}
-
function zoomSymlog(domain, anchor, scale, constant) {
return zoom$1(domain, anchor, scale, symlog$1(constant), symexp(constant));
}
-
function quarter(date) {
return 1 + ~~(new Date(date).getMonth() / 3);
}
-
function utcquarter(date) {
return 1 + ~~(new Date(date).getUTCMonth() / 3);
}
-
function array$5(_) {
return _ != null ? isArray(_) ? _ : [_] : [];
}
+
/**
* Span-preserving range clamp. If the span of the input range is less
* than (max - min) and an endpoint exceeds either the min or max value,
* the range is translated such that the span is preserved and one
* endpoint touches the boundary of the min/max range.
* If the span exceeds (max - min), the range [min, max] is returned.
*/
-
-
function clampRange(range, min, max) {
let lo = range[0],
- hi = range[1],
- span;
-
+ hi = range[1],
+ span;
if (hi < lo) {
span = hi;
hi = lo;
lo = span;
}
-
span = hi - lo;
return span >= max - min ? [min, max] : [lo = Math.min(Math.max(lo, min), max - span), lo + span];
}
-
function isFunction(_) {
return typeof _ === 'function';
}
-
const DESCENDING = 'descending';
-
function compare$1(fields, orders, opt) {
opt = opt || {};
orders = array$5(orders) || [];
const ord = [],
- get = [],
- fmap = {},
- gen = opt.comparator || comparator;
+ get = [],
+ fmap = {},
+ gen = opt.comparator || comparator;
array$5(fields).forEach((f, i) => {
if (f == null) return;
ord.push(orders[i] === DESCENDING ? -1 : 1);
get.push(f = isFunction(f) ? f : field$1(f, null, opt));
(accessorFields(f) || []).forEach(_ => fmap[_] = 1);
});
return get.length === 0 ? null : accessor(gen(get, ord), Object.keys(fmap));
}
-
const ascending$2 = (u, v) => (u < v || u == null) && v != null ? -1 : (u > v || v == null) && u != null ? 1 : (v = v instanceof Date ? +v : v, u = u instanceof Date ? +u : u) !== u && v === v ? -1 : v !== v && u === u ? 1 : 0;
-
const comparator = (fields, orders) => fields.length === 1 ? compare1(fields[0], orders[0]) : compareN(fields, orders, fields.length);
-
const compare1 = (field, order) => function (a, b) {
return ascending$2(field(a), field(b)) * order;
};
-
const compareN = (fields, orders, n) => {
orders.push(0); // pad zero for convenient lookup
-
return function (a, b) {
let f,
- c = 0,
- i = -1;
-
+ c = 0,
+ i = -1;
while (c === 0 && ++i < n) {
f = fields[i];
c = ascending$2(f(a), f(b));
}
-
return c * orders[i];
};
};
-
- function constant$4(_) {
+ function constant$5(_) {
return isFunction(_) ? _ : () => _;
}
-
function debounce(delay, handler) {
let tid;
return e => {
if (tid) clearTimeout(tid);
tid = setTimeout(() => (handler(e), tid = null), delay);
};
}
-
function extend$1(_) {
for (let x, k, i = 1, len = arguments.length; i < len; ++i) {
x = arguments[i];
-
for (k in x) {
_[k] = x[k];
}
}
-
return _;
}
+
/**
* Return an array with minimum and maximum values, in the
* form [min, max]. Ignores null, undefined, and NaN values.
*/
-
-
function extent(array, f) {
let i = 0,
- n,
- v,
- min,
- max;
-
+ n,
+ v,
+ min,
+ max;
if (array && (n = array.length)) {
if (f == null) {
// find first valid value
for (v = array[i]; i < n && (v == null || v !== v); v = array[++i]);
+ min = max = v;
- min = max = v; // visit all other values
-
+ // visit all other values
for (; i < n; ++i) {
- v = array[i]; // skip null/undefined; NaN will fail all comparisons
-
+ v = array[i];
+ // skip null/undefined; NaN will fail all comparisons
if (v != null) {
if (v < min) min = v;
if (v > max) max = v;
}
}
} else {
// find first valid value
for (v = f(array[i]); i < n && (v == null || v !== v); v = f(array[++i]));
+ min = max = v;
- min = max = v; // visit all other values
-
+ // visit all other values
for (; i < n; ++i) {
- v = f(array[i]); // skip null/undefined; NaN will fail all comparisons
-
+ v = f(array[i]);
+ // skip null/undefined; NaN will fail all comparisons
if (v != null) {
if (v < min) min = v;
if (v > max) max = v;
}
}
}
}
-
return [min, max];
}
-
function extentIndex(array, f) {
const n = array.length;
let i = -1,
- a,
- b,
- c,
- u,
- v;
-
+ a,
+ b,
+ c,
+ u,
+ v;
if (f == null) {
while (++i < n) {
b = array[i];
-
if (b != null && b >= b) {
a = c = b;
break;
}
}
-
if (i === n) return [-1, -1];
u = v = i;
-
while (++i < n) {
b = array[i];
-
if (b != null) {
if (a > b) {
a = b;
u = i;
}
-
if (c < b) {
c = b;
v = i;
}
}
}
} else {
while (++i < n) {
b = f(array[i], i, array);
-
if (b != null && b >= b) {
a = c = b;
break;
}
}
-
if (i === n) return [-1, -1];
u = v = i;
-
while (++i < n) {
b = f(array[i], i, array);
-
if (b != null) {
if (a > b) {
a = b;
u = i;
}
-
if (c < b) {
c = b;
v = i;
}
}
}
}
-
return [u, v];
}
-
const hop = Object.prototype.hasOwnProperty;
-
function has$1(object, property) {
return hop.call(object, property);
}
-
const NULL = {};
-
function fastmap(input) {
let obj = {},
- test;
-
+ test;
function has$1$1(key) {
return has$1(obj, key) && obj[key] !== NULL;
}
-
const map = {
size: 0,
empty: 0,
object: obj,
has: has$1$1,
-
get(key) {
return has$1$1(key) ? obj[key] : undefined;
},
-
set(key, value) {
if (!has$1$1(key)) {
++map.size;
if (obj[key] === NULL) --map.empty;
}
-
obj[key] = value;
return this;
},
-
delete(key) {
if (has$1$1(key)) {
--map.size;
++map.empty;
obj[key] = NULL;
}
-
return this;
},
-
clear() {
map.size = map.empty = 0;
map.object = obj = {};
},
-
test(_) {
if (arguments.length) {
test = _;
return map;
} else {
return test;
}
},
-
clean() {
const next = {};
let size = 0;
-
for (const key in obj) {
const value = obj[key];
-
if (value !== NULL && (!test || !test(value))) {
next[key] = value;
++size;
}
}
-
map.size = size;
map.empty = 0;
map.object = obj = next;
}
-
};
if (input) Object.keys(input).forEach(key => {
map.set(key, input[key]);
});
return map;
}
-
function flush(range, value, threshold, left, right, center) {
if (!threshold && threshold !== 0) return center;
const t = +threshold;
let a = range[0],
- b = peek$1(range),
- l; // swap endpoints if range is reversed
+ b = peek$1(range),
+ l;
+ // swap endpoints if range is reversed
if (b < a) {
l = a;
a = b;
b = l;
- } // compare value to endpoints
+ }
-
+ // compare value to endpoints
l = Math.abs(value - a);
- const r = Math.abs(b - value); // adjust if value is within threshold distance of endpoint
+ const r = Math.abs(b - value);
+ // adjust if value is within threshold distance of endpoint
return l < r && l <= t ? left : r <= t ? right : center;
}
-
function inherits(child, parent, members) {
const proto = child.prototype = Object.create(parent.prototype);
Object.defineProperty(proto, 'constructor', {
value: child,
writable: true,
enumerable: true,
configurable: true
});
return extend$1(proto, members);
}
+
/**
* Predicate that returns true if the value lies within the span
* of the given range. The left and right flags control the use
* of inclusive (true) or exclusive (false) comparisons.
*/
-
-
function inrange(value, range, left, right) {
let r0 = range[0],
- r1 = range[range.length - 1],
- t;
-
+ r1 = range[range.length - 1],
+ t;
if (r0 > r1) {
t = r0;
r0 = r1;
r1 = t;
}
-
left = left === undefined || left;
right = right === undefined || right;
return (left ? r0 <= value : r0 < value) && (right ? value <= r1 : value < r1);
}
-
function isBoolean$1(_) {
return typeof _ === 'boolean';
}
-
function isDate$1(_) {
return Object.prototype.toString.call(_) === '[object Date]';
}
-
function isIterable(_) {
return _ && isFunction(_[Symbol.iterator]);
}
-
function isNumber$1(_) {
return typeof _ === 'number';
}
-
function isRegExp(_) {
return Object.prototype.toString.call(_) === '[object RegExp]';
}
-
function isString(_) {
return typeof _ === 'string';
}
-
function key(fields, flat, opt) {
if (fields) {
fields = flat ? array$5(fields).map(f => f.replace(/\\(.)/g, '$1')) : array$5(fields);
}
-
const len = fields && fields.length,
- gen = opt && opt.get || getter,
- map = f => gen(flat ? [f] : splitAccessPath(f));
-
+ gen = opt && opt.get || getter$1,
+ map = f => gen(flat ? [f] : splitAccessPath(f));
let fn;
-
if (!len) {
fn = function () {
return '';
};
} else if (len === 1) {
const get = map(fields[0]);
-
fn = function (_) {
return '' + get(_);
};
} else {
const get = fields.map(map);
-
fn = function (_) {
let s = '' + get[0](_),
- i = 0;
-
+ i = 0;
while (++i < len) s += '|' + get[i](_);
-
return s;
};
}
-
return accessor(fn, fields, 'key');
}
-
function lerp(array, frac) {
const lo = array[0],
- hi = peek$1(array),
- f = +frac;
+ hi = peek$1(array),
+ f = +frac;
return !f ? lo : f === 1 ? hi : lo + f * (hi - lo);
}
+ const DEFAULT_MAX_SIZE = 10000;
- const DEFAULT_MAX_SIZE = 10000; // adapted from https://github.com/dominictarr/hashlru/ (MIT License)
-
+ // adapted from https://github.com/dominictarr/hashlru/ (MIT License)
function lruCache(maxsize) {
maxsize = +maxsize || DEFAULT_MAX_SIZE;
let curr, prev, size;
-
const clear = () => {
curr = {};
prev = {};
size = 0;
};
-
const update = (key, value) => {
if (++size > maxsize) {
prev = curr;
curr = {};
size = 1;
}
-
return curr[key] = value;
};
-
clear();
return {
clear,
has: key => has$1(curr, key) || has$1(prev, key),
get: key => has$1(curr, key) ? curr[key] : has$1(prev, key) ? update(key, prev[key]) : undefined,
set: (key, value) => has$1(curr, key) ? curr[key] = value : update(key, value)
};
}
-
function merge$3(compare, array0, array1, output) {
const n0 = array0.length,
- n1 = array1.length;
+ n1 = array1.length;
if (!n1) return array0;
if (!n0) return array1;
const merged = output || new array0.constructor(n0 + n1);
let i0 = 0,
- i1 = 0,
- i = 0;
-
+ i1 = 0,
+ i = 0;
for (; i0 < n0 && i1 < n1; ++i) {
merged[i] = compare(array0[i0], array1[i1]) > 0 ? array1[i1++] : array0[i0++];
}
-
for (; i0 < n0; ++i0, ++i) {
merged[i] = array0[i0];
}
-
for (; i1 < n1; ++i1, ++i) {
merged[i] = array1[i1];
}
-
return merged;
}
-
function repeat(str, reps) {
let s = '';
-
while (--reps >= 0) s += str;
-
return s;
}
-
function pad$2(str, length, padchar, align) {
const c = padchar || ' ',
- s = str + '',
- n = length - s.length;
+ s = str + '',
+ n = length - s.length;
return n <= 0 ? s : align === 'left' ? repeat(c, n) + s : align === 'center' ? repeat(c, ~~(n / 2)) + s + repeat(c, Math.ceil(n / 2)) : s + repeat(c, n);
}
+
/**
* Return the numerical span of an array: the difference between
* the last and first values.
*/
-
-
function span(array) {
return array && peek$1(array) - array[0] || 0;
}
-
function $(x) {
- return isArray(x) ? '[' + x.map($) + ']' : isObject(x) || isString(x) ? // Output valid JSON and JS source strings.
+ return isArray(x) ? '[' + x.map($) + ']' : isObject(x) || isString(x) ?
+ // Output valid JSON and JS source strings.
// See http://timelessrepo.com/json-isnt-a-javascript-subset
JSON.stringify(x).replace('\u2028', '\\u2028').replace('\u2029', '\\u2029') : x;
}
-
function toBoolean(_) {
return _ == null || _ === '' ? null : !_ || _ === 'false' || _ === '0' ? false : !!_;
}
-
const defaultParser = _ => isNumber$1(_) ? _ : isDate$1(_) ? _ : Date.parse(_);
-
function toDate(_, parser) {
parser = parser || defaultParser;
return _ == null || _ === '' ? null : parser(_);
}
-
function toString(_) {
return _ == null || _ === '' ? null : _ + '';
}
-
function toSet(_) {
const s = {},
- n = _.length;
-
+ n = _.length;
for (let i = 0; i < n; ++i) s[_[i]] = true;
-
return s;
}
-
function truncate$1(str, length, align, ellipsis) {
const e = ellipsis != null ? ellipsis : '\u2026',
- s = str + '',
- n = s.length,
- l = Math.max(0, length - e.length);
+ s = str + '',
+ n = s.length,
+ l = Math.max(0, length - e.length);
return n <= length ? s : align === 'left' ? e + s.slice(n - l) : align === 'center' ? s.slice(0, Math.ceil(l / 2)) + e + s.slice(n - ~~(l / 2)) : s.slice(0, l) + e;
}
-
function visitArray(array, filter, visitor) {
if (array) {
if (filter) {
const n = array.length;
-
for (let i = 0; i < n; ++i) {
const t = filter(array[i]);
if (t) visitor(t, i, array);
}
} else {
@@ -864,166 +712,145 @@
}
}
}
var EOL = {},
- EOF = {},
- QUOTE = 34,
- NEWLINE = 10,
- RETURN = 13;
-
+ EOF = {},
+ QUOTE = 34,
+ NEWLINE = 10,
+ RETURN = 13;
function objectConverter(columns) {
return new Function("d", "return {" + columns.map(function (name, i) {
return JSON.stringify(name) + ": d[" + i + "] || \"\"";
}).join(",") + "}");
}
-
function customConverter(columns, f) {
var object = objectConverter(columns);
return function (row, i) {
return f(object(row), i, columns);
};
- } // Compute unique columns in order of discovery.
+ }
-
+ // Compute unique columns in order of discovery.
function inferColumns(rows) {
var columnSet = Object.create(null),
- columns = [];
+ columns = [];
rows.forEach(function (row) {
for (var column in row) {
if (!(column in columnSet)) {
columns.push(columnSet[column] = column);
}
}
});
return columns;
}
-
function pad$1(value, width) {
var s = value + "",
- length = s.length;
+ length = s.length;
return length < width ? new Array(width - length + 1).join(0) + s : s;
}
-
function formatYear$1(year) {
return year < 0 ? "-" + pad$1(-year, 6) : year > 9999 ? "+" + pad$1(year, 6) : pad$1(year, 4);
}
-
function formatDate(date) {
var hours = date.getUTCHours(),
- minutes = date.getUTCMinutes(),
- seconds = date.getUTCSeconds(),
- milliseconds = date.getUTCMilliseconds();
+ minutes = date.getUTCMinutes(),
+ seconds = date.getUTCSeconds(),
+ milliseconds = date.getUTCMilliseconds();
return isNaN(date) ? "Invalid Date" : formatYear$1(date.getUTCFullYear()) + "-" + pad$1(date.getUTCMonth() + 1, 2) + "-" + pad$1(date.getUTCDate(), 2) + (milliseconds ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + ":" + pad$1(seconds, 2) + "." + pad$1(milliseconds, 3) + "Z" : seconds ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + ":" + pad$1(seconds, 2) + "Z" : minutes || hours ? "T" + pad$1(hours, 2) + ":" + pad$1(minutes, 2) + "Z" : "");
}
-
function dsvFormat (delimiter) {
var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
- DELIMITER = delimiter.charCodeAt(0);
-
+ DELIMITER = delimiter.charCodeAt(0);
function parse(text, f) {
var convert,
- columns,
- rows = parseRows(text, function (row, i) {
- if (convert) return convert(row, i - 1);
- columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
- });
+ columns,
+ rows = parseRows(text, function (row, i) {
+ if (convert) return convert(row, i - 1);
+ columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
+ });
rows.columns = columns || [];
return rows;
}
-
function parseRows(text, f) {
var rows = [],
- // output rows
- N = text.length,
- I = 0,
- // current character index
- n = 0,
- // current line number
- t,
- // current token
- eof = N <= 0,
- // current token followed by EOF?
- eol = false; // current token followed by EOL?
- // Strip the trailing newline.
+ // output rows
+ N = text.length,
+ I = 0,
+ // current character index
+ n = 0,
+ // current line number
+ t,
+ // current token
+ eof = N <= 0,
+ // current token followed by EOF?
+ eol = false; // current token followed by EOL?
+ // Strip the trailing newline.
if (text.charCodeAt(N - 1) === NEWLINE) --N;
if (text.charCodeAt(N - 1) === RETURN) --N;
-
function token() {
if (eof) return EOF;
- if (eol) return eol = false, EOL; // Unescape quotes.
+ if (eol) return eol = false, EOL;
+ // Unescape quotes.
var i,
- j = I,
- c;
-
+ j = I,
+ c;
if (text.charCodeAt(j) === QUOTE) {
while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
-
if ((i = I) >= N) eof = true;else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;else if (c === RETURN) {
eol = true;
if (text.charCodeAt(I) === NEWLINE) ++I;
}
return text.slice(j + 1, i - 1).replace(/""/g, "\"");
- } // Find next delimiter or newline.
+ }
-
+ // Find next delimiter or newline.
while (I < N) {
if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;else if (c === RETURN) {
eol = true;
if (text.charCodeAt(I) === NEWLINE) ++I;
} else if (c !== DELIMITER) continue;
return text.slice(j, i);
- } // Return last token before EOF.
+ }
-
+ // Return last token before EOF.
return eof = true, text.slice(j, N);
}
-
while ((t = token()) !== EOF) {
var row = [];
-
while (t !== EOL && t !== EOF) row.push(t), t = token();
-
if (f && (row = f(row, n++)) == null) continue;
rows.push(row);
}
-
return rows;
}
-
function preformatBody(rows, columns) {
return rows.map(function (row) {
return columns.map(function (column) {
return formatValue(row[column]);
}).join(delimiter);
});
}
-
function format(rows, columns) {
if (columns == null) columns = inferColumns(rows);
return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n");
}
-
function formatBody(rows, columns) {
if (columns == null) columns = inferColumns(rows);
return preformatBody(rows, columns).join("\n");
}
-
function formatRows(rows) {
return rows.map(formatRow).join("\n");
}
-
function formatRow(row) {
return row.map(formatValue).join(delimiter);
}
-
function formatValue(value) {
return value == null ? "" : value instanceof Date ? formatDate(value) : reFormat.test(value += "") ? "\"" + value.replace(/"/g, "\"\"") + "\"" : value;
}
-
return {
parse: parse,
parseRows: parseRows,
format: format,
formatBody: formatBody,
@@ -1038,34 +865,31 @@
}
function transform$3 (transform) {
if (transform == null) return identity$5;
var x0,
- y0,
- kx = transform.scale[0],
- ky = transform.scale[1],
- dx = transform.translate[0],
- dy = transform.translate[1];
+ y0,
+ kx = transform.scale[0],
+ ky = transform.scale[1],
+ dx = transform.translate[0],
+ dy = transform.translate[1];
return function (input, i) {
if (!i) x0 = y0 = 0;
var j = 2,
- n = input.length,
- output = new Array(n);
+ n = input.length,
+ output = new Array(n);
output[0] = (x0 += input[0]) * kx + dx;
output[1] = (y0 += input[1]) * ky + dy;
-
while (j < n) output[j] = input[j], ++j;
-
return output;
};
}
function reverse$1 (array, n) {
var t,
- j = array.length,
- i = j - n;
-
+ j = array.length,
+ i = j - n;
while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
}
function feature (topology, o) {
if (typeof o === "string") o = topology.objects[o];
@@ -1074,16 +898,15 @@
features: o.geometries.map(function (o) {
return feature$1(topology, o);
})
} : feature$1(topology, o);
}
-
function feature$1(topology, o) {
var id = o.id,
- bbox = o.bbox,
- properties = o.properties == null ? {} : o.properties,
- geometry = object$1(topology, o);
+ bbox = o.bbox,
+ properties = o.properties == null ? {} : o.properties,
+ geometry = object$1(topology, o);
return id == null && bbox == null ? {
type: "Feature",
properties: properties,
geometry: geometry
} : bbox == null ? {
@@ -1097,127 +920,100 @@
bbox: bbox,
properties: properties,
geometry: geometry
};
}
-
function object$1(topology, o) {
var transformPoint = transform$3(topology.transform),
- arcs = topology.arcs;
-
+ arcs = topology.arcs;
function arc(i, points) {
if (points.length) points.pop();
-
for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) {
points.push(transformPoint(a[k], k));
}
-
if (i < 0) reverse$1(points, n);
}
-
function point(p) {
return transformPoint(p);
}
-
function line(arcs) {
var points = [];
-
for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
-
if (points.length < 2) points.push(points[0]); // This should never happen per the specification.
-
return points;
}
-
function ring(arcs) {
var points = line(arcs);
-
while (points.length < 4) points.push(points[0]); // This may happen if an arc has only two points.
-
-
return points;
}
-
function polygon(arcs) {
return arcs.map(ring);
}
-
function geometry(o) {
var type = o.type,
- coordinates;
-
+ coordinates;
switch (type) {
case "GeometryCollection":
return {
type: type,
geometries: o.geometries.map(geometry)
};
-
case "Point":
coordinates = point(o.coordinates);
break;
-
case "MultiPoint":
coordinates = o.coordinates.map(point);
break;
-
case "LineString":
coordinates = line(o.arcs);
break;
-
case "MultiLineString":
coordinates = o.arcs.map(line);
break;
-
case "Polygon":
coordinates = polygon(o.arcs);
break;
-
case "MultiPolygon":
coordinates = o.arcs.map(polygon);
break;
-
default:
return null;
}
-
return {
type: type,
coordinates: coordinates
};
}
-
return geometry(o);
}
function stitch (topology, arcs) {
var stitchedArcs = {},
- fragmentByStart = {},
- fragmentByEnd = {},
- fragments = [],
- emptyIndex = -1; // Stitch empty arcs first, since they may be subsumed by other arcs.
+ fragmentByStart = {},
+ fragmentByEnd = {},
+ fragments = [],
+ emptyIndex = -1;
+ // Stitch empty arcs first, since they may be subsumed by other arcs.
arcs.forEach(function (i, j) {
var arc = topology.arcs[i < 0 ? ~i : i],
- t;
-
+ t;
if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
}
});
arcs.forEach(function (i) {
var e = ends(i),
- start = e[0],
- end = e[1],
- f,
- g;
-
+ start = e[0],
+ end = e[1],
+ f,
+ g;
if (f = fragmentByEnd[start]) {
delete fragmentByEnd[f.end];
f.push(i);
f.end = end;
-
if (g = fragmentByStart[end]) {
delete fragmentByStart[g.start];
var fg = g === f ? f : f.concat(g);
fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
} else {
@@ -1225,11 +1021,10 @@
}
} else if (f = fragmentByStart[end]) {
delete fragmentByStart[f.start];
f.unshift(i);
f.start = start;
-
if (g = fragmentByEnd[start]) {
delete fragmentByEnd[g.end];
var gf = g === f ? f : g.concat(f);
fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
} else {
@@ -1238,21 +1033,19 @@
} else {
f = [i];
fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
}
});
-
function ends(i) {
var arc = topology.arcs[i < 0 ? ~i : i],
- p0 = arc[0],
- p1;
+ p0 = arc[0],
+ p1;
if (topology.transform) p1 = [0, 0], arc.forEach(function (dp) {
p1[0] += dp[0], p1[1] += dp[1];
});else p1 = arc[arc.length - 1];
return i < 0 ? [p1, p0] : [p0, p1];
}
-
function flush(fragmentByEnd, fragmentByStart) {
for (var k in fragmentByEnd) {
var f = fragmentByEnd[k];
delete fragmentByStart[f.start];
delete f.start;
@@ -1261,11 +1054,10 @@
stitchedArcs[i < 0 ? ~i : i] = 1;
});
fragments.push(f);
}
}
-
flush(fragmentByEnd, fragmentByStart);
flush(fragmentByStart, fragmentByEnd);
arcs.forEach(function (i) {
if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]);
});
@@ -1281,57 +1073,47 @@
return {
type: "MultiLineString",
arcs: stitch(topology, arcs)
};
}
-
function extractArcs(topology, object, filter) {
var arcs = [],
- geomsByArc = [],
- geom;
-
+ geomsByArc = [],
+ geom;
function extract0(i) {
var j = i < 0 ? ~i : i;
(geomsByArc[j] || (geomsByArc[j] = [])).push({
i: i,
g: geom
});
}
-
function extract1(arcs) {
arcs.forEach(extract0);
}
-
function extract2(arcs) {
arcs.forEach(extract1);
}
-
function extract3(arcs) {
arcs.forEach(extract2);
}
-
function geometry(o) {
switch (geom = o, o.type) {
case "GeometryCollection":
o.geometries.forEach(geometry);
break;
-
case "LineString":
extract1(o.arcs);
break;
-
case "MultiLineString":
case "Polygon":
extract2(o.arcs);
break;
-
case "MultiPolygon":
extract3(o.arcs);
break;
}
}
-
geometry(object);
geomsByArc.forEach(filter == null ? function (geoms) {
arcs.push(geoms[0].i);
} : function (geoms) {
if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i);
@@ -1341,68 +1123,70 @@
function ascending$1(a, b) {
return a == null || b == null ? NaN : a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
+ function descending$1(a, b) {
+ return a == null || b == null ? NaN : b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
+ }
+
function bisector(f) {
- let delta = f;
- let compare1 = f;
- let compare2 = f;
+ let compare1, compare2, delta;
+ // If an accessor is specified, promote it to a comparator. In this case we
+ // can test whether the search value is (self-) comparable. We can’t do this
+ // for a comparator (except for specific, known comparators) because we can’t
+ // tell if the comparator is symmetric, and an asymmetric comparator can’t be
+ // used to test whether a single value is comparable.
if (f.length !== 2) {
- delta = (d, x) => f(d) - x;
-
compare1 = ascending$1;
-
compare2 = (d, x) => ascending$1(f(d), x);
+ delta = (d, x) => f(d) - x;
+ } else {
+ compare1 = f === ascending$1 || f === descending$1 ? f : zero$2;
+ compare2 = f;
+ delta = f;
}
-
function left(a, x) {
let lo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
let hi = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : a.length;
-
if (lo < hi) {
if (compare1(x, x) !== 0) return hi;
-
do {
const mid = lo + hi >>> 1;
if (compare2(a[mid], x) < 0) lo = mid + 1;else hi = mid;
} while (lo < hi);
}
-
return lo;
}
-
function right(a, x) {
let lo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
let hi = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : a.length;
-
if (lo < hi) {
if (compare1(x, x) !== 0) return hi;
-
do {
const mid = lo + hi >>> 1;
if (compare2(a[mid], x) <= 0) lo = mid + 1;else hi = mid;
} while (lo < hi);
}
-
return lo;
}
-
function center(a, x) {
let lo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
let hi = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : a.length;
const i = left(a, x, lo, hi - 1);
return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
}
-
return {
left,
center,
right
};
}
+ function zero$2() {
+ return 0;
+ }
function number$6(x) {
return x === null ? NaN : +x;
}
function* numbers$2(values, valueof) {
@@ -1412,11 +1196,10 @@
yield value;
}
}
} else {
let index = -1;
-
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
yield value;
}
}
@@ -1425,38 +1208,34 @@
const ascendingBisect = bisector(ascending$1);
const bisectRight$1 = ascendingBisect.right;
const bisectLeft$1 = ascendingBisect.left;
bisector(number$6).center;
- var bisect$1 = bisectRight$1;
function variance(values, valueof) {
let count = 0;
let delta;
let mean = 0;
let sum = 0;
-
if (valueof === undefined) {
for (let value of values) {
if (value != null && (value = +value) >= value) {
delta = value - mean;
mean += delta / ++count;
sum += delta * (value - mean);
}
}
} else {
let index = -1;
-
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
delta = value - mean;
mean += delta / ++count;
sum += delta * (value - mean);
}
}
}
-
if (count > 1) return sum / (count - 1);
}
function deviation(values, valueof) {
const v = variance(values, valueof);
@@ -1467,57 +1246,48 @@
class Adder {
constructor() {
this._partials = new Float64Array(32);
this._n = 0;
}
-
add(x) {
const p = this._partials;
let i = 0;
-
for (let j = 0; j < this._n && j < 32; j++) {
const y = p[j],
- hi = x + y,
- lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
+ hi = x + y,
+ lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
if (lo) p[i++] = lo;
x = hi;
}
-
p[i] = x;
this._n = i + 1;
return this;
}
-
valueOf() {
const p = this._partials;
let n = this._n,
- x,
- y,
- lo,
- hi = 0;
-
+ x,
+ y,
+ lo,
+ hi = 0;
if (n > 0) {
hi = p[--n];
-
while (n > 0) {
x = hi;
y = p[--n];
hi = x + y;
lo = y - (hi - x);
if (lo) break;
}
-
if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
y = lo * 2;
x = hi + y;
if (y == x - hi) hi = x;
}
}
-
return hi;
}
-
}
class InternMap extends Map {
constructor(entries) {
let key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : keyof;
@@ -1530,27 +1300,22 @@
value: key
}
});
if (entries != null) for (const [key, value] of entries) this.set(key, value);
}
-
get(key) {
return super.get(intern_get(this, key));
}
-
has(key) {
return super.has(intern_get(this, key));
}
-
set(key, value) {
return super.set(intern_set(this, key), value);
}
-
delete(key) {
return super.delete(intern_delete(this, key));
}
-
}
class InternSet extends Set {
constructor(values) {
let key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : keyof;
super();
@@ -1562,68 +1327,50 @@
value: key
}
});
if (values != null) for (const value of values) this.add(value);
}
-
has(value) {
return super.has(intern_get(this, value));
}
-
add(value) {
return super.add(intern_set(this, value));
}
-
delete(value) {
return super.delete(intern_delete(this, value));
}
-
}
-
function intern_get(_ref, value) {
let {
_intern,
_key
} = _ref;
-
const key = _key(value);
-
return _intern.has(key) ? _intern.get(key) : value;
}
-
function intern_set(_ref2, value) {
let {
_intern,
_key
} = _ref2;
-
const key = _key(value);
-
if (_intern.has(key)) return _intern.get(key);
-
_intern.set(key, value);
-
return value;
}
-
function intern_delete(_ref3, value) {
let {
_intern,
_key
} = _ref3;
-
const key = _key(value);
-
if (_intern.has(key)) {
value = _intern.get(key);
-
_intern.delete(key);
}
-
return value;
}
-
function keyof(value) {
return value !== null && typeof value === "object" ? value.valueOf() : value;
}
function permute(source, keys) {
@@ -1642,112 +1389,112 @@
}
function ascendingDefined(a, b) {
return (a == null || !(a >= a)) - (b == null || !(b >= b)) || (a < b ? -1 : a > b ? 1 : 0);
}
- var e10 = Math.sqrt(50),
- e5 = Math.sqrt(10),
- e2 = Math.sqrt(2);
+ const e10 = Math.sqrt(50),
+ e5 = Math.sqrt(10),
+ e2 = Math.sqrt(2);
+ function tickSpec(start, stop, count) {
+ const step = (stop - start) / Math.max(0, count),
+ power = Math.floor(Math.log10(step)),
+ error = step / Math.pow(10, power),
+ factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1;
+ let i1, i2, inc;
+ if (power < 0) {
+ inc = Math.pow(10, -power) / factor;
+ i1 = Math.round(start * inc);
+ i2 = Math.round(stop * inc);
+ if (i1 / inc < start) ++i1;
+ if (i2 / inc > stop) --i2;
+ inc = -inc;
+ } else {
+ inc = Math.pow(10, power) * factor;
+ i1 = Math.round(start / inc);
+ i2 = Math.round(stop / inc);
+ if (i1 * inc < start) ++i1;
+ if (i2 * inc > stop) --i2;
+ }
+ if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2);
+ return [i1, i2, inc];
+ }
function ticks(start, stop, count) {
- var reverse,
- i = -1,
- n,
- ticks,
- step;
stop = +stop, start = +start, count = +count;
- if (start === stop && count > 0) return [start];
- if (reverse = stop < start) n = start, start = stop, stop = n;
- if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
-
- if (step > 0) {
- let r0 = Math.round(start / step),
- r1 = Math.round(stop / step);
- if (r0 * step < start) ++r0;
- if (r1 * step > stop) --r1;
- ticks = new Array(n = r1 - r0 + 1);
-
- while (++i < n) ticks[i] = (r0 + i) * step;
+ if (!(count > 0)) return [];
+ if (start === stop) return [start];
+ const reverse = stop < start,
+ [i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count);
+ if (!(i2 >= i1)) return [];
+ const n = i2 - i1 + 1,
+ ticks = new Array(n);
+ if (reverse) {
+ if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) / -inc;else for (let i = 0; i < n; ++i) ticks[i] = (i2 - i) * inc;
} else {
- step = -step;
- let r0 = Math.round(start * step),
- r1 = Math.round(stop * step);
- if (r0 / step < start) ++r0;
- if (r1 / step > stop) --r1;
- ticks = new Array(n = r1 - r0 + 1);
-
- while (++i < n) ticks[i] = (r0 + i) / step;
+ if (inc < 0) for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) / -inc;else for (let i = 0; i < n; ++i) ticks[i] = (i1 + i) * inc;
}
-
- if (reverse) ticks.reverse();
return ticks;
}
function tickIncrement(start, stop, count) {
- var step = (stop - start) / Math.max(0, count),
- power = Math.floor(Math.log(step) / Math.LN10),
- error = step / Math.pow(10, power);
- return power >= 0 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
+ stop = +stop, start = +start, count = +count;
+ return tickSpec(start, stop, count)[2];
}
function tickStep(start, stop, count) {
- var step0 = Math.abs(stop - start) / Math.max(0, count),
- step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
- error = step0 / step1;
- if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
- return stop < start ? -step1 : step1;
+ stop = +stop, start = +start, count = +count;
+ const reverse = stop < start,
+ inc = reverse ? tickIncrement(stop, start, count) : tickIncrement(start, stop, count);
+ return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc);
}
function max$2(values, valueof) {
let max;
-
if (valueof === undefined) {
for (const value of values) {
if (value != null && (max < value || max === undefined && value >= value)) {
max = value;
}
}
} else {
let index = -1;
-
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null && (max < value || max === undefined && value >= value)) {
max = value;
}
}
}
-
return max;
}
function min$2(values, valueof) {
let min;
-
if (valueof === undefined) {
for (const value of values) {
if (value != null && (min > value || min === undefined && value >= value)) {
min = value;
}
}
} else {
let index = -1;
-
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null && (min > value || min === undefined && value >= value)) {
min = value;
}
}
}
-
return min;
}
+ // Based on https://github.com/mourner/quickselect
// ISC license, Copyright 2018 Vladimir Agafonkin.
-
function quickselect(array, k) {
let left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
- let right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
+ let right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Infinity;
let compare = arguments.length > 4 ? arguments[4] : undefined;
+ k = Math.floor(k);
+ left = Math.floor(Math.max(0, left));
+ right = Math.floor(Math.min(array.length - 1, right));
+ if (!(left <= k && k <= right)) return array;
compare = compare === undefined ? ascendingDefined : compareDefined(compare);
-
while (right > left) {
if (right - left > 600) {
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
@@ -1755,84 +1502,74 @@
const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
quickselect(array, k, newLeft, newRight, compare);
}
-
const t = array[k];
let i = left;
let j = right;
swap$1(array, left, k);
if (compare(array[right], t) > 0) swap$1(array, left, right);
-
while (i < j) {
swap$1(array, i, j), ++i, --j;
-
while (compare(array[i], t) < 0) ++i;
-
while (compare(array[j], t) > 0) --j;
}
-
if (compare(array[left], t) === 0) swap$1(array, left, j);else ++j, swap$1(array, j, right);
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
-
return array;
}
-
function swap$1(array, i, j) {
const t = array[i];
array[i] = array[j];
array[j] = t;
}
function quantile$1(values, p, valueof) {
values = Float64Array.from(numbers$2(values, valueof));
- if (!(n = values.length)) return;
- if ((p = +p) <= 0 || n < 2) return min$2(values);
+ if (!(n = values.length) || isNaN(p = +p)) return;
+ if (p <= 0 || n < 2) return min$2(values);
if (p >= 1) return max$2(values);
var n,
- i = (n - 1) * p,
- i0 = Math.floor(i),
- value0 = max$2(quickselect(values, i0).subarray(0, i0 + 1)),
- value1 = min$2(values.subarray(i0 + 1));
+ i = (n - 1) * p,
+ i0 = Math.floor(i),
+ value0 = max$2(quickselect(values, i0).subarray(0, i0 + 1)),
+ value1 = min$2(values.subarray(i0 + 1));
return value0 + (value1 - value0) * (i - i0);
}
function quantileSorted(values, p) {
let valueof = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : number$6;
- if (!(n = values.length)) return;
- if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
+ if (!(n = values.length) || isNaN(p = +p)) return;
+ if (p <= 0 || n < 2) return +valueof(values[0], 0, values);
if (p >= 1) return +valueof(values[n - 1], n - 1, values);
var n,
- i = (n - 1) * p,
- i0 = Math.floor(i),
- value0 = +valueof(values[i0], i0, values),
- value1 = +valueof(values[i0 + 1], i0 + 1, values);
+ i = (n - 1) * p,
+ i0 = Math.floor(i),
+ value0 = +valueof(values[i0], i0, values),
+ value1 = +valueof(values[i0 + 1], i0 + 1, values);
return value0 + (value1 - value0) * (i - i0);
}
function mean(values, valueof) {
let count = 0;
let sum = 0;
-
if (valueof === undefined) {
for (let value of values) {
if (value != null && (value = +value) >= value) {
++count, sum += value;
}
}
} else {
let index = -1;
-
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
++count, sum += value;
}
}
}
-
if (count) return sum / count;
}
function median(values, valueof) {
return quantile$1(values, 0.5, valueof);
@@ -1841,125 +1578,104 @@
function* flatten(arrays) {
for (const array of arrays) {
yield* array;
}
}
-
function merge$2(arrays) {
return Array.from(flatten(arrays));
}
function range$3(start, stop, step) {
start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
var i = -1,
- n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
- range = new Array(n);
-
+ n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
+ range = new Array(n);
while (++i < n) {
range[i] = start + i * step;
}
-
return range;
}
function sum$1(values, valueof) {
let sum = 0;
-
- if (valueof === undefined) {
+ {
for (let value of values) {
if (value = +value) {
sum += value;
}
}
- } else {
- let index = -1;
-
- for (let value of values) {
- if (value = +valueof(value, ++index, values)) {
- sum += value;
- }
- }
}
-
return sum;
}
function intersection(values) {
for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
others[_key - 1] = arguments[_key];
}
-
values = new InternSet(values);
others = others.map(set$4);
-
out: for (const value of values) {
for (const other of others) {
if (!other.has(value)) {
values.delete(value);
continue out;
}
}
}
-
return values;
}
-
function set$4(values) {
return values instanceof InternSet ? values : new InternSet(values);
}
function union() {
const set = new InternSet();
-
for (var _len = arguments.length, others = new Array(_len), _key = 0; _key < _len; _key++) {
others[_key] = arguments[_key];
}
-
for (const other of others) {
for (const o of other) {
set.add(o);
}
}
-
return set;
}
function formatDecimal (x) {
return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
- } // Computes the decimal coefficient and exponent of the specified number x with
+ }
+
+ // Computes the decimal coefficient and exponent of the specified number x with
// significant digits p, where x is positive and p is in [1, 21] or undefined.
// For example, formatDecimalParts(1.23) returns ["123", 0].
-
function formatDecimalParts(x, p) {
if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
-
var i,
- coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
- // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
+ coefficient = x.slice(0, i);
+ // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
}
function exponent (x) {
return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
}
function formatGroup (grouping, thousands) {
return function (value, width) {
var i = value.length,
- t = [],
- j = 0,
- g = grouping[0],
- length = 0;
-
+ t = [],
+ j = 0,
+ g = grouping[0],
+ length = 0;
while (i > 0 && g > 0) {
if (length + g + 1 > width) g = Math.max(1, width - length);
t.push(value.substring(i -= g, i + g));
if ((length += g + 1) > width) break;
g = grouping[j = (j + 1) % grouping.length];
}
-
return t.reverse().join(thousands);
};
}
function formatNumerals (numerals) {
@@ -2000,11 +1716,10 @@
this.comma = !!specifier.comma;
this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
this.trim = !!specifier.trim;
this.type = specifier.type === undefined ? "" : specifier.type + "";
}
-
FormatSpecifier.prototype.toString = function () {
return this.fill + this.align + this.sign + this.symbol + (this.zero ? "0" : "") + (this.width === undefined ? "" : Math.max(1, this.width | 0)) + (this.comma ? "," : "") + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0)) + (this.trim ? "~" : "") + this.type;
};
// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
@@ -2012,42 +1727,39 @@
out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
switch (s[i]) {
case ".":
i0 = i1 = i;
break;
-
case "0":
if (i0 === 0) i0 = i;
i1 = i;
break;
-
default:
if (!+s[i]) break out;
if (i0 > 0) i0 = 0;
break;
}
}
-
return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
}
var prefixExponent;
function formatPrefixAuto (x, p) {
var d = formatDecimalParts(x, p);
if (!d) return x + "";
var coefficient = d[0],
- exponent = d[1],
- i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
- n = coefficient.length;
+ exponent = d[1],
+ i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
+ n = coefficient.length;
return i === n ? coefficient : i > n ? coefficient + new Array(i - n + 1).join("0") : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
}
function formatRounded (x, p) {
var d = formatDecimalParts(x, p);
if (!d) return x + "";
var coefficient = d[0],
- exponent = d[1];
+ exponent = d[1];
return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) : coefficient + new Array(exponent - coefficient.length + 2).join("0");
}
var formatTypes = {
"%": (x, p) => (x * 100).toFixed(p),
@@ -2068,137 +1780,141 @@
function identity$4 (x) {
return x;
}
var map$1 = Array.prototype.map,
- prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
+ prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
function formatLocale$1 (locale) {
var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$1.call(locale.grouping, Number), locale.thousands + ""),
- currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
- currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
- decimal = locale.decimal === undefined ? "." : locale.decimal + "",
- numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$1.call(locale.numerals, String)),
- percent = locale.percent === undefined ? "%" : locale.percent + "",
- minus = locale.minus === undefined ? "−" : locale.minus + "",
- nan = locale.nan === undefined ? "NaN" : locale.nan + "";
-
+ currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
+ currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
+ decimal = locale.decimal === undefined ? "." : locale.decimal + "",
+ numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$1.call(locale.numerals, String)),
+ percent = locale.percent === undefined ? "%" : locale.percent + "",
+ minus = locale.minus === undefined ? "−" : locale.minus + "",
+ nan = locale.nan === undefined ? "NaN" : locale.nan + "";
function newFormat(specifier) {
specifier = formatSpecifier(specifier);
var fill = specifier.fill,
- align = specifier.align,
- sign = specifier.sign,
- symbol = specifier.symbol,
- zero = specifier.zero,
- width = specifier.width,
- comma = specifier.comma,
- precision = specifier.precision,
- trim = specifier.trim,
- type = specifier.type; // The "n" type is an alias for ",g".
+ align = specifier.align,
+ sign = specifier.sign,
+ symbol = specifier.symbol,
+ zero = specifier.zero,
+ width = specifier.width,
+ comma = specifier.comma,
+ precision = specifier.precision,
+ trim = specifier.trim,
+ type = specifier.type;
- if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
- else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
+ // The "n" type is an alias for ",g".
+ if (type === "n") comma = true, type = "g";
- if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
- // For SI-prefix, the suffix is lazily computed.
+ // The "" type, and any invalid type, is an alias for ".12~g".
+ else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
+ // If zero fill is specified, padding goes after sign and before digits.
+ if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "=";
+
+ // Compute the prefix and suffix.
+ // For SI-prefix, the suffix is lazily computed.
var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
- suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
+ suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
+
+ // What format function should we use?
// Is this an integer type?
// Can this type generate exponential notation?
-
var formatType = formatTypes[type],
- maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
+ maybeSuffix = /[defgprs%]/.test(type);
+
+ // Set the default precision if not specified,
// or clamp the specified precision to the supported range.
// For significant precision, it must be in [1, 21].
// For fixed precision, it must be in [0, 20].
-
precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
-
function format(value) {
var valuePrefix = prefix,
- valueSuffix = suffix,
- i,
- n,
- c;
-
+ valueSuffix = suffix,
+ i,
+ n,
+ c;
if (type === "c") {
valueSuffix = formatType(value) + valueSuffix;
value = "";
} else {
- value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
+ value = +value;
- var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
+ // Determine the sign. -0 is not less than 0, but 1 / -0 is!
+ var valueNegative = value < 0 || 1 / value < 0;
- value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
+ // Perform the initial formatting.
+ value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
- if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
+ // Trim insignificant zeros.
+ if (trim) value = formatTrim(value);
- if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
+ // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
+ if (valueNegative && +value === 0 && sign !== "+") valueNegative = false;
+ // Compute the prefix and suffix.
valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
- valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
- // grouped, and fractional or exponential “suffix” part that is not.
+ valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
+ // Break the formatted value into the integer “value” part that can be
+ // grouped, and fractional or exponential “suffix” part that is not.
if (maybeSuffix) {
i = -1, n = value.length;
-
while (++i < n) {
if (c = value.charCodeAt(i), 48 > c || c > 57) {
valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
value = value.slice(0, i);
break;
}
}
}
- } // If the fill character is not "0", grouping is applied before padding.
+ }
+ // If the fill character is not "0", grouping is applied before padding.
+ if (comma && !zero) value = group(value, Infinity);
- if (comma && !zero) value = group(value, Infinity); // Compute the padding.
-
+ // Compute the padding.
var length = valuePrefix.length + value.length + valueSuffix.length,
- padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
+ padding = length < width ? new Array(width - length + 1).join(fill) : "";
- if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
+ // If the fill character is "0", grouping is applied after padding.
+ if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
+ // Reconstruct the final output based on the desired alignment.
switch (align) {
case "<":
value = valuePrefix + value + valueSuffix + padding;
break;
-
case "=":
value = valuePrefix + padding + value + valueSuffix;
break;
-
case "^":
value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
break;
-
default:
value = padding + valuePrefix + value + valueSuffix;
break;
}
-
return numerals(value);
}
-
format.toString = function () {
return specifier + "";
};
-
return format;
}
-
function formatPrefix(specifier, value) {
var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
- e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
- k = Math.pow(10, -e),
- prefix = prefixes[8 + e / 3];
+ e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
+ k = Math.pow(10, -e),
+ prefix = prefixes[8 + e / 3];
return function (value) {
return f(k * value) + prefix;
};
}
-
return {
format: newFormat,
formatPrefix: formatPrefix
};
}
@@ -2229,320 +1945,315 @@
function precisionRound (step, max) {
step = Math.abs(step), max = Math.abs(max) - step;
return Math.max(0, exponent(max) - exponent(step)) + 1;
}
- var t0$2 = new Date(),
- t1$1 = new Date();
- function newInterval(floori, offseti, count, field) {
+ const t0$2 = new Date(),
+ t1$1 = new Date();
+ function timeInterval$1(floori, offseti, count, field) {
function interval(date) {
return floori(date = arguments.length === 0 ? new Date() : new Date(+date)), date;
}
-
- interval.floor = function (date) {
+ interval.floor = date => {
return floori(date = new Date(+date)), date;
};
-
- interval.ceil = function (date) {
+ interval.ceil = date => {
return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
};
-
- interval.round = function (date) {
- var d0 = interval(date),
- d1 = interval.ceil(date);
+ interval.round = date => {
+ const d0 = interval(date),
+ d1 = interval.ceil(date);
return date - d0 < d1 - date ? d0 : d1;
};
-
- interval.offset = function (date, step) {
+ interval.offset = (date, step) => {
return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
};
-
- interval.range = function (start, stop, step) {
- var range = [],
- previous;
+ interval.range = (start, stop, step) => {
+ const range = [];
start = interval.ceil(start);
step = step == null ? 1 : Math.floor(step);
if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
-
+ let previous;
do range.push(previous = new Date(+start)), offseti(start, step), floori(start); while (previous < start && start < stop);
-
return range;
};
-
- interval.filter = function (test) {
- return newInterval(function (date) {
+ interval.filter = test => {
+ return timeInterval$1(date => {
if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
- }, function (date, step) {
+ }, (date, step) => {
if (date >= date) {
if (step < 0) while (++step <= 0) {
while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
-
} else while (--step >= 0) {
while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
-
}
}
});
};
-
if (count) {
- interval.count = function (start, end) {
+ interval.count = (start, end) => {
t0$2.setTime(+start), t1$1.setTime(+end);
floori(t0$2), floori(t1$1);
return Math.floor(count(t0$2, t1$1));
};
-
- interval.every = function (step) {
+ interval.every = step => {
step = Math.floor(step);
- return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval : interval.filter(field ? function (d) {
- return field(d) % step === 0;
- } : function (d) {
- return interval.count(0, d) % step === 0;
- });
+ return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval : interval.filter(field ? d => field(d) % step === 0 : d => interval.count(0, d) % step === 0);
};
}
-
return interval;
}
- var millisecond = newInterval(function () {// noop
- }, function (date, step) {
+ const millisecond = timeInterval$1(() => {
+ // noop
+ }, (date, step) => {
date.setTime(+date + step);
- }, function (start, end) {
+ }, (start, end) => {
return end - start;
- }); // An optimized implementation for this simple case.
+ });
- millisecond.every = function (k) {
+ // An optimized implementation for this simple case.
+ millisecond.every = k => {
k = Math.floor(k);
if (!isFinite(k) || !(k > 0)) return null;
if (!(k > 1)) return millisecond;
- return newInterval(function (date) {
+ return timeInterval$1(date => {
date.setTime(Math.floor(date / k) * k);
- }, function (date, step) {
+ }, (date, step) => {
date.setTime(+date + step * k);
- }, function (start, end) {
+ }, (start, end) => {
return (end - start) / k;
});
};
+ millisecond.range;
- var utcMillisecond = millisecond;
-
const durationSecond$1 = 1000;
const durationMinute$1 = durationSecond$1 * 60;
const durationHour$1 = durationMinute$1 * 60;
const durationDay$1 = durationHour$1 * 24;
const durationWeek$1 = durationDay$1 * 7;
const durationMonth$1 = durationDay$1 * 30;
const durationYear$1 = durationDay$1 * 365;
- var second = newInterval(function (date) {
+ const second = timeInterval$1(date => {
date.setTime(date - date.getMilliseconds());
- }, function (date, step) {
+ }, (date, step) => {
date.setTime(+date + step * durationSecond$1);
- }, function (start, end) {
+ }, (start, end) => {
return (end - start) / durationSecond$1;
- }, function (date) {
+ }, date => {
return date.getUTCSeconds();
});
- var utcSecond = second;
+ second.range;
- var minute = newInterval(function (date) {
+ const timeMinute = timeInterval$1(date => {
date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond$1);
- }, function (date, step) {
+ }, (date, step) => {
date.setTime(+date + step * durationMinute$1);
- }, function (start, end) {
+ }, (start, end) => {
return (end - start) / durationMinute$1;
- }, function (date) {
+ }, date => {
return date.getMinutes();
});
- var timeMinute = minute;
+ timeMinute.range;
+ const utcMinute = timeInterval$1(date => {
+ date.setUTCSeconds(0, 0);
+ }, (date, step) => {
+ date.setTime(+date + step * durationMinute$1);
+ }, (start, end) => {
+ return (end - start) / durationMinute$1;
+ }, date => {
+ return date.getUTCMinutes();
+ });
+ utcMinute.range;
- var hour = newInterval(function (date) {
+ const timeHour = timeInterval$1(date => {
date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond$1 - date.getMinutes() * durationMinute$1);
- }, function (date, step) {
+ }, (date, step) => {
date.setTime(+date + step * durationHour$1);
- }, function (start, end) {
+ }, (start, end) => {
return (end - start) / durationHour$1;
- }, function (date) {
+ }, date => {
return date.getHours();
});
- var timeHour = hour;
+ timeHour.range;
+ const utcHour = timeInterval$1(date => {
+ date.setUTCMinutes(0, 0, 0);
+ }, (date, step) => {
+ date.setTime(+date + step * durationHour$1);
+ }, (start, end) => {
+ return (end - start) / durationHour$1;
+ }, date => {
+ return date.getUTCHours();
+ });
+ utcHour.range;
- var day = newInterval(date => date.setHours(0, 0, 0, 0), (date, step) => date.setDate(date.getDate() + step), (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1, date => date.getDate() - 1);
- var timeDay = day;
+ const timeDay = timeInterval$1(date => date.setHours(0, 0, 0, 0), (date, step) => date.setDate(date.getDate() + step), (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1, date => date.getDate() - 1);
+ timeDay.range;
+ const utcDay = timeInterval$1(date => {
+ date.setUTCHours(0, 0, 0, 0);
+ }, (date, step) => {
+ date.setUTCDate(date.getUTCDate() + step);
+ }, (start, end) => {
+ return (end - start) / durationDay$1;
+ }, date => {
+ return date.getUTCDate() - 1;
+ });
+ utcDay.range;
+ const unixDay = timeInterval$1(date => {
+ date.setUTCHours(0, 0, 0, 0);
+ }, (date, step) => {
+ date.setUTCDate(date.getUTCDate() + step);
+ }, (start, end) => {
+ return (end - start) / durationDay$1;
+ }, date => {
+ return Math.floor(date / durationDay$1);
+ });
+ unixDay.range;
- function weekday$1(i) {
- return newInterval(function (date) {
+ function timeWeekday(i) {
+ return timeInterval$1(date => {
date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
date.setHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setDate(date.getDate() + step * 7);
- }, function (start, end) {
+ }, (start, end) => {
return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationWeek$1;
});
}
+ const timeSunday = timeWeekday(0);
+ const timeMonday = timeWeekday(1);
+ const timeTuesday = timeWeekday(2);
+ const timeWednesday = timeWeekday(3);
+ const timeThursday = timeWeekday(4);
+ const timeFriday = timeWeekday(5);
+ const timeSaturday = timeWeekday(6);
+ timeSunday.range;
+ timeMonday.range;
+ timeTuesday.range;
+ timeWednesday.range;
+ timeThursday.range;
+ timeFriday.range;
+ timeSaturday.range;
+ function utcWeekday(i) {
+ return timeInterval$1(date => {
+ date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
+ date.setUTCHours(0, 0, 0, 0);
+ }, (date, step) => {
+ date.setUTCDate(date.getUTCDate() + step * 7);
+ }, (start, end) => {
+ return (end - start) / durationWeek$1;
+ });
+ }
+ const utcSunday = utcWeekday(0);
+ const utcMonday = utcWeekday(1);
+ const utcTuesday = utcWeekday(2);
+ const utcWednesday = utcWeekday(3);
+ const utcThursday = utcWeekday(4);
+ const utcFriday = utcWeekday(5);
+ const utcSaturday = utcWeekday(6);
+ utcSunday.range;
+ utcMonday.range;
+ utcTuesday.range;
+ utcWednesday.range;
+ utcThursday.range;
+ utcFriday.range;
+ utcSaturday.range;
- var sunday = weekday$1(0);
- var monday = weekday$1(1);
- weekday$1(2);
- weekday$1(3);
- var thursday = weekday$1(4);
- weekday$1(5);
- weekday$1(6);
-
- var month = newInterval(function (date) {
+ const timeMonth = timeInterval$1(date => {
date.setDate(1);
date.setHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setMonth(date.getMonth() + step);
- }, function (start, end) {
+ }, (start, end) => {
return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
- }, function (date) {
+ }, date => {
return date.getMonth();
});
- var timeMonth = month;
+ timeMonth.range;
+ const utcMonth = timeInterval$1(date => {
+ date.setUTCDate(1);
+ date.setUTCHours(0, 0, 0, 0);
+ }, (date, step) => {
+ date.setUTCMonth(date.getUTCMonth() + step);
+ }, (start, end) => {
+ return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
+ }, date => {
+ return date.getUTCMonth();
+ });
+ utcMonth.range;
- var year = newInterval(function (date) {
+ const timeYear = timeInterval$1(date => {
date.setMonth(0, 1);
date.setHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setFullYear(date.getFullYear() + step);
- }, function (start, end) {
+ }, (start, end) => {
return end.getFullYear() - start.getFullYear();
- }, function (date) {
+ }, date => {
return date.getFullYear();
- }); // An optimized implementation for this simple case.
+ });
- year.every = function (k) {
- return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function (date) {
+ // An optimized implementation for this simple case.
+ timeYear.every = k => {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval$1(date => {
date.setFullYear(Math.floor(date.getFullYear() / k) * k);
date.setMonth(0, 1);
date.setHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setFullYear(date.getFullYear() + step * k);
});
};
-
- var timeYear = year;
-
- var utcMinute = newInterval(function (date) {
- date.setUTCSeconds(0, 0);
- }, function (date, step) {
- date.setTime(+date + step * durationMinute$1);
- }, function (start, end) {
- return (end - start) / durationMinute$1;
- }, function (date) {
- return date.getUTCMinutes();
- });
- var utcMinute$1 = utcMinute;
-
- var utcHour = newInterval(function (date) {
- date.setUTCMinutes(0, 0, 0);
- }, function (date, step) {
- date.setTime(+date + step * durationHour$1);
- }, function (start, end) {
- return (end - start) / durationHour$1;
- }, function (date) {
- return date.getUTCHours();
- });
- var utcHour$1 = utcHour;
-
- var utcDay = newInterval(function (date) {
- date.setUTCHours(0, 0, 0, 0);
- }, function (date, step) {
- date.setUTCDate(date.getUTCDate() + step);
- }, function (start, end) {
- return (end - start) / durationDay$1;
- }, function (date) {
- return date.getUTCDate() - 1;
- });
- var utcDay$1 = utcDay;
-
- function utcWeekday(i) {
- return newInterval(function (date) {
- date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
- date.setUTCHours(0, 0, 0, 0);
- }, function (date, step) {
- date.setUTCDate(date.getUTCDate() + step * 7);
- }, function (start, end) {
- return (end - start) / durationWeek$1;
- });
- }
-
- var utcSunday = utcWeekday(0);
- var utcMonday = utcWeekday(1);
- utcWeekday(2);
- utcWeekday(3);
- var utcThursday = utcWeekday(4);
- utcWeekday(5);
- utcWeekday(6);
-
- var utcMonth = newInterval(function (date) {
- date.setUTCDate(1);
- date.setUTCHours(0, 0, 0, 0);
- }, function (date, step) {
- date.setUTCMonth(date.getUTCMonth() + step);
- }, function (start, end) {
- return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
- }, function (date) {
- return date.getUTCMonth();
- });
- var utcMonth$1 = utcMonth;
-
- var utcYear = newInterval(function (date) {
+ timeYear.range;
+ const utcYear = timeInterval$1(date => {
date.setUTCMonth(0, 1);
date.setUTCHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setUTCFullYear(date.getUTCFullYear() + step);
- }, function (start, end) {
+ }, (start, end) => {
return end.getUTCFullYear() - start.getUTCFullYear();
- }, function (date) {
+ }, date => {
return date.getUTCFullYear();
- }); // An optimized implementation for this simple case.
+ });
- utcYear.every = function (k) {
- return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function (date) {
+ // An optimized implementation for this simple case.
+ utcYear.every = k => {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval$1(date => {
date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
date.setUTCMonth(0, 1);
date.setUTCHours(0, 0, 0, 0);
- }, function (date, step) {
+ }, (date, step) => {
date.setUTCFullYear(date.getUTCFullYear() + step * k);
});
};
+ utcYear.range;
- var utcYear$1 = utcYear;
-
function ticker(year, month, week, day, hour, minute) {
- const tickIntervals = [[utcSecond, 1, durationSecond$1], [utcSecond, 5, 5 * durationSecond$1], [utcSecond, 15, 15 * durationSecond$1], [utcSecond, 30, 30 * durationSecond$1], [minute, 1, durationMinute$1], [minute, 5, 5 * durationMinute$1], [minute, 15, 15 * durationMinute$1], [minute, 30, 30 * durationMinute$1], [hour, 1, durationHour$1], [hour, 3, 3 * durationHour$1], [hour, 6, 6 * durationHour$1], [hour, 12, 12 * durationHour$1], [day, 1, durationDay$1], [day, 2, 2 * durationDay$1], [week, 1, durationWeek$1], [month, 1, durationMonth$1], [month, 3, 3 * durationMonth$1], [year, 1, durationYear$1]];
-
+ const tickIntervals = [[second, 1, durationSecond$1], [second, 5, 5 * durationSecond$1], [second, 15, 15 * durationSecond$1], [second, 30, 30 * durationSecond$1], [minute, 1, durationMinute$1], [minute, 5, 5 * durationMinute$1], [minute, 15, 15 * durationMinute$1], [minute, 30, 30 * durationMinute$1], [hour, 1, durationHour$1], [hour, 3, 3 * durationHour$1], [hour, 6, 6 * durationHour$1], [hour, 12, 12 * durationHour$1], [day, 1, durationDay$1], [day, 2, 2 * durationDay$1], [week, 1, durationWeek$1], [month, 1, durationMonth$1], [month, 3, 3 * durationMonth$1], [year, 1, durationYear$1]];
function ticks(start, stop, count) {
const reverse = stop < start;
if (reverse) [start, stop] = [stop, start];
const interval = count && typeof count.range === "function" ? count : tickInterval(start, stop, count);
const ticks = interval ? interval.range(start, +stop + 1) : []; // inclusive stop
-
return reverse ? ticks.reverse() : ticks;
}
-
function tickInterval(start, stop, count) {
const target = Math.abs(stop - start) / count;
const i = bisector(_ref => {
let [,, step] = _ref;
return step;
}).right(tickIntervals, target);
if (i === tickIntervals.length) return year.every(tickStep(start / durationYear$1, stop / durationYear$1, count));
- if (i === 0) return utcMillisecond.every(Math.max(tickStep(start, stop, count), 1));
+ if (i === 0) return millisecond.every(Math.max(tickStep(start, stop, count), 1));
const [t, step] = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
return t.every(step);
}
-
return [ticks, tickInterval];
}
+ const [utcTicks, utcTickInterval] = ticker(utcYear, utcMonth, utcSunday, unixDay, utcHour, utcMinute);
+ const [timeTicks, timeTickInterval] = ticker(timeYear, timeMonth, timeSunday, timeDay, timeHour, timeMinute);
- const [utcTicks, utcTickInterval] = ticker(utcYear$1, utcMonth$1, utcSunday, utcDay$1, utcHour$1, utcMinute$1);
- const [timeTicks, timeTickInterval] = ticker(timeYear, timeMonth, sunday, timeDay, timeHour, timeMinute);
-
const YEAR = 'year';
const QUARTER = 'quarter';
const MONTH = 'month';
const WEEK = 'week';
const DATE = 'date';
@@ -2552,34 +2263,32 @@
const MINUTES = 'minutes';
const SECONDS = 'seconds';
const MILLISECONDS = 'milliseconds';
const TIME_UNITS = [YEAR, QUARTER, MONTH, WEEK, DATE, DAY, DAYOFYEAR, HOURS, MINUTES, SECONDS, MILLISECONDS];
const UNITS = TIME_UNITS.reduce((o, u, i) => (o[u] = 1 + i, o), {});
-
function timeUnits(units) {
const u = array$5(units).slice(),
- m = {}; // check validity
+ m = {};
+ // check validity
if (!u.length) error('Missing time unit.');
u.forEach(unit => {
if (has$1(UNITS, unit)) {
m[unit] = 1;
} else {
- error("Invalid time unit: ".concat(unit, "."));
+ error(`Invalid time unit: ${unit}.`);
}
});
const numTypes = (m[WEEK] || m[DAY] ? 1 : 0) + (m[QUARTER] || m[MONTH] || m[DATE] ? 1 : 0) + (m[DAYOFYEAR] ? 1 : 0);
-
if (numTypes > 1) {
- error("Incompatible time units: ".concat(units));
- } // ensure proper sort order
+ error(`Incompatible time units: ${units}`);
+ }
-
+ // ensure proper sort order
u.sort((a, b) => UNITS[a] - UNITS[b]);
return u;
}
-
const defaultSpecifiers = {
[YEAR]: '%Y ',
[QUARTER]: 'Q%q ',
[MONTH]: '%b ',
[DATE]: '%d ',
@@ -2588,147 +2297,125 @@
[DAYOFYEAR]: '%j ',
[HOURS]: '%H:00',
[MINUTES]: '00:%M',
[SECONDS]: ':%S',
[MILLISECONDS]: '.%L',
- ["".concat(YEAR, "-").concat(MONTH)]: '%Y-%m ',
- ["".concat(YEAR, "-").concat(MONTH, "-").concat(DATE)]: '%Y-%m-%d ',
- ["".concat(HOURS, "-").concat(MINUTES)]: '%H:%M'
+ [`${YEAR}-${MONTH}`]: '%Y-%m ',
+ [`${YEAR}-${MONTH}-${DATE}`]: '%Y-%m-%d ',
+ [`${HOURS}-${MINUTES}`]: '%H:%M'
};
-
function timeUnitSpecifier(units, specifiers) {
const s = extend$1({}, defaultSpecifiers, specifiers),
- u = timeUnits(units),
- n = u.length;
+ u = timeUnits(units),
+ n = u.length;
let fmt = '',
- start = 0,
- end,
- key;
-
+ start = 0,
+ end,
+ key;
for (start = 0; start < n;) {
for (end = u.length; end > start; --end) {
key = u.slice(start, end).join('-');
-
if (s[key] != null) {
fmt += s[key];
start = end;
break;
}
}
}
-
return fmt.trim();
}
-
const t0$1 = new Date();
-
function localYear(y) {
t0$1.setFullYear(y);
t0$1.setMonth(0);
t0$1.setDate(1);
t0$1.setHours(0, 0, 0, 0);
return t0$1;
}
-
function dayofyear(d) {
return localDayOfYear(new Date(d));
}
-
function week(d) {
return localWeekNum(new Date(d));
}
-
function localDayOfYear(d) {
return timeDay.count(localYear(d.getFullYear()) - 1, d);
}
-
function localWeekNum(d) {
- return sunday.count(localYear(d.getFullYear()) - 1, d);
+ return timeSunday.count(localYear(d.getFullYear()) - 1, d);
}
-
function localFirst(y) {
return localYear(y).getDay();
}
-
function localDate$1(y, m, d, H, M, S, L) {
if (0 <= y && y < 100) {
const date = new Date(-1, m, d, H, M, S, L);
date.setFullYear(y);
return date;
}
-
return new Date(y, m, d, H, M, S, L);
}
-
function utcdayofyear(d) {
return utcDayOfYear(new Date(d));
}
-
function utcweek(d) {
return utcWeekNum(new Date(d));
}
-
function utcDayOfYear(d) {
const y = Date.UTC(d.getUTCFullYear(), 0, 1);
- return utcDay$1.count(y - 1, d);
+ return utcDay.count(y - 1, d);
}
-
function utcWeekNum(d) {
const y = Date.UTC(d.getUTCFullYear(), 0, 1);
return utcSunday.count(y - 1, d);
}
-
function utcFirst(y) {
t0$1.setTime(Date.UTC(y, 0, 1));
return t0$1.getUTCDay();
}
-
function utcDate$1(y, m, d, H, M, S, L) {
if (0 <= y && y < 100) {
const date = new Date(Date.UTC(-1, m, d, H, M, S, L));
date.setUTCFullYear(d.y);
return date;
}
-
return new Date(Date.UTC(y, m, d, H, M, S, L));
}
-
function floor(units, step, get, inv, newDate) {
const s = step || 1,
- b = peek$1(units),
- _ = (unit, p, key) => {
- key = key || unit;
- return getUnit(get[key], inv[key], unit === b && s, p);
- };
-
+ b = peek$1(units),
+ _ = (unit, p, key) => {
+ key = key || unit;
+ return getUnit(get[key], inv[key], unit === b && s, p);
+ };
const t = new Date(),
- u = toSet(units),
- y = u[YEAR] ? _(YEAR) : constant$4(2012),
- m = u[MONTH] ? _(MONTH) : u[QUARTER] ? _(QUARTER) : zero$2,
- d = u[WEEK] && u[DAY] ? _(DAY, 1, WEEK + DAY) : u[WEEK] ? _(WEEK, 1) : u[DAY] ? _(DAY, 1) : u[DATE] ? _(DATE, 1) : u[DAYOFYEAR] ? _(DAYOFYEAR, 1) : one$2,
- H = u[HOURS] ? _(HOURS) : zero$2,
- M = u[MINUTES] ? _(MINUTES) : zero$2,
- S = u[SECONDS] ? _(SECONDS) : zero$2,
- L = u[MILLISECONDS] ? _(MILLISECONDS) : zero$2;
+ u = toSet(units),
+ y = u[YEAR] ? _(YEAR) : constant$5(2012),
+ m = u[MONTH] ? _(MONTH) : u[QUARTER] ? _(QUARTER) : zero$3,
+ d = u[WEEK] && u[DAY] ? _(DAY, 1, WEEK + DAY) : u[WEEK] ? _(WEEK, 1) : u[DAY] ? _(DAY, 1) : u[DATE] ? _(DATE, 1) : u[DAYOFYEAR] ? _(DAYOFYEAR, 1) : one$2,
+ H = u[HOURS] ? _(HOURS) : zero$3,
+ M = u[MINUTES] ? _(MINUTES) : zero$3,
+ S = u[SECONDS] ? _(SECONDS) : zero$3,
+ L = u[MILLISECONDS] ? _(MILLISECONDS) : zero$3;
return function (v) {
t.setTime(+v);
const year = y(t);
return newDate(year, m(t), d(t, year), H(t), M(t), S(t), L(t));
};
}
-
function getUnit(f, inv, step, phase) {
const u = step <= 1 ? f : phase ? (d, y) => phase + step * Math.floor((f(d, y) - phase) / step) : (d, y) => step * Math.floor(f(d, y) / step);
return inv ? (d, y) => inv(u(d, y), y) : u;
- } // returns the day of the year based on week number, day of week,
- // and the day of the week for the first day of the year
+ }
-
+ // returns the day of the year based on week number, day of week,
+ // and the day of the week for the first day of the year
function weekday(week, day, firstDay) {
return day + week * 7 - (firstDay + 6) % 7;
- } // -- LOCAL TIME --
+ }
+ // -- LOCAL TIME --
const localGet = {
[YEAR]: d => d.getFullYear(),
[QUARTER]: d => Math.floor(d.getMonth() / 3),
[MONTH]: d => d.getMonth(),
@@ -2744,15 +2431,15 @@
};
const localInv = {
[QUARTER]: q => 3 * q,
[WEEK]: (w, y) => weekday(w, 0, localFirst(y))
};
-
function timeFloor(units, step) {
return floor(units, step || 1, localGet, localInv, localDate$1);
- } // -- UTC TIME --
+ }
+ // -- UTC TIME --
const utcGet = {
[YEAR]: d => d.getUTCFullYear(),
[QUARTER]: d => Math.floor(d.getUTCMonth() / 3),
[MONTH]: d => d.getUTCMonth(),
@@ -2768,110 +2455,96 @@
};
const utcInv = {
[QUARTER]: q => 3 * q,
[WEEK]: (w, y) => weekday(w, 0, utcFirst(y))
};
-
function utcFloor(units, step) {
return floor(units, step || 1, utcGet, utcInv, utcDate$1);
}
-
const timeIntervals = {
[YEAR]: timeYear,
[QUARTER]: timeMonth.every(3),
[MONTH]: timeMonth,
- [WEEK]: sunday,
+ [WEEK]: timeSunday,
[DATE]: timeDay,
[DAY]: timeDay,
[DAYOFYEAR]: timeDay,
[HOURS]: timeHour,
[MINUTES]: timeMinute,
- [SECONDS]: utcSecond,
- [MILLISECONDS]: utcMillisecond
+ [SECONDS]: second,
+ [MILLISECONDS]: millisecond
};
const utcIntervals = {
- [YEAR]: utcYear$1,
- [QUARTER]: utcMonth$1.every(3),
- [MONTH]: utcMonth$1,
+ [YEAR]: utcYear,
+ [QUARTER]: utcMonth.every(3),
+ [MONTH]: utcMonth,
[WEEK]: utcSunday,
- [DATE]: utcDay$1,
- [DAY]: utcDay$1,
- [DAYOFYEAR]: utcDay$1,
- [HOURS]: utcHour$1,
- [MINUTES]: utcMinute$1,
- [SECONDS]: utcSecond,
- [MILLISECONDS]: utcMillisecond
+ [DATE]: utcDay,
+ [DAY]: utcDay,
+ [DAYOFYEAR]: utcDay,
+ [HOURS]: utcHour,
+ [MINUTES]: utcMinute,
+ [SECONDS]: second,
+ [MILLISECONDS]: millisecond
};
-
function timeInterval(unit) {
return timeIntervals[unit];
}
-
function utcInterval(unit) {
return utcIntervals[unit];
}
-
function offset$3(ival, date, step) {
return ival ? ival.offset(date, step) : undefined;
}
-
function timeOffset(unit, date, step) {
return offset$3(timeInterval(unit), date, step);
}
-
function utcOffset(unit, date, step) {
return offset$3(utcInterval(unit), date, step);
}
-
function sequence$1(ival, start, stop, step) {
return ival ? ival.range(start, stop, step) : undefined;
}
-
function timeSequence(unit, start, stop, step) {
return sequence$1(timeInterval(unit), start, stop, step);
}
-
function utcSequence(unit, start, stop, step) {
return sequence$1(utcInterval(unit), start, stop, step);
}
-
const durationSecond = 1000,
- durationMinute = durationSecond * 60,
- durationHour = durationMinute * 60,
- durationDay = durationHour * 24,
- durationWeek = durationDay * 7,
- durationMonth = durationDay * 30,
- durationYear = durationDay * 365;
+ durationMinute = durationSecond * 60,
+ durationHour = durationMinute * 60,
+ durationDay = durationHour * 24,
+ durationWeek = durationDay * 7,
+ durationMonth = durationDay * 30,
+ durationYear = durationDay * 365;
const Milli = [YEAR, MONTH, DATE, HOURS, MINUTES, SECONDS, MILLISECONDS],
- Seconds = Milli.slice(0, -1),
- Minutes = Seconds.slice(0, -1),
- Hours = Minutes.slice(0, -1),
- Day = Hours.slice(0, -1),
- Week = [YEAR, WEEK],
- Month = [YEAR, MONTH],
- Year = [YEAR];
+ Seconds = Milli.slice(0, -1),
+ Minutes = Seconds.slice(0, -1),
+ Hours = Minutes.slice(0, -1),
+ Day = Hours.slice(0, -1),
+ Week = [YEAR, WEEK],
+ Month = [YEAR, MONTH],
+ Year = [YEAR];
const intervals = [[Seconds, 1, durationSecond], [Seconds, 5, 5 * durationSecond], [Seconds, 15, 15 * durationSecond], [Seconds, 30, 30 * durationSecond], [Minutes, 1, durationMinute], [Minutes, 5, 5 * durationMinute], [Minutes, 15, 15 * durationMinute], [Minutes, 30, 30 * durationMinute], [Hours, 1, durationHour], [Hours, 3, 3 * durationHour], [Hours, 6, 6 * durationHour], [Hours, 12, 12 * durationHour], [Day, 1, durationDay], [Week, 1, durationWeek], [Month, 1, durationMonth], [Month, 3, 3 * durationMonth], [Year, 1, durationYear]];
-
function bin$1(opt) {
const ext = opt.extent,
- max = opt.maxbins || 40,
- target = Math.abs(span(ext)) / max;
+ max = opt.maxbins || 40,
+ target = Math.abs(span(ext)) / max;
let i = bisector(i => i[2]).right(intervals, target),
- units,
- step;
-
+ units,
+ step;
if (i === intervals.length) {
units = Year, step = tickStep(ext[0] / durationYear, ext[1] / durationYear, max);
} else if (i) {
i = intervals[target / intervals[i - 1][2] < intervals[i][2] / target ? i - 1 : i];
units = i[0];
step = i[1];
} else {
units = Milli;
step = Math.max(tickStep(ext[0], ext[1], max), 1);
}
-
return {
units,
step
};
}
@@ -2880,24 +2553,20 @@
if (0 <= d.y && d.y < 100) {
var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
date.setFullYear(d.y);
return date;
}
-
return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
}
-
function utcDate(d) {
if (0 <= d.y && d.y < 100) {
var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
date.setUTCFullYear(d.y);
return date;
}
-
return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
}
-
function newDate(y, m, d) {
return {
y: y,
m: m,
d: d,
@@ -2905,30 +2574,29 @@
M: 0,
S: 0,
L: 0
};
}
-
function formatLocale(locale) {
var locale_dateTime = locale.dateTime,
- locale_date = locale.date,
- locale_time = locale.time,
- locale_periods = locale.periods,
- locale_weekdays = locale.days,
- locale_shortWeekdays = locale.shortDays,
- locale_months = locale.months,
- locale_shortMonths = locale.shortMonths;
+ locale_date = locale.date,
+ locale_time = locale.time,
+ locale_periods = locale.periods,
+ locale_weekdays = locale.days,
+ locale_shortWeekdays = locale.shortDays,
+ locale_months = locale.months,
+ locale_shortMonths = locale.shortMonths;
var periodRe = formatRe(locale_periods),
- periodLookup = formatLookup(locale_periods),
- weekdayRe = formatRe(locale_weekdays),
- weekdayLookup = formatLookup(locale_weekdays),
- shortWeekdayRe = formatRe(locale_shortWeekdays),
- shortWeekdayLookup = formatLookup(locale_shortWeekdays),
- monthRe = formatRe(locale_months),
- monthLookup = formatLookup(locale_months),
- shortMonthRe = formatRe(locale_shortMonths),
- shortMonthLookup = formatLookup(locale_shortMonths);
+ periodLookup = formatLookup(locale_periods),
+ weekdayRe = formatRe(locale_weekdays),
+ weekdayLookup = formatLookup(locale_weekdays),
+ shortWeekdayRe = formatRe(locale_shortWeekdays),
+ shortWeekdayLookup = formatLookup(locale_shortWeekdays),
+ monthRe = formatRe(locale_months),
+ monthLookup = formatLookup(locale_months),
+ shortMonthRe = formatRe(locale_shortMonths),
+ shortMonthLookup = formatLookup(locale_shortMonths);
var formats = {
"a": formatShortWeekday,
"A": formatWeekday,
"b": formatShortMonth,
"B": formatMonth,
@@ -3026,558 +2694,459 @@
"X": parseLocaleTime,
"y": parseYear,
"Y": parseFullYear,
"Z": parseZone,
"%": parseLiteralPercent
- }; // These recursive directive definitions must be deferred.
+ };
+ // These recursive directive definitions must be deferred.
formats.x = newFormat(locale_date, formats);
formats.X = newFormat(locale_time, formats);
formats.c = newFormat(locale_dateTime, formats);
utcFormats.x = newFormat(locale_date, utcFormats);
utcFormats.X = newFormat(locale_time, utcFormats);
utcFormats.c = newFormat(locale_dateTime, utcFormats);
-
function newFormat(specifier, formats) {
return function (date) {
var string = [],
- i = -1,
- j = 0,
- n = specifier.length,
- c,
- pad,
- format;
+ i = -1,
+ j = 0,
+ n = specifier.length,
+ c,
+ pad,
+ format;
if (!(date instanceof Date)) date = new Date(+date);
-
while (++i < n) {
if (specifier.charCodeAt(i) === 37) {
string.push(specifier.slice(j, i));
if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);else pad = c === "e" ? " " : "0";
if (format = formats[c]) c = format(date, pad);
string.push(c);
j = i + 1;
}
}
-
string.push(specifier.slice(j, i));
return string.join("");
};
}
-
function newParse(specifier, Z) {
return function (string) {
var d = newDate(1900, undefined, 1),
- i = parseSpecifier(d, specifier, string += "", 0),
- week,
- day;
- if (i != string.length) return null; // If a UNIX timestamp is specified, return it.
+ i = parseSpecifier(d, specifier, string += "", 0),
+ week,
+ day;
+ if (i != string.length) return null;
+ // If a UNIX timestamp is specified, return it.
if ("Q" in d) return new Date(d.Q);
- if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0)); // If this is utcParse, never use the local timezone.
+ if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0));
- if (Z && !("Z" in d)) d.Z = 0; // The am-pm flag is 0 for AM, and 1 for PM.
+ // If this is utcParse, never use the local timezone.
+ if (Z && !("Z" in d)) d.Z = 0;
- if ("p" in d) d.H = d.H % 12 + d.p * 12; // If the month was not specified, inherit from the quarter.
+ // The am-pm flag is 0 for AM, and 1 for PM.
+ if ("p" in d) d.H = d.H % 12 + d.p * 12;
- if (d.m === undefined) d.m = "q" in d ? d.q : 0; // Convert day-of-week and week-of-year to day-of-year.
+ // If the month was not specified, inherit from the quarter.
+ if (d.m === undefined) d.m = "q" in d ? d.q : 0;
+ // Convert day-of-week and week-of-year to day-of-year.
if ("V" in d) {
if (d.V < 1 || d.V > 53) return null;
if (!("w" in d)) d.w = 1;
-
if ("Z" in d) {
week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay();
week = day > 4 || day === 0 ? utcMonday.ceil(week) : utcMonday(week);
- week = utcDay$1.offset(week, (d.V - 1) * 7);
+ week = utcDay.offset(week, (d.V - 1) * 7);
d.y = week.getUTCFullYear();
d.m = week.getUTCMonth();
d.d = week.getUTCDate() + (d.w + 6) % 7;
} else {
week = localDate(newDate(d.y, 0, 1)), day = week.getDay();
- week = day > 4 || day === 0 ? monday.ceil(week) : monday(week);
+ week = day > 4 || day === 0 ? timeMonday.ceil(week) : timeMonday(week);
week = timeDay.offset(week, (d.V - 1) * 7);
d.y = week.getFullYear();
d.m = week.getMonth();
d.d = week.getDate() + (d.w + 6) % 7;
}
} else if ("W" in d || "U" in d) {
if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
day = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay();
d.m = 0;
d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;
- } // If a time zone is specified, all fields are interpreted as UTC and then
- // offset according to the specified time zone.
+ }
-
+ // If a time zone is specified, all fields are interpreted as UTC and then
+ // offset according to the specified time zone.
if ("Z" in d) {
d.H += d.Z / 100 | 0;
d.M += d.Z % 100;
return utcDate(d);
- } // Otherwise, all fields are in local time.
+ }
-
+ // Otherwise, all fields are in local time.
return localDate(d);
};
}
-
function parseSpecifier(d, specifier, string, j) {
var i = 0,
- n = specifier.length,
- m = string.length,
- c,
- parse;
-
+ n = specifier.length,
+ m = string.length,
+ c,
+ parse;
while (i < n) {
if (j >= m) return -1;
c = specifier.charCodeAt(i++);
-
if (c === 37) {
c = specifier.charAt(i++);
parse = parses[c in pads ? specifier.charAt(i++) : c];
if (!parse || (j = parse(d, string, j)) < 0) return -1;
} else if (c != string.charCodeAt(j++)) {
return -1;
}
}
-
return j;
}
-
function parsePeriod(d, string, i) {
var n = periodRe.exec(string.slice(i));
return n ? (d.p = periodLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
}
-
function parseShortWeekday(d, string, i) {
var n = shortWeekdayRe.exec(string.slice(i));
return n ? (d.w = shortWeekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
}
-
function parseWeekday(d, string, i) {
var n = weekdayRe.exec(string.slice(i));
return n ? (d.w = weekdayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
}
-
function parseShortMonth(d, string, i) {
var n = shortMonthRe.exec(string.slice(i));
return n ? (d.m = shortMonthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
}
-
function parseMonth(d, string, i) {
var n = monthRe.exec(string.slice(i));
return n ? (d.m = monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
}
-
function parseLocaleDateTime(d, string, i) {
return parseSpecifier(d, locale_dateTime, string, i);
}
-
function parseLocaleDate(d, string, i) {
return parseSpecifier(d, locale_date, string, i);
}
-
function parseLocaleTime(d, string, i) {
return parseSpecifier(d, locale_time, string, i);
}
-
function formatShortWeekday(d) {
return locale_shortWeekdays[d.getDay()];
}
-
function formatWeekday(d) {
return locale_weekdays[d.getDay()];
}
-
function formatShortMonth(d) {
return locale_shortMonths[d.getMonth()];
}
-
function formatMonth(d) {
return locale_months[d.getMonth()];
}
-
function formatPeriod(d) {
return locale_periods[+(d.getHours() >= 12)];
}
-
function formatQuarter(d) {
return 1 + ~~(d.getMonth() / 3);
}
-
function formatUTCShortWeekday(d) {
return locale_shortWeekdays[d.getUTCDay()];
}
-
function formatUTCWeekday(d) {
return locale_weekdays[d.getUTCDay()];
}
-
function formatUTCShortMonth(d) {
return locale_shortMonths[d.getUTCMonth()];
}
-
function formatUTCMonth(d) {
return locale_months[d.getUTCMonth()];
}
-
function formatUTCPeriod(d) {
return locale_periods[+(d.getUTCHours() >= 12)];
}
-
function formatUTCQuarter(d) {
return 1 + ~~(d.getUTCMonth() / 3);
}
-
return {
format: function (specifier) {
var f = newFormat(specifier += "", formats);
-
f.toString = function () {
return specifier;
};
-
return f;
},
parse: function (specifier) {
var p = newParse(specifier += "", false);
-
p.toString = function () {
return specifier;
};
-
return p;
},
utcFormat: function (specifier) {
var f = newFormat(specifier += "", utcFormats);
-
f.toString = function () {
return specifier;
};
-
return f;
},
utcParse: function (specifier) {
var p = newParse(specifier += "", true);
-
p.toString = function () {
return specifier;
};
-
return p;
}
};
}
var pads = {
- "-": "",
- "_": " ",
- "0": "0"
- },
- numberRe = /^\s*\d+/,
- // note: ignores next directive
- percentRe = /^%/,
- requoteRe = /[\\^$*+?|[\]().{}]/g;
-
+ "-": "",
+ "_": " ",
+ "0": "0"
+ },
+ numberRe = /^\s*\d+/,
+ // note: ignores next directive
+ percentRe = /^%/,
+ requoteRe = /[\\^$*+?|[\]().{}]/g;
function pad(value, fill, width) {
var sign = value < 0 ? "-" : "",
- string = (sign ? -value : value) + "",
- length = string.length;
+ string = (sign ? -value : value) + "",
+ length = string.length;
return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
}
-
function requote(s) {
return s.replace(requoteRe, "\\$&");
}
-
function formatRe(names) {
return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
}
-
function formatLookup(names) {
return new Map(names.map((name, i) => [name.toLowerCase(), i]));
}
-
function parseWeekdayNumberSunday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.w = +n[0], i + n[0].length) : -1;
}
-
function parseWeekdayNumberMonday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.u = +n[0], i + n[0].length) : -1;
}
-
function parseWeekNumberSunday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.U = +n[0], i + n[0].length) : -1;
}
-
function parseWeekNumberISO(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.V = +n[0], i + n[0].length) : -1;
}
-
function parseWeekNumberMonday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.W = +n[0], i + n[0].length) : -1;
}
-
function parseFullYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 4));
return n ? (d.y = +n[0], i + n[0].length) : -1;
}
-
function parseYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
}
-
function parseZone(d, string, i) {
var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
}
-
function parseQuarter(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1;
}
-
function parseMonthNumber(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
}
-
function parseDayOfMonth(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.d = +n[0], i + n[0].length) : -1;
}
-
function parseDayOfYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 3));
return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
}
-
function parseHour24(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.H = +n[0], i + n[0].length) : -1;
}
-
function parseMinutes(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.M = +n[0], i + n[0].length) : -1;
}
-
function parseSeconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.S = +n[0], i + n[0].length) : -1;
}
-
function parseMilliseconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 3));
return n ? (d.L = +n[0], i + n[0].length) : -1;
}
-
function parseMicroseconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 6));
return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
}
-
function parseLiteralPercent(d, string, i) {
var n = percentRe.exec(string.slice(i, i + 1));
return n ? i + n[0].length : -1;
}
-
function parseUnixTimestamp(d, string, i) {
var n = numberRe.exec(string.slice(i));
return n ? (d.Q = +n[0], i + n[0].length) : -1;
}
-
function parseUnixTimestampSeconds(d, string, i) {
var n = numberRe.exec(string.slice(i));
return n ? (d.s = +n[0], i + n[0].length) : -1;
}
-
function formatDayOfMonth(d, p) {
return pad(d.getDate(), p, 2);
}
-
function formatHour24(d, p) {
return pad(d.getHours(), p, 2);
}
-
function formatHour12(d, p) {
return pad(d.getHours() % 12 || 12, p, 2);
}
-
function formatDayOfYear(d, p) {
return pad(1 + timeDay.count(timeYear(d), d), p, 3);
}
-
function formatMilliseconds(d, p) {
return pad(d.getMilliseconds(), p, 3);
}
-
function formatMicroseconds(d, p) {
return formatMilliseconds(d, p) + "000";
}
-
function formatMonthNumber(d, p) {
return pad(d.getMonth() + 1, p, 2);
}
-
function formatMinutes(d, p) {
return pad(d.getMinutes(), p, 2);
}
-
function formatSeconds(d, p) {
return pad(d.getSeconds(), p, 2);
}
-
function formatWeekdayNumberMonday(d) {
var day = d.getDay();
return day === 0 ? 7 : day;
}
-
function formatWeekNumberSunday(d, p) {
- return pad(sunday.count(timeYear(d) - 1, d), p, 2);
+ return pad(timeSunday.count(timeYear(d) - 1, d), p, 2);
}
-
function dISO(d) {
var day = d.getDay();
- return day >= 4 || day === 0 ? thursday(d) : thursday.ceil(d);
+ return day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d);
}
-
function formatWeekNumberISO(d, p) {
d = dISO(d);
- return pad(thursday.count(timeYear(d), d) + (timeYear(d).getDay() === 4), p, 2);
+ return pad(timeThursday.count(timeYear(d), d) + (timeYear(d).getDay() === 4), p, 2);
}
-
function formatWeekdayNumberSunday(d) {
return d.getDay();
}
-
function formatWeekNumberMonday(d, p) {
- return pad(monday.count(timeYear(d) - 1, d), p, 2);
+ return pad(timeMonday.count(timeYear(d) - 1, d), p, 2);
}
-
function formatYear(d, p) {
return pad(d.getFullYear() % 100, p, 2);
}
-
function formatYearISO(d, p) {
d = dISO(d);
return pad(d.getFullYear() % 100, p, 2);
}
-
function formatFullYear(d, p) {
return pad(d.getFullYear() % 10000, p, 4);
}
-
function formatFullYearISO(d, p) {
var day = d.getDay();
- d = day >= 4 || day === 0 ? thursday(d) : thursday.ceil(d);
+ d = day >= 4 || day === 0 ? timeThursday(d) : timeThursday.ceil(d);
return pad(d.getFullYear() % 10000, p, 4);
}
-
function formatZone(d) {
var z = d.getTimezoneOffset();
return (z > 0 ? "-" : (z *= -1, "+")) + pad(z / 60 | 0, "0", 2) + pad(z % 60, "0", 2);
}
-
function formatUTCDayOfMonth(d, p) {
return pad(d.getUTCDate(), p, 2);
}
-
function formatUTCHour24(d, p) {
return pad(d.getUTCHours(), p, 2);
}
-
function formatUTCHour12(d, p) {
return pad(d.getUTCHours() % 12 || 12, p, 2);
}
-
function formatUTCDayOfYear(d, p) {
- return pad(1 + utcDay$1.count(utcYear$1(d), d), p, 3);
+ return pad(1 + utcDay.count(utcYear(d), d), p, 3);
}
-
function formatUTCMilliseconds(d, p) {
return pad(d.getUTCMilliseconds(), p, 3);
}
-
function formatUTCMicroseconds(d, p) {
return formatUTCMilliseconds(d, p) + "000";
}
-
function formatUTCMonthNumber(d, p) {
return pad(d.getUTCMonth() + 1, p, 2);
}
-
function formatUTCMinutes(d, p) {
return pad(d.getUTCMinutes(), p, 2);
}
-
function formatUTCSeconds(d, p) {
return pad(d.getUTCSeconds(), p, 2);
}
-
function formatUTCWeekdayNumberMonday(d) {
var dow = d.getUTCDay();
return dow === 0 ? 7 : dow;
}
-
function formatUTCWeekNumberSunday(d, p) {
- return pad(utcSunday.count(utcYear$1(d) - 1, d), p, 2);
+ return pad(utcSunday.count(utcYear(d) - 1, d), p, 2);
}
-
function UTCdISO(d) {
var day = d.getUTCDay();
return day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d);
}
-
function formatUTCWeekNumberISO(d, p) {
d = UTCdISO(d);
- return pad(utcThursday.count(utcYear$1(d), d) + (utcYear$1(d).getUTCDay() === 4), p, 2);
+ return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);
}
-
function formatUTCWeekdayNumberSunday(d) {
return d.getUTCDay();
}
-
function formatUTCWeekNumberMonday(d, p) {
- return pad(utcMonday.count(utcYear$1(d) - 1, d), p, 2);
+ return pad(utcMonday.count(utcYear(d) - 1, d), p, 2);
}
-
function formatUTCYear(d, p) {
return pad(d.getUTCFullYear() % 100, p, 2);
}
-
function formatUTCYearISO(d, p) {
d = UTCdISO(d);
return pad(d.getUTCFullYear() % 100, p, 2);
}
-
function formatUTCFullYear(d, p) {
return pad(d.getUTCFullYear() % 10000, p, 4);
}
-
function formatUTCFullYearISO(d, p) {
var day = d.getUTCDay();
d = day >= 4 || day === 0 ? utcThursday(d) : utcThursday.ceil(d);
return pad(d.getUTCFullYear() % 10000, p, 4);
}
-
function formatUTCZone() {
return "+0000";
}
-
function formatLiteralPercent() {
return "%";
}
-
function formatUnixTimestamp(d) {
return +d;
}
-
function formatUnixTimestampSeconds(d) {
return Math.floor(+d / 1000);
}
var locale$1;
@@ -3606,226 +3175,192 @@
function memoize(method) {
const cache = {};
return spec => cache[spec] || (cache[spec] = method(spec));
}
-
function trimZeroes(numberFormat, decimalChar) {
return x => {
const str = numberFormat(x),
- dec = str.indexOf(decimalChar);
+ dec = str.indexOf(decimalChar);
if (dec < 0) return str;
let idx = rightmostDigit(str, dec);
const end = idx < str.length ? str.slice(idx) : '';
-
while (--idx > dec) if (str[idx] !== '0') {
++idx;
break;
}
-
return str.slice(0, idx) + end;
};
}
-
function rightmostDigit(str, dec) {
let i = str.lastIndexOf('e'),
- c;
+ c;
if (i > 0) return i;
-
for (i = str.length; --i > dec;) {
c = str.charCodeAt(i);
if (c >= 48 && c <= 57) return i + 1; // is digit
}
}
-
function numberLocale(locale) {
const format = memoize(locale.format),
- formatPrefix = locale.formatPrefix;
+ formatPrefix = locale.formatPrefix;
return {
format,
formatPrefix,
-
formatFloat(spec) {
const s = formatSpecifier(spec || ',');
-
if (s.precision == null) {
s.precision = 12;
-
switch (s.type) {
case '%':
s.precision -= 2;
break;
-
case 'e':
s.precision -= 1;
break;
}
-
- return trimZeroes(format(s), // number format
+ return trimZeroes(format(s),
+ // number format
format('.1f')(1)[1] // decimal point character
);
} else {
return format(s);
}
},
-
formatSpan(start, stop, count, specifier) {
specifier = formatSpecifier(specifier == null ? ',f' : specifier);
const step = tickStep(start, stop, count),
- value = Math.max(Math.abs(start), Math.abs(stop));
+ value = Math.max(Math.abs(start), Math.abs(stop));
let precision;
-
if (specifier.precision == null) {
switch (specifier.type) {
case 's':
{
if (!isNaN(precision = precisionPrefix(step, value))) {
specifier.precision = precision;
}
-
return formatPrefix(specifier, value);
}
-
case '':
case 'e':
case 'g':
case 'p':
case 'r':
{
if (!isNaN(precision = precisionRound(step, value))) {
specifier.precision = precision - (specifier.type === 'e');
}
-
break;
}
-
case 'f':
case '%':
{
if (!isNaN(precision = precisionFixed(step))) {
specifier.precision = precision - (specifier.type === '%') * 2;
}
-
break;
}
}
}
-
return format(specifier);
}
-
};
}
-
let defaultNumberLocale;
resetNumberFormatDefaultLocale();
-
function resetNumberFormatDefaultLocale() {
return defaultNumberLocale = numberLocale({
format: format$3,
formatPrefix: formatPrefix
});
}
-
function numberFormatLocale(definition) {
return numberLocale(formatLocale$1(definition));
}
-
function numberFormatDefaultLocale(definition) {
return arguments.length ? defaultNumberLocale = numberFormatLocale(definition) : defaultNumberLocale;
}
-
function timeMultiFormat(format, interval, spec) {
spec = spec || {};
-
if (!isObject(spec)) {
- error("Invalid time multi-format specifier: ".concat(spec));
+ error(`Invalid time multi-format specifier: ${spec}`);
}
-
const second = interval(SECONDS),
- minute = interval(MINUTES),
- hour = interval(HOURS),
- day = interval(DATE),
- week = interval(WEEK),
- month = interval(MONTH),
- quarter = interval(QUARTER),
- year = interval(YEAR),
- L = format(spec[MILLISECONDS] || '.%L'),
- S = format(spec[SECONDS] || ':%S'),
- M = format(spec[MINUTES] || '%I:%M'),
- H = format(spec[HOURS] || '%I %p'),
- d = format(spec[DATE] || spec[DAY] || '%a %d'),
- w = format(spec[WEEK] || '%b %d'),
- m = format(spec[MONTH] || '%B'),
- q = format(spec[QUARTER] || '%B'),
- y = format(spec[YEAR] || '%Y');
+ minute = interval(MINUTES),
+ hour = interval(HOURS),
+ day = interval(DATE),
+ week = interval(WEEK),
+ month = interval(MONTH),
+ quarter = interval(QUARTER),
+ year = interval(YEAR),
+ L = format(spec[MILLISECONDS] || '.%L'),
+ S = format(spec[SECONDS] || ':%S'),
+ M = format(spec[MINUTES] || '%I:%M'),
+ H = format(spec[HOURS] || '%I %p'),
+ d = format(spec[DATE] || spec[DAY] || '%a %d'),
+ w = format(spec[WEEK] || '%b %d'),
+ m = format(spec[MONTH] || '%B'),
+ q = format(spec[QUARTER] || '%B'),
+ y = format(spec[YEAR] || '%Y');
return date => (second(date) < date ? L : minute(date) < date ? S : hour(date) < date ? M : day(date) < date ? H : month(date) < date ? week(date) < date ? d : w : year(date) < date ? quarter(date) < date ? m : q : y)(date);
}
-
function timeLocale(locale) {
const timeFormat = memoize(locale.format),
- utcFormat = memoize(locale.utcFormat);
+ utcFormat = memoize(locale.utcFormat);
return {
timeFormat: spec => isString(spec) ? timeFormat(spec) : timeMultiFormat(timeFormat, timeInterval, spec),
utcFormat: spec => isString(spec) ? utcFormat(spec) : timeMultiFormat(utcFormat, utcInterval, spec),
timeParse: memoize(locale.parse),
utcParse: memoize(locale.utcParse)
};
}
-
let defaultTimeLocale;
resetTimeFormatDefaultLocale();
-
function resetTimeFormatDefaultLocale() {
return defaultTimeLocale = timeLocale({
format: timeFormat$1,
parse: timeParse$1,
utcFormat: utcFormat$1,
utcParse: utcParse$1
});
}
-
function timeFormatLocale(definition) {
return timeLocale(formatLocale(definition));
}
-
function timeFormatDefaultLocale(definition) {
return arguments.length ? defaultTimeLocale = timeFormatLocale(definition) : defaultTimeLocale;
}
-
const createLocale = (number, time) => extend$1({}, number, time);
-
function locale(numberSpec, timeSpec) {
const number = numberSpec ? numberFormatLocale(numberSpec) : numberFormatDefaultLocale();
const time = timeSpec ? timeFormatLocale(timeSpec) : timeFormatDefaultLocale();
return createLocale(number, time);
}
-
function defaultLocale(numberSpec, timeSpec) {
const args = arguments.length;
-
if (args && args !== 2) {
error('defaultLocale expects either zero or two arguments.');
}
-
return args ? createLocale(numberFormatDefaultLocale(numberSpec), timeFormatDefaultLocale(timeSpec)) : createLocale(numberFormatDefaultLocale(), timeFormatDefaultLocale());
}
-
function resetDefaultLocale() {
resetNumberFormatDefaultLocale();
resetTimeFormatDefaultLocale();
return defaultLocale();
}
- const protocol_re = /^(data:|([A-Za-z]+:)?\/\/)/; // Matches allowed URIs. From https://github.com/cure53/DOMPurify/blob/master/src/regexp.js with added file://
+ // Matches absolute URLs with optional protocol
+ // https://... file://... //...
+ const protocol_re = /^(data:|([A-Za-z]+:)?\/\/)/;
+ // Matches allowed URIs. From https://github.com/cure53/DOMPurify/blob/master/src/regexp.js with added file://
const allowed_re = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|file|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; // eslint-disable-line no-useless-escape
-
const whitespace_re = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
- // Special treatment in node.js for the file: protocol
+ // Special treatment in node.js for the file: protocol
const fileProtocol = 'file://';
+
/**
* Factory for a loader constructor that provides methods for requesting
* files from either the network or disk, and for sanitizing request URIs.
* @param {function} fetch - The Fetch API for HTTP network requests.
* If null or undefined, HTTP loading will be disabled.
@@ -3833,77 +3368,71 @@
* If null or undefined, local file loading will be disabled.
* @return {function} A loader constructor with the following signature:
* param {object} [options] - Optional default loading options to use.
* return {object} - A new loader instance.
*/
-
function loaderFactory(fetch, fs) {
return options => ({
options: options || {},
sanitize: sanitize,
load: load$1,
fileAccess: !!fs,
file: fileLoader(fs),
http: httpLoader(fetch)
});
}
+
/**
* Load an external resource, typically either from the web or from the local
* filesystem. This function uses {@link sanitize} to first sanitize the uri,
* then calls either {@link http} (for web requests) or {@link file} (for
* filesystem loading).
* @param {string} uri - The resource indicator (e.g., URL or filename).
* @param {object} [options] - Optional loading options. These options will
* override any existing default options.
* @return {Promise} - A promise that resolves to the loaded content.
*/
-
-
async function load$1(uri, options) {
const opt = await this.sanitize(uri, options),
- url = opt.href;
+ url = opt.href;
return opt.localFile ? this.file(url) : this.http(url, options);
}
+
/**
* URI sanitizer function.
* @param {string} uri - The uri (url or filename) to check.
* @param {object} options - An options hash.
* @return {Promise} - A promise that resolves to an object containing
* sanitized uri data, or rejects it the input uri is deemed invalid.
* The properties of the resolved object are assumed to be
* valid attributes for an HTML 'a' tag. The sanitized uri *must* be
* provided by the 'href' property of the returned object.
*/
-
-
async function sanitize(uri, options) {
options = extend$1({}, this.options, options);
const fileAccess = this.fileAccess,
- result = {
- href: null
- };
+ result = {
+ href: null
+ };
let isFile, loadFile, base;
const isAllowed = allowed_re.test(uri.replace(whitespace_re, ''));
-
if (uri == null || typeof uri !== 'string' || !isAllowed) {
error('Sanitize failure, invalid URI: ' + $(uri));
}
+ const hasProtocol = protocol_re.test(uri);
- const hasProtocol = protocol_re.test(uri); // if relative url (no protocol/host), prepend baseURL
-
+ // if relative url (no protocol/host), prepend baseURL
if ((base = options.baseURL) && !hasProtocol) {
// Ensure that there is a slash between the baseURL (e.g. hostname) and url
if (!uri.startsWith('/') && !base.endsWith('/')) {
uri = '/' + uri;
}
-
uri = base + uri;
- } // should we load from file system?
+ }
-
+ // should we load from file system?
loadFile = (isFile = uri.startsWith(fileProtocol)) || options.mode === 'file' || options.mode !== 'http' && !hasProtocol && fileAccess;
-
if (isFile) {
// strip file protocol
uri = uri.slice(fileProtocol.length);
} else if (uri.startsWith('//')) {
if (options.defaultProtocol === 'file') {
@@ -3912,623 +3441,544 @@
loadFile = true;
} else {
// if relative protocol (starts with '//'), prepend default protocol
uri = (options.defaultProtocol || 'http') + ':' + uri;
}
- } // set non-enumerable mode flag to indicate local file load
+ }
-
+ // set non-enumerable mode flag to indicate local file load
Object.defineProperty(result, 'localFile', {
value: !!loadFile
- }); // set uri
+ });
- result.href = uri; // set default result target, if specified
+ // set uri
+ result.href = uri;
+ // set default result target, if specified
if (options.target) {
result.target = options.target + '';
- } // set default result rel, if specified (#1542)
+ }
-
+ // set default result rel, if specified (#1542)
if (options.rel) {
result.rel = options.rel + '';
- } // provide control over cross-origin image handling (#2238)
- // https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
+ }
-
+ // provide control over cross-origin image handling (#2238)
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
if (options.context === 'image' && options.crossOrigin) {
result.crossOrigin = options.crossOrigin + '';
- } // return
+ }
-
+ // return
return result;
}
+
/**
* File system loader factory.
* @param {object} fs - The file system interface.
* @return {function} - A file loader with the following signature:
* param {string} filename - The file system path to load.
* param {string} filename - The file system path to load.
* return {Promise} A promise that resolves to the file contents.
*/
-
-
function fileLoader(fs) {
return fs ? filename => new Promise((accept, reject) => {
fs.readFile(filename, (error, data) => {
if (error) reject(error);else accept(data);
});
}) : fileReject;
}
+
/**
* Default file system loader that simply rejects.
*/
-
-
async function fileReject() {
error('No file system access.');
}
+
/**
* HTTP request handler factory.
* @param {function} fetch - The Fetch API method.
* @return {function} - An http loader with the following signature:
* param {string} url - The url to request.
* param {object} options - An options hash.
* return {Promise} - A promise that resolves to the file contents.
*/
-
-
function httpLoader(fetch) {
return fetch ? async function (url, options) {
const opt = extend$1({}, this.options.http, options),
- type = options && options.response,
- response = await fetch(url, opt);
+ type = options && options.response,
+ response = await fetch(url, opt);
return !response.ok ? error(response.status + '' + response.statusText) : isFunction(response[type]) ? response[type]() : response.text();
} : httpReject;
}
+
/**
* Default http request handler that simply rejects.
*/
-
-
async function httpReject() {
error('No HTTP fetch method available.');
}
-
const isValid = _ => _ != null && _ === _;
-
const isBoolean = _ => _ === 'true' || _ === 'false' || _ === true || _ === false;
-
const isDate = _ => !Number.isNaN(Date.parse(_));
-
const isNumber = _ => !Number.isNaN(+_) && !(_ instanceof Date);
-
const isInteger = _ => isNumber(_) && Number.isInteger(+_);
-
const typeParsers = {
boolean: toBoolean,
integer: toNumber,
number: toNumber,
date: toDate,
string: toString,
unknown: identity$6
};
const typeTests = [isBoolean, isInteger, isNumber, isDate];
const typeList = ['boolean', 'integer', 'number', 'date'];
-
function inferType(values, field) {
if (!values || !values.length) return 'unknown';
const n = values.length,
- m = typeTests.length,
- a = typeTests.map((_, i) => i + 1);
-
+ m = typeTests.length,
+ a = typeTests.map((_, i) => i + 1);
for (let i = 0, t = 0, j, value; i < n; ++i) {
value = field ? values[i][field] : values[i];
-
for (j = 0; j < m; ++j) {
if (a[j] && isValid(value) && !typeTests[j](value)) {
a[j] = 0;
++t;
if (t === typeTests.length) return 'string';
}
}
}
-
return typeList[a.reduce((u, v) => u === 0 ? v : u, 0) - 1];
}
-
function inferTypes(data, fields) {
return fields.reduce((types, field) => {
types[field] = inferType(data, field);
return types;
}, {});
}
-
function delimitedFormat(delimiter) {
const parse = function (data, format) {
const delim = {
delimiter: delimiter
};
return dsv(data, format ? extend$1(format, delim) : delim);
};
-
parse.responseType = 'text';
return parse;
}
-
function dsv(data, format) {
if (format.header) {
data = format.header.map($).join(format.delimiter) + '\n' + data;
}
-
return dsvFormat(format.delimiter).parse(data + '');
}
-
dsv.responseType = 'text';
-
function isBuffer(_) {
return typeof Buffer === 'function' && isFunction(Buffer.isBuffer) ? Buffer.isBuffer(_) : false;
}
-
function json(data, format) {
const prop = format && format.property ? field$1(format.property) : identity$6;
return isObject(data) && !isBuffer(data) ? parseJSON(prop(data), format) : prop(JSON.parse(data));
}
-
json.responseType = 'json';
-
function parseJSON(data, format) {
if (!isArray(data) && isIterable(data)) {
data = [...data];
}
-
return format && format.copy ? JSON.parse(JSON.stringify(data)) : data;
}
-
const filters = {
interior: (a, b) => a !== b,
exterior: (a, b) => a === b
};
-
function topojson(data, format) {
let method, object, property, filter;
data = json(data, format);
-
if (format && format.feature) {
method = feature;
property = format.feature;
} else if (format && format.mesh) {
method = mesh;
property = format.mesh;
filter = filters[format.filter];
} else {
error('Missing TopoJSON feature or mesh parameter.');
}
-
object = (object = data.objects[property]) ? method(data, object, filter) : error('Invalid TopoJSON object: ' + property);
return object && object.features || [object];
}
-
topojson.responseType = 'json';
const format$2 = {
dsv: dsv,
csv: delimitedFormat(','),
tsv: delimitedFormat('\t'),
json: json,
topojson: topojson
};
-
function formats$1(name, reader) {
if (arguments.length > 1) {
format$2[name] = reader;
return this;
} else {
return has$1(format$2, name) ? format$2[name] : null;
}
}
-
function responseType(type) {
const f = formats$1(type);
return f && f.responseType || 'text';
}
-
function read(data, schema, timeParser, utcParser) {
schema = schema || {};
const reader = formats$1(schema.type || 'json');
if (!reader) error('Unknown data format type: ' + schema.type);
data = reader(data, schema);
if (schema.parse) parse$6(data, schema.parse, timeParser, utcParser);
if (has$1(data, 'columns')) delete data.columns;
return data;
}
-
function parse$6(data, types, timeParser, utcParser) {
if (!data.length) return; // early exit for empty data
const locale = timeFormatDefaultLocale();
timeParser = timeParser || locale.timeParse;
utcParser = utcParser || locale.utcParse;
let fields = data.columns || Object.keys(data[0]),
- datum,
- field,
- i,
- j,
- n,
- m;
+ datum,
+ field,
+ i,
+ j,
+ n,
+ m;
if (types === 'auto') types = inferTypes(data, fields);
fields = Object.keys(types);
const parsers = fields.map(field => {
const type = types[field];
let parts, pattern;
-
if (type && (type.startsWith('date:') || type.startsWith('utc:'))) {
parts = type.split(/:(.+)?/, 2); // split on first :
-
pattern = parts[1];
-
if (pattern[0] === '\'' && pattern[pattern.length - 1] === '\'' || pattern[0] === '"' && pattern[pattern.length - 1] === '"') {
pattern = pattern.slice(1, -1);
}
-
const parse = parts[0] === 'utc' ? utcParser : timeParser;
return parse(pattern);
}
-
if (!typeParsers[type]) {
throw Error('Illegal format pattern: ' + field + ':' + type);
}
-
return typeParsers[type];
});
-
for (i = 0, n = data.length, m = fields.length; i < n; ++i) {
datum = data[i];
-
for (j = 0; j < m; ++j) {
field = fields[j];
datum[field] = parsers[j](datum[field]);
}
}
}
-
- const loader = loaderFactory(typeof fetch !== 'undefined' && fetch, // use built-in fetch API
+ const loader = loaderFactory(typeof fetch !== 'undefined' && fetch,
+ // use built-in fetch API
null // no file system access
);
function UniqueList(idFunc) {
const $ = idFunc || identity$6,
- list = [],
- ids = {};
-
+ list = [],
+ ids = {};
list.add = _ => {
const id = $(_);
-
if (!ids[id]) {
ids[id] = 1;
list.push(_);
}
-
return list;
};
-
list.remove = _ => {
const id = $(_);
-
if (ids[id]) {
ids[id] = 0;
const idx = list.indexOf(_);
if (idx >= 0) list.splice(idx, 1);
}
-
return list;
};
-
return list;
}
+
/**
* Invoke and await a potentially async callback function. If
* an error occurs, trap it and route to Dataflow.error.
* @param {Dataflow} df - The dataflow instance
* @param {function} callback - A callback function to invoke
* and then await. The dataflow will be passed as the single
* argument to the function.
*/
-
-
async function asyncCallback(df, callback) {
try {
await callback(df);
} catch (err) {
df.error(err);
}
}
-
const TUPLE_ID_KEY = Symbol('vega_id');
let TUPLE_ID = 1;
+
/**
* Checks if an input value is a registered tuple.
* @param {*} t - The value to check.
* @return {boolean} True if the input is a tuple, false otherwise.
*/
-
function isTuple(t) {
return !!(t && tupleid(t));
}
+
/**
* Returns the id of a tuple.
* @param {object} t - The input tuple.
* @return {*} the tuple id.
*/
-
-
function tupleid(t) {
return t[TUPLE_ID_KEY];
}
+
/**
* Sets the id of a tuple.
* @param {object} t - The input tuple.
* @param {*} id - The id value to set.
* @return {object} the input tuple.
*/
-
-
function setid(t, id) {
t[TUPLE_ID_KEY] = id;
return t;
}
+
/**
* Ingest an object or value as a data tuple.
* If the input value is an object, an id field will be added to it. For
* efficiency, the input object is modified directly. A copy is not made.
* If the input value is a literal, it will be wrapped in a new object
* instance, with the value accessible as the 'data' property.
* @param datum - The value to ingest.
* @return {object} The ingested data tuple.
*/
-
-
function ingest$1(datum) {
const t = datum === Object(datum) ? datum : {
data: datum
};
return tupleid(t) ? t : setid(t, TUPLE_ID++);
}
+
/**
* Given a source tuple, return a derived copy.
* @param {object} t - The source tuple.
* @return {object} The derived tuple.
*/
-
-
function derive(t) {
return rederive(t, ingest$1({}));
}
+
/**
* Rederive a derived tuple by copying values from the source tuple.
* @param {object} t - The source tuple.
* @param {object} d - The derived tuple.
* @return {object} The derived tuple.
*/
-
-
function rederive(t, d) {
for (const k in t) d[k] = t[k];
-
return d;
}
+
/**
* Replace an existing tuple with a new tuple.
* @param {object} t - The existing data tuple.
* @param {object} d - The new tuple that replaces the old.
* @return {object} The new tuple.
*/
-
-
function replace$1(t, d) {
return setid(d, tupleid(t));
}
+
/**
* Generate an augmented comparator function that provides stable
* sorting by tuple id when the given comparator produces ties.
* @param {function} cmp - The comparator to augment.
* @param {function} [f] - Optional tuple accessor function.
* @return {function} An augmented comparator function.
*/
-
-
function stableCompare(cmp, f) {
return !cmp ? null : f ? (a, b) => cmp(a, b) || tupleid(f(a)) - tupleid(f(b)) : (a, b) => cmp(a, b) || tupleid(a) - tupleid(b);
}
-
function isChangeSet(v) {
return v && v.constructor === changeset;
}
-
function changeset() {
const add = [],
- // insert tuples
- rem = [],
- // remove tuples
- mod = [],
- // modify tuples
- remp = [],
- // remove by predicate
- modp = []; // modify by predicate
-
+ // insert tuples
+ rem = [],
+ // remove tuples
+ mod = [],
+ // modify tuples
+ remp = [],
+ // remove by predicate
+ modp = []; // modify by predicate
let clean = null,
- reflow = false;
+ reflow = false;
return {
constructor: changeset,
-
insert(t) {
const d = array$5(t),
- n = d.length;
-
+ n = d.length;
for (let i = 0; i < n; ++i) add.push(d[i]);
-
return this;
},
-
remove(t) {
const a = isFunction(t) ? remp : rem,
- d = array$5(t),
- n = d.length;
-
+ d = array$5(t),
+ n = d.length;
for (let i = 0; i < n; ++i) a.push(d[i]);
-
return this;
},
-
modify(t, field, value) {
const m = {
field: field,
- value: constant$4(value)
+ value: constant$5(value)
};
-
if (isFunction(t)) {
m.filter = t;
modp.push(m);
} else {
m.tuple = t;
mod.push(m);
}
-
return this;
},
-
encode(t, set) {
if (isFunction(t)) modp.push({
filter: t,
field: set
});else mod.push({
tuple: t,
field: set
});
return this;
},
-
clean(value) {
clean = value;
return this;
},
-
reflow() {
reflow = true;
return this;
},
-
pulse(pulse, tuples) {
const cur = {},
- out = {};
- let i, n, m, f, t, id; // build lookup table of current tuples
+ out = {};
+ let i, n, m, f, t, id;
+ // build lookup table of current tuples
for (i = 0, n = tuples.length; i < n; ++i) {
cur[tupleid(tuples[i])] = 1;
- } // process individual tuples to remove
+ }
-
+ // process individual tuples to remove
for (i = 0, n = rem.length; i < n; ++i) {
t = rem[i];
cur[tupleid(t)] = -1;
- } // process predicate-based removals
+ }
-
+ // process predicate-based removals
for (i = 0, n = remp.length; i < n; ++i) {
f = remp[i];
tuples.forEach(t => {
if (f(t)) cur[tupleid(t)] = -1;
});
- } // process all add tuples
+ }
-
+ // process all add tuples
for (i = 0, n = add.length; i < n; ++i) {
t = add[i];
id = tupleid(t);
-
if (cur[id]) {
// tuple already resides in dataset
// if flagged for both add and remove, cancel
cur[id] = 1;
} else {
// tuple does not reside in dataset, add
pulse.add.push(ingest$1(add[i]));
}
- } // populate pulse rem list
+ }
-
+ // populate pulse rem list
for (i = 0, n = tuples.length; i < n; ++i) {
t = tuples[i];
if (cur[tupleid(t)] < 0) pulse.rem.push(t);
- } // modify helper method
+ }
-
+ // modify helper method
function modify(t, f, v) {
if (v) {
t[f] = v(t);
} else {
pulse.encode = f;
}
-
if (!reflow) out[tupleid(t)] = t;
- } // process individual tuples to modify
+ }
-
+ // process individual tuples to modify
for (i = 0, n = mod.length; i < n; ++i) {
m = mod[i];
t = m.tuple;
f = m.field;
id = cur[tupleid(t)];
-
if (id > 0) {
modify(t, f, m.value);
pulse.modifies(f);
}
- } // process predicate-based modifications
+ }
-
+ // process predicate-based modifications
for (i = 0, n = modp.length; i < n; ++i) {
m = modp[i];
f = m.filter;
tuples.forEach(t => {
if (f(t) && cur[tupleid(t)] > 0) {
modify(t, m.field, m.value);
}
});
pulse.modifies(m.field);
- } // upon reflow request, populate mod with all non-removed tuples
- // otherwise, populate mod with modified tuples only
+ }
-
+ // upon reflow request, populate mod with all non-removed tuples
+ // otherwise, populate mod with modified tuples only
if (reflow) {
pulse.mod = rem.length || remp.length ? tuples.filter(t => cur[tupleid(t)] > 0) : tuples.slice();
} else {
for (id in out) pulse.mod.push(out[id]);
- } // set pulse garbage collection request
+ }
-
+ // set pulse garbage collection request
if (clean || clean == null && (rem.length || remp.length)) {
pulse.clean(true);
}
-
return pulse;
}
-
};
}
-
const CACHE = '_:mod:_';
+
/**
* Hash that tracks modifications to assigned values.
* Callers *must* use the set method to update values.
*/
-
function Parameters() {
Object.defineProperty(this, CACHE, {
writable: true,
value: {}
});
}
-
Parameters.prototype = {
/**
* Set a parameter value. If the parameter value changes, the parameter
* will be recorded as modified.
* @param {string} name - The parameter name.
@@ -4539,27 +3989,24 @@
* even if the value is unchanged.
* @return {Parameters} - This parameter object.
*/
set(name, index, value, force) {
const o = this,
- v = o[name],
- mod = o[CACHE];
-
+ v = o[name],
+ mod = o[CACHE];
if (index != null && index >= 0) {
if (v[index] !== value || force) {
v[index] = value;
mod[index + ':' + name] = -1;
mod[name] = -1;
}
} else if (v !== value || force) {
o[name] = value;
mod[name] = isArray(value) ? 1 + value.length : -1;
}
-
return o;
},
-
/**
* Tests if one or more parameters has been modified. If invoked with no
* arguments, returns true if any parameter value has changed. If the first
* argument is array, returns trues if any parameter name in the array has
* changed. Otherwise, tests if the given name and optional array index has
@@ -4568,44 +4015,40 @@
* @param {number} [index=undefined] - The parameter array index to test.
* @return {boolean} - Returns true if a queried parameter was modified.
*/
modified(name, index) {
const mod = this[CACHE];
-
if (!arguments.length) {
for (const k in mod) {
if (mod[k]) return true;
}
-
return false;
} else if (isArray(name)) {
for (let k = 0; k < name.length; ++k) {
if (mod[name[k]]) return true;
}
-
return false;
}
-
return index != null && index >= 0 ? index + 1 < mod[name] || !!mod[index + ':' + name] : !!mod[name];
},
-
/**
* Clears the modification records. After calling this method,
* all parameters are considered unmodified.
*/
clear() {
this[CACHE] = {};
return this;
}
-
};
let OP_ID = 0;
const PULSE = 'pulse',
- NO_PARAMS = new Parameters(); // Boolean Flags
+ NO_PARAMS = new Parameters();
+ // Boolean Flags
const SKIP$1$1 = 1,
- MODIFIED = 2;
+ MODIFIED = 2;
+
/**
* An Operator is a processing node in a dataflow graph.
* Each operator stores a value and an optional value update function.
* Operators can accept a hash of named parameters. Parameter values can
* either be direct (JavaScript literals, arrays, objects) or indirect
@@ -4619,45 +4062,39 @@
* @param {object} [params] - The parameters for this operator.
* @param {boolean} [react=true] - Flag indicating if this operator should
* listen for changes to upstream operators included as parameters.
* @see parameters
*/
-
function Operator(init, update, params, react) {
this.id = ++OP_ID;
this.value = init;
this.stamp = -1;
this.rank = -1;
this.qrank = -1;
this.flags = 0;
-
if (update) {
this._update = update;
}
-
if (params) this.parameters(params, react);
}
-
function flag(bit) {
return function (state) {
const f = this.flags;
if (arguments.length === 0) return !!(f & bit);
this.flags = state ? f | bit : f & ~bit;
return this;
};
}
-
Operator.prototype = {
/**
* Returns a list of target operators dependent on this operator.
* If this list does not exist, it is created and then returned.
* @return {UniqueList}
*/
targets() {
return this._targets || (this._targets = UniqueList(id));
},
-
/**
* Sets the value of this operator.
* @param {*} value - the value to set.
* @return {Number} Returns 1 if the operator value has changed
* according to strict equality, returns 0 otherwise.
@@ -4668,29 +4105,26 @@
return 1;
} else {
return 0;
}
},
-
/**
* Indicates that operator evaluation should be skipped on the next pulse.
* This operator will still propagate incoming pulses, but its update function
* will not be invoked. The skip flag is reset after every pulse, so calling
* this method will affect processing of the next pulse only.
*/
skip: flag(SKIP$1$1),
-
/**
* Indicates that this operator's value has been modified on its most recent
* pulse. Normally modification is checked via strict equality; however, in
* some cases it is more efficient to update the internal state of an object.
* In those cases, the modified flag can be used to trigger propagation. Once
* set, the modification flag persists across pulses until unset. The flag can
* be used with the last timestamp to test if a modification is recent.
*/
modified: flag(MODIFIED),
-
/**
* Sets the parameters for this operator. The parameter values are analyzed for
* operator instances. If found, this operator will be added as a dependency
* of the parameterizing operator. Operator values are dynamically marshalled
* from each operator parameter prior to evaluation. If a parameter value is
@@ -4707,34 +4141,30 @@
* @return {Operator[]} - An array of upstream dependencies.
*/
parameters(params, react, initonly) {
react = react !== false;
const argval = this._argval = this._argval || new Parameters(),
- argops = this._argops = this._argops || [],
- deps = [];
+ argops = this._argops = this._argops || [],
+ deps = [];
let name, value, n, i;
-
const add = (name, index, value) => {
if (value instanceof Operator) {
if (value !== this) {
if (react) value.targets().add(this);
deps.push(value);
}
-
argops.push({
op: value,
name: name,
index: index
});
} else {
argval.set(name, index, value);
}
};
-
for (name in params) {
value = params[name];
-
if (name === PULSE) {
array$5(value).forEach(op => {
if (!(op instanceof Operator)) {
error('Pulse parameters must be operator instances.');
} else if (op !== this) {
@@ -4743,82 +4173,69 @@
}
});
this.source = value;
} else if (isArray(value)) {
argval.set(name, -1, Array(n = value.length));
-
for (i = 0; i < n; ++i) add(name, i, value[i]);
} else {
add(name, -1, value);
}
}
-
this.marshall().clear(); // initialize values
-
if (initonly) argops.initonly = true;
return deps;
},
-
/**
* Internal method for marshalling parameter values.
* Visits each operator dependency to pull the latest value.
* @return {Parameters} A Parameters object to pass to the update function.
*/
marshall(stamp) {
const argval = this._argval || NO_PARAMS,
- argops = this._argops;
+ argops = this._argops;
let item, i, op, mod;
-
if (argops) {
const n = argops.length;
-
for (i = 0; i < n; ++i) {
item = argops[i];
op = item.op;
mod = op.modified() && op.stamp === stamp;
argval.set(item.name, item.index, op.value, mod);
}
-
if (argops.initonly) {
for (i = 0; i < n; ++i) {
item = argops[i];
item.op.targets().remove(this);
}
-
this._argops = null;
this._update = null;
}
}
-
return argval;
},
-
/**
* Detach this operator from the dataflow.
* Unregisters listeners on upstream dependencies.
*/
detach() {
const argops = this._argops;
let i, n, item, op;
-
if (argops) {
for (i = 0, n = argops.length; i < n; ++i) {
item = argops[i];
op = item.op;
-
if (op._targets) {
op._targets.remove(this);
}
}
- } // remove references to the source and pulse object,
- // if present, to prevent memory leaks of old data.
+ }
-
+ // remove references to the source and pulse object,
+ // if present, to prevent memory leaks of old data.
this.pulse = null;
this.source = null;
},
-
/**
* Delegate method to perform operator processing.
* Subclasses can override this method to perform custom processing.
* By default, it marshalls parameters and calls the update function
* if that function is defined. If the update function does not
@@ -4828,24 +4245,21 @@
* @return The output pulse or StopPropagation. A falsy return value
* (including undefined) will let the input pulse pass through.
*/
evaluate(pulse) {
const update = this._update;
-
if (update) {
const params = this.marshall(pulse.stamp),
- v = update.call(this, params, pulse);
+ v = update.call(this, params, pulse);
params.clear();
-
if (v !== this.value) {
this.value = v;
} else if (!this.modified()) {
return pulse.StopPropagation;
}
}
},
-
/**
* Run this operator for the current pulse. If this operator has already
* been run at (or after) the pulse timestamp, returns StopPropagation.
* Internally, this method calls {@link evaluate} to perform processing.
* If {@link evaluate} returns a falsy value, the input pulse is returned.
@@ -4854,22 +4268,20 @@
* @return the output pulse for this operator (or StopPropagation)
*/
run(pulse) {
if (pulse.stamp < this.stamp) return pulse.StopPropagation;
let rv;
-
if (this.skip()) {
this.skip(false);
rv = 0;
} else {
rv = this.evaluate(pulse);
}
-
return this.pulse = rv || pulse;
}
-
};
+
/**
* Add an operator to the dataflow graph. This function accepts a
* variety of input argument types. The basic signature supports an
* initial value, update function and parameters. If the first parameter
* is an Operator instance, it will be added directly. If it is a
@@ -4882,185 +4294,160 @@
* @param {object} [params] - The operator parameters.
* @param {boolean} [react=true] - Flag indicating if this operator should
* listen for changes to upstream operators included as parameters.
* @return {Operator} - The added operator.
*/
-
function add$3(init, update, params, react) {
let shift = 1,
- op;
-
+ op;
if (init instanceof Operator) {
op = init;
} else if (init && init.prototype instanceof Operator) {
op = new init();
} else if (isFunction(init)) {
op = new Operator(null, init);
} else {
shift = 0;
op = new Operator(init, update);
}
-
this.rank(op);
-
if (shift) {
react = params;
params = update;
}
-
if (params) this.connect(op, op.parameters(params, react));
this.touch(op);
return op;
}
+
/**
* Connect a target operator as a dependent of source operators.
* If necessary, this method will rerank the target operator and its
* dependents to ensure propagation proceeds in a topologically sorted order.
* @param {Operator} target - The target operator.
* @param {Array<Operator>} - The source operators that should propagate
* to the target operator.
*/
-
-
function connect(target, sources) {
const targetRank = target.rank,
- n = sources.length;
-
+ n = sources.length;
for (let i = 0; i < n; ++i) {
if (targetRank < sources[i].rank) {
this.rerank(target);
return;
}
}
}
-
let STREAM_ID = 0;
+
/**
* Models an event stream.
* @constructor
* @param {function(Object, number): boolean} [filter] - Filter predicate.
* Events pass through when truthy, events are suppressed when falsy.
* @param {function(Object): *} [apply] - Applied to input events to produce
* new event values.
* @param {function(Object)} [receive] - Event callback function to invoke
* upon receipt of a new event. Use to override standard event processing.
*/
-
function EventStream(filter, apply, receive) {
this.id = ++STREAM_ID;
this.value = null;
if (receive) this.receive = receive;
if (filter) this._filter = filter;
if (apply) this._apply = apply;
}
+
/**
* Creates a new event stream instance with the provided
* (optional) filter, apply and receive functions.
* @param {function(Object, number): boolean} [filter] - Filter predicate.
* Events pass through when truthy, events are suppressed when falsy.
* @param {function(Object): *} [apply] - Applied to input events to produce
* new event values.
* @see EventStream
*/
-
-
function stream(filter, apply, receive) {
return new EventStream(filter, apply, receive);
}
-
EventStream.prototype = {
_filter: truthy,
_apply: identity$6,
-
targets() {
return this._targets || (this._targets = UniqueList(id));
},
-
consume(_) {
if (!arguments.length) return !!this._consume;
this._consume = !!_;
return this;
},
-
receive(evt) {
if (this._filter(evt)) {
const val = this.value = this._apply(evt),
- trg = this._targets,
- n = trg ? trg.length : 0;
-
+ trg = this._targets,
+ n = trg ? trg.length : 0;
for (let i = 0; i < n; ++i) trg[i].receive(val);
-
if (this._consume) {
evt.preventDefault();
evt.stopPropagation();
}
}
},
-
filter(filter) {
const s = stream(filter);
this.targets().add(s);
return s;
},
-
apply(apply) {
const s = stream(null, apply);
this.targets().add(s);
return s;
},
-
merge() {
const s = stream();
this.targets().add(s);
-
for (let i = 0, n = arguments.length; i < n; ++i) {
arguments[i].targets().add(s);
}
-
return s;
},
-
throttle(pause) {
let t = -1;
return this.filter(() => {
const now = Date.now();
-
if (now - t > pause) {
t = now;
return 1;
} else {
return 0;
}
});
},
-
debounce(delay) {
const s = stream();
this.targets().add(stream(null, null, debounce(delay, e => {
const df = e.dataflow;
s.receive(e);
if (df && df.run) df.run();
})));
return s;
},
-
between(a, b) {
let active = false;
a.targets().add(stream(null, null, () => active = true));
b.targets().add(stream(null, null, () => active = false));
return this.filter(() => active);
},
-
detach() {
// ensures compatibility with operators (#2753)
// remove references to other streams and filter functions that may
// be bound to subcontexts that need to be garbage collected.
this._filter = truthy;
this._targets = null;
}
-
};
+
/**
* Create a new event stream from an event source.
* @param {object} source - The event source to monitor. The input must
* support the addEventListener method.
* @param {string} type - The event type.
@@ -5068,47 +4455,40 @@
* @param {function(object): *} [apply] - Event application function.
* If provided, this function will be invoked and the result will be
* used as the downstream event value.
* @return {EventStream}
*/
-
function events$1(source, type, filter, apply) {
const df = this,
- s = stream(filter, apply),
- send = function (e) {
- e.dataflow = df;
-
- try {
- s.receive(e);
- } catch (error) {
- df.error(error);
- } finally {
- df.run();
- }
- };
-
+ s = stream(filter, apply),
+ send = function (e) {
+ e.dataflow = df;
+ try {
+ s.receive(e);
+ } catch (error) {
+ df.error(error);
+ } finally {
+ df.run();
+ }
+ };
let sources;
-
if (typeof source === 'string' && typeof document !== 'undefined') {
sources = document.querySelectorAll(source);
} else {
sources = array$5(source);
}
-
const n = sources.length;
-
for (let i = 0; i < n; ++i) {
sources[i].addEventListener(type, send);
}
-
return s;
}
-
function parse$5(data, format) {
const locale = this.locale();
return read(data, format, locale.timeParse, locale.utcParse);
}
+
/**
* Ingests new data into the dataflow. First parses the data using the
* vega-loader read method, then pulses a changeset to the target operator.
* @param {Operator} target - The Operator to target with ingested data,
* typically a Collect transform instance.
@@ -5116,16 +4496,15 @@
* be a string or an object. For CSV, TSV, etc should be a string.
* @param {object} format - The data format description for parsing
* loaded data. This object is passed to the vega-loader read method.
* @returns {Dataflow}
*/
-
-
function ingest(target, data, format) {
data = this.parse(data, format);
return this.pulse(target, this.changeset().insert(data));
}
+
/**
* Request data from an external source, parse it, and return a Promise.
* @param {string} url - The URL from which to load the data. This string
* is passed to the vega-loader load method.
* @param {object} [format] - The data format description for parsing
@@ -5133,68 +4512,59 @@
* @return {Promise} A Promise that resolves upon completion of the request.
* The resolved object contains the following properties:
* - data: an array of parsed data (or null upon error)
* - status: a code for success (0), load fail (-1), or parse fail (-2)
*/
-
-
async function request(url, format) {
const df = this;
let status = 0,
- data;
-
+ data;
try {
data = await df.loader().load(url, {
context: 'dataflow',
response: responseType(format && format.type)
});
-
try {
data = df.parse(data, format);
} catch (err) {
status = -2;
df.warn('Data ingestion failed', url, err);
}
} catch (err) {
status = -1;
df.warn('Loading failed', url, err);
}
-
return {
data,
status
};
}
-
async function preload(target, url, format) {
const df = this,
- pending = df._pending || loadPending(df);
+ pending = df._pending || loadPending(df);
pending.requests += 1;
const res = await df.request(url, format);
df.pulse(target, df.changeset().remove(truthy).insert(res.data || []));
pending.done();
return res;
}
-
function loadPending(df) {
let accept;
const pending = new Promise(a => accept = a);
pending.requests = 0;
-
pending.done = () => {
if (--pending.requests === 0) {
df._pending = null;
accept(df);
}
};
-
return df._pending = pending;
}
-
const SKIP$2 = {
skip: true
};
+
/**
* Perform operator updates in response to events. Applies an
* update function to compute a new operator value. If the update function
* returns a {@link ChangeSet}, the operator will be pulsed with those tuple
* changes. Otherwise, the operator value will be updated to the return value.
@@ -5217,125 +4587,109 @@
* be skipped: it will not be evaluated, but its dependents will be.
* @param {boolean} [options.force] - If true, the operator will
* be re-evaluated even if its value has not changed.
* @return {Dataflow}
*/
-
function on(source, target, update, params, options) {
const fn = source instanceof Operator ? onOperator : onStream;
fn(this, source, target, update, params, options);
return this;
}
-
function onStream(df, stream, target, update, params, options) {
const opt = extend$1({}, options, SKIP$2);
let func, op;
- if (!isFunction(target)) target = constant$4(target);
-
+ if (!isFunction(target)) target = constant$5(target);
if (update === undefined) {
func = e => df.touch(target(e));
} else if (isFunction(update)) {
op = new Operator(null, update, params, false);
-
func = e => {
op.evaluate(e);
const t = target(e),
- v = op.value;
+ v = op.value;
isChangeSet(v) ? df.pulse(t, v, options) : df.update(t, v, opt);
};
} else {
func = e => df.update(target(e), update, opt);
}
-
stream.apply(func);
}
-
function onOperator(df, source, target, update, params, options) {
if (update === undefined) {
source.targets().add(target);
} else {
const opt = options || {},
- op = new Operator(null, updater(target, update), params, false);
+ op = new Operator(null, updater(target, update), params, false);
op.modified(opt.force);
op.rank = source.rank; // immediately follow source
-
source.targets().add(op); // add dependency
if (target) {
op.skip(true); // skip first invocation
-
op.value = target.value; // initialize value
-
op.targets().add(target); // chain dependencies
-
df.connect(target, [op]); // rerank as needed, #1672
}
}
}
-
function updater(target, update) {
- update = isFunction(update) ? update : constant$4(update);
+ update = isFunction(update) ? update : constant$5(update);
return target ? function (_, pulse) {
const value = update(_, pulse);
-
if (!target.skip()) {
target.skip(value !== this.value).value = value;
}
-
return value;
} : update;
}
+
/**
* Assigns a rank to an operator. Ranks are assigned in increasing order
* by incrementing an internal rank counter.
* @param {Operator} op - The operator to assign a rank.
*/
-
-
function rank(op) {
op.rank = ++this._rank;
}
+
/**
* Re-ranks an operator and all downstream target dependencies. This
* is necessary when upstream dependencies of higher rank are added to
* a target operator.
* @param {Operator} op - The operator to re-rank.
*/
-
-
function rerank(op) {
const queue = [op];
let cur, list, i;
-
while (queue.length) {
this.rank(cur = queue.pop());
-
if (list = cur._targets) {
for (i = list.length; --i >= 0;) {
queue.push(cur = list[i]);
if (cur === op) error('Cycle detected in dataflow graph.');
}
}
}
}
+
/**
* Sentinel value indicating pulse propagation should stop.
*/
+ const StopPropagation = {};
-
- const StopPropagation = {}; // Pulse visit type flags
-
+ // Pulse visit type flags
const ADD = 1 << 0,
- REM = 1 << 1,
- MOD$1 = 1 << 2,
- ADD_REM = ADD | REM,
- ADD_MOD = ADD | MOD$1,
- ALL = ADD | REM | MOD$1,
- REFLOW = 1 << 3,
- SOURCE = 1 << 4,
- NO_SOURCE = 1 << 5,
- NO_FIELDS = 1 << 6;
+ REM = 1 << 1,
+ MOD$1 = 1 << 2,
+ ADD_REM = ADD | REM,
+ ADD_MOD = ADD | MOD$1,
+ ALL = ADD | REM | MOD$1,
+ REFLOW = 1 << 3,
+ SOURCE = 1 << 4,
+ NO_SOURCE = 1 << 5,
+ NO_FIELDS = 1 << 6;
+
/**
* A Pulse enables inter-operator communication during a run of the
* dataflow graph. In addition to the current timestamp, a pulse may also
* contain a change-set of added, removed or modified data tuples, as well as
* a pointer to a full backing data source. Tuple change sets may not
@@ -5355,99 +4709,83 @@
* @param {string} [encode] - An optional encoding set name, which is then
* accessible as Pulse.encode. Operators can respond to (or ignore) this
* setting as appropriate. This parameter can be used in conjunction with
* the Encode transform in the vega-encode module.
*/
-
function Pulse(dataflow, stamp, encode) {
this.dataflow = dataflow;
this.stamp = stamp == null ? -1 : stamp;
this.add = [];
this.rem = [];
this.mod = [];
this.fields = null;
this.encode = encode || null;
}
-
function materialize(data, filter) {
const out = [];
visitArray(data, filter, _ => out.push(_));
return out;
}
-
function filter$1(pulse, flags) {
const map = {};
pulse.visit(flags, t => {
map[tupleid(t)] = 1;
});
return t => map[tupleid(t)] ? null : t;
}
-
function addFilter(a, b) {
return a ? (t, i) => a(t, i) && b(t, i) : b;
}
-
Pulse.prototype = {
/**
* Sentinel value indicating pulse propagation should stop.
*/
StopPropagation,
-
/**
* Boolean flag indicating ADD (added) tuples.
*/
ADD,
-
/**
* Boolean flag indicating REM (removed) tuples.
*/
REM,
-
/**
* Boolean flag indicating MOD (modified) tuples.
*/
MOD: MOD$1,
-
/**
* Boolean flag indicating ADD (added) and REM (removed) tuples.
*/
ADD_REM,
-
/**
* Boolean flag indicating ADD (added) and MOD (modified) tuples.
*/
ADD_MOD,
-
/**
* Boolean flag indicating ADD, REM and MOD tuples.
*/
ALL,
-
/**
* Boolean flag indicating all tuples in a data source
* except for the ADD, REM and MOD tuples.
*/
REFLOW,
-
/**
* Boolean flag indicating a 'pass-through' to a
* backing data source, ignoring ADD, REM and MOD tuples.
*/
SOURCE,
-
/**
* Boolean flag indicating that source data should be
* suppressed when creating a forked pulse.
*/
NO_SOURCE,
-
/**
* Boolean flag indicating that field modifications should be
* suppressed when creating a forked pulse.
*/
NO_FIELDS,
-
/**
* Creates a new pulse based on the values of this pulse.
* The dataflow, time stamp and field modification values are copied over.
* By default, new empty ADD, REM and MOD arrays are created.
* @param {number} flags - Integer of boolean flags indicating which (if any)
@@ -5458,11 +4796,10 @@
* @see init
*/
fork(flags) {
return new Pulse(this.dataflow).init(this, flags);
},
-
/**
* Creates a copy of this pulse with new materialized array
* instances for the ADD, REM, MOD, and SOURCE arrays.
* The dataflow, time stamp and field modification values are copied over.
* @return {Pulse} - The cloned pulse instance.
@@ -5474,11 +4811,10 @@
p.rem = p.rem.slice();
p.mod = p.mod.slice();
if (p.source) p.source = p.source.slice();
return p.materialize(ALL | SOURCE);
},
-
/**
* Returns a pulse that adds all tuples from a backing source. This is
* useful for cases where operators are added to a dataflow after an
* upstream data pipeline has already been processed, ensuring that
* new operators can observe all tuples within a stream.
@@ -5489,22 +4825,19 @@
*/
addAll() {
let p = this;
const reuse = !p.source || p.add === p.rem // special case for indexed set (e.g., crossfilter)
|| !p.rem.length && p.source.length === p.add.length;
-
if (reuse) {
return p;
} else {
p = new Pulse(this.dataflow).init(this);
p.add = p.source;
p.rem = []; // new operators can ignore rem #2769
-
return p;
}
},
-
/**
* Initialize this pulse based on the values of another pulse. This method
* is used internally by {@link fork} to initialize a new forked tuple.
* The dataflow, time stamp and field modification values are copied over.
* By default, new empty ADD, REM and MOD arrays are created.
@@ -5518,59 +4851,51 @@
*/
init(src, flags) {
const p = this;
p.stamp = src.stamp;
p.encode = src.encode;
-
if (src.fields && !(flags & NO_FIELDS)) {
p.fields = src.fields;
}
-
if (flags & ADD) {
p.addF = src.addF;
p.add = src.add;
} else {
p.addF = null;
p.add = [];
}
-
if (flags & REM) {
p.remF = src.remF;
p.rem = src.rem;
} else {
p.remF = null;
p.rem = [];
}
-
if (flags & MOD$1) {
p.modF = src.modF;
p.mod = src.mod;
} else {
p.modF = null;
p.mod = [];
}
-
if (flags & NO_SOURCE) {
p.srcF = null;
p.source = null;
} else {
p.srcF = src.srcF;
p.source = src.source;
if (src.cleans) p.cleans = src.cleans;
}
-
return p;
},
-
/**
* Schedules a function to run after pulse propagation completes.
* @param {function} func - The function to run.
*/
runAfter(func) {
this.dataflow.runAfter(func);
},
-
/**
* Indicates if tuples have been added, removed or modified.
* @param {number} [flags] - The tuple types (ADD, REM or MOD) to query.
* Defaults to ALL, returning true if any tuple type has changed.
* @return {boolean} - Returns true if one or more queried tuple types have
@@ -5578,31 +4903,27 @@
*/
changed(flags) {
const f = flags || ALL;
return f & ADD && this.add.length || f & REM && this.rem.length || f & MOD$1 && this.mod.length;
},
-
/**
* Forces a "reflow" of tuple values, such that all tuples in the backing
* source are added to the MOD set, unless already present in the ADD set.
* @param {boolean} [fork=false] - If true, returns a forked copy of this
* pulse, and invokes reflow on that derived pulse.
* @return {Pulse} - The reflowed pulse instance.
*/
reflow(fork) {
if (fork) return this.fork(ALL).reflow();
const len = this.add.length,
- src = this.source && this.source.length;
-
+ src = this.source && this.source.length;
if (src && src !== len) {
this.mod = this.source;
if (len) this.filter(MOD$1, filter$1(this, ADD));
}
-
return this;
},
-
/**
* Get/set metadata to pulse requesting garbage collection
* to reclaim currently unused resources.
*/
clean(value) {
@@ -5611,29 +4932,25 @@
return this;
} else {
return this.cleans;
}
},
-
/**
* Marks one or more data field names as modified to assist dependency
* tracking and incremental processing by transform operators.
* @param {string|Array<string>} _ - The field(s) to mark as modified.
* @return {Pulse} - This pulse instance.
*/
modifies(_) {
const hash = this.fields || (this.fields = {});
-
if (isArray(_)) {
_.forEach(f => hash[f] = true);
} else {
hash[_] = true;
}
-
return this;
},
-
/**
* Checks if one or more data fields have been modified during this pulse
* propagation timestamp.
* @param {string|Array<string>} _ - The field(s) to check for modified.
* @param {boolean} nomod - If true, will check the modified flag even if
@@ -5643,11 +4960,10 @@
*/
modified(_, nomod) {
const fields = this.fields;
return !((nomod || this.mod.length) && fields) ? false : !arguments.length ? !!fields : isArray(_) ? _.some(f => fields[f]) : fields[_];
},
-
/**
* Adds a filter function to one more tuple sets. Filters are applied to
* backing tuple arrays, to determine the actual set of tuples considered
* added, removed or modified. They can be used to delay materialization of
* a tuple set in order to avoid expensive array copies. In addition, the
@@ -5667,81 +4983,70 @@
if (flags & REM) p.remF = addFilter(p.remF, filter);
if (flags & MOD$1) p.modF = addFilter(p.modF, filter);
if (flags & SOURCE) p.srcF = addFilter(p.srcF, filter);
return p;
},
-
/**
* Materialize one or more tuple sets in this pulse. If the tuple set(s) have
* a registered filter function, it will be applied and the tuple set(s) will
* be replaced with materialized tuple arrays.
* @param {number} flags - Flags indicating the tuple set(s) to materialize.
* @return {Pulse} - Returns this pulse instance.
*/
materialize(flags) {
flags = flags || ALL;
const p = this;
-
if (flags & ADD && p.addF) {
p.add = materialize(p.add, p.addF);
p.addF = null;
}
-
if (flags & REM && p.remF) {
p.rem = materialize(p.rem, p.remF);
p.remF = null;
}
-
if (flags & MOD$1 && p.modF) {
p.mod = materialize(p.mod, p.modF);
p.modF = null;
}
-
if (flags & SOURCE && p.srcF) {
p.source = p.source.filter(p.srcF);
p.srcF = null;
}
-
return p;
},
-
/**
* Visit one or more tuple sets in this pulse.
* @param {number} flags - Flags indicating the tuple set(s) to visit.
* Legal values are ADD, REM, MOD and SOURCE (if a backing data source
* has been set).
* @param {function(object):*} - Visitor function invoked per-tuple.
* @return {Pulse} - Returns this pulse instance.
*/
visit(flags, visitor) {
const p = this,
- v = visitor;
-
+ v = visitor;
if (flags & SOURCE) {
visitArray(p.source, p.srcF, v);
return p;
}
-
if (flags & ADD) visitArray(p.add, p.addF, v);
if (flags & REM) visitArray(p.rem, p.remF, v);
if (flags & MOD$1) visitArray(p.mod, p.modF, v);
const src = p.source;
-
if (flags & REFLOW && src) {
const sum = p.add.length + p.mod.length;
if (sum === src.length) ;else if (sum) {
visitArray(src, filter$1(p, ADD_MOD), v);
} else {
// if no add/rem/mod tuples, visit source
visitArray(src, p.srcF, v);
}
}
-
return p;
}
-
};
+
/**
* Represents a set of multiple pulses. Used as input for operators
* that accept multiple pulses at a time. Contained pulses are
* accessible via the public "pulses" array property. This pulse doe
* not carry added, removed or modified tuples directly. However,
@@ -5750,81 +5055,66 @@
* @constructor
* @param {Dataflow} dataflow - The backing dataflow instance.
* @param {number} stamp - The timestamp.
* @param {Array<Pulse>} pulses - The sub-pulses for this multi-pulse.
*/
-
function MultiPulse(dataflow, stamp, pulses, encode) {
const p = this;
let c = 0;
this.dataflow = dataflow;
this.stamp = stamp;
this.fields = null;
this.encode = encode || null;
this.pulses = pulses;
-
for (const pulse of pulses) {
if (pulse.stamp !== stamp) continue;
-
if (pulse.fields) {
const hash = p.fields || (p.fields = {});
-
for (const f in pulse.fields) {
hash[f] = 1;
}
}
-
if (pulse.changed(p.ADD)) c |= p.ADD;
if (pulse.changed(p.REM)) c |= p.REM;
if (pulse.changed(p.MOD)) c |= p.MOD;
}
-
this.changes = c;
}
-
inherits(MultiPulse, Pulse, {
/**
* Creates a new pulse based on the values of this pulse.
* The dataflow, time stamp and field modification values are copied over.
* @return {Pulse}
*/
fork(flags) {
const p = new Pulse(this.dataflow).init(this, flags & this.NO_FIELDS);
-
if (flags !== undefined) {
if (flags & p.ADD) this.visit(p.ADD, t => p.add.push(t));
if (flags & p.REM) this.visit(p.REM, t => p.rem.push(t));
if (flags & p.MOD) this.visit(p.MOD, t => p.mod.push(t));
}
-
return p;
},
-
changed(flags) {
return this.changes & flags;
},
-
modified(_) {
const p = this,
- fields = p.fields;
+ fields = p.fields;
return !(fields && p.changes & p.MOD) ? 0 : isArray(_) ? _.some(f => fields[f]) : fields[_];
},
-
filter() {
error('MultiPulse does not support filtering.');
},
-
materialize() {
error('MultiPulse does not support materialization.');
},
-
visit(flags, visitor) {
const p = this,
- pulses = p.pulses,
- n = pulses.length;
+ pulses = p.pulses,
+ n = pulses.length;
let i = 0;
-
if (flags & p.SOURCE) {
for (; i < n; ++i) {
pulses[i].visit(flags, visitor);
}
} else {
@@ -5832,15 +5122,14 @@
if (pulses[i].stamp === p.stamp) {
pulses[i].visit(flags, visitor);
}
}
}
-
return p;
}
-
});
+
/* eslint-disable require-atomic-updates */
/**
* Evaluates the dataflow and returns a Promise that resolves when pulse
* propagation completes. This method will increment the current timestamp
@@ -5859,100 +5148,99 @@
* after dataflow evaluation completes. The callback will be invoked
* after those registered via {@link runAfter}.
* @return {Promise} - A promise that resolves to this dataflow after
* evaluation completes.
*/
-
async function evaluate(encode, prerun, postrun) {
const df = this,
- async = []; // if the pulse value is set, this is a re-entrant call
+ async = [];
- if (df._pulse) return reentrant(df); // wait for pending datasets to load
+ // if the pulse value is set, this is a re-entrant call
+ if (df._pulse) return reentrant(df);
- if (df._pending) await df._pending; // invoke prerun function, if provided
+ // wait for pending datasets to load
+ if (df._pending) await df._pending;
- if (prerun) await asyncCallback(df, prerun); // exit early if there are no updates
+ // invoke prerun function, if provided
+ if (prerun) await asyncCallback(df, prerun);
+ // exit early if there are no updates
if (!df._touched.length) {
df.debug('Dataflow invoked, but nothing to do.');
return df;
- } // increment timestamp clock
+ }
+ // increment timestamp clock
+ const stamp = ++df._clock;
- const stamp = ++df._clock; // set the current pulse
+ // set the current pulse
+ df._pulse = new Pulse(df, stamp, encode);
- df._pulse = new Pulse(df, stamp, encode); // initialize priority queue, reset touched operators
-
+ // initialize priority queue, reset touched operators
df._touched.forEach(op => df._enqueue(op, true));
-
df._touched = UniqueList(id);
let count = 0,
- op,
- next,
- error;
-
+ op,
+ next,
+ error;
try {
while (df._heap.size() > 0) {
// dequeue operator with highest priority
- op = df._heap.pop(); // re-queue if rank changed
+ op = df._heap.pop();
+ // re-queue if rank changed
if (op.rank !== op.qrank) {
df._enqueue(op, true);
-
continue;
- } // otherwise, evaluate the operator
+ }
-
+ // otherwise, evaluate the operator
next = op.run(df._getPulse(op, encode));
-
if (next.then) {
// await if operator returns a promise directly
next = await next;
} else if (next.async) {
// queue parallel asynchronous execution
async.push(next.async);
next = StopPropagation;
- } // propagate evaluation, enqueue dependent operators
+ }
-
+ // propagate evaluation, enqueue dependent operators
if (next !== StopPropagation) {
if (op._targets) op._targets.forEach(op => df._enqueue(op));
- } // increment visit counter
+ }
-
+ // increment visit counter
++count;
}
} catch (err) {
df._heap.clear();
-
error = err;
- } // reset pulse map
+ }
-
+ // reset pulse map
df._input = {};
df._pulse = null;
- df.debug("Pulse ".concat(stamp, ": ").concat(count, " operators"));
-
+ df.debug(`Pulse ${stamp}: ${count} operators`);
if (error) {
df._postrun = [];
df.error(error);
- } // invoke callbacks queued via runAfter
+ }
-
+ // invoke callbacks queued via runAfter
if (df._postrun.length) {
const pr = df._postrun.sort((a, b) => b.priority - a.priority);
-
df._postrun = [];
-
for (let i = 0; i < pr.length; ++i) {
await asyncCallback(df, pr[i].callback);
}
- } // invoke postrun function, if provided
+ }
+ // invoke postrun function, if provided
+ if (postrun) await asyncCallback(df, postrun);
- if (postrun) await asyncCallback(df, postrun); // handle non-blocking asynchronous callbacks
-
+ // handle non-blocking asynchronous callbacks
if (async.length) {
Promise.all(async).then(cb => df.runAsync(null, () => {
cb.forEach(f => {
try {
f(df);
@@ -5960,13 +5248,13 @@
df.error(err);
}
});
}));
}
-
return df;
}
+
/**
* Queues dataflow evaluation to run once any other queued evaluations have
* completed and returns a Promise that resolves when the queued pulse
* propagation completes. If provided, a callback function will be invoked
* immediately before evaluation commences. This method will ensure a
@@ -5982,22 +5270,20 @@
* after dataflow evaluation completes. The callback will be invoked
* after those registered via {@link runAfter}.
* @return {Promise} - A promise that resolves to this dataflow after
* evaluation completes.
*/
-
-
async function runAsync(encode, prerun, postrun) {
// await previously queued functions
- while (this._running) await this._running; // run dataflow, manage running promise
+ while (this._running) await this._running;
-
+ // run dataflow, manage running promise
const clear = () => this._running = null;
-
(this._running = this.evaluate(encode, prerun, postrun)).then(clear, clear);
return this._running;
}
+
/**
* Requests dataflow evaluation and the immediately returns this dataflow
* instance. If there are pending data loading or other asynchronous
* operations, the dataflow will evaluate asynchronously after this method
* has been invoked. To track when dataflow evaluation completes, use the
@@ -6013,15 +5299,14 @@
* @param {function} [postrun] - An optional callback function to invoke
* after dataflow evaluation completes. The callback will be invoked
* after those registered via {@link runAfter}.
* @return {Dataflow} - This dataflow instance.
*/
-
-
function run(encode, prerun, postrun) {
return this._pulse ? reentrant(this) : (this.evaluate(encode, prerun, postrun), this);
}
+
/**
* Schedules a callback function to be invoked after the current pulse
* propagation completes. If no propagation is currently occurring,
* the function is invoked immediately. Callbacks scheduled via runAfter
* are invoked immediately upon completion of the current cycle, before
@@ -6038,12 +5323,10 @@
* currently occurring.
* @param {number} [priority] - A priority value used to sort registered
* callbacks to determine execution order. This argument is intended
* for internal Vega use only.
*/
-
-
function runAfter(callback, enqueue, priority) {
if (this._pulse || enqueue) {
// pulse propagation is currently running, queue to run after
this._postrun.push({
priority: priority || 0,
@@ -6056,19 +5339,19 @@
} catch (err) {
this.error(err);
}
}
}
+
/**
* Raise an error for re-entrant dataflow evaluation.
*/
-
-
function reentrant(df) {
df.error('Dataflow already running. Use runAsync() to chain invocations.');
return df;
}
+
/**
* Enqueue an operator into the priority queue for evaluation. The operator
* will be enqueued if it has no registered pulse for the current cycle, or if
* the force argument is true. Upon enqueue, this method also sets the
* operator's qrank to the current rank value.
@@ -6076,22 +5359,19 @@
* @param {boolean} [force] - A flag indicating if the operator should be
* forceably added to the queue, even if it has already been previously
* enqueued during the current pulse propagation. This is useful when the
* dataflow graph is dynamically modified and the operator rank changes.
*/
-
-
function enqueue(op, force) {
const q = op.stamp < this._clock;
if (q) op.stamp = this._clock;
-
if (q || force) {
op.qrank = op.rank;
-
this._heap.push(op);
}
}
+
/**
* Provide a correct pulse for evaluating an operator. If the operator has an
* explicit source operator, we will try to pull the pulse(s) from it.
* If there is an array of source operators, we build a multi-pulse.
* Otherwise, we return a current pulse with correct source data.
@@ -6100,36 +5380,30 @@
* Else we use the pulse from the pulse map, but copy the source tuple array.
* @param {Operator} op - The operator for which to get an input pulse.
* @param {string} [encode] - An (optional) encoding set name with which to
* annotate the returned pulse. See {@link run} for more information.
*/
-
-
function getPulse(op, encode) {
const s = op.source,
- stamp = this._clock;
+ stamp = this._clock;
return s && isArray(s) ? new MultiPulse(this, stamp, s.map(_ => _.pulse), encode) : this._input[op.id] || singlePulse(this._pulse, s && s.pulse);
}
-
function singlePulse(p, s) {
if (s && s.stamp === p.stamp) {
return s;
}
-
p = p.fork();
-
if (s && s !== StopPropagation) {
p.source = s.source;
}
-
return p;
}
-
const NO_OPT = {
skip: false,
force: false
};
+
/**
* Touches an operator, scheduling it to be evaluated. If invoked outside of
* a pulse propagation, the operator will be evaluated the next time this
* dataflow is run. If invoked in the midst of pulse propagation, the operator
* will be queued for evaluation if and only if the operator has not yet been
@@ -6138,25 +5412,23 @@
* @param {object} [options] - Additional options hash.
* @param {boolean} [options.skip] - If true, the operator will
* be skipped: it will not be evaluated, but its dependents will be.
* @return {Dataflow}
*/
-
function touch(op, options) {
const opt = options || NO_OPT;
-
if (this._pulse) {
// if in midst of propagation, add to priority queue
this._enqueue(op);
} else {
// otherwise, queue for next propagation
this._touched.add(op);
}
-
if (opt.skip) op.skip(true);
return this;
}
+
/**
* Updates the value of the given operator.
* @param {Operator} op - The operator to update.
* @param {*} value - The value to set.
* @param {object} [options] - Additional options hash.
@@ -6164,21 +5436,18 @@
* be re-evaluated even if its value has not changed.
* @param {boolean} [options.skip] - If true, the operator will
* be skipped: it will not be evaluated, but its dependents will be.
* @return {Dataflow}
*/
-
-
function update$6(op, value, options) {
const opt = options || NO_OPT;
-
if (op.set(value) || opt.force) {
this.touch(op, opt);
}
-
return this;
}
+
/**
* Pulses an operator with a changeset of tuples. If invoked outside of
* a pulse propagation, the pulse will be applied the next time this
* dataflow is run. If invoked in the midst of pulse propagation, the pulse
* will be added to the set of active pulses and will be applied if and
@@ -6189,21 +5458,18 @@
* @param {object} [options] - Additional options hash.
* @param {boolean} [options.skip] - If true, the operator will
* be skipped: it will not be evaluated, but its dependents will be.
* @return {Dataflow}
*/
-
-
function pulse(op, changeset, options) {
this.touch(op, options || NO_OPT);
const p = new Pulse(this, this._clock + (this._pulse ? 0 : 1)),
- t = op.pulse && op.pulse.source || [];
+ t = op.pulse && op.pulse.source || [];
p.target = op;
this._input[op.id] = changeset.pulse(p, t);
return this;
}
-
function Heap(cmp) {
let nodes = [];
return {
clear: () => nodes = [],
size: () => nodes.length,
@@ -6213,97 +5479,81 @@
return siftdown(nodes, 0, nodes.length - 1, cmp);
},
pop: () => {
const last = nodes.pop();
let item;
-
if (nodes.length) {
item = nodes[0];
nodes[0] = last;
siftup(nodes, 0, cmp);
} else {
item = last;
}
-
return item;
}
};
}
-
function siftdown(array, start, idx, cmp) {
let parent, pidx;
const item = array[idx];
-
while (idx > start) {
pidx = idx - 1 >> 1;
parent = array[pidx];
-
if (cmp(item, parent) < 0) {
array[idx] = parent;
idx = pidx;
continue;
}
-
break;
}
-
return array[idx] = item;
}
-
function siftup(array, idx, cmp) {
const start = idx,
- end = array.length,
- item = array[idx];
+ end = array.length,
+ item = array[idx];
let cidx = (idx << 1) + 1,
- ridx;
-
+ ridx;
while (cidx < end) {
ridx = cidx + 1;
-
if (ridx < end && cmp(array[cidx], array[ridx]) >= 0) {
cidx = ridx;
}
-
array[idx] = array[cidx];
idx = cidx;
cidx = (idx << 1) + 1;
}
-
array[idx] = item;
return siftdown(array, start, idx, cmp);
}
+
/**
* A dataflow graph for reactive processing of data streams.
* @constructor
*/
-
-
function Dataflow() {
this.logger(logger());
this.logLevel(Error$1);
this._clock = 0;
this._rank = 0;
this._locale = defaultLocale();
-
try {
this._loader = loader();
- } catch (e) {// do nothing if loader module is unavailable
+ } catch (e) {
+ // do nothing if loader module is unavailable
}
-
this._touched = UniqueList(id);
this._input = {};
this._pulse = null;
this._heap = Heap((a, b) => a.qrank - b.qrank);
this._postrun = [];
}
-
function logMethod(method) {
return function () {
return this._log[method].apply(this, arguments);
};
}
-
Dataflow.prototype = {
/**
* The current timestamp of this dataflow. This value reflects the
* timestamp of the previous dataflow run. The dataflow is initialized
* with a stamp value of 0. The initial run of the dataflow will have
@@ -6312,11 +5562,10 @@
* @return {number} - The current timestamp value.
*/
stamp() {
return this._clock;
},
-
/**
* Gets or sets the loader instance to use for data file loading. A
* loader object must provide a "load" method for loading files and a
* "sanitize" method for checking URL/filename validity. Both methods
* should accept a URI and options hash as arguments, and return a Promise
@@ -6333,11 +5582,10 @@
return this;
} else {
return this._loader;
}
},
-
/**
* Gets or sets the locale instance to use for formatting and parsing
* string values. The locale object should be provided by the
* vega-format library, and include methods such as format, timeFormat,
* utcFormat, timeParse, and utcParse.
@@ -6351,11 +5599,10 @@
return this;
} else {
return this._locale;
}
},
-
/**
* Get or set the logger instance used to log messages. If no arguments are
* provided, returns the current logger instance. Otherwise, sets the logger
* and return this Dataflow instance. Provided loggers must support the full
* API of logger objects generated by the vega-util logger method. Note that
@@ -6368,47 +5615,41 @@
return this;
} else {
return this._log;
}
},
-
/**
* Logs an error message. By default, logged messages are written to console
* output. The message will only be logged if the current log level is high
* enough to permit error messages.
*/
error: logMethod('error'),
-
/**
* Logs a warning message. By default, logged messages are written to console
* output. The message will only be logged if the current log level is high
* enough to permit warning messages.
*/
warn: logMethod('warn'),
-
/**
* Logs a information message. By default, logged messages are written to
* console output. The message will only be logged if the current log level is
* high enough to permit information messages.
*/
info: logMethod('info'),
-
/**
* Logs a debug message. By default, logged messages are written to console
* output. The message will only be logged if the current log level is high
* enough to permit debug messages.
*/
debug: logMethod('debug'),
-
/**
* Get or set the current log level. If an argument is provided, it
* will be used as the new log level.
* @param {number} [level] - Should be one of None, Warn, Info
* @return {number} - The current log level.
*/
logLevel: logMethod('level'),
-
/**
* Empty entry threshold for garbage cleaning. Map data structures will
* perform cleaning once the number of empty entries exceeds this value.
*/
cleanThreshold: 1e4,
@@ -6436,23 +5677,22 @@
runAsync,
runAfter,
_enqueue: enqueue,
_getPulse: getPulse
};
+
/**
* Abstract class for operators that process data tuples.
* Subclasses must provide a {@link transform} method for operator processing.
* @constructor
* @param {*} [init] - The initial value for this operator.
* @param {object} [params] - The parameters for this operator.
* @param {Operator} [source] - The operator from which to receive pulses.
*/
-
function Transform(init, params) {
Operator.call(this, init, null, params);
}
-
inherits(Transform, Operator, {
/**
* Overrides {@link Operator.evaluate} for transform operators.
* Internally, this method calls {@link evaluate} to perform processing.
* If {@link evaluate} returns a falsy value, the input pulse is returned.
@@ -6461,60 +5701,51 @@
* @return the output pulse for this operator (or StopPropagation)
*/
run(pulse) {
if (pulse.stamp < this.stamp) return pulse.StopPropagation;
let rv;
-
if (this.skip()) {
this.skip(false);
} else {
rv = this.evaluate(pulse);
}
-
rv = rv || pulse;
-
if (rv.then) {
rv = rv.then(_ => this.pulse = _);
} else if (rv !== pulse.StopPropagation) {
this.pulse = rv;
}
-
return rv;
},
-
/**
* Overrides {@link Operator.evaluate} for transform operators.
* Marshalls parameter values and then invokes {@link transform}.
* @param {Pulse} pulse - the current dataflow pulse.
* @return {Pulse} The output pulse (or StopPropagation). A falsy return
value (including undefined) will let the input pulse pass through.
*/
evaluate(pulse) {
const params = this.marshall(pulse.stamp),
- out = this.transform(params, pulse);
+ out = this.transform(params, pulse);
params.clear();
return out;
},
-
/**
* Process incoming pulses.
* Subclasses should override this method to implement transforms.
* @param {Parameters} _ - The operator parameter values.
* @param {Pulse} pulse - The current dataflow pulse.
* @return {Pulse} The output pulse (or StopPropagation). A falsy return
* value (including undefined) will let the input pulse pass through.
*/
transform() {}
-
});
const transforms = {};
-
function definition$1(type) {
const t = transform$2(type);
return t && t.Definition || null;
}
-
function transform$2(type) {
type = type && type.toLowerCase();
return has$1(transforms, type) ? transforms[type] : null;
}
@@ -6525,217 +5756,188 @@
yield value;
}
}
} else {
let index = -1;
-
for (let value of values) {
value = valueof(value, ++index, values);
-
if (value != null && value !== '' && (value = +value) >= value) {
yield value;
}
}
}
}
-
function quantiles(array, p, f) {
- const values = Float64Array.from(numbers$1(array, f)); // don't depend on return value from typed array sort call
- // protects against undefined sort results in Safari (vega/vega-lite#4964)
+ const values = Float64Array.from(numbers$1(array, f));
+ // don't depend on return value from typed array sort call
+ // protects against undefined sort results in Safari (vega/vega-lite#4964)
values.sort(ascending$1);
return p.map(_ => quantileSorted(values, _));
}
-
function quartiles(array, f) {
return quantiles(array, [0.25, 0.50, 0.75], f);
- } // Theory, Practice, and Visualization. Wiley.
+ }
-
+ // Scott, D. W. (1992) Multivariate Density Estimation:
+ // Theory, Practice, and Visualization. Wiley.
function estimateBandwidth(array, f) {
const n = array.length,
- d = deviation(array, f),
- q = quartiles(array, f),
- h = (q[2] - q[0]) / 1.34,
- v = Math.min(d, h) || d || Math.abs(q[0]) || 1;
+ d = deviation(array, f),
+ q = quartiles(array, f),
+ h = (q[2] - q[0]) / 1.34,
+ v = Math.min(d, h) || d || Math.abs(q[0]) || 1;
return 1.06 * v * Math.pow(n, -0.2);
}
-
function bin(_) {
// determine range
const maxb = _.maxbins || 20,
- base = _.base || 10,
- logb = Math.log(base),
- div = _.divide || [5, 2];
+ base = _.base || 10,
+ logb = Math.log(base),
+ div = _.divide || [5, 2];
let min = _.extent[0],
- max = _.extent[1],
- step,
- level,
- minstep,
- v,
- i,
- n;
+ max = _.extent[1],
+ step,
+ level,
+ minstep,
+ v,
+ i,
+ n;
const span = _.span || max - min || Math.abs(min) || 1;
-
if (_.step) {
// if step size is explicitly given, use that
step = _.step;
} else if (_.steps) {
// if provided, limit choice to acceptable step sizes
v = span / maxb;
-
for (i = 0, n = _.steps.length; i < n && _.steps[i] < v; ++i);
-
step = _.steps[Math.max(0, i - 1)];
} else {
// else use span to determine step size
level = Math.ceil(Math.log(maxb) / logb);
minstep = _.minstep || 0;
- step = Math.max(minstep, Math.pow(base, Math.round(Math.log(span) / logb) - level)); // increase step size if too many bins
+ step = Math.max(minstep, Math.pow(base, Math.round(Math.log(span) / logb) - level));
+ // increase step size if too many bins
while (Math.ceil(span / step) > maxb) {
step *= base;
- } // decrease step size if allowed
+ }
-
+ // decrease step size if allowed
for (i = 0, n = div.length; i < n; ++i) {
v = step / div[i];
if (v >= minstep && span / v <= maxb) step = v;
}
- } // update precision, min and max
+ }
-
+ // update precision, min and max
v = Math.log(step);
const precision = v >= 0 ? 0 : ~~(-v / logb) + 1,
- eps = Math.pow(base, -precision - 1);
-
+ eps = Math.pow(base, -precision - 1);
if (_.nice || _.nice === undefined) {
v = Math.floor(min / step + eps) * step;
min = min < v ? v - step : v;
max = Math.ceil(max / step) * step;
}
-
return {
start: min,
stop: max === min ? min + step : max,
step: step
};
}
-
exports.random = Math.random;
-
function setRandom(r) {
exports.random = r;
}
-
function bootstrapCI(array, samples, alpha, f) {
if (!array.length) return [undefined, undefined];
const values = Float64Array.from(numbers$1(array, f)),
- n = values.length,
- m = samples;
+ n = values.length,
+ m = samples;
let a, i, j, mu;
-
for (j = 0, mu = Array(m); j < m; ++j) {
for (a = 0, i = 0; i < n; ++i) {
a += values[~~(exports.random() * n)];
}
-
mu[j] = a / n;
}
-
mu.sort(ascending$1);
return [quantile$1(mu, alpha / 2), quantile$1(mu, 1 - alpha / 2)];
- } // Dot density binning for dot plot construction.
+ }
+
+ // Dot density binning for dot plot construction.
// Based on Leland Wilkinson, Dot Plots, The American Statistician, 1999.
// https://www.cs.uic.edu/~wilkinson/Publications/dotplots.pdf
-
-
function dotbin(array, step, smooth, f) {
f = f || (_ => _);
-
const n = array.length,
- v = new Float64Array(n);
+ v = new Float64Array(n);
let i = 0,
- j = 1,
- a = f(array[0]),
- b = a,
- w = a + step,
- x;
-
+ j = 1,
+ a = f(array[0]),
+ b = a,
+ w = a + step,
+ x;
for (; j < n; ++j) {
x = f(array[j]);
-
if (x >= w) {
b = (a + b) / 2;
-
for (; i < j; ++i) v[i] = b;
-
w = x + step;
a = x;
}
-
b = x;
}
-
b = (a + b) / 2;
-
for (; i < j; ++i) v[i] = b;
-
return smooth ? smoothing(v, step + step / 4) : v;
- } // perform smoothing to reduce variance
+ }
+
+ // perform smoothing to reduce variance
// swap points between "adjacent" stacks
// Wilkinson defines adjacent as within step/4 units
-
-
function smoothing(v, thresh) {
const n = v.length;
let a = 0,
- b = 1,
- c,
- d; // get left stack
+ b = 1,
+ c,
+ d;
+ // get left stack
while (v[a] === v[b]) ++b;
-
while (b < n) {
// get right stack
c = b + 1;
+ while (v[b] === v[c]) ++c;
- while (v[b] === v[c]) ++c; // are stacks adjacent?
+ // are stacks adjacent?
// if so, compare sizes and swap as needed
-
-
if (v[b] - v[b - 1] < thresh) {
d = b + (a + c - b - b >> 1);
-
while (d < b) v[d++] = v[b];
-
while (d > b) v[d--] = v[a];
- } // update left stack indices
+ }
-
+ // update left stack indices
a = b;
b = c;
}
-
return v;
}
-
- function lcg$1(seed) {
+ function lcg$2(seed) {
// Random numbers using a Linear Congruential Generator with seed value
// Uses glibc values from https://en.wikipedia.org/wiki/Linear_congruential_generator
return function () {
seed = (1103515245 * seed + 12345) % 2147483647;
return seed / 2147483647;
};
}
-
function integer(min, max) {
if (max == null) {
max = min;
min = 0;
}
-
let a, b, d;
const dist = {
min(_) {
if (arguments.length) {
a = _ || 0;
@@ -6743,94 +5945,79 @@
return dist;
} else {
return a;
}
},
-
max(_) {
if (arguments.length) {
b = _ || 0;
d = b - a;
return dist;
} else {
return b;
}
},
-
sample() {
return a + Math.floor(d * exports.random());
},
-
pdf(x) {
return x === Math.floor(x) && x >= a && x < b ? 1 / d : 0;
},
-
cdf(x) {
const v = Math.floor(x);
return v < a ? 0 : v >= b ? 1 : (v - a + 1) / d;
},
-
icdf(p) {
return p >= 0 && p <= 1 ? a - 1 + Math.floor(p * d) : NaN;
}
-
};
return dist.min(min).max(max);
}
-
const SQRT2PI = Math.sqrt(2 * Math.PI);
const SQRT2 = Math.SQRT2;
let nextSample = NaN;
-
function sampleNormal(mean, stdev) {
mean = mean || 0;
stdev = stdev == null ? 1 : stdev;
let x = 0,
- y = 0,
- rds,
- c;
-
+ y = 0,
+ rds,
+ c;
if (nextSample === nextSample) {
x = nextSample;
nextSample = NaN;
} else {
do {
x = exports.random() * 2 - 1;
y = exports.random() * 2 - 1;
rds = x * x + y * y;
} while (rds === 0 || rds > 1);
-
c = Math.sqrt(-2 * Math.log(rds) / rds); // Box-Muller transform
-
x *= c;
nextSample = y * c;
}
-
return mean + x * stdev;
}
-
function densityNormal(value, mean, stdev) {
stdev = stdev == null ? 1 : stdev;
const z = (value - (mean || 0)) / stdev;
return Math.exp(-0.5 * z * z) / (stdev * SQRT2PI);
- } // Approximation from West (2009)
- // Better Approximations to Cumulative Normal Functions
+ }
-
+ // Approximation from West (2009)
+ // Better Approximations to Cumulative Normal Functions
function cumulativeNormal(value, mean, stdev) {
mean = mean || 0;
stdev = stdev == null ? 1 : stdev;
const z = (value - mean) / stdev,
- Z = Math.abs(z);
+ Z = Math.abs(z);
let cd;
-
if (Z > 37) {
cd = 0;
} else {
const exp = Math.exp(-Z * Z / 2);
let sum;
-
if (Z < 7.07106781186547) {
sum = 3.52624965998911e-02 * Z + 0.700383064443688;
sum = sum * Z + 6.37396220353165;
sum = sum * Z + 33.912866078383;
sum = sum * Z + 112.079291497871;
@@ -6852,31 +6039,29 @@
sum = Z + 2 / sum;
sum = Z + 1 / sum;
cd = exp / sum / 2.506628274631;
}
}
-
return z > 0 ? 1 - cd : cd;
- } // Approximation of Probit function using inverse error function.
+ }
-
+ // Approximation of Probit function using inverse error function.
function quantileNormal(p, mean, stdev) {
if (p < 0 || p > 1) return NaN;
return (mean || 0) + (stdev == null ? 1 : stdev) * SQRT2 * erfinv(2 * p - 1);
- } // Approximate inverse error function. Implementation from "Approximating
+ }
+
+ // Approximate inverse error function. Implementation from "Approximating
// the erfinv function" by Mike Giles, GPU Computing Gems, volume 2, 2010.
// Ported from Apache Commons Math, http://www.apache.org/licenses/LICENSE-2.0
-
-
function erfinv(x) {
// beware that the logarithm argument must be
// commputed as (1.0 - x) * (1.0 + x),
// it must NOT be simplified as 1.0 - x * x as this
// would induce rounding errors near the boundaries +/-1
let w = -Math.log((1 - x) * (1 + x)),
- p;
-
+ p;
if (w < 6.25) {
w -= 3.125;
p = -3.6444120640178196996e-21;
p = -1.685059138182016589e-19 + p * w;
p = 1.2858480715256400167e-18 + p * w;
@@ -6941,14 +6126,12 @@
p = 1.0103004648645343977 + p * w;
p = 4.8499064014085844221 + p * w;
} else {
p = Infinity;
}
-
return p * x;
}
-
function gaussian(mean, stdev) {
let mu, sigma;
const dist = {
mean(_) {
if (arguments.length) {
@@ -6956,28 +6139,25 @@
return dist;
} else {
return mu;
}
},
-
stdev(_) {
if (arguments.length) {
sigma = _ == null ? 1 : _;
return dist;
} else {
return sigma;
}
},
-
sample: () => sampleNormal(mu, sigma),
pdf: value => densityNormal(value, mu, sigma),
cdf: value => cumulativeNormal(value, mu, sigma),
icdf: p => quantileNormal(p, mu, sigma)
};
return dist.mean(mean).stdev(stdev);
}
-
function kde(support, bandwidth) {
const kernel = gaussian();
let n = 0;
const dist = {
data(_) {
@@ -6987,74 +6167,59 @@
return dist.bandwidth(bandwidth);
} else {
return support;
}
},
-
bandwidth(_) {
if (!arguments.length) return bandwidth;
bandwidth = _;
if (!bandwidth && support) bandwidth = estimateBandwidth(support);
return dist;
},
-
sample() {
return support[~~(exports.random() * n)] + bandwidth * kernel.sample();
},
-
pdf(x) {
let y = 0,
- i = 0;
-
+ i = 0;
for (; i < n; ++i) {
y += kernel.pdf((x - support[i]) / bandwidth);
}
-
return y / bandwidth / n;
},
-
cdf(x) {
let y = 0,
- i = 0;
-
+ i = 0;
for (; i < n; ++i) {
y += kernel.cdf((x - support[i]) / bandwidth);
}
-
return y / n;
},
-
icdf() {
throw Error('KDE icdf not supported.');
}
-
};
return dist.data(support);
}
-
function sampleLogNormal(mean, stdev) {
mean = mean || 0;
stdev = stdev == null ? 1 : stdev;
return Math.exp(mean + sampleNormal() * stdev);
}
-
function densityLogNormal(value, mean, stdev) {
if (value <= 0) return 0;
mean = mean || 0;
stdev = stdev == null ? 1 : stdev;
const z = (Math.log(value) - mean) / stdev;
return Math.exp(-0.5 * z * z) / (stdev * SQRT2PI * value);
}
-
function cumulativeLogNormal(value, mean, stdev) {
return cumulativeNormal(Math.log(value), mean, stdev);
}
-
function quantileLogNormal(p, mean, stdev) {
return Math.exp(quantileNormal(p, mean, stdev));
}
-
function lognormal(mean, stdev) {
let mu, sigma;
const dist = {
mean(_) {
if (arguments.length) {
@@ -7062,157 +6227,127 @@
return dist;
} else {
return mu;
}
},
-
stdev(_) {
if (arguments.length) {
sigma = _ == null ? 1 : _;
return dist;
} else {
return sigma;
}
},
-
sample: () => sampleLogNormal(mu, sigma),
pdf: value => densityLogNormal(value, mu, sigma),
cdf: value => cumulativeLogNormal(value, mu, sigma),
icdf: p => quantileLogNormal(p, mu, sigma)
};
return dist.mean(mean).stdev(stdev);
}
-
function mixture$1(dists, weights) {
let m = 0,
- w;
-
+ w;
function normalize(x) {
const w = [];
let sum = 0,
- i;
-
+ i;
for (i = 0; i < m; ++i) {
sum += w[i] = x[i] == null ? 1 : +x[i];
}
-
for (i = 0; i < m; ++i) {
w[i] /= sum;
}
-
return w;
}
-
const dist = {
weights(_) {
if (arguments.length) {
w = normalize(weights = _ || []);
return dist;
}
-
return weights;
},
-
distributions(_) {
if (arguments.length) {
if (_) {
m = _.length;
dists = _;
} else {
m = 0;
dists = [];
}
-
return dist.weights(weights);
}
-
return dists;
},
-
sample() {
const r = exports.random();
let d = dists[m - 1],
- v = w[0],
- i = 0; // first select distribution
+ v = w[0],
+ i = 0;
+ // first select distribution
for (; i < m - 1; v += w[++i]) {
if (r < v) {
d = dists[i];
break;
}
- } // then sample from it
-
-
+ }
+ // then sample from it
return d.sample();
},
-
pdf(x) {
let p = 0,
- i = 0;
-
+ i = 0;
for (; i < m; ++i) {
p += w[i] * dists[i].pdf(x);
}
-
return p;
},
-
cdf(x) {
let p = 0,
- i = 0;
-
+ i = 0;
for (; i < m; ++i) {
p += w[i] * dists[i].cdf(x);
}
-
return p;
},
-
icdf() {
throw Error('Mixture icdf not supported.');
}
-
};
return dist.distributions(dists).weights(weights);
}
-
function sampleUniform(min, max) {
if (max == null) {
max = min == null ? 1 : min;
min = 0;
}
-
return min + (max - min) * exports.random();
}
-
function densityUniform(value, min, max) {
if (max == null) {
max = min == null ? 1 : min;
min = 0;
}
-
return value >= min && value <= max ? 1 / (max - min) : 0;
}
-
function cumulativeUniform(value, min, max) {
if (max == null) {
max = min == null ? 1 : min;
min = 0;
}
-
return value < min ? 0 : value > max ? 1 : (value - min) / (max - min);
}
-
function quantileUniform(p, min, max) {
if (max == null) {
max = min == null ? 1 : min;
min = 0;
}
-
return p >= 0 && p <= 1 ? min + p * (max - min) : NaN;
}
-
function uniform(min, max) {
let a, b;
const dist = {
min(_) {
if (arguments.length) {
@@ -7220,481 +6355,451 @@
return dist;
} else {
return a;
}
},
-
max(_) {
if (arguments.length) {
b = _ == null ? 1 : _;
return dist;
} else {
return b;
}
},
-
sample: () => sampleUniform(a, b),
pdf: value => densityUniform(value, a, b),
cdf: value => cumulativeUniform(value, a, b),
icdf: p => quantileUniform(p, a, b)
};
-
if (max == null) {
max = min == null ? 1 : min;
min = 0;
}
-
return dist.min(min).max(max);
- } // Ordinary Least Squares
+ }
+ function constant$4(data, x, y) {
+ let mean = 0,
+ n = 0;
+ for (const d of data) {
+ const val = y(d);
+ if (x(d) == null || val == null || isNaN(val)) continue;
+ mean += (val - mean) / ++n;
+ }
+ return {
+ coef: [mean],
+ predict: () => mean,
+ rSquared: 0
+ };
+ }
-
+ // Ordinary Least Squares
function ols(uX, uY, uXY, uX2) {
const delta = uX2 - uX * uX,
- slope = Math.abs(delta) < 1e-24 ? 0 : (uXY - uX * uY) / delta,
- intercept = uY - slope * uX;
+ slope = Math.abs(delta) < 1e-24 ? 0 : (uXY - uX * uY) / delta,
+ intercept = uY - slope * uX;
return [intercept, slope];
}
-
function points(data, x, y, sort) {
data = data.filter(d => {
let u = x(d),
- v = y(d);
+ v = y(d);
return u != null && (u = +u) >= u && v != null && (v = +v) >= v;
});
-
if (sort) {
data.sort((a, b) => x(a) - x(b));
}
-
const n = data.length,
- X = new Float64Array(n),
- Y = new Float64Array(n); // extract values, calculate means
+ X = new Float64Array(n),
+ Y = new Float64Array(n);
+ // extract values, calculate means
let i = 0,
- ux = 0,
- uy = 0,
- xv,
- yv,
- d;
-
+ ux = 0,
+ uy = 0,
+ xv,
+ yv,
+ d;
for (d of data) {
X[i] = xv = +x(d);
Y[i] = yv = +y(d);
++i;
ux += (xv - ux) / i;
uy += (yv - uy) / i;
- } // mean center the data
+ }
-
+ // mean center the data
for (i = 0; i < n; ++i) {
X[i] -= ux;
Y[i] -= uy;
}
-
return [X, Y, ux, uy];
}
-
function visitPoints(data, x, y, callback) {
let i = -1,
- u,
- v;
-
+ u,
+ v;
for (const d of data) {
u = x(d);
v = y(d);
-
if (u != null && (u = +u) >= u && v != null && (v = +v) >= v) {
callback(u, v, ++i);
}
}
- } // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
+ }
-
+ // Adapted from d3-regression by Harry Stevens
+ // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
function rSquared(data, x, y, uY, predict) {
let SSE = 0,
- SST = 0;
+ SST = 0;
visitPoints(data, x, y, (dx, dy) => {
const sse = dy - predict(dx),
- sst = dy - uY;
+ sst = dy - uY;
SSE += sse * sse;
SST += sst * sst;
});
return 1 - SSE / SST;
- } // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
+ }
-
+ // Adapted from d3-regression by Harry Stevens
+ // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
function linear$2(data, x, y) {
let X = 0,
- Y = 0,
- XY = 0,
- X2 = 0,
- n = 0;
+ Y = 0,
+ XY = 0,
+ X2 = 0,
+ n = 0;
visitPoints(data, x, y, (dx, dy) => {
++n;
X += (dx - X) / n;
Y += (dy - Y) / n;
XY += (dx * dy - XY) / n;
X2 += (dx * dx - X2) / n;
});
-
const coef = ols(X, Y, XY, X2),
- predict = x => coef[0] + coef[1] * x;
-
+ predict = x => coef[0] + coef[1] * x;
return {
coef: coef,
predict: predict,
rSquared: rSquared(data, x, y, Y, predict)
};
- } // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
+ }
-
+ // Adapted from d3-regression by Harry Stevens
+ // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
function log$3(data, x, y) {
let X = 0,
- Y = 0,
- XY = 0,
- X2 = 0,
- n = 0;
+ Y = 0,
+ XY = 0,
+ X2 = 0,
+ n = 0;
visitPoints(data, x, y, (dx, dy) => {
++n;
dx = Math.log(dx);
X += (dx - X) / n;
Y += (dy - Y) / n;
XY += (dx * dy - XY) / n;
X2 += (dx * dx - X2) / n;
});
-
const coef = ols(X, Y, XY, X2),
- predict = x => coef[0] + coef[1] * Math.log(x);
-
+ predict = x => coef[0] + coef[1] * Math.log(x);
return {
coef: coef,
predict: predict,
rSquared: rSquared(data, x, y, Y, predict)
};
}
-
function exp$1(data, x, y) {
// eslint-disable-next-line no-unused-vars
const [xv, yv, ux, uy] = points(data, x, y);
let YL = 0,
- XY = 0,
- XYL = 0,
- X2Y = 0,
- n = 0,
- dx,
- ly,
- xy;
+ XY = 0,
+ XYL = 0,
+ X2Y = 0,
+ n = 0,
+ dx,
+ ly,
+ xy;
visitPoints(data, x, y, (_, dy) => {
dx = xv[n++];
ly = Math.log(dy);
xy = dx * dy;
YL += (dy * ly - YL) / n;
XY += (xy - XY) / n;
XYL += (xy * ly - XYL) / n;
X2Y += (dx * xy - X2Y) / n;
});
-
const [c0, c1] = ols(XY / uy, YL / uy, XYL / uy, X2Y / uy),
- predict = x => Math.exp(c0 + c1 * (x - ux));
-
+ predict = x => Math.exp(c0 + c1 * (x - ux));
return {
coef: [Math.exp(c0 - c1 * ux), c1],
predict: predict,
rSquared: rSquared(data, x, y, uy, predict)
};
- } // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
+ }
-
+ // Adapted from d3-regression by Harry Stevens
+ // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
function pow$3(data, x, y) {
let X = 0,
- Y = 0,
- XY = 0,
- X2 = 0,
- YS = 0,
- n = 0;
+ Y = 0,
+ XY = 0,
+ X2 = 0,
+ YS = 0,
+ n = 0;
visitPoints(data, x, y, (dx, dy) => {
const lx = Math.log(dx),
- ly = Math.log(dy);
+ ly = Math.log(dy);
++n;
X += (lx - X) / n;
Y += (ly - Y) / n;
XY += (lx * ly - XY) / n;
X2 += (lx * lx - X2) / n;
YS += (dy - YS) / n;
});
-
const coef = ols(X, Y, XY, X2),
- predict = x => coef[0] * Math.pow(x, coef[1]);
-
+ predict = x => coef[0] * Math.pow(x, coef[1]);
coef[0] = Math.exp(coef[0]);
return {
coef: coef,
predict: predict,
rSquared: rSquared(data, x, y, YS, predict)
};
}
-
function quad(data, x, y) {
const [xv, yv, ux, uy] = points(data, x, y),
- n = xv.length;
+ n = xv.length;
let X2 = 0,
- X3 = 0,
- X4 = 0,
- XY = 0,
- X2Y = 0,
- i,
- dx,
- dy,
- x2;
-
+ X3 = 0,
+ X4 = 0,
+ XY = 0,
+ X2Y = 0,
+ i,
+ dx,
+ dy,
+ x2;
for (i = 0; i < n;) {
dx = xv[i];
dy = yv[i++];
x2 = dx * dx;
X2 += (x2 - X2) / i;
X3 += (x2 * dx - X3) / i;
X4 += (x2 * x2 - X4) / i;
XY += (dx * dy - XY) / i;
X2Y += (x2 * dy - X2Y) / i;
}
-
const X2X2 = X4 - X2 * X2,
- d = X2 * X2X2 - X3 * X3,
- a = (X2Y * X2 - XY * X3) / d,
- b = (XY * X2X2 - X2Y * X3) / d,
- c = -a * X2,
- predict = x => {
- x = x - ux;
- return a * x * x + b * x + c + uy;
- }; // transform coefficients back from mean-centered space
+ d = X2 * X2X2 - X3 * X3,
+ a = (X2Y * X2 - XY * X3) / d,
+ b = (XY * X2X2 - X2Y * X3) / d,
+ c = -a * X2,
+ predict = x => {
+ x = x - ux;
+ return a * x * x + b * x + c + uy;
+ };
-
+ // transform coefficients back from mean-centered space
return {
coef: [c - b * ux + a * ux * ux + uy, b - 2 * a * ux, a],
predict: predict,
rSquared: rSquared(data, x, y, uy, predict)
};
- } // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
+ }
+
+ // Adapted from d3-regression by Harry Stevens
+ // License: https://github.com/HarryStevens/d3-regression/blob/master/LICENSE
// ... which was adapted from regression-js by Tom Alexander
// Source: https://github.com/Tom-Alexander/regression-js/blob/master/src/regression.js#L246
// License: https://github.com/Tom-Alexander/regression-js/blob/master/LICENSE
-
-
function poly(data, x, y, order) {
// use more efficient methods for lower orders
+ if (order === 0) return constant$4(data, x, y);
if (order === 1) return linear$2(data, x, y);
if (order === 2) return quad(data, x, y);
const [xv, yv, ux, uy] = points(data, x, y),
- n = xv.length,
- lhs = [],
- rhs = [],
- k = order + 1;
+ n = xv.length,
+ lhs = [],
+ rhs = [],
+ k = order + 1;
let i, j, l, v, c;
-
for (i = 0; i < k; ++i) {
for (l = 0, v = 0; l < n; ++l) {
v += Math.pow(xv[l], i) * yv[l];
}
-
lhs.push(v);
c = new Float64Array(k);
-
for (j = 0; j < k; ++j) {
for (l = 0, v = 0; l < n; ++l) {
v += Math.pow(xv[l], i + j);
}
-
c[j] = v;
}
-
rhs.push(c);
}
-
rhs.push(lhs);
-
const coef = gaussianElimination(rhs),
- predict = x => {
- x -= ux;
- let y = uy + coef[0] + coef[1] * x + coef[2] * x * x;
-
- for (i = 3; i < k; ++i) y += coef[i] * Math.pow(x, i);
-
- return y;
- };
-
+ predict = x => {
+ x -= ux;
+ let y = uy + coef[0] + coef[1] * x + coef[2] * x * x;
+ for (i = 3; i < k; ++i) y += coef[i] * Math.pow(x, i);
+ return y;
+ };
return {
coef: uncenter(k, coef, -ux, uy),
predict: predict,
rSquared: rSquared(data, x, y, uy, predict)
};
}
-
function uncenter(k, a, x, y) {
const z = Array(k);
- let i, j, v, c; // initialize to zero
+ let i, j, v, c;
- for (i = 0; i < k; ++i) z[i] = 0; // polynomial expansion
+ // initialize to zero
+ for (i = 0; i < k; ++i) z[i] = 0;
-
+ // polynomial expansion
for (i = k - 1; i >= 0; --i) {
v = a[i];
c = 1;
z[i] += v;
-
for (j = 1; j <= i; ++j) {
c *= (i + 1 - j) / j; // binomial coefficent
-
z[i - j] += v * Math.pow(x, j) * c;
}
- } // bias term
+ }
-
+ // bias term
z[0] += y;
return z;
- } // Given an array for a two-dimensional matrix and the polynomial order,
- // solve A * x = b using Gaussian elimination.
+ }
-
+ // Given an array for a two-dimensional matrix and the polynomial order,
+ // solve A * x = b using Gaussian elimination.
function gaussianElimination(matrix) {
const n = matrix.length - 1,
- coef = [];
+ coef = [];
let i, j, k, r, t;
-
for (i = 0; i < n; ++i) {
r = i; // max row
-
for (j = i + 1; j < n; ++j) {
if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][r])) {
r = j;
}
}
-
for (k = i; k < n + 1; ++k) {
t = matrix[k][i];
matrix[k][i] = matrix[k][r];
matrix[k][r] = t;
}
-
for (j = i + 1; j < n; ++j) {
for (k = n; k >= i; k--) {
matrix[k][j] -= matrix[k][i] * matrix[i][j] / matrix[i][i];
}
}
}
-
for (j = n - 1; j >= 0; --j) {
t = 0;
-
for (k = j + 1; k < n; ++k) {
t += matrix[k][j] * coef[k];
}
-
coef[j] = (matrix[n][j] - t) / matrix[j][j];
}
-
return coef;
}
-
const maxiters = 2,
- epsilon$6 = 1e-12; // Adapted from science.js by Jason Davies
+ epsilon$6 = 1e-12;
+
+ // Adapted from science.js by Jason Davies
// Source: https://github.com/jasondavies/science.js/blob/master/src/stats/loess.js
// License: https://github.com/jasondavies/science.js/blob/master/LICENSE
-
function loess(data, x, y, bandwidth) {
const [xv, yv, ux, uy] = points(data, x, y, true),
- n = xv.length,
- bw = Math.max(2, ~~(bandwidth * n)),
- // # nearest neighbors
- yhat = new Float64Array(n),
- residuals = new Float64Array(n),
- robustWeights = new Float64Array(n).fill(1);
-
+ n = xv.length,
+ bw = Math.max(2, ~~(bandwidth * n)),
+ // # nearest neighbors
+ yhat = new Float64Array(n),
+ residuals = new Float64Array(n),
+ robustWeights = new Float64Array(n).fill(1);
for (let iter = -1; ++iter <= maxiters;) {
const interval = [0, bw - 1];
-
for (let i = 0; i < n; ++i) {
const dx = xv[i],
- i0 = interval[0],
- i1 = interval[1],
- edge = dx - xv[i0] > xv[i1] - dx ? i0 : i1;
+ i0 = interval[0],
+ i1 = interval[1],
+ edge = dx - xv[i0] > xv[i1] - dx ? i0 : i1;
let W = 0,
- X = 0,
- Y = 0,
- XY = 0,
- X2 = 0;
+ X = 0,
+ Y = 0,
+ XY = 0,
+ X2 = 0;
const denom = 1 / Math.abs(xv[edge] - dx || 1); // avoid singularity!
for (let k = i0; k <= i1; ++k) {
const xk = xv[k],
- yk = yv[k],
- w = tricube(Math.abs(dx - xk) * denom) * robustWeights[k],
- xkw = xk * w;
+ yk = yv[k],
+ w = tricube(Math.abs(dx - xk) * denom) * robustWeights[k],
+ xkw = xk * w;
W += w;
X += xkw;
Y += yk * w;
XY += yk * xkw;
X2 += xk * xkw;
- } // linear regression fit
+ }
-
+ // linear regression fit
const [a, b] = ols(X / W, Y / W, XY / W, X2 / W);
yhat[i] = a + b * dx;
residuals[i] = Math.abs(yv[i] - yhat[i]);
updateInterval(xv, i + 1, interval);
}
-
if (iter === maxiters) {
break;
}
-
const medianResidual = median(residuals);
if (Math.abs(medianResidual) < epsilon$6) break;
-
for (let i = 0, arg, w; i < n; ++i) {
- arg = residuals[i] / (6 * medianResidual); // default to epsilon (rather than zero) for large deviations
+ arg = residuals[i] / (6 * medianResidual);
+ // default to epsilon (rather than zero) for large deviations
// keeping weights tiny but non-zero prevents singularites
-
robustWeights[i] = arg >= 1 ? epsilon$6 : (w = 1 - arg * arg) * w;
}
}
-
return output$1(xv, yhat, ux, uy);
- } // weighting kernel for local regression
+ }
-
+ // weighting kernel for local regression
function tricube(x) {
return (x = 1 - x * x * x) * x * x;
- } // advance sliding window interval of nearest neighbors
+ }
-
+ // advance sliding window interval of nearest neighbors
function updateInterval(xv, i, interval) {
const val = xv[i];
let left = interval[0],
- right = interval[1] + 1;
- if (right >= xv.length) return; // step right if distance to new right edge is <= distance to old left edge
- // step when distance is equal to ensure movement over duplicate x values
+ right = interval[1] + 1;
+ if (right >= xv.length) return;
+ // step right if distance to new right edge is <= distance to old left edge
+ // step when distance is equal to ensure movement over duplicate x values
while (i > left && xv[right] - val <= val - xv[left]) {
interval[0] = ++left;
interval[1] = right;
++right;
}
- } // generate smoothed output points
- // average points with repeated x values
+ }
-
+ // generate smoothed output points
+ // average points with repeated x values
function output$1(xv, yhat, ux, uy) {
const n = xv.length,
- out = [];
+ out = [];
let i = 0,
- cnt = 0,
- prev = [],
- v;
-
+ cnt = 0,
+ prev = [],
+ v;
for (; i < n; ++i) {
v = xv[i] + ux;
-
if (prev[0] === v) {
// average output values via online update
prev[1] += (yhat[i] - prev[1]) / ++cnt;
} else {
// add new output point
@@ -7702,58 +6807,51 @@
prev[1] += uy;
prev = [v, yhat[i]];
out.push(prev);
}
}
-
prev[1] += uy;
return out;
- } // subdivide up to accuracy of 0.5 degrees
+ }
+ // subdivide up to accuracy of 0.5 degrees
+ const MIN_RADIANS = 0.5 * Math.PI / 180;
- const MIN_RADIANS = 0.5 * Math.PI / 180; // Adaptively sample an interpolated function over a domain extent
-
+ // Adaptively sample an interpolated function over a domain extent
function sampleCurve(f, extent, minSteps, maxSteps) {
minSteps = minSteps || 25;
maxSteps = Math.max(minSteps, maxSteps || 200);
-
const point = x => [x, f(x)],
- minX = extent[0],
- maxX = extent[1],
- span = maxX - minX,
- stop = span / maxSteps,
- prev = [point(minX)],
- next = [];
-
+ minX = extent[0],
+ maxX = extent[1],
+ span = maxX - minX,
+ stop = span / maxSteps,
+ prev = [point(minX)],
+ next = [];
if (minSteps === maxSteps) {
// no adaptation, sample uniform grid directly and return
for (let i = 1; i < maxSteps; ++i) {
prev.push(point(minX + i / minSteps * span));
}
-
prev.push(point(maxX));
return prev;
} else {
// sample minimum points on uniform grid
// then move on to perform adaptive refinement
next.push(point(maxX));
-
for (let i = minSteps; --i > 0;) {
next.push(point(minX + i / minSteps * span));
}
}
-
let p0 = prev[0];
let p1 = next[next.length - 1];
const sx = 1 / span;
const sy = scaleY(p0[1], next);
-
while (p1) {
// midpoint for potential curve subdivision
const pm = point((p0[0] + p1[0]) / 2);
const dx = pm[0] - p0[0] >= stop;
-
if (dx && angleDelta(p0, pm, p1, sx, sy) > MIN_RADIANS) {
// maximum resolution has not yet been met, and
// subdivision midpoint is sufficiently different from endpoint
// save subdivision, push midpoint onto the visitation stack
next.push(pm);
@@ -7762,63 +6860,51 @@
// skip subdivision, store endpoint, move to next point on the stack
p0 = p1;
prev.push(p1);
next.pop();
}
-
p1 = next[next.length - 1];
}
-
return prev;
}
-
function scaleY(init, points) {
let ymin = init;
let ymax = init;
const n = points.length;
-
for (let i = 0; i < n; ++i) {
const y = points[i][1];
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
-
return 1 / (ymax - ymin);
}
-
function angleDelta(p, q, r, sx, sy) {
const a0 = Math.atan2(sy * (r[1] - p[1]), sx * (r[0] - p[0])),
- a1 = Math.atan2(sy * (q[1] - p[1]), sx * (q[0] - p[0]));
+ a1 = Math.atan2(sy * (q[1] - p[1]), sx * (q[0] - p[0]));
return Math.abs(a0 - a1);
}
function multikey(f) {
return x => {
const n = f.length;
let i = 1,
- k = String(f[0](x));
-
+ k = String(f[0](x));
for (; i < n; ++i) {
k += '|' + f[i](x);
}
-
return k;
};
}
-
function groupkey(fields) {
return !fields || !fields.length ? function () {
return '';
} : fields.length === 1 ? fields[0] : multikey(fields);
}
-
function measureName(op, field, as) {
return as || op + (!field ? '' : '_' + field);
}
-
const noop$4 = () => {};
-
const base_op = {
init: noop$4,
add: noop$4,
rem: noop$4,
idx: 0
@@ -7841,11 +6927,11 @@
valid: {
value: m => m.valid
},
sum: {
init: m => m.sum = 0,
- value: m => m.sum,
+ value: m => m.valid ? m.sum : undefined,
add: (m, v) => m.sum += +v,
rem: (m, v) => m.sum -= v
},
product: {
init: m => m.product = 1,
@@ -7967,289 +7053,249 @@
rem: (m, v) => {
if (v >= m.max) m.argmax = undefined;
},
req: ['max', 'values'],
idx: 3
+ },
+ exponential: {
+ init: (m, r) => {
+ m.exp = 0;
+ m.exp_r = r;
+ },
+ value: m => m.valid ? m.exp * (1 - m.exp_r) / (1 - m.exp_r ** m.valid) : undefined,
+ add: (m, v) => m.exp = m.exp_r * m.exp + v,
+ rem: (m, v) => m.exp = (m.exp - v / m.exp_r ** (m.valid - 1)) / m.exp_r
+ },
+ exponentialb: {
+ value: m => m.valid ? m.exp * (1 - m.exp_r) : undefined,
+ req: ['exponential'],
+ idx: 1
}
};
- const ValidAggregateOps = Object.keys(AggregateOps);
-
+ const ValidAggregateOps = Object.keys(AggregateOps).filter(d => d !== '__count__');
function measure(key, value) {
- return out => extend$1({
+ return (out, aggregate_param) => extend$1({
name: key,
+ aggregate_param: aggregate_param,
out: out || key
}, base_op, value);
}
-
- ValidAggregateOps.forEach(key => {
+ [...ValidAggregateOps, '__count__'].forEach(key => {
AggregateOps[key] = measure(key, AggregateOps[key]);
});
-
- function createMeasure(op, name) {
- return AggregateOps[op](name);
+ function createMeasure(op, param, name) {
+ return AggregateOps[op](name, param);
}
-
function compareIndex(a, b) {
return a.idx - b.idx;
}
-
function resolve(agg) {
const map = {};
agg.forEach(a => map[a.name] = a);
-
const getreqs = a => {
if (!a.req) return;
a.req.forEach(key => {
if (!map[key]) getreqs(map[key] = AggregateOps[key]());
});
};
-
agg.forEach(getreqs);
return Object.values(map).sort(compareIndex);
}
-
function init() {
this.valid = 0;
this.missing = 0;
-
- this._ops.forEach(op => op.init(this));
+ this._ops.forEach(op => op.aggregate_param == null ? op.init(this) : op.init(this, op.aggregate_param));
}
-
function add$2(v, t) {
if (v == null || v === '') {
++this.missing;
return;
}
-
if (v !== v) return;
++this.valid;
-
this._ops.forEach(op => op.add(this, v, t));
}
-
function rem(v, t) {
if (v == null || v === '') {
--this.missing;
return;
}
-
if (v !== v) return;
--this.valid;
-
this._ops.forEach(op => op.rem(this, v, t));
}
-
function set$3(t) {
this._out.forEach(op => t[op.out] = op.value(this));
-
return t;
}
-
function compileMeasures(agg, field) {
const get = field || identity$6,
- ops = resolve(agg),
- out = agg.slice().sort(compareIndex);
-
+ ops = resolve(agg),
+ out = agg.slice().sort(compareIndex);
function ctr(cell) {
this._ops = ops;
this._out = out;
this.cell = cell;
this.init();
}
-
ctr.prototype.init = init;
ctr.prototype.add = add$2;
ctr.prototype.rem = rem;
ctr.prototype.set = set$3;
ctr.prototype.get = get;
ctr.fields = agg.map(op => op.out);
return ctr;
}
-
function TupleStore(key) {
this._key = key ? field$1(key) : tupleid;
this.reset();
}
-
const prototype$1 = TupleStore.prototype;
-
prototype$1.reset = function () {
this._add = [];
this._rem = [];
this._ext = null;
this._get = null;
this._q = null;
};
-
prototype$1.add = function (v) {
this._add.push(v);
};
-
prototype$1.rem = function (v) {
this._rem.push(v);
};
-
prototype$1.values = function () {
this._get = null;
if (this._rem.length === 0) return this._add;
const a = this._add,
- r = this._rem,
- k = this._key,
- n = a.length,
- m = r.length,
- x = Array(n - m),
- map = {};
- let i, j, v; // use unique key field to clear removed values
+ r = this._rem,
+ k = this._key,
+ n = a.length,
+ m = r.length,
+ x = Array(n - m),
+ map = {};
+ let i, j, v;
+ // use unique key field to clear removed values
for (i = 0; i < m; ++i) {
map[k(r[i])] = 1;
}
-
for (i = 0, j = 0; i < n; ++i) {
if (map[k(v = a[i])]) {
map[k(v)] = 0;
} else {
x[j++] = v;
}
}
-
this._rem = [];
return this._add = x;
- }; // memoizing statistics methods
+ };
+ // memoizing statistics methods
prototype$1.distinct = function (get) {
const v = this.values(),
- map = {};
+ map = {};
let n = v.length,
- count = 0,
- s;
-
+ count = 0,
+ s;
while (--n >= 0) {
s = get(v[n]) + '';
-
if (!has$1(map, s)) {
map[s] = 1;
++count;
}
}
-
return count;
};
-
prototype$1.extent = function (get) {
if (this._get !== get || !this._ext) {
const v = this.values(),
- i = extentIndex(v, get);
+ i = extentIndex(v, get);
this._ext = [v[i[0]], v[i[1]]];
this._get = get;
}
-
return this._ext;
};
-
prototype$1.argmin = function (get) {
return this.extent(get)[0] || {};
};
-
prototype$1.argmax = function (get) {
return this.extent(get)[1] || {};
};
-
prototype$1.min = function (get) {
const m = this.extent(get)[0];
return m != null ? get(m) : undefined;
};
-
prototype$1.max = function (get) {
const m = this.extent(get)[1];
return m != null ? get(m) : undefined;
};
-
prototype$1.quartile = function (get) {
if (this._get !== get || !this._q) {
this._q = quartiles(this.values(), get);
this._get = get;
}
-
return this._q;
};
-
prototype$1.q1 = function (get) {
return this.quartile(get)[0];
};
-
prototype$1.q2 = function (get) {
return this.quartile(get)[1];
};
-
prototype$1.q3 = function (get) {
return this.quartile(get)[2];
};
-
prototype$1.ci = function (get) {
if (this._get !== get || !this._ci) {
this._ci = bootstrapCI(this.values(), 1000, 0.05, get);
this._get = get;
}
-
return this._ci;
};
-
prototype$1.ci0 = function (get) {
return this.ci(get)[0];
};
-
prototype$1.ci1 = function (get) {
return this.ci(get)[1];
};
+
/**
* Group-by aggregation operator.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors to groupby.
* @param {Array<function(object): *>} [params.fields] - An array of accessors to aggregate.
* @param {Array<string>} [params.ops] - An array of strings indicating aggregation operations.
+ * @param {Array<number>} [params.aggregate_params] - An optional array of parameters for aggregation operations.
* @param {Array<string>} [params.as] - An array of output field names for aggregated values.
* @param {boolean} [params.cross=false] - A flag indicating that the full
* cross-product of groupby values should be generated, including empty cells.
* If true, the drop parameter is ignored and empty cells are retained.
* @param {boolean} [params.drop=true] - A flag indicating if empty cells should be removed.
*/
-
-
function Aggregate$1(params) {
Transform.call(this, null, params);
this._adds = []; // array of added output tuples
-
this._mods = []; // array of modified output tuples
-
this._alen = 0; // number of active added tuples
-
this._mlen = 0; // number of active modified tuples
-
this._drop = true; // should empty aggregation cells be removed
-
this._cross = false; // produce full cross-product of group-by values
this._dims = []; // group-by dimension accessors
-
this._dnames = []; // group-by dimension names
this._measures = []; // collection of aggregation monoids
-
this._countOnly = false; // flag indicating only count aggregation
-
this._counts = null; // collection of count fields
-
this._prev = null; // previous aggregation cells
this._inputs = null; // array of dependent input tuple field names
-
this._outputs = null; // array of output tuple field names
}
-
Aggregate$1.Definition = {
'type': 'Aggregate',
'metadata': {
'generates': true,
'changes': true
@@ -8262,10 +7308,15 @@
'name': 'ops',
'type': 'enum',
'array': true,
'values': ValidAggregateOps
}, {
+ 'name': 'aggregate_params',
+ 'type': 'number',
+ 'null': true,
+ 'array': true
+ }, {
'name': 'fields',
'type': 'field',
'null': true,
'array': true
}, {
@@ -8287,325 +7338,284 @@
}]
};
inherits(Aggregate$1, Transform, {
transform(_, pulse) {
const aggr = this,
- out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- mod = _.modified();
-
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
+ mod = _.modified();
aggr.stamp = out.stamp;
-
if (aggr.value && (mod || pulse.modified(aggr._inputs, true))) {
aggr._prev = aggr.value;
- aggr.value = mod ? aggr.init(_) : {};
+ aggr.value = mod ? aggr.init(_) : Object.create(null);
pulse.visit(pulse.SOURCE, t => aggr.add(t));
} else {
aggr.value = aggr.value || aggr.init(_);
pulse.visit(pulse.REM, t => aggr.rem(t));
pulse.visit(pulse.ADD, t => aggr.add(t));
- } // Indicate output fields and return aggregate tuples.
+ }
+ // Indicate output fields and return aggregate tuples.
+ out.modifies(aggr._outputs);
- out.modifies(aggr._outputs); // Should empty cells be dropped?
+ // Should empty cells be dropped?
+ aggr._drop = _.drop !== false;
- aggr._drop = _.drop !== false; // If domain cross-product requested, generate empty cells as needed
+ // If domain cross-product requested, generate empty cells as needed
// and ensure that empty cells are not dropped
-
if (_.cross && aggr._dims.length > 1) {
aggr._drop = false;
aggr.cross();
}
-
if (pulse.clean() && aggr._drop) {
out.clean(true).runAfter(() => this.clean());
}
-
return aggr.changes(out);
},
-
cross() {
const aggr = this,
- curr = aggr.value,
- dims = aggr._dnames,
- vals = dims.map(() => ({})),
- n = dims.length; // collect all group-by domain values
+ curr = aggr.value,
+ dims = aggr._dnames,
+ vals = dims.map(() => ({})),
+ n = dims.length;
+ // collect all group-by domain values
function collect(cells) {
let key, i, t, v;
-
for (key in cells) {
t = cells[key].tuple;
-
for (i = 0; i < n; ++i) {
vals[i][v = t[dims[i]]] = v;
}
}
}
-
collect(aggr._prev);
- collect(curr); // iterate over key cross-product, create cells as needed
+ collect(curr);
+ // iterate over key cross-product, create cells as needed
function generate(base, tuple, index) {
const name = dims[index],
- v = vals[index++];
-
+ v = vals[index++];
for (const k in v) {
const key = base ? base + '|' + k : k;
tuple[name] = v[k];
if (index < n) generate(key, tuple, index);else if (!curr[key]) aggr.cell(key, tuple);
}
}
-
generate('', {}, 0);
},
-
init(_) {
// initialize input and output fields
const inputs = this._inputs = [],
- outputs = this._outputs = [],
- inputMap = {};
-
+ outputs = this._outputs = [],
+ inputMap = {};
function inputVisit(get) {
const fields = array$5(accessorFields(get)),
- n = fields.length;
+ n = fields.length;
let i = 0,
- f;
-
+ f;
for (; i < n; ++i) {
if (!inputMap[f = fields[i]]) {
inputMap[f] = 1;
inputs.push(f);
}
}
- } // initialize group-by dimensions
+ }
-
+ // initialize group-by dimensions
this._dims = array$5(_.groupby);
this._dnames = this._dims.map(d => {
const dname = accessorName(d);
inputVisit(d);
outputs.push(dname);
return dname;
});
- this.cellkey = _.key ? _.key : groupkey(this._dims); // initialize aggregate measures
+ this.cellkey = _.key ? _.key : groupkey(this._dims);
+ // initialize aggregate measures
this._countOnly = true;
this._counts = [];
this._measures = [];
const fields = _.fields || [null],
- ops = _.ops || ['count'],
- as = _.as || [],
- n = fields.length,
- map = {};
- let field, op, m, mname, outname, i;
-
+ ops = _.ops || ['count'],
+ aggregate_params = _.aggregate_params || [null],
+ as = _.as || [],
+ n = fields.length,
+ map = {};
+ let field, op, aggregate_param, m, mname, outname, i;
if (n !== ops.length) {
error('Unmatched number of fields and aggregate ops.');
}
-
for (i = 0; i < n; ++i) {
field = fields[i];
op = ops[i];
-
+ aggregate_param = aggregate_params[i] || null;
if (field == null && op !== 'count') {
error('Null aggregate field specified.');
}
-
mname = accessorName(field);
outname = measureName(op, mname, as[i]);
outputs.push(outname);
-
if (op === 'count') {
this._counts.push(outname);
-
continue;
}
-
m = map[mname];
-
if (!m) {
inputVisit(field);
m = map[mname] = [];
m.field = field;
-
this._measures.push(m);
}
-
if (op !== 'count') this._countOnly = false;
- m.push(createMeasure(op, outname));
+ m.push(createMeasure(op, aggregate_param, outname));
}
-
this._measures = this._measures.map(m => compileMeasures(m, m.field));
- return {}; // aggregation cells (this.value)
+ return Object.create(null); // aggregation cells (this.value)
},
-
// -- Cell Management -----
- cellkey: groupkey(),
+ cellkey: groupkey(),
cell(key, t) {
let cell = this.value[key];
-
if (!cell) {
cell = this.value[key] = this.newcell(key, t);
this._adds[this._alen++] = cell;
} else if (cell.num === 0 && this._drop && cell.stamp < this.stamp) {
cell.stamp = this.stamp;
this._adds[this._alen++] = cell;
} else if (cell.stamp < this.stamp) {
cell.stamp = this.stamp;
this._mods[this._mlen++] = cell;
}
-
return cell;
},
-
newcell(key, t) {
const cell = {
key: key,
num: 0,
agg: null,
tuple: this.newtuple(t, this._prev && this._prev[key]),
stamp: this.stamp,
store: false
};
-
if (!this._countOnly) {
const measures = this._measures,
- n = measures.length;
+ n = measures.length;
cell.agg = Array(n);
-
for (let i = 0; i < n; ++i) {
cell.agg[i] = new measures[i](cell);
}
}
-
if (cell.store) {
cell.data = new TupleStore();
}
-
return cell;
},
-
newtuple(t, p) {
const names = this._dnames,
- dims = this._dims,
- n = dims.length,
- x = {};
-
+ dims = this._dims,
+ n = dims.length,
+ x = {};
for (let i = 0; i < n; ++i) {
x[names[i]] = dims[i](t);
}
-
return p ? replace$1(p.tuple, x) : ingest$1(x);
},
-
clean() {
const cells = this.value;
-
for (const key in cells) {
if (cells[key].num === 0) {
delete cells[key];
}
}
},
-
// -- Process Tuples -----
+
add(t) {
const key = this.cellkey(t),
- cell = this.cell(key, t);
+ cell = this.cell(key, t);
cell.num += 1;
if (this._countOnly) return;
if (cell.store) cell.data.add(t);
const agg = cell.agg;
-
for (let i = 0, n = agg.length; i < n; ++i) {
agg[i].add(agg[i].get(t), t);
}
},
-
rem(t) {
const key = this.cellkey(t),
- cell = this.cell(key, t);
+ cell = this.cell(key, t);
cell.num -= 1;
if (this._countOnly) return;
if (cell.store) cell.data.rem(t);
const agg = cell.agg;
-
for (let i = 0, n = agg.length; i < n; ++i) {
agg[i].rem(agg[i].get(t), t);
}
},
-
celltuple(cell) {
const tuple = cell.tuple,
- counts = this._counts; // consolidate stored values
+ counts = this._counts;
+ // consolidate stored values
if (cell.store) {
cell.data.values();
- } // update tuple properties
+ }
-
+ // update tuple properties
for (let i = 0, n = counts.length; i < n; ++i) {
tuple[counts[i]] = cell.num;
}
-
if (!this._countOnly) {
const agg = cell.agg;
-
for (let i = 0, n = agg.length; i < n; ++i) {
agg[i].set(tuple);
}
}
-
return tuple;
},
-
changes(out) {
const adds = this._adds,
- mods = this._mods,
- prev = this._prev,
- drop = this._drop,
- add = out.add,
- rem = out.rem,
- mod = out.mod;
+ mods = this._mods,
+ prev = this._prev,
+ drop = this._drop,
+ add = out.add,
+ rem = out.rem,
+ mod = out.mod;
let cell, key, i, n;
if (prev) for (key in prev) {
cell = prev[key];
if (!drop || cell.num) rem.push(cell.tuple);
}
-
for (i = 0, n = this._alen; i < n; ++i) {
add.push(this.celltuple(adds[i]));
adds[i] = null; // for garbage collection
}
-
for (i = 0, n = this._mlen; i < n; ++i) {
cell = mods[i];
(cell.num === 0 && drop ? rem : mod).push(this.celltuple(cell));
mods[i] = null; // for garbage collection
}
-
this._alen = this._mlen = 0; // reset list of active cells
-
this._prev = null;
return out;
}
-
});
+
+ // epsilon bias to offset floating point error (#1737)
const EPSILON$1 = 1e-14;
+
/**
* Generates a binning function for discretizing data.
* @constructor
* @param {object} params - The parameters for this operator. The
* provided values should be valid options for the {@link bin} function.
* @param {function(object): *} params.field - The data field to bin.
*/
-
function Bin(params) {
Transform.call(this, null, params);
}
-
Bin.Definition = {
'type': 'Bin',
'metadata': {
'modifies': true
},
@@ -8669,114 +7679,99 @@
}]
};
inherits(Bin, Transform, {
transform(_, pulse) {
const band = _.interval !== false,
- bins = this._bins(_),
- start = bins.start,
- step = bins.step,
- as = _.as || ['bin0', 'bin1'],
- b0 = as[0],
- b1 = as[1];
-
+ bins = this._bins(_),
+ start = bins.start,
+ step = bins.step,
+ as = _.as || ['bin0', 'bin1'],
+ b0 = as[0],
+ b1 = as[1];
let flag;
-
if (_.modified()) {
pulse = pulse.reflow(true);
flag = pulse.SOURCE;
} else {
flag = pulse.modified(accessorFields(_.field)) ? pulse.ADD_MOD : pulse.ADD;
}
-
pulse.visit(flag, band ? t => {
- const v = bins(t); // minimum bin value (inclusive)
-
- t[b0] = v; // maximum bin value (exclusive)
+ const v = bins(t);
+ // minimum bin value (inclusive)
+ t[b0] = v;
+ // maximum bin value (exclusive)
// use convoluted math for better floating point agreement
// see https://github.com/vega/vega/issues/830
// infinite values propagate through this formula! #2227
-
t[b1] = v == null ? null : start + step * (1 + (v - start) / step);
} : t => t[b0] = bins(t));
return pulse.modifies(band ? as : b0);
},
-
_bins(_) {
if (this.value && !_.modified()) {
return this.value;
}
-
const field = _.field,
- bins = bin(_),
- step = bins.step;
+ bins = bin(_),
+ step = bins.step;
let start = bins.start,
- stop = start + Math.ceil((bins.stop - start) / step) * step,
- a,
- d;
-
+ stop = start + Math.ceil((bins.stop - start) / step) * step,
+ a,
+ d;
if ((a = _.anchor) != null) {
d = a - (start + step * Math.floor((a - start) / step));
start += d;
stop += d;
}
-
const f = function (t) {
let v = toNumber(field(t));
return v == null ? null : v < start ? -Infinity : v > stop ? +Infinity : (v = Math.max(start, Math.min(v, stop - step)), start + step * Math.floor(EPSILON$1 + (v - start) / step));
};
-
f.start = start;
f.stop = bins.stop;
f.step = step;
return this.value = accessor(f, accessorFields(field), _.name || 'bin_' + accessorName(field));
}
-
});
-
function SortedList(idFunc, source, input) {
const $ = idFunc;
let data = source || [],
- add = input || [],
- rem = {},
- cnt = 0;
+ add = input || [],
+ rem = {},
+ cnt = 0;
return {
add: t => add.push(t),
remove: t => rem[$(t)] = ++cnt,
size: () => data.length,
data: (compare, resort) => {
if (cnt) {
data = data.filter(t => !rem[$(t)]);
rem = {};
cnt = 0;
}
-
if (resort && compare) {
data.sort(compare);
}
-
if (add.length) {
data = compare ? merge$3(compare, data, add.sort(compare)) : data.concat(add);
add = [];
}
-
return data;
}
};
}
+
/**
* Collects all data tuples that pass through this operator.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(*,*): number} [params.sort] - An optional
* comparator function for additionally sorting the collected tuples.
*/
-
-
function Collect$1(params) {
Transform.call(this, [], params);
}
-
Collect$1.Definition = {
'type': 'Collect',
'metadata': {
'source': true
},
@@ -8786,58 +7781,53 @@
}]
};
inherits(Collect$1, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.ALL),
- list = SortedList(tupleid, this.value, out.materialize(out.ADD).add),
- sort = _.sort,
- mod = pulse.changed() || sort && (_.modified('sort') || pulse.modified(sort.fields));
+ list = SortedList(tupleid, this.value, out.materialize(out.ADD).add),
+ sort = _.sort,
+ mod = pulse.changed() || sort && (_.modified('sort') || pulse.modified(sort.fields));
out.visit(out.REM, list.remove);
this.modified(mod);
- this.value = out.source = list.data(stableCompare(sort), mod); // propagate tree root if defined
+ this.value = out.source = list.data(stableCompare(sort), mod);
+ // propagate tree root if defined
if (pulse.source && pulse.source.root) {
this.value.root = pulse.source.root;
}
-
return out;
}
-
});
+
/**
* Generates a comparator function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<string|function>} params.fields - The fields to compare.
* @param {Array<string>} [params.orders] - The sort orders.
* Each entry should be one of "ascending" (default) or "descending".
*/
-
function Compare$1(params) {
Operator.call(this, null, update$5, params);
}
-
inherits(Compare$1, Operator);
-
function update$5(_) {
return this.value && !_.modified() ? this.value : compare$1(_.fields, _.orders);
}
+
/**
* Count regexp-defined pattern occurrences in a text field.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - An accessor for the text field.
* @param {string} [params.pattern] - RegExp string defining the text pattern.
* @param {string} [params.case] - One of 'lower', 'upper' or null (mixed) case.
* @param {string} [params.stopwords] - RegExp string of words to ignore.
*/
-
-
function CountPattern(params) {
Transform.call(this, null, params);
}
-
CountPattern.Definition = {
'type': 'CountPattern',
'metadata': {
'generates': true,
'changes': true
@@ -8865,88 +7855,72 @@
'array': true,
'length': 2,
'default': ['text', 'count']
}]
};
-
function tokenize(text, tcase, match) {
switch (tcase) {
case 'upper':
text = text.toUpperCase();
break;
-
case 'lower':
text = text.toLowerCase();
break;
}
-
return text.match(match);
}
-
inherits(CountPattern, Transform, {
transform(_, pulse) {
const process = update => tuple => {
var tokens = tokenize(get(tuple), _.case, match) || [],
- t;
-
+ t;
for (var i = 0, n = tokens.length; i < n; ++i) {
if (!stop.test(t = tokens[i])) update(t);
}
};
-
const init = this._parameterCheck(_, pulse),
- counts = this._counts,
- match = this._match,
- stop = this._stop,
- get = _.field,
- as = _.as || ['text', 'count'],
- add = process(t => counts[t] = 1 + (counts[t] || 0)),
- rem = process(t => counts[t] -= 1);
-
+ counts = this._counts,
+ match = this._match,
+ stop = this._stop,
+ get = _.field,
+ as = _.as || ['text', 'count'],
+ add = process(t => counts[t] = 1 + (counts[t] || 0)),
+ rem = process(t => counts[t] -= 1);
if (init) {
pulse.visit(pulse.SOURCE, add);
} else {
pulse.visit(pulse.ADD, add);
pulse.visit(pulse.REM, rem);
}
-
return this._finish(pulse, as); // generate output tuples
},
-
_parameterCheck(_, pulse) {
let init = false;
-
if (_.modified('stopwords') || !this._stop) {
this._stop = new RegExp('^' + (_.stopwords || '') + '$', 'i');
init = true;
}
-
if (_.modified('pattern') || !this._match) {
this._match = new RegExp(_.pattern || '[\\w\']+', 'g');
init = true;
}
-
if (_.modified('field') || pulse.modified(_.field.fields)) {
init = true;
}
-
if (init) this._counts = {};
return init;
},
-
_finish(pulse, as) {
const counts = this._counts,
- tuples = this._tuples || (this._tuples = {}),
- text = as[0],
- count = as[1],
- out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
+ tuples = this._tuples || (this._tuples = {}),
+ text = as[0],
+ count = as[1],
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
let w, t, c;
-
for (w in counts) {
t = tuples[w];
c = counts[w] || 0;
-
if (!t && c) {
tuples[w] = t = ingest$1({});
t[text] = w;
t[count] = c;
out.add.push(t);
@@ -8957,28 +7931,25 @@
} else if (t[count] !== c) {
t[count] = c;
out.mod.push(t);
}
}
-
return out.modifies(as);
}
-
});
+
/**
* Perform a cross-product of a tuple stream with itself.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object):boolean} [params.filter] - An optional filter
* function for selectively including tuples in the cross product.
* @param {Array<string>} [params.as] - The names of the output fields.
*/
-
function Cross(params) {
Transform.call(this, null, params);
}
-
Cross.Definition = {
'type': 'Cross',
'metadata': {
'generates': true
},
@@ -8994,66 +7965,57 @@
}]
};
inherits(Cross, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE),
- as = _.as || ['a', 'b'],
- a = as[0],
- b = as[1],
- reset = !this.value || pulse.changed(pulse.ADD_REM) || _.modified('as') || _.modified('filter');
-
+ as = _.as || ['a', 'b'],
+ a = as[0],
+ b = as[1],
+ reset = !this.value || pulse.changed(pulse.ADD_REM) || _.modified('as') || _.modified('filter');
let data = this.value;
-
if (reset) {
if (data) out.rem = data;
data = pulse.materialize(pulse.SOURCE).source;
out.add = this.value = cross(data, a, b, _.filter || truthy);
} else {
out.mod = data;
}
-
out.source = this.value;
return out.modifies(as);
}
-
});
-
function cross(input, a, b, filter) {
var data = [],
- t = {},
- n = input.length,
- i = 0,
- j,
- left;
-
+ t = {},
+ n = input.length,
+ i = 0,
+ j,
+ left;
for (; i < n; ++i) {
t[a] = left = input[i];
-
for (j = 0; j < n; ++j) {
t[b] = input[j];
-
if (filter(t)) {
data.push(ingest$1(t));
t = {};
t[a] = left;
}
}
}
-
return data;
}
-
const Distributions = {
kde: kde,
mixture: mixture$1,
normal: gaussian,
lognormal: lognormal,
uniform: uniform
};
const DISTRIBUTIONS = 'distributions',
- FUNCTION = 'function',
- FIELD = 'field';
+ FUNCTION = 'function',
+ FIELD = 'field';
+
/**
* Parse a parameter object for a probability distribution.
* @param {object} def - The distribution parameter object.
* @param {function():Array<object>} - A method for requesting
* source data. Used for distributions (such as KDE) that
@@ -9061,35 +8023,35 @@
* invoked if the 'from' parameter for a target data source
* is not provided. Typically this method returns backing
* source data for a Pulse object.
* @return {object} - The output distribution object.
*/
-
function parse$4(def, data) {
const func = def[FUNCTION];
-
if (!has$1(Distributions, func)) {
error('Unknown distribution function: ' + func);
}
-
const d = Distributions[func]();
-
for (const name in def) {
// if data field, extract values
if (name === FIELD) {
d.data((def.from || data()).map(def[name]));
- } // if distribution mixture, recurse to parse each definition
+ }
+
+ // if distribution mixture, recurse to parse each definition
else if (name === DISTRIBUTIONS) {
d[name](def[name].map(_ => parse$4(_, data)));
- } // otherwise, simply set the parameter
+ }
+
+ // otherwise, simply set the parameter
else if (typeof d[name] === FUNCTION) {
d[name](def[name]);
}
}
-
return d;
}
+
/**
* Grid sample points for a probability density. Given a distribution and
* a sampling extent, will generate points suitable for plotting either
* PDF (probability density function) or CDF (cumulative distribution
* function) curves.
@@ -9110,16 +8072,13 @@
* @param {number} [params.steps] - The exact number of curve samples for
* plotting the density. If specified, overrides both minsteps and maxsteps
* to set an exact number of uniform samples. Useful in conjunction with
* a fixed extent to ensure consistent sample points for stacked densities.
*/
-
-
function Density(params) {
Transform.call(this, null, params);
}
-
const distributions = [{
'key': {
'function': 'normal'
},
'params': [{
@@ -9227,80 +8186,70 @@
}]
};
inherits(Density, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
-
if (!this.value || pulse.changed() || _.modified()) {
const dist = parse$4(_.distribution, source$1(pulse)),
- minsteps = _.steps || _.minsteps || 25,
- maxsteps = _.steps || _.maxsteps || 200;
+ minsteps = _.steps || _.minsteps || 25,
+ maxsteps = _.steps || _.maxsteps || 200;
let method = _.method || 'pdf';
-
if (method !== 'pdf' && method !== 'cdf') {
error('Invalid density method: ' + method);
}
-
if (!_.extent && !dist.data) {
error('Missing density extent parameter.');
}
-
method = dist[method];
const as = _.as || ['value', 'density'],
- domain = _.extent || extent(dist.data()),
- values = sampleCurve(method, domain, minsteps, maxsteps).map(v => {
- const tuple = {};
- tuple[as[0]] = v[0];
- tuple[as[1]] = v[1];
- return ingest$1(tuple);
- });
+ domain = _.extent || extent(dist.data()),
+ values = sampleCurve(method, domain, minsteps, maxsteps).map(v => {
+ const tuple = {};
+ tuple[as[0]] = v[0];
+ tuple[as[1]] = v[1];
+ return ingest$1(tuple);
+ });
if (this.value) out.rem = this.value;
this.value = out.add = out.source = values;
}
-
return out;
}
-
});
-
function source$1(pulse) {
return () => pulse.materialize(pulse.SOURCE).source;
}
+ // use either provided alias or accessor field name
function fieldNames(fields, as) {
if (!fields) return null;
return fields.map((f, i) => as[i] || accessorName(f));
}
-
function partition$1$1(data, groupby, field) {
const groups = [],
- get = f => f(t);
+ get = f => f(t);
+ let map, i, n, t, k, g;
- let map, i, n, t, k, g; // partition data points into groups
-
+ // partition data points into groups
if (groupby == null) {
groups.push(data.map(field));
} else {
for (map = {}, i = 0, n = data.length; i < n; ++i) {
t = data[i];
k = groupby.map(get);
g = map[k];
-
if (!g) {
map[k] = g = [];
g.dims = k;
groups.push(g);
}
-
g.push(field(t));
}
}
-
return groups;
}
-
const Output$5 = 'bin';
+
/**
* Dot density binning for dot plot construction.
* Based on Leland Wilkinson, Dot Plots, The American Statistician, 1999.
* https://www.cs.uic.edu/~wilkinson/Publications/dotplots.pdf
* @constructor
@@ -9310,15 +8259,13 @@
* @param {number} [params.step] - The step size (bin width) within which dots should be
* stacked. Defaults to 1/30 of the extent of the data *field*.
* @param {boolean} [params.smooth=false] - A boolean flag indicating if dot density
* stacks should be smoothed to reduce variance.
*/
-
function DotBin(params) {
Transform.call(this, null, params);
}
-
DotBin.Definition = {
'type': 'DotBin',
'metadata': {
'modifies': true
},
@@ -9341,86 +8288,76 @@
'name': 'as',
'type': 'string',
'default': Output$5
}]
};
-
const autostep = (data, field) => span(extent(data, field)) / 30;
-
inherits(DotBin, Transform, {
transform(_, pulse) {
if (this.value && !(_.modified() || pulse.changed())) {
return pulse; // early exit
}
-
const source = pulse.materialize(pulse.SOURCE).source,
- groups = partition$1$1(pulse.source, _.groupby, identity$6),
- smooth = _.smooth || false,
- field = _.field,
- step = _.step || autostep(source, field),
- sort = stableCompare((a, b) => field(a) - field(b)),
- as = _.as || Output$5,
- n = groups.length; // compute dotplot bins per group
+ groups = partition$1$1(pulse.source, _.groupby, identity$6),
+ smooth = _.smooth || false,
+ field = _.field,
+ step = _.step || autostep(source, field),
+ sort = stableCompare((a, b) => field(a) - field(b)),
+ as = _.as || Output$5,
+ n = groups.length;
+ // compute dotplot bins per group
let min = Infinity,
- max = -Infinity,
- i = 0,
- j;
-
+ max = -Infinity,
+ i = 0,
+ j;
for (; i < n; ++i) {
const g = groups[i].sort(sort);
j = -1;
-
for (const v of dotbin(g, step, smooth, field)) {
if (v < min) min = v;
if (v > max) max = v;
g[++j][as] = v;
}
}
-
this.value = {
start: min,
stop: max,
step: step
};
return pulse.reflow(true).modifies(as);
}
-
});
+
/**
* Wraps an expression function with access to external parameters.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function} params.expr - The expression function. The
* function should accept both a datum and a parameter object.
* This operator's value will be a new function that wraps the
* expression function with access to this operator's parameters.
*/
-
function Expression$1(params) {
Operator.call(this, null, update$4, params);
this.modified(true);
}
-
inherits(Expression$1, Operator);
-
function update$4(_) {
const expr = _.expr;
return this.value && !_.modified('expr') ? this.value : accessor(datum => expr(datum, _), accessorFields(expr), accessorName(expr));
}
+
/**
* Computes extents (min/max) for a data field.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The field over which to compute extends.
*/
-
-
function Extent(params) {
Transform.call(this, [undefined, undefined], params);
}
-
Extent.Definition = {
'type': 'Extent',
'metadata': {},
'params': [{
'name': 'field',
@@ -9429,150 +8366,132 @@
}]
};
inherits(Extent, Transform, {
transform(_, pulse) {
const extent = this.value,
- field = _.field,
- mod = pulse.changed() || pulse.modified(field.fields) || _.modified('field');
-
+ field = _.field,
+ mod = pulse.changed() || pulse.modified(field.fields) || _.modified('field');
let min = extent[0],
- max = extent[1];
-
+ max = extent[1];
if (mod || min == null) {
min = +Infinity;
max = -Infinity;
}
-
pulse.visit(mod ? pulse.SOURCE : pulse.ADD, t => {
const v = toNumber(field(t));
-
if (v != null) {
// NaNs will fail all comparisons!
if (v < min) min = v;
if (v > max) max = v;
}
});
-
if (!Number.isFinite(min) || !Number.isFinite(max)) {
let name = accessorName(field);
- if (name) name = " for field \"".concat(name, "\"");
- pulse.dataflow.warn("Infinite extent".concat(name, ": [").concat(min, ", ").concat(max, "]"));
+ if (name) name = ` for field "${name}"`;
+ pulse.dataflow.warn(`Infinite extent${name}: [${min}, ${max}]`);
min = max = undefined;
}
-
this.value = [min, max];
}
-
});
+
/**
* Provides a bridge between a parent transform and a target subflow that
* consumes only a subset of the tuples that pass through the parent.
* @constructor
* @param {Pulse} pulse - A pulse to use as the value of this operator.
* @param {Transform} parent - The parent transform (typically a Facet instance).
*/
-
function Subflow(pulse, parent) {
Operator.call(this, pulse);
this.parent = parent;
this.count = 0;
}
-
inherits(Subflow, Operator, {
/**
* Routes pulses from this subflow to a target transform.
* @param {Transform} target - A transform that receives the subflow of tuples.
*/
connect(target) {
this.detachSubflow = target.detachSubflow;
this.targets().add(target);
return target.source = this;
},
-
/**
* Add an 'add' tuple to the subflow pulse.
* @param {Tuple} t - The tuple being added.
*/
add(t) {
this.count += 1;
this.value.add.push(t);
},
-
/**
* Add a 'rem' tuple to the subflow pulse.
* @param {Tuple} t - The tuple being removed.
*/
rem(t) {
this.count -= 1;
this.value.rem.push(t);
},
-
/**
* Add a 'mod' tuple to the subflow pulse.
* @param {Tuple} t - The tuple being modified.
*/
mod(t) {
this.value.mod.push(t);
},
-
/**
* Re-initialize this operator's pulse value.
* @param {Pulse} pulse - The pulse to copy from.
* @see Pulse.init
*/
init(pulse) {
this.value.init(pulse, pulse.NO_SOURCE);
},
-
/**
* Evaluate this operator. This method overrides the
* default behavior to simply return the contained pulse value.
* @return {Pulse}
*/
evaluate() {
// assert: this.value.stamp === pulse.stamp
return this.value;
}
-
});
+
/**
* Facets a dataflow into a set of subflows based on a key.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(Dataflow, string): Operator} params.subflow - A function
* that generates a subflow of operators and returns its root operator.
* @param {function(object): *} params.key - The key field to facet by.
*/
-
function Facet$1(params) {
Transform.call(this, {}, params);
this._keys = fastmap(); // cache previously calculated key values
+
// keep track of active subflows, use as targets array for listeners
// this allows us to limit propagation to only updated subflows
-
const a = this._targets = [];
a.active = 0;
-
a.forEach = f => {
for (let i = 0, n = a.active; i < n; ++i) {
f(a[i], i, a);
}
};
}
-
inherits(Facet$1, Transform, {
activate(flow) {
this._targets[this._targets.active++] = flow;
},
-
// parent argument provided by PreFacet subclass
subflow(key, flow, pulse, parent) {
const flows = this.value;
let sf = has$1(flows, key) && flows[key],
- df,
- p;
-
+ df,
+ p;
if (!sf) {
p = parent || (p = this._group[key]) && p.tuple;
df = pulse.dataflow;
sf = new Subflow(pulse.fork(pulse.NO_SOURCE), this);
df.add(sf).connect(flow(df, key, p));
@@ -9580,84 +8499,71 @@
this.activate(sf);
} else if (sf.value.stamp < pulse.stamp) {
sf.init(pulse);
this.activate(sf);
}
-
return sf;
},
-
clean() {
const flows = this.value;
let detached = 0;
-
for (const key in flows) {
if (flows[key].count === 0) {
const detach = flows[key].detachSubflow;
if (detach) detach();
delete flows[key];
++detached;
}
- } // remove inactive targets from the active targets array
+ }
-
+ // remove inactive targets from the active targets array
if (detached) {
const active = this._targets.filter(sf => sf && sf.count > 0);
-
this.initTargets(active);
}
},
-
initTargets(act) {
const a = this._targets,
- n = a.length,
- m = act ? act.length : 0;
+ n = a.length,
+ m = act ? act.length : 0;
let i = 0;
-
for (; i < m; ++i) {
a[i] = act[i];
}
-
for (; i < n && a[i] != null; ++i) {
a[i] = null; // ensure old flows can be garbage collected
}
-
a.active = m;
},
-
transform(_, pulse) {
const df = pulse.dataflow,
- key = _.key,
- flow = _.subflow,
- cache = this._keys,
- rekey = _.modified('key'),
- subflow = key => this.subflow(key, flow, pulse);
-
+ key = _.key,
+ flow = _.subflow,
+ cache = this._keys,
+ rekey = _.modified('key'),
+ subflow = key => this.subflow(key, flow, pulse);
this._group = _.group || {};
this.initTargets(); // reset list of active subflows
pulse.visit(pulse.REM, t => {
const id = tupleid(t),
- k = cache.get(id);
-
+ k = cache.get(id);
if (k !== undefined) {
cache.delete(id);
subflow(k).rem(t);
}
});
pulse.visit(pulse.ADD, t => {
const k = key(t);
cache.set(tupleid(t), k);
subflow(k).add(t);
});
-
if (rekey || pulse.modified(key.fields)) {
pulse.visit(pulse.MOD, t => {
const id = tupleid(t),
- k0 = cache.get(id),
- k1 = key(t);
-
+ k0 = cache.get(id),
+ k1 = key(t);
if (k0 === k1) {
subflow(k1).mod(t);
} else {
cache.set(id, k1);
subflow(k0).rem(t);
@@ -9667,70 +8573,61 @@
} else if (pulse.changed(pulse.MOD)) {
pulse.visit(pulse.MOD, t => {
subflow(cache.get(tupleid(t))).mod(t);
});
}
-
if (rekey) {
pulse.visit(pulse.REFLOW, t => {
const id = tupleid(t),
- k0 = cache.get(id),
- k1 = key(t);
-
+ k0 = cache.get(id),
+ k1 = key(t);
if (k0 !== k1) {
cache.set(id, k1);
subflow(k0).rem(t);
subflow(k1).add(t);
}
});
}
-
if (pulse.clean()) {
df.runAfter(() => {
this.clean();
cache.clean();
});
} else if (cache.empty > df.cleanThreshold) {
df.runAfter(cache.clean);
}
-
return pulse;
}
-
});
+
/**
* Generates one or more field accessor functions.
* If the 'name' parameter is an array, an array of field accessors
* will be created and the 'as' parameter will be ignored.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {string} params.name - The field name(s) to access.
* @param {string} params.as - The accessor function name.
*/
-
function Field$1(params) {
Operator.call(this, null, update$3, params);
}
-
inherits(Field$1, Operator);
-
function update$3(_) {
return this.value && !_.modified() ? this.value : isArray(_.name) ? array$5(_.name).map(f => field$1(f)) : field$1(_.name, _.as);
}
+
/**
* Filters data tuples according to a predicate function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.expr - The predicate expression function
* that determines a tuple's filter status. Truthy values pass the filter.
*/
-
-
function Filter(params) {
Transform.call(this, fastmap(), params);
}
-
Filter.Definition = {
'type': 'Filter',
'metadata': {
'changes': true
},
@@ -9741,54 +8638,49 @@
}]
};
inherits(Filter, Transform, {
transform(_, pulse) {
const df = pulse.dataflow,
- cache = this.value,
- // cache ids of filtered tuples
- output = pulse.fork(),
- add = output.add,
- rem = output.rem,
- mod = output.mod,
- test = _.expr;
+ cache = this.value,
+ // cache ids of filtered tuples
+ output = pulse.fork(),
+ add = output.add,
+ rem = output.rem,
+ mod = output.mod,
+ test = _.expr;
let isMod = true;
pulse.visit(pulse.REM, t => {
const id = tupleid(t);
if (!cache.has(id)) rem.push(t);else cache.delete(id);
});
pulse.visit(pulse.ADD, t => {
if (test(t, _)) add.push(t);else cache.set(tupleid(t), 1);
});
-
function revisit(t) {
const id = tupleid(t),
- b = test(t, _),
- s = cache.get(id);
-
+ b = test(t, _),
+ s = cache.get(id);
if (b && s) {
cache.delete(id);
add.push(t);
} else if (!b && !s) {
cache.set(id, 1);
rem.push(t);
} else if (isMod && b && !s) {
mod.push(t);
}
}
-
pulse.visit(pulse.MOD, revisit);
-
if (_.modified()) {
isMod = false;
pulse.visit(pulse.REFLOW, revisit);
}
-
if (cache.empty > df.cleanThreshold) df.runAfter(cache.clean);
return output;
}
-
});
+
/**
* Flattens array-typed field values into new data objects.
* If multiple fields are specified, they are treated as parallel arrays,
* with output values included for each matching index (or null if missing).
* @constructor
@@ -9799,15 +8691,13 @@
* value. If unspecified, no index field is included in the output.
* @param {Array<string>} [params.as] - Output field names for flattened
* array fields. Any unspecified fields will use the field name provided
* by the fields accessors.
*/
-
function Flatten(params) {
Transform.call(this, [], params);
}
-
Flatten.Definition = {
'type': 'Flatten',
'metadata': {
'generates': true
},
@@ -9826,60 +8716,56 @@
}]
};
inherits(Flatten, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE),
- fields = _.fields,
- as = fieldNames(fields, _.as || []),
- index = _.index || null,
- m = as.length; // remove any previous results
+ fields = _.fields,
+ as = fieldNames(fields, _.as || []),
+ index = _.index || null,
+ m = as.length;
- out.rem = this.value; // generate flattened tuples
+ // remove any previous results
+ out.rem = this.value;
+ // generate flattened tuples
pulse.visit(pulse.SOURCE, t => {
const arrays = fields.map(f => f(t)),
- maxlen = arrays.reduce((l, a) => Math.max(l, a.length), 0);
+ maxlen = arrays.reduce((l, a) => Math.max(l, a.length), 0);
let i = 0,
- j,
- d,
- v;
-
+ j,
+ d,
+ v;
for (; i < maxlen; ++i) {
d = derive(t);
-
for (j = 0; j < m; ++j) {
d[as[j]] = (v = arrays[j][i]) == null ? null : v;
}
-
if (index) {
d[index] = i;
}
-
out.add.push(d);
}
});
this.value = out.source = out.add;
if (index) out.modifies(index);
return out.modifies(as);
}
-
});
+
/**
* Folds one more tuple fields into multiple tuples in which the field
* name and values are available under new 'key' and 'value' fields.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.fields - An array of field accessors
* for the tuple fields that should be folded.
* @param {Array<string>} [params.as] - Output field names for folded key
* and value fields, defaults to ['key', 'value'].
*/
-
function Fold(params) {
Transform.call(this, [], params);
}
-
Fold.Definition = {
'type': 'Fold',
'metadata': {
'generates': true
},
@@ -9897,16 +8783,16 @@
}]
};
inherits(Fold, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE),
- fields = _.fields,
- fnames = fields.map(accessorName),
- as = _.as || ['key', 'value'],
- k = as[0],
- v = as[1],
- n = fields.length;
+ fields = _.fields,
+ fnames = fields.map(accessorName),
+ as = _.as || ['key', 'value'],
+ k = as[0],
+ v = as[1],
+ n = fields.length;
out.rem = this.value;
pulse.visit(pulse.SOURCE, t => {
for (let i = 0, d; i < n; ++i) {
d = derive(t);
d[k] = fnames[i];
@@ -9915,26 +8801,24 @@
}
});
this.value = out.source = out.add;
return out.modifies(as);
}
-
});
+
/**
* Invokes a function for each data tuple and saves the results as a new field.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.expr - The formula function to invoke for each tuple.
* @param {string} params.as - The field name under which to save the result.
* @param {boolean} [params.initonly=false] - If true, the formula is applied to
* added tuples only, and does not update in response to modifications.
*/
-
function Formula(params) {
Transform.call(this, null, params);
}
-
Formula.Definition = {
'type': 'Formula',
'metadata': {
'modifies': true
},
@@ -9952,80 +8836,72 @@
}]
};
inherits(Formula, Transform, {
transform(_, pulse) {
const func = _.expr,
- as = _.as,
- mod = _.modified(),
- flag = _.initonly ? pulse.ADD : mod ? pulse.SOURCE : pulse.modified(func.fields) || pulse.modified(as) ? pulse.ADD_MOD : pulse.ADD;
-
+ as = _.as,
+ mod = _.modified(),
+ flag = _.initonly ? pulse.ADD : mod ? pulse.SOURCE : pulse.modified(func.fields) || pulse.modified(as) ? pulse.ADD_MOD : pulse.ADD;
if (mod) {
// parameters updated, need to reflow
pulse = pulse.materialize().reflow(true);
}
-
if (!_.initonly) {
pulse.modifies(as);
}
-
return pulse.visit(flag, t => t[as] = func(t, _));
}
-
});
+
/**
* Generates data tuples using a provided generator function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(Parameters): object} params.generator - A tuple generator
* function. This function is given the operator parameters as input.
* Changes to any additional parameters will not trigger re-calculation
* of previously generated tuples. Only future tuples are affected.
* @param {number} params.size - The number of tuples to produce.
*/
-
function Generate(params) {
Transform.call(this, [], params);
}
-
inherits(Generate, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.ALL),
- gen = _.generator;
+ gen = _.generator;
let data = this.value,
- num = _.size - data.length,
- add,
- rem,
- t;
-
+ num = _.size - data.length,
+ add,
+ rem,
+ t;
if (num > 0) {
// need more tuples, generate and add
for (add = []; --num >= 0;) {
add.push(t = ingest$1(gen(_)));
data.push(t);
}
-
out.add = out.add.length ? out.materialize(out.ADD).add.concat(add) : add;
} else {
// need fewer tuples, remove
rem = data.slice(0, -num);
out.rem = out.rem.length ? out.materialize(out.REM).rem.concat(rem) : rem;
data = data.slice(-num);
}
-
out.source = this.value = data;
return out;
}
-
});
const Methods$1 = {
value: 'value',
median: median,
mean: mean,
min: min$2,
max: max$2
};
const Empty$1 = [];
+
/**
* Impute missing values.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to impute.
@@ -10041,15 +8917,13 @@
* @param {string} [method='value'] - The imputation method to use. One of
* 'value', 'mean', 'median', 'max', 'min'.
* @param {*} [value=0] - The constant value to use for imputation
* when using method 'value'.
*/
-
function Impute(params) {
Transform.call(this, [], params);
}
-
Impute.Definition = {
'type': 'Impute',
'metadata': {
'changes': true
},
@@ -10076,128 +8950,113 @@
}, {
'name': 'value',
'default': 0
}]
};
-
function getValue(_) {
var m = _.method || Methods$1.value,
- v;
-
+ v;
if (Methods$1[m] == null) {
error('Unrecognized imputation method: ' + m);
} else if (m === Methods$1.value) {
v = _.value !== undefined ? _.value : 0;
return () => v;
} else {
return Methods$1[m];
}
}
-
function getField$1(_) {
const f = _.field;
return t => t ? f(t) : NaN;
}
-
inherits(Impute, Transform, {
transform(_, pulse) {
var out = pulse.fork(pulse.ALL),
- impute = getValue(_),
- field = getField$1(_),
- fName = accessorName(_.field),
- kName = accessorName(_.key),
- gNames = (_.groupby || []).map(accessorName),
- groups = partition$4(pulse.source, _.groupby, _.key, _.keyvals),
- curr = [],
- prev = this.value,
- m = groups.domain.length,
- group,
- value,
- gVals,
- kVal,
- g,
- i,
- j,
- l,
- n,
- t;
-
+ impute = getValue(_),
+ field = getField$1(_),
+ fName = accessorName(_.field),
+ kName = accessorName(_.key),
+ gNames = (_.groupby || []).map(accessorName),
+ groups = partition$4(pulse.source, _.groupby, _.key, _.keyvals),
+ curr = [],
+ prev = this.value,
+ m = groups.domain.length,
+ group,
+ value,
+ gVals,
+ kVal,
+ g,
+ i,
+ j,
+ l,
+ n,
+ t;
for (g = 0, l = groups.length; g < l; ++g) {
group = groups[g];
gVals = group.values;
- value = NaN; // add tuples for missing values
+ value = NaN;
+ // add tuples for missing values
for (j = 0; j < m; ++j) {
if (group[j] != null) continue;
kVal = groups.domain[j];
t = {
_impute: true
};
-
for (i = 0, n = gVals.length; i < n; ++i) t[gNames[i]] = gVals[i];
-
t[kName] = kVal;
t[fName] = Number.isNaN(value) ? value = impute(group, field) : value;
curr.push(ingest$1(t));
}
- } // update pulse with imputed tuples
+ }
-
+ // update pulse with imputed tuples
if (curr.length) out.add = out.materialize(out.ADD).add.concat(curr);
if (prev.length) out.rem = out.materialize(out.REM).rem.concat(prev);
this.value = curr;
return out;
}
-
});
-
function partition$4(data, groupby, key, keyvals) {
var get = f => f(t),
- groups = [],
- domain = keyvals ? keyvals.slice() : [],
- kMap = {},
- gMap = {},
- gVals,
- gKey,
- group,
- i,
- j,
- k,
- n,
- t;
-
+ groups = [],
+ domain = keyvals ? keyvals.slice() : [],
+ kMap = {},
+ gMap = {},
+ gVals,
+ gKey,
+ group,
+ i,
+ j,
+ k,
+ n,
+ t;
domain.forEach((k, i) => kMap[k] = i + 1);
-
for (i = 0, n = data.length; i < n; ++i) {
t = data[i];
k = key(t);
j = kMap[k] || (kMap[k] = domain.push(k));
gKey = (gVals = groupby ? groupby.map(get) : Empty$1) + '';
-
if (!(group = gMap[gKey])) {
group = gMap[gKey] = [];
groups.push(group);
group.values = gVals;
}
-
group[j - 1] = t;
}
-
groups.domain = domain;
return groups;
}
+
/**
* Extend input tuples with aggregate values.
* Calcuates aggregate values and joins them with the input stream.
* @constructor
*/
-
-
function JoinAggregate(params) {
Aggregate$1.call(this, params);
}
-
JoinAggregate.Definition = {
'type': 'JoinAggregate',
'metadata': {
'modifies': true
},
@@ -10226,51 +9085,48 @@
}]
};
inherits(JoinAggregate, Aggregate$1, {
transform(_, pulse) {
const aggr = this,
- mod = _.modified();
+ mod = _.modified();
+ let cells;
- let cells; // process all input tuples to calculate aggregates
-
+ // process all input tuples to calculate aggregates
if (aggr.value && (mod || pulse.modified(aggr._inputs, true))) {
cells = aggr.value = mod ? aggr.init(_) : {};
pulse.visit(pulse.SOURCE, t => aggr.add(t));
} else {
cells = aggr.value = aggr.value || this.init(_);
pulse.visit(pulse.REM, t => aggr.rem(t));
pulse.visit(pulse.ADD, t => aggr.add(t));
- } // update aggregation cells
+ }
+ // update aggregation cells
+ aggr.changes();
- aggr.changes(); // write aggregate values to input tuples
-
+ // write aggregate values to input tuples
pulse.visit(pulse.SOURCE, t => {
extend$1(t, cells[aggr.cellkey(t)].tuple);
});
return pulse.reflow(mod).modifies(this._outputs);
},
-
changes() {
const adds = this._adds,
- mods = this._mods;
+ mods = this._mods;
let i, n;
-
for (i = 0, n = this._alen; i < n; ++i) {
this.celltuple(adds[i]);
adds[i] = null; // for garbage collection
}
-
for (i = 0, n = this._mlen; i < n; ++i) {
this.celltuple(mods[i]);
mods[i] = null; // for garbage collection
}
-
this._alen = this._mlen = 0; // reset list of active cells
}
-
});
+
/**
* Compute kernel density estimates (KDE) for one or more data groups.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors
@@ -10298,15 +9154,13 @@
* @param {number} [params.steps] - The exact number of curve samples for
* plotting the density. If specified, overrides both minsteps and maxsteps
* to set an exact number of uniform samples. Useful in conjunction with
* a fixed extent to ensure consistent sample points for stacked densities.
*/
-
function KDE(params) {
Transform.call(this, null, params);
}
-
KDE.Definition = {
'type': 'KDE',
'metadata': {
'generates': true
},
@@ -10359,101 +9213,86 @@
}]
};
inherits(KDE, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
-
if (!this.value || pulse.changed() || _.modified()) {
const source = pulse.materialize(pulse.SOURCE).source,
- groups = partition$1$1(source, _.groupby, _.field),
- names = (_.groupby || []).map(accessorName),
- bandwidth = _.bandwidth,
- method = _.cumulative ? 'cdf' : 'pdf',
- as = _.as || ['value', 'density'],
- values = [];
+ groups = partition$1$1(source, _.groupby, _.field),
+ names = (_.groupby || []).map(accessorName),
+ bandwidth = _.bandwidth,
+ method = _.cumulative ? 'cdf' : 'pdf',
+ as = _.as || ['value', 'density'],
+ values = [];
let domain = _.extent,
- minsteps = _.steps || _.minsteps || 25,
- maxsteps = _.steps || _.maxsteps || 200;
-
+ minsteps = _.steps || _.minsteps || 25,
+ maxsteps = _.steps || _.maxsteps || 200;
if (method !== 'pdf' && method !== 'cdf') {
error('Invalid density method: ' + method);
}
-
if (_.resolve === 'shared') {
if (!domain) domain = extent(source, _.field);
minsteps = maxsteps = _.steps || maxsteps;
}
-
groups.forEach(g => {
const density = kde(g, bandwidth)[method],
- scale = _.counts ? g.length : 1,
- local = domain || extent(g);
+ scale = _.counts ? g.length : 1,
+ local = domain || extent(g);
sampleCurve(density, local, minsteps, maxsteps).forEach(v => {
const t = {};
-
for (let i = 0; i < names.length; ++i) {
t[names[i]] = g.dims[i];
}
-
t[as[0]] = v[0];
t[as[1]] = v[1] * scale;
values.push(ingest$1(t));
});
});
if (this.value) out.rem = this.value;
this.value = out.add = out.source = values;
}
-
return out;
}
-
});
+
/**
* Generates a key function.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<string>} params.fields - The field name(s) for the key function.
* @param {boolean} params.flat - A boolean flag indicating if the field names
* should be treated as flat property names, side-stepping nested field
* lookups normally indicated by dot or bracket notation.
*/
-
function Key$1(params) {
Operator.call(this, null, update$2, params);
}
-
inherits(Key$1, Operator);
-
function update$2(_) {
return this.value && !_.modified() ? this.value : key(_.fields, _.flat);
}
+
/**
* Load and parse data from an external source. Marshalls parameter
* values and then invokes the Dataflow request method.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {string} params.url - The URL to load from.
* @param {object} params.format - The data format options.
*/
-
-
function Load$1(params) {
Transform.call(this, [], params);
this._pending = null;
}
-
inherits(Load$1, Transform, {
transform(_, pulse) {
const df = pulse.dataflow;
-
if (this._pending) {
// update state and return pulse
return output(this, pulse, this._pending);
}
-
if (stop(_)) return pulse.StopPropagation;
-
if (_.values) {
// parse and ingest values, return output pulse
return output(this, pulse, df.parse(_.values, _.format));
} else if (_.async) {
// return promise for non-blocking async loading
@@ -10467,41 +9306,36 @@
} else {
// return promise for synchronous loading
return df.request(_.url, _.format).then(res => output(this, pulse, array$5(res.data)));
}
}
-
});
-
function stop(_) {
return _.modified('async') && !(_.modified('values') || _.modified('url') || _.modified('format'));
}
-
function output(op, pulse, data) {
data.forEach(ingest$1);
const out = pulse.fork(pulse.NO_FIELDS & pulse.NO_SOURCE);
out.rem = op.value;
op.value = out.source = out.add = data;
op._pending = null;
if (out.rem.length) out.clean(true);
return out;
}
+
/**
* Extend tuples by joining them with values from a lookup table.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Map} params.index - The lookup table map.
* @param {Array<function(object): *} params.fields - The fields to lookup.
* @param {Array<string>} params.as - Output field names for each lookup value.
* @param {*} [params.default] - A default value to use if lookup fails.
*/
-
-
function Lookup(params) {
Transform.call(this, {}, params);
}
-
Lookup.Definition = {
'type': 'Lookup',
'metadata': {
'modifies': true
},
@@ -10536,137 +9370,117 @@
}]
};
inherits(Lookup, Transform, {
transform(_, pulse) {
const keys = _.fields,
- index = _.index,
- values = _.values,
- defaultValue = _.default == null ? null : _.default,
- reset = _.modified(),
- n = keys.length;
-
+ index = _.index,
+ values = _.values,
+ defaultValue = _.default == null ? null : _.default,
+ reset = _.modified(),
+ n = keys.length;
let flag = reset ? pulse.SOURCE : pulse.ADD,
- out = pulse,
- as = _.as,
- set,
- m,
- mods;
-
+ out = pulse,
+ as = _.as,
+ set,
+ m,
+ mods;
if (values) {
m = values.length;
-
if (n > 1 && !as) {
error('Multi-field lookup requires explicit "as" parameter.');
}
-
if (as && as.length !== n * m) {
error('The "as" parameter has too few output field names.');
}
-
as = as || values.map(accessorName);
-
set = function (t) {
for (var i = 0, k = 0, j, v; i < n; ++i) {
v = index.get(keys[i](t));
if (v == null) for (j = 0; j < m; ++j, ++k) t[as[k]] = defaultValue;else for (j = 0; j < m; ++j, ++k) t[as[k]] = values[j](v);
}
};
} else {
if (!as) {
error('Missing output field names.');
}
-
set = function (t) {
for (var i = 0, v; i < n; ++i) {
v = index.get(keys[i](t));
t[as[i]] = v == null ? defaultValue : v;
}
};
}
-
if (reset) {
out = pulse.reflow(true);
} else {
mods = keys.some(k => pulse.modified(k.fields));
flag |= mods ? pulse.MOD : 0;
}
-
pulse.visit(flag, set);
return out.modifies(as);
}
-
});
+
/**
* Computes global min/max extents over a collection of extents.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<Array<number>>} params.extents - The input extents.
*/
-
function MultiExtent$1(params) {
Operator.call(this, null, update$1, params);
}
-
inherits(MultiExtent$1, Operator);
-
function update$1(_) {
if (this.value && !_.modified()) {
return this.value;
}
-
const ext = _.extents,
- n = ext.length;
+ n = ext.length;
let min = +Infinity,
- max = -Infinity,
- i,
- e;
-
+ max = -Infinity,
+ i,
+ e;
for (i = 0; i < n; ++i) {
e = ext[i];
if (e[0] < min) min = e[0];
if (e[1] > max) max = e[1];
}
-
return [min, max];
}
+
/**
* Merge a collection of value arrays.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<Array<*>>} params.values - The input value arrrays.
*/
-
-
function MultiValues$1(params) {
Operator.call(this, null, update, params);
}
-
inherits(MultiValues$1, Operator);
-
function update(_) {
return this.value && !_.modified() ? this.value : _.values.reduce((data, _) => data.concat(_), []);
}
+
/**
* Operator whose value is simply its parameter hash. This operator is
* useful for enabling reactive updates to values of nested objects.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
-
function Params$2(params) {
Transform.call(this, null, params);
}
-
inherits(Params$2, Transform, {
transform(_, pulse) {
this.modified(_.modified());
this.value = _;
return pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS); // do not pass tuples
}
-
});
+
/**
* Aggregate and pivot selected field values to become new fields.
* This operator is useful to construction cross-tabulations.
* @constructor
* @param {Array<function(object): *>} [params.groupby] - An array of accessors
@@ -10679,15 +9493,13 @@
* applied per cell in the output stream. The default is "sum".
* @param {number} [params.limit] - An optional parameter indicating the maximum
* number of pivoted fields to generate. The pivoted field names are sorted in
* ascending order prior to enforcing the limit.
*/
-
function Pivot(params) {
Aggregate$1.call(this, params);
}
-
Pivot.Definition = {
'type': 'Pivot',
'metadata': {
'generates': true,
'changes': true
@@ -10718,84 +9530,79 @@
'type': 'field'
}]
};
inherits(Pivot, Aggregate$1, {
_transform: Aggregate$1.prototype.transform,
-
transform(_, pulse) {
return this._transform(aggregateParams(_, pulse), pulse);
}
+ });
- }); // Shoehorn a pivot transform into an aggregate transform!
+ // Shoehorn a pivot transform into an aggregate transform!
// First collect all unique pivot field values.
// Then generate aggregate fields for each output pivot field.
-
function aggregateParams(_, pulse) {
const key = _.field,
- value = _.value,
- op = (_.op === 'count' ? '__count__' : _.op) || 'sum',
- fields = accessorFields(key).concat(accessorFields(value)),
- keys = pivotKeys(key, _.limit || 0, pulse); // if data stream content changes, pivot fields may change
- // flag parameter modification to ensure re-initialization
+ value = _.value,
+ op = (_.op === 'count' ? '__count__' : _.op) || 'sum',
+ fields = accessorFields(key).concat(accessorFields(value)),
+ keys = pivotKeys(key, _.limit || 0, pulse);
+ // if data stream content changes, pivot fields may change
+ // flag parameter modification to ensure re-initialization
if (pulse.changed()) _.set('__pivot__', null, null, true);
return {
key: _.key,
groupby: _.groupby,
ops: keys.map(() => op),
fields: keys.map(k => get$4(k, key, value, fields)),
as: keys.map(k => k + ''),
modified: _.modified.bind(_)
};
- } // Generate aggregate field accessor.
- // Output NaN for non-existent values; aggregator will ignore!
+ }
-
+ // Generate aggregate field accessor.
+ // Output NaN for non-existent values; aggregator will ignore!
function get$4(k, key, value, fields) {
return accessor(d => key(d) === k ? value(d) : NaN, fields, k + '');
- } // Collect (and optionally limit) all unique pivot values.
+ }
-
+ // Collect (and optionally limit) all unique pivot values.
function pivotKeys(key, limit, pulse) {
const map = {},
- list = [];
+ list = [];
pulse.visit(pulse.SOURCE, t => {
const k = key(t);
-
if (!map[k]) {
map[k] = 1;
list.push(k);
}
});
list.sort(ascending$2);
return limit ? list.slice(0, limit) : list;
}
+
/**
* Partitions pre-faceted data into tuple subflows.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(Dataflow, string): Operator} params.subflow - A function
* that generates a subflow of operators and returns its root operator.
* @param {function(object): Array<object>} params.field - The field
* accessor for an array of subflow tuple objects.
*/
-
-
function PreFacet$1(params) {
Facet$1.call(this, params);
}
-
inherits(PreFacet$1, Facet$1, {
transform(_, pulse) {
const flow = _.subflow,
- field = _.field,
- subflow = t => this.subflow(tupleid(t), flow, pulse, t);
-
+ field = _.field,
+ subflow = t => this.subflow(tupleid(t), flow, pulse, t);
if (_.modified('field') || field && pulse.modified(accessorFields(field))) {
error('PreFacet does not support field modification.');
}
-
this.initTargets(); // reset list of active subflows
if (field) {
pulse.visit(pulse.MOD, t => {
const sf = subflow(t);
@@ -10812,19 +9619,17 @@
} else {
pulse.visit(pulse.MOD, t => subflow(t).mod(t));
pulse.visit(pulse.ADD, t => subflow(t).add(t));
pulse.visit(pulse.REM, t => subflow(t).rem(t));
}
-
if (pulse.clean()) {
pulse.runAfter(() => this.clean());
}
-
return pulse;
}
-
});
+
/**
* Performs a relational projection, copying selected fields from source
* tuples to a new set of derived tuples.
* @constructor
* @param {object} params - The parameters for this operator.
@@ -10833,15 +9638,13 @@
* copied with names unchanged.
* @param {Array<string>} [params.as] - Output field names for each projected
* field. Any unspecified fields will use the field name provided by
* the field accessor.
*/
-
function Project(params) {
Transform.call(this, null, params);
}
-
Project.Definition = {
'type': 'Project',
'metadata': {
'generates': true,
'changes': true
@@ -10858,22 +9661,20 @@
}]
};
inherits(Project, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE),
- fields = _.fields,
- as = fieldNames(_.fields, _.as || []),
- derive = fields ? (s, t) => project(s, t, fields, as) : rederive;
+ fields = _.fields,
+ as = fieldNames(_.fields, _.as || []),
+ derive = fields ? (s, t) => project(s, t, fields, as) : rederive;
let lut;
-
if (this.value) {
lut = this.value;
} else {
pulse = pulse.addAll();
lut = this.value = {};
}
-
pulse.visit(pulse.REM, t => {
const id = tupleid(t);
out.rem.push(lut[id]);
lut[id] = null;
});
@@ -10885,40 +9686,35 @@
pulse.visit(pulse.MOD, t => {
out.mod.push(derive(t, lut[tupleid(t)]));
});
return out;
}
-
});
-
function project(s, t, fields, as) {
for (let i = 0, n = fields.length; i < n; ++i) {
t[as[i]] = fields[i](s);
}
-
return t;
}
+
/**
* Proxy the value of another operator as a pure signal value.
* Ensures no tuples are propagated.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {*} params.value - The value to proxy, becomes the value of this operator.
*/
-
-
function Proxy$1(params) {
Transform.call(this, null, params);
}
-
inherits(Proxy$1, Transform, {
transform(_, pulse) {
this.value = _.value;
return _.modified('value') ? pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS) : pulse.StopPropagation;
}
-
});
+
/**
* Generates sample quantile values from an input data stream.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - An accessor for the data field
@@ -10931,15 +9727,13 @@
* @param {Array<number>} [params.step=0.01] - A probability step size for
* sampling quantile values. All values from one-half the step size up to
* 1 (exclusive) will be sampled. This parameter is only used if the
* *quantiles* parameter is not provided.
*/
-
function Quantile$1(params) {
Transform.call(this, null, params);
}
-
Quantile$1.Definition = {
'type': 'Quantile',
'metadata': {
'generates': true,
'changes': true
@@ -10969,71 +9763,62 @@
};
const EPSILON$2 = 1e-14;
inherits(Quantile$1, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- as = _.as || ['prob', 'value'];
-
+ as = _.as || ['prob', 'value'];
if (this.value && !_.modified() && !pulse.changed()) {
out.source = this.value;
return out;
}
-
const source = pulse.materialize(pulse.SOURCE).source,
- groups = partition$1$1(source, _.groupby, _.field),
- names = (_.groupby || []).map(accessorName),
- values = [],
- step = _.step || 0.01,
- p = _.probs || range$3(step / 2, 1 - EPSILON$2, step),
- n = p.length;
+ groups = partition$1$1(source, _.groupby, _.field),
+ names = (_.groupby || []).map(accessorName),
+ values = [],
+ step = _.step || 0.01,
+ p = _.probs || range$3(step / 2, 1 - EPSILON$2, step),
+ n = p.length;
groups.forEach(g => {
const q = quantiles(g, p);
-
for (let i = 0; i < n; ++i) {
const t = {};
-
for (let i = 0; i < names.length; ++i) {
t[names[i]] = g.dims[i];
}
-
t[as[0]] = p[i];
t[as[1]] = q[i];
values.push(ingest$1(t));
}
});
if (this.value) out.rem = this.value;
this.value = out.add = out.source = values;
return out;
}
-
});
+
/**
* Relays a data stream between data processing pipelines.
* If the derive parameter is set, this transform will create derived
* copies of observed tuples. This provides derived data streams in which
* modifications to the tuples do not pollute an upstream data source.
* @param {object} params - The parameters for this operator.
* @param {number} [params.derive=false] - Boolean flag indicating if
* the transform should make derived copies of incoming tuples.
* @constructor
*/
-
function Relay$1(params) {
Transform.call(this, null, params);
}
-
inherits(Relay$1, Transform, {
transform(_, pulse) {
let out, lut;
-
if (this.value) {
lut = this.value;
} else {
out = pulse = pulse.addAll();
lut = this.value = {};
}
-
if (_.derive) {
out = pulse.fork(pulse.NO_SOURCE);
pulse.visit(pulse.REM, t => {
const id = tupleid(t);
out.rem.push(lut[id]);
@@ -11044,39 +9829,34 @@
lut[tupleid(t)] = dt;
out.add.push(dt);
});
pulse.visit(pulse.MOD, t => {
const dt = lut[tupleid(t)];
-
for (const k in t) {
- dt[k] = t[k]; // down stream writes may overwrite re-derived tuples
+ dt[k] = t[k];
+ // down stream writes may overwrite re-derived tuples
// conservatively mark all source fields as modified
-
out.modifies(k);
}
-
out.mod.push(dt);
});
}
-
return out;
}
-
});
+
/**
* Samples tuples passing through this operator.
* Uses reservoir sampling to maintain a representative sample.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {number} [params.size=1000] - The maximum number of samples.
*/
-
function Sample(params) {
Transform.call(this, [], params);
this.count = 0;
}
-
Sample.Definition = {
'type': 'Sample',
'metadata': {},
'params': [{
'name': 'size',
@@ -11085,110 +9865,94 @@
}]
};
inherits(Sample, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE),
- mod = _.modified('size'),
- num = _.size,
- map = this.value.reduce((m, t) => (m[tupleid(t)] = 1, m), {});
-
+ mod = _.modified('size'),
+ num = _.size,
+ map = this.value.reduce((m, t) => (m[tupleid(t)] = 1, m), {});
let res = this.value,
- cnt = this.count,
- cap = 0; // sample reservoir update function
+ cnt = this.count,
+ cap = 0;
+ // sample reservoir update function
function update(t) {
let p, idx;
-
if (res.length < num) {
res.push(t);
} else {
idx = ~~((cnt + 1) * exports.random());
-
if (idx < res.length && idx >= cap) {
p = res[idx];
if (map[tupleid(p)]) out.rem.push(p); // eviction
-
res[idx] = t;
}
}
-
++cnt;
}
-
if (pulse.rem.length) {
// find all tuples that should be removed, add to output
pulse.visit(pulse.REM, t => {
const id = tupleid(t);
-
if (map[id]) {
map[id] = -1;
out.rem.push(t);
}
-
--cnt;
- }); // filter removed tuples out of the sample reservoir
+ });
+ // filter removed tuples out of the sample reservoir
res = res.filter(t => map[tupleid(t)] !== -1);
}
-
if ((pulse.rem.length || mod) && res.length < num && pulse.source) {
// replenish sample if backing data source is available
cap = cnt = res.length;
pulse.visit(pulse.SOURCE, t => {
// update, but skip previously sampled tuples
if (!map[tupleid(t)]) update(t);
});
cap = -1;
}
-
if (mod && res.length > num) {
const n = res.length - num;
-
for (let i = 0; i < n; ++i) {
map[tupleid(res[i])] = -1;
out.rem.push(res[i]);
}
-
res = res.slice(n);
}
-
if (pulse.mod.length) {
// propagate modified tuples in the sample reservoir
pulse.visit(pulse.MOD, t => {
if (map[tupleid(t)]) out.mod.push(t);
});
}
-
if (pulse.add.length) {
// update sample reservoir
pulse.visit(pulse.ADD, update);
}
-
if (pulse.add.length || cap < 0) {
// output newly added tuples
out.add = res.filter(t => !map[tupleid(t)]);
}
-
this.count = cnt;
this.value = out.source = res;
return out;
}
-
});
+
/**
* Generates data tuples for a specified sequence range of numbers.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {number} params.start - The first number in the sequence.
* @param {number} params.stop - The last number (exclusive) in the sequence.
* @param {number} [params.step=1] - The step size between numbers in the sequence.
*/
-
function Sequence(params) {
Transform.call(this, null, params);
}
-
Sequence.Definition = {
'type': 'Sequence',
'metadata': {
'generates': true,
'changes': true
@@ -11213,52 +9977,48 @@
};
inherits(Sequence, Transform, {
transform(_, pulse) {
if (this.value && !_.modified()) return;
const out = pulse.materialize().fork(pulse.MOD),
- as = _.as || 'data';
+ as = _.as || 'data';
out.rem = this.value ? pulse.rem.concat(this.value) : pulse.rem;
this.value = range$3(_.start, _.stop, _.step || 1).map(v => {
const t = {};
t[as] = v;
return ingest$1(t);
});
out.add = pulse.add.concat(this.value);
return out;
}
-
});
+
/**
* Propagates a new pulse without any tuples so long as the input
* pulse contains some added, removed or modified tuples.
* @param {object} params - The parameters for this operator.
* @constructor
*/
-
function Sieve$1(params) {
Transform.call(this, null, params);
this.modified(true); // always treat as modified
}
-
inherits(Sieve$1, Transform, {
transform(_, pulse) {
this.value = pulse.source;
return pulse.changed() ? pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS) : pulse.StopPropagation;
}
-
});
+
/**
* Discretize dates to specific time units.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The data field containing date/time values.
*/
-
function TimeUnit(params) {
Transform.call(this, null, params);
}
-
const OUTPUT = ['unit0', 'unit1'];
TimeUnit.Definition = {
'type': 'TimeUnit',
'metadata': {
'modifies': true
@@ -11302,34 +10062,30 @@
}]
};
inherits(TimeUnit, Transform, {
transform(_, pulse) {
const field = _.field,
- band = _.interval !== false,
- utc = _.timezone === 'utc',
- floor = this._floor(_, pulse),
- offset = (utc ? utcInterval : timeInterval)(floor.unit).offset,
- as = _.as || OUTPUT,
- u0 = as[0],
- u1 = as[1],
- step = floor.step;
-
+ band = _.interval !== false,
+ utc = _.timezone === 'utc',
+ floor = this._floor(_, pulse),
+ offset = (utc ? utcInterval : timeInterval)(floor.unit).offset,
+ as = _.as || OUTPUT,
+ u0 = as[0],
+ u1 = as[1],
+ step = floor.step;
let min = floor.start || Infinity,
- max = floor.stop || -Infinity,
- flag = pulse.ADD;
-
+ max = floor.stop || -Infinity,
+ flag = pulse.ADD;
if (_.modified() || pulse.changed(pulse.REM) || pulse.modified(accessorFields(field))) {
pulse = pulse.reflow(true);
flag = pulse.SOURCE;
min = Infinity;
max = -Infinity;
}
-
pulse.visit(flag, t => {
const v = field(t);
let a, b;
-
if (v == null) {
t[u0] = null;
if (band) t[u1] = null;
} else {
t[u0] = a = b = floor(v);
@@ -11340,110 +10096,100 @@
});
floor.start = min;
floor.stop = max;
return pulse.modifies(band ? as : u0);
},
-
_floor(_, pulse) {
- const utc = _.timezone === 'utc'; // get parameters
+ const utc = _.timezone === 'utc';
+ // get parameters
const {
units,
step
} = _.units ? {
units: _.units,
step: _.step || 1
} : bin$1({
extent: _.extent || extent(pulse.materialize(pulse.SOURCE).source, _.field),
maxbins: _.maxbins
- }); // check / standardize time units
+ });
+ // check / standardize time units
const tunits = timeUnits(units),
- prev = this.value || {},
- floor = (utc ? utcFloor : timeFloor)(tunits, step);
+ prev = this.value || {},
+ floor = (utc ? utcFloor : timeFloor)(tunits, step);
floor.unit = peek$1(tunits);
floor.units = tunits;
floor.step = step;
floor.start = prev.start;
floor.stop = prev.stop;
return this.value = floor;
}
-
});
+
/**
* An index that maps from unique, string-coerced, field values to tuples.
* Assumes that the field serves as a unique key with no duplicate values.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The field accessor to index.
*/
-
function TupleIndex(params) {
Transform.call(this, fastmap(), params);
}
-
inherits(TupleIndex, Transform, {
transform(_, pulse) {
const df = pulse.dataflow,
- field = _.field,
- index = this.value,
- set = t => index.set(field(t), t);
-
+ field = _.field,
+ index = this.value,
+ set = t => index.set(field(t), t);
let mod = true;
-
if (_.modified('field') || pulse.modified(field.fields)) {
index.clear();
pulse.visit(pulse.SOURCE, set);
} else if (pulse.changed()) {
pulse.visit(pulse.REM, t => index.delete(field(t)));
pulse.visit(pulse.ADD, set);
} else {
mod = false;
}
-
this.modified(mod);
if (index.empty > df.cleanThreshold) df.runAfter(index.clean);
return pulse.fork();
}
-
});
+
/**
* Extracts an array of values. Assumes the source data has already been
* reduced as needed (e.g., by an upstream Aggregate transform).
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The domain field to extract.
* @param {function(*,*): number} [params.sort] - An optional
* comparator function for sorting the values. The comparator will be
* applied to backing tuples prior to value extraction.
*/
-
function Values$1(params) {
Transform.call(this, null, params);
}
-
inherits(Values$1, Transform, {
transform(_, pulse) {
const run = !this.value || _.modified('field') || _.modified('sort') || pulse.changed() || _.sort && pulse.modified(_.sort.fields);
-
if (run) {
this.value = (_.sort ? pulse.source.slice().sort(stableCompare(_.sort)) : pulse.source).map(_.field);
}
}
-
});
-
function WindowOp(op, field, param, as) {
const fn = WindowOps[op](field, param);
return {
- init: fn.init || zero$2,
+ init: fn.init || zero$3,
update: function (w, t) {
t[as] = fn.next(w);
}
};
}
-
const WindowOps = {
row_number: function () {
return {
next: w => w.index + 1
};
@@ -11452,29 +10198,29 @@
let rank;
return {
init: () => rank = 1,
next: w => {
const i = w.index,
- data = w.data;
+ data = w.data;
return i && w.compare(data[i - 1], data[i]) ? rank = i + 1 : rank;
}
};
},
dense_rank: function () {
let drank;
return {
init: () => drank = 1,
next: w => {
const i = w.index,
- d = w.data;
+ d = w.data;
return i && w.compare(d[i - 1], d[i]) ? ++drank : drank;
}
};
},
percent_rank: function () {
const rank = WindowOps.rank(),
- next = rank.next;
+ next = rank.next;
return {
init: rank.init,
next: w => (next(w) - 1) / (w.data.length - 1)
};
},
@@ -11482,28 +10228,25 @@
let cume;
return {
init: () => cume = 0,
next: w => {
const d = w.data,
- c = w.compare;
+ c = w.compare;
let i = w.index;
-
if (cume < i) {
while (i + 1 < d.length && !c(d[i], d[i + 1])) ++i;
-
cume = i;
}
-
return (1 + cume) / d.length;
}
};
},
ntile: function (field, num) {
num = +num;
if (!(num > 0)) error('ntile num must be greater than zero.');
const cume = WindowOps.cume_dist(),
- next = cume.next;
+ next = cume.next;
return {
init: cume.init,
next: w => Math.ceil(num * next(w))
};
},
@@ -11519,11 +10262,11 @@
lead: function (field, offset) {
offset = +offset || 1;
return {
next: w => {
const i = w.index + offset,
- d = w.data;
+ d = w.data;
return i < d.length ? field(d[i]) : null;
}
};
},
first_value: function (field) {
@@ -11565,189 +10308,164 @@
return w.index <= i ? v : (i = find$2(field, d, w.index)) < 0 ? (i = d.length, v = null) : v = field(d[i]);
}
};
}
};
-
function find$2(field, data, index) {
for (let n = data.length; index < n; ++index) {
const v = field(data[index]);
if (v != null) return index;
}
-
return -1;
}
-
const ValidWindowOps = Object.keys(WindowOps);
-
function WindowState(_) {
const ops = array$5(_.ops),
- fields = array$5(_.fields),
- params = array$5(_.params),
- as = array$5(_.as),
- outputs = this.outputs = [],
- windows = this.windows = [],
- inputs = {},
- map = {},
- counts = [],
- measures = [];
+ fields = array$5(_.fields),
+ params = array$5(_.params),
+ aggregate_params = array$5(_.aggregate_params),
+ as = array$5(_.as),
+ outputs = this.outputs = [],
+ windows = this.windows = [],
+ inputs = {},
+ map = {},
+ counts = [],
+ measures = [];
let countOnly = true;
-
function visitInputs(f) {
array$5(accessorFields(f)).forEach(_ => inputs[_] = 1);
}
-
visitInputs(_.sort);
ops.forEach((op, i) => {
const field = fields[i],
- mname = accessorName(field),
- name = measureName(op, mname, as[i]);
+ param = params[i],
+ aggregate_param = aggregate_params[i] || null,
+ mname = accessorName(field),
+ name = measureName(op, mname, as[i]);
visitInputs(field);
- outputs.push(name); // Window operation
+ outputs.push(name);
+ // Window operation
if (has$1(WindowOps, op)) {
- windows.push(WindowOp(op, fields[i], params[i], name));
- } // Aggregate operation
+ windows.push(WindowOp(op, field, param, name));
+ }
+
+ // Aggregate operation
else {
if (field == null && op !== 'count') {
error('Null aggregate field specified.');
}
-
if (op === 'count') {
counts.push(name);
return;
}
-
countOnly = false;
let m = map[mname];
-
if (!m) {
m = map[mname] = [];
m.field = field;
measures.push(m);
}
-
- m.push(createMeasure(op, name));
+ m.push(createMeasure(op, aggregate_param, name));
}
});
-
if (counts.length || measures.length) {
this.cell = cell(measures, counts, countOnly);
}
-
this.inputs = Object.keys(inputs);
}
-
const prototype = WindowState.prototype;
-
prototype.init = function () {
this.windows.forEach(_ => _.init());
if (this.cell) this.cell.init();
};
-
prototype.update = function (w, t) {
const cell = this.cell,
- wind = this.windows,
- data = w.data,
- m = wind && wind.length;
+ wind = this.windows,
+ data = w.data,
+ m = wind && wind.length;
let j;
-
if (cell) {
for (j = w.p0; j < w.i0; ++j) cell.rem(data[j]);
-
for (j = w.p1; j < w.i1; ++j) cell.add(data[j]);
-
cell.set(t);
}
-
for (j = 0; j < m; ++j) wind[j].update(w, t);
};
-
function cell(measures, counts, countOnly) {
measures = measures.map(m => compileMeasures(m, m.field));
const cell = {
num: 0,
agg: null,
store: false,
count: counts
};
-
if (!countOnly) {
var n = measures.length,
- a = cell.agg = Array(n),
- i = 0;
-
+ a = cell.agg = Array(n),
+ i = 0;
for (; i < n; ++i) a[i] = new measures[i](cell);
}
-
if (cell.store) {
var store = cell.data = new TupleStore();
}
-
cell.add = function (t) {
cell.num += 1;
if (countOnly) return;
if (store) store.add(t);
-
for (let i = 0; i < n; ++i) {
a[i].add(a[i].get(t), t);
}
};
-
cell.rem = function (t) {
cell.num -= 1;
if (countOnly) return;
if (store) store.rem(t);
-
for (let i = 0; i < n; ++i) {
a[i].rem(a[i].get(t), t);
}
};
-
cell.set = function (t) {
- let i, n; // consolidate stored values
+ let i, n;
- if (store) store.values(); // update tuple properties
+ // consolidate stored values
+ if (store) store.values();
+ // update tuple properties
for (i = 0, n = counts.length; i < n; ++i) t[counts[i]] = cell.num;
-
if (!countOnly) for (i = 0, n = a.length; i < n; ++i) a[i].set(t);
};
-
cell.init = function () {
cell.num = 0;
if (store) store.reset();
-
for (let i = 0; i < n; ++i) a[i].init();
};
-
return cell;
}
+
/**
* Perform window calculations and write results to the input stream.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(*,*): number} [params.sort] - A comparator function for sorting tuples within a window.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors by which to partition tuples into separate windows.
* @param {Array<string>} params.ops - An array of strings indicating window operations to perform.
* @param {Array<function(object): *>} [params.fields] - An array of accessors
* for data fields to use as inputs to window operations.
* @param {Array<*>} [params.params] - An array of parameter values for window operations.
+ * @param {Array<number>} [params.aggregate_params] - An optional array of parameter values for aggregation operations.
* @param {Array<string>} [params.as] - An array of output field names for window operations.
* @param {Array<number>} [params.frame] - Window frame definition as two-element array.
* @param {boolean} [params.ignorePeers=false] - If true, base window frame boundaries on row
* number alone, ignoring peers with identical sort values. If false (default),
* the window boundaries will be adjusted to include peer values.
*/
-
-
function Window(params) {
Transform.call(this, {}, params);
this._mlen = 0;
this._mods = [];
}
-
Window.Definition = {
'type': 'Window',
'metadata': {
'modifies': true
},
@@ -11767,10 +10485,15 @@
'name': 'params',
'type': 'number',
'null': true,
'array': true
}, {
+ 'name': 'aggregate_params',
+ 'type': 'number',
+ 'null': true,
+ 'array': true
+ }, {
'name': 'fields',
'type': 'field',
'null': true,
'array': true
}, {
@@ -11792,102 +10515,92 @@
}]
};
inherits(Window, Transform, {
transform(_, pulse) {
this.stamp = pulse.stamp;
-
const mod = _.modified(),
- cmp = stableCompare(_.sort),
- key = groupkey(_.groupby),
- group = t => this.group(key(t)); // initialize window state
+ cmp = stableCompare(_.sort),
+ key = groupkey(_.groupby),
+ group = t => this.group(key(t));
-
+ // initialize window state
let state = this.state;
-
if (!state || mod) {
state = this.state = new WindowState(_);
- } // partition input tuples
+ }
-
+ // partition input tuples
if (mod || pulse.modified(state.inputs)) {
this.value = {};
pulse.visit(pulse.SOURCE, t => group(t).add(t));
} else {
pulse.visit(pulse.REM, t => group(t).remove(t));
pulse.visit(pulse.ADD, t => group(t).add(t));
- } // perform window calculations for each modified partition
+ }
-
+ // perform window calculations for each modified partition
for (let i = 0, n = this._mlen; i < n; ++i) {
processPartition(this._mods[i], state, cmp, _);
}
-
this._mlen = 0;
- this._mods = []; // TODO don't reflow everything?
+ this._mods = [];
+ // TODO don't reflow everything?
return pulse.reflow(mod).modifies(state.outputs);
},
-
group(key) {
let group = this.value[key];
-
if (!group) {
group = this.value[key] = SortedList(tupleid);
group.stamp = -1;
}
-
if (group.stamp < this.stamp) {
group.stamp = this.stamp;
this._mods[this._mlen++] = group;
}
-
return group;
}
-
});
-
function processPartition(list, state, cmp, _) {
const sort = _.sort,
- range = sort && !_.ignorePeers,
- frame = _.frame || [null, 0],
- data = list.data(cmp),
- // use cmp for stable sort
- n = data.length,
- b = range ? bisector(sort) : null,
- w = {
- i0: 0,
- i1: 0,
- p0: 0,
- p1: 0,
- index: 0,
- data: data,
- compare: sort || constant$4(-1)
- };
+ range = sort && !_.ignorePeers,
+ frame = _.frame || [null, 0],
+ data = list.data(cmp),
+ // use cmp for stable sort
+ n = data.length,
+ b = range ? bisector(sort) : null,
+ w = {
+ i0: 0,
+ i1: 0,
+ p0: 0,
+ p1: 0,
+ index: 0,
+ data: data,
+ compare: sort || constant$5(-1)
+ };
state.init();
-
for (let i = 0; i < n; ++i) {
setWindow(w, frame, i, n);
if (range) adjustRange(w, b);
state.update(w, data[i]);
}
}
-
function setWindow(w, f, i, n) {
w.p0 = w.i0;
w.p1 = w.i1;
w.i0 = f[0] == null ? 0 : Math.max(0, i - Math.abs(f[0]));
w.i1 = f[1] == null ? n : Math.min(n, i + Math.abs(f[1]) + 1);
w.index = i;
- } // if frame type is 'range', adjust window for peer values
+ }
-
+ // if frame type is 'range', adjust window for peer values
function adjustRange(w, bisect) {
const r0 = w.i0,
- r1 = w.i1 - 1,
- c = w.compare,
- d = w.data,
- n = d.length - 1;
+ r1 = w.i1 - 1,
+ c = w.compare,
+ d = w.data,
+ n = d.length - 1;
if (r0 > 0 && !c(d[r0], d[r0 - 1])) w.i0 = bisect.left(d, d[r0]);
if (r1 < n && !c(d[r1], d[r1 + 1])) w.i1 = bisect.right(d, d[r1]);
}
var tx = /*#__PURE__*/Object.freeze({
@@ -11932,372 +10645,435 @@
tupleindex: TupleIndex,
values: Values$1,
window: Window
});
- const pi$3 = Math.PI,
- tau$3 = 2 * pi$3,
- epsilon$5 = 1e-6,
- tauEpsilon = tau$3 - epsilon$5;
-
- function Path$1() {
- this._x0 = this._y0 = // start of current subpath
- this._x1 = this._y1 = null; // end of current subpath
-
- this._ = "";
+ function constant$3 (x) {
+ return function constant() {
+ return x;
+ };
}
- function path$3() {
- return new Path$1();
+ const abs$2 = Math.abs;
+ const atan2$1 = Math.atan2;
+ const cos$2 = Math.cos;
+ const max$1 = Math.max;
+ const min$1 = Math.min;
+ const sin$2 = Math.sin;
+ const sqrt$3 = Math.sqrt;
+ const epsilon$5 = 1e-12;
+ const pi$3 = Math.PI;
+ const halfPi$2 = pi$3 / 2;
+ const tau$3 = 2 * pi$3;
+ function acos$1(x) {
+ return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x);
}
+ function asin$2(x) {
+ return x >= 1 ? halfPi$2 : x <= -1 ? -halfPi$2 : Math.asin(x);
+ }
- Path$1.prototype = path$3.prototype = {
- constructor: Path$1,
- moveTo: function (x, y) {
- this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
- },
- closePath: function () {
+ const pi$2 = Math.PI,
+ tau$2 = 2 * pi$2,
+ epsilon$4 = 1e-6,
+ tauEpsilon = tau$2 - epsilon$4;
+ function append$1(strings) {
+ this._ += strings[0];
+ for (let i = 1, n = strings.length; i < n; ++i) {
+ this._ += arguments[i] + strings[i];
+ }
+ }
+ function appendRound$1(digits) {
+ let d = Math.floor(digits);
+ if (!(d >= 0)) throw new Error(`invalid digits: ${digits}`);
+ if (d > 15) return append$1;
+ const k = 10 ** d;
+ return function (strings) {
+ this._ += strings[0];
+ for (let i = 1, n = strings.length; i < n; ++i) {
+ this._ += Math.round(arguments[i] * k) / k + strings[i];
+ }
+ };
+ }
+ let Path$1 = class Path {
+ constructor(digits) {
+ this._x0 = this._y0 =
+ // start of current subpath
+ this._x1 = this._y1 = null; // end of current subpath
+ this._ = "";
+ this._append = digits == null ? append$1 : appendRound$1(digits);
+ }
+ moveTo(x, y) {
+ this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`;
+ }
+ closePath() {
if (this._x1 !== null) {
this._x1 = this._x0, this._y1 = this._y0;
- this._ += "Z";
+ this._append`Z`;
}
- },
- lineTo: function (x, y) {
- this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
- },
- quadraticCurveTo: function (x1, y1, x, y) {
- this._ += "Q" + +x1 + "," + +y1 + "," + (this._x1 = +x) + "," + (this._y1 = +y);
- },
- bezierCurveTo: function (x1, y1, x2, y2, x, y) {
- this._ += "C" + +x1 + "," + +y1 + "," + +x2 + "," + +y2 + "," + (this._x1 = +x) + "," + (this._y1 = +y);
- },
- arcTo: function (x1, y1, x2, y2, r) {
+ }
+ lineTo(x, y) {
+ this._append`L${this._x1 = +x},${this._y1 = +y}`;
+ }
+ quadraticCurveTo(x1, y1, x, y) {
+ this._append`Q${+x1},${+y1},${this._x1 = +x},${this._y1 = +y}`;
+ }
+ bezierCurveTo(x1, y1, x2, y2, x, y) {
+ this._append`C${+x1},${+y1},${+x2},${+y2},${this._x1 = +x},${this._y1 = +y}`;
+ }
+ arcTo(x1, y1, x2, y2, r) {
x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
- var x0 = this._x1,
- y0 = this._y1,
- x21 = x2 - x1,
- y21 = y2 - y1,
- x01 = x0 - x1,
- y01 = y0 - y1,
- l01_2 = x01 * x01 + y01 * y01; // Is the radius negative? Error.
- if (r < 0) throw new Error("negative radius: " + r); // Is this path empty? Move to (x1,y1).
+ // Is the radius negative? Error.
+ if (r < 0) throw new Error(`negative radius: ${r}`);
+ let x0 = this._x1,
+ y0 = this._y1,
+ x21 = x2 - x1,
+ y21 = y2 - y1,
+ x01 = x0 - x1,
+ y01 = y0 - y1,
+ l01_2 = x01 * x01 + y01 * y01;
+ // Is this path empty? Move to (x1,y1).
if (this._x1 === null) {
- this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
- } // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
- else if (!(l01_2 > epsilon$5)) ; // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
+ this._append`M${this._x1 = x1},${this._y1 = y1}`;
+ }
+
+ // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
+ else if (!(l01_2 > epsilon$4)) ;
+
+ // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
// Equivalently, is (x1,y1) coincident with (x2,y2)?
// Or, is the radius zero? Line to (x1,y1).
- else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$5) || !r) {
- this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
- } // Otherwise, draw an arc!
+ else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$4) || !r) {
+ this._append`L${this._x1 = x1},${this._y1 = y1}`;
+ }
+
+ // Otherwise, draw an arc!
else {
- var x20 = x2 - x0,
- y20 = y2 - y0,
- l21_2 = x21 * x21 + y21 * y21,
- l20_2 = x20 * x20 + y20 * y20,
- l21 = Math.sqrt(l21_2),
- l01 = Math.sqrt(l01_2),
- l = r * Math.tan((pi$3 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
- t01 = l / l01,
- t21 = l / l21; // If the start tangent is not coincident with (x0,y0), line to.
+ let x20 = x2 - x0,
+ y20 = y2 - y0,
+ l21_2 = x21 * x21 + y21 * y21,
+ l20_2 = x20 * x20 + y20 * y20,
+ l21 = Math.sqrt(l21_2),
+ l01 = Math.sqrt(l01_2),
+ l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
+ t01 = l / l01,
+ t21 = l / l21;
- if (Math.abs(t01 - 1) > epsilon$5) {
- this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
+ // If the start tangent is not coincident with (x0,y0), line to.
+ if (Math.abs(t01 - 1) > epsilon$4) {
+ this._append`L${x1 + t01 * x01},${y1 + t01 * y01}`;
}
-
- this._ += "A" + r + "," + r + ",0,0," + +(y01 * x20 > x01 * y20) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
+ this._append`A${r},${r},0,0,${+(y01 * x20 > x01 * y20)},${this._x1 = x1 + t21 * x21},${this._y1 = y1 + t21 * y21}`;
}
- },
- arc: function (x, y, r, a0, a1, ccw) {
+ }
+ arc(x, y, r, a0, a1, ccw) {
x = +x, y = +y, r = +r, ccw = !!ccw;
- var dx = r * Math.cos(a0),
- dy = r * Math.sin(a0),
- x0 = x + dx,
- y0 = y + dy,
- cw = 1 ^ ccw,
- da = ccw ? a0 - a1 : a1 - a0; // Is the radius negative? Error.
- if (r < 0) throw new Error("negative radius: " + r); // Is this path empty? Move to (x0,y0).
+ // Is the radius negative? Error.
+ if (r < 0) throw new Error(`negative radius: ${r}`);
+ let dx = r * Math.cos(a0),
+ dy = r * Math.sin(a0),
+ x0 = x + dx,
+ y0 = y + dy,
+ cw = 1 ^ ccw,
+ da = ccw ? a0 - a1 : a1 - a0;
+ // Is this path empty? Move to (x0,y0).
if (this._x1 === null) {
- this._ += "M" + x0 + "," + y0;
- } // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
- else if (Math.abs(this._x1 - x0) > epsilon$5 || Math.abs(this._y1 - y0) > epsilon$5) {
- this._ += "L" + x0 + "," + y0;
- } // Is this arc empty? We’re done.
+ this._append`M${x0},${y0}`;
+ }
+ // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
+ else if (Math.abs(this._x1 - x0) > epsilon$4 || Math.abs(this._y1 - y0) > epsilon$4) {
+ this._append`L${x0},${y0}`;
+ }
- if (!r) return; // Does the angle go the wrong way? Flip the direction.
+ // Is this arc empty? We’re done.
+ if (!r) return;
- if (da < 0) da = da % tau$3 + tau$3; // Is this a complete circle? Draw two arcs to complete the circle.
+ // Does the angle go the wrong way? Flip the direction.
+ if (da < 0) da = da % tau$2 + tau$2;
+ // Is this a complete circle? Draw two arcs to complete the circle.
if (da > tauEpsilon) {
- this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
- } // Is this arc non-empty? Draw an arc!
- else if (da > epsilon$5) {
- this._ += "A" + r + "," + r + ",0," + +(da >= pi$3) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
+ this._append`A${r},${r},0,1,${cw},${x - dx},${y - dy}A${r},${r},0,1,${cw},${this._x1 = x0},${this._y1 = y0}`;
}
- },
- rect: function (x, y, w, h) {
- this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + +w + "v" + +h + "h" + -w + "Z";
- },
- toString: function () {
+
+ // Is this arc non-empty? Draw an arc!
+ else if (da > epsilon$4) {
+ this._append`A${r},${r},0,${+(da >= pi$2)},${cw},${this._x1 = x + r * Math.cos(a1)},${this._y1 = y + r * Math.sin(a1)}`;
+ }
+ }
+ rect(x, y, w, h) {
+ this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${w = +w}v${+h}h${-w}Z`;
+ }
+ toString() {
return this._;
}
};
+ function path$3() {
+ return new Path$1();
+ }
- function constant$3 (x) {
- return function constant() {
- return x;
+ // Allow instanceof d3.path
+ path$3.prototype = Path$1.prototype;
+
+ function withPath(shape) {
+ let digits = 3;
+ shape.digits = function (_) {
+ if (!arguments.length) return digits;
+ if (_ == null) {
+ digits = null;
+ } else {
+ const d = Math.floor(_);
+ if (!(d >= 0)) throw new RangeError(`invalid digits: ${_}`);
+ digits = d;
+ }
+ return shape;
};
+ return () => new Path$1(digits);
}
- const abs$2 = Math.abs;
- const atan2$1 = Math.atan2;
- const cos$2 = Math.cos;
- const max$1 = Math.max;
- const min$1 = Math.min;
- const sin$2 = Math.sin;
- const sqrt$3 = Math.sqrt;
- const epsilon$4 = 1e-12;
- const pi$2 = Math.PI;
- const halfPi$2 = pi$2 / 2;
- const tau$2 = 2 * pi$2;
- function acos$1(x) {
- return x > 1 ? 0 : x < -1 ? pi$2 : Math.acos(x);
- }
- function asin$2(x) {
- return x >= 1 ? halfPi$2 : x <= -1 ? -halfPi$2 : Math.asin(x);
- }
-
function arcInnerRadius(d) {
return d.innerRadius;
}
-
function arcOuterRadius(d) {
return d.outerRadius;
}
-
function arcStartAngle(d) {
return d.startAngle;
}
-
function arcEndAngle(d) {
return d.endAngle;
}
-
function arcPadAngle(d) {
return d && d.padAngle; // Note: optional!
}
-
function intersect$3(x0, y0, x1, y1, x2, y2, x3, y3) {
var x10 = x1 - x0,
- y10 = y1 - y0,
- x32 = x3 - x2,
- y32 = y3 - y2,
- t = y32 * x10 - x32 * y10;
- if (t * t < epsilon$4) return;
+ y10 = y1 - y0,
+ x32 = x3 - x2,
+ y32 = y3 - y2,
+ t = y32 * x10 - x32 * y10;
+ if (t * t < epsilon$5) return;
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;
return [x0 + t * x10, y0 + t * y10];
- } // Compute perpendicular offset line of length rc.
- // http://mathworld.wolfram.com/Circle-LineIntersection.html
+ }
-
+ // Compute perpendicular offset line of length rc.
+ // http://mathworld.wolfram.com/Circle-LineIntersection.html
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
var x01 = x0 - x1,
- y01 = y0 - y1,
- lo = (cw ? rc : -rc) / sqrt$3(x01 * x01 + y01 * y01),
- ox = lo * y01,
- oy = -lo * x01,
- x11 = x0 + ox,
- y11 = y0 + oy,
- x10 = x1 + ox,
- y10 = y1 + oy,
- x00 = (x11 + x10) / 2,
- y00 = (y11 + y10) / 2,
- dx = x10 - x11,
- dy = y10 - y11,
- d2 = dx * dx + dy * dy,
- r = r1 - rc,
- D = x11 * y10 - x10 * y11,
- d = (dy < 0 ? -1 : 1) * sqrt$3(max$1(0, r * r * d2 - D * D)),
- cx0 = (D * dy - dx * d) / d2,
- cy0 = (-D * dx - dy * d) / d2,
- cx1 = (D * dy + dx * d) / d2,
- cy1 = (-D * dx + dy * d) / d2,
- dx0 = cx0 - x00,
- dy0 = cy0 - y00,
- dx1 = cx1 - x00,
- dy1 = cy1 - y00; // Pick the closer of the two intersection points.
- // TODO Is there a faster way to determine which intersection to use?
+ y01 = y0 - y1,
+ lo = (cw ? rc : -rc) / sqrt$3(x01 * x01 + y01 * y01),
+ ox = lo * y01,
+ oy = -lo * x01,
+ x11 = x0 + ox,
+ y11 = y0 + oy,
+ x10 = x1 + ox,
+ y10 = y1 + oy,
+ x00 = (x11 + x10) / 2,
+ y00 = (y11 + y10) / 2,
+ dx = x10 - x11,
+ dy = y10 - y11,
+ d2 = dx * dx + dy * dy,
+ r = r1 - rc,
+ D = x11 * y10 - x10 * y11,
+ d = (dy < 0 ? -1 : 1) * sqrt$3(max$1(0, r * r * d2 - D * D)),
+ cx0 = (D * dy - dx * d) / d2,
+ cy0 = (-D * dx - dy * d) / d2,
+ cx1 = (D * dy + dx * d) / d2,
+ cy1 = (-D * dx + dy * d) / d2,
+ dx0 = cx0 - x00,
+ dy0 = cy0 - y00,
+ dx1 = cx1 - x00,
+ dy1 = cy1 - y00;
+ // Pick the closer of the two intersection points.
+ // TODO Is there a faster way to determine which intersection to use?
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
return {
cx: cx0,
cy: cy0,
x01: -ox,
y01: -oy,
x11: cx0 * (r1 / r - 1),
y11: cy0 * (r1 / r - 1)
};
}
-
function arc$2$1 () {
var innerRadius = arcInnerRadius,
- outerRadius = arcOuterRadius,
- cornerRadius = constant$3(0),
- padRadius = null,
- startAngle = arcStartAngle,
- endAngle = arcEndAngle,
- padAngle = arcPadAngle,
- context = null;
-
+ outerRadius = arcOuterRadius,
+ cornerRadius = constant$3(0),
+ padRadius = null,
+ startAngle = arcStartAngle,
+ endAngle = arcEndAngle,
+ padAngle = arcPadAngle,
+ context = null,
+ path = withPath(arc);
function arc() {
var buffer,
- r,
- r0 = +innerRadius.apply(this, arguments),
- r1 = +outerRadius.apply(this, arguments),
- a0 = startAngle.apply(this, arguments) - halfPi$2,
- a1 = endAngle.apply(this, arguments) - halfPi$2,
- da = abs$2(a1 - a0),
- cw = a1 > a0;
- if (!context) context = buffer = path$3(); // Ensure that the outer radius is always larger than the inner radius.
+ r,
+ r0 = +innerRadius.apply(this, arguments),
+ r1 = +outerRadius.apply(this, arguments),
+ a0 = startAngle.apply(this, arguments) - halfPi$2,
+ a1 = endAngle.apply(this, arguments) - halfPi$2,
+ da = abs$2(a1 - a0),
+ cw = a1 > a0;
+ if (!context) context = buffer = path();
- if (r1 < r0) r = r1, r1 = r0, r0 = r; // Is it a point?
+ // Ensure that the outer radius is always larger than the inner radius.
+ if (r1 < r0) r = r1, r1 = r0, r0 = r;
- if (!(r1 > epsilon$4)) context.moveTo(0, 0); // Or is it a circle or annulus?
- else if (da > tau$2 - epsilon$4) {
+ // Is it a point?
+ if (!(r1 > epsilon$5)) context.moveTo(0, 0);
+
+ // Or is it a circle or annulus?
+ else if (da > tau$3 - epsilon$5) {
context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0));
context.arc(0, 0, r1, a0, a1, !cw);
-
- if (r0 > epsilon$4) {
+ if (r0 > epsilon$5) {
context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1));
context.arc(0, 0, r0, a1, a0, cw);
}
- } // Or is it a circular or annular sector?
+ }
+
+ // Or is it a circular or annular sector?
else {
var a01 = a0,
- a11 = a1,
- a00 = a0,
- a10 = a1,
- da0 = da,
- da1 = da,
- ap = padAngle.apply(this, arguments) / 2,
- rp = ap > epsilon$4 && (padRadius ? +padRadius.apply(this, arguments) : sqrt$3(r0 * r0 + r1 * r1)),
- rc = min$1(abs$2(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
- rc0 = rc,
- rc1 = rc,
- t0,
- t1; // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
+ a11 = a1,
+ a00 = a0,
+ a10 = a1,
+ da0 = da,
+ da1 = da,
+ ap = padAngle.apply(this, arguments) / 2,
+ rp = ap > epsilon$5 && (padRadius ? +padRadius.apply(this, arguments) : sqrt$3(r0 * r0 + r1 * r1)),
+ rc = min$1(abs$2(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
+ rc0 = rc,
+ rc1 = rc,
+ t0,
+ t1;
- if (rp > epsilon$4) {
+ // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
+ if (rp > epsilon$5) {
var p0 = asin$2(rp / r0 * sin$2(ap)),
- p1 = asin$2(rp / r1 * sin$2(ap));
- if ((da0 -= p0 * 2) > epsilon$4) p0 *= cw ? 1 : -1, a00 += p0, a10 -= p0;else da0 = 0, a00 = a10 = (a0 + a1) / 2;
- if ((da1 -= p1 * 2) > epsilon$4) p1 *= cw ? 1 : -1, a01 += p1, a11 -= p1;else da1 = 0, a01 = a11 = (a0 + a1) / 2;
+ p1 = asin$2(rp / r1 * sin$2(ap));
+ if ((da0 -= p0 * 2) > epsilon$5) p0 *= cw ? 1 : -1, a00 += p0, a10 -= p0;else da0 = 0, a00 = a10 = (a0 + a1) / 2;
+ if ((da1 -= p1 * 2) > epsilon$5) p1 *= cw ? 1 : -1, a01 += p1, a11 -= p1;else da1 = 0, a01 = a11 = (a0 + a1) / 2;
}
-
var x01 = r1 * cos$2(a01),
- y01 = r1 * sin$2(a01),
- x10 = r0 * cos$2(a10),
- y10 = r0 * sin$2(a10); // Apply rounded corners?
+ y01 = r1 * sin$2(a01),
+ x10 = r0 * cos$2(a10),
+ y10 = r0 * sin$2(a10);
- if (rc > epsilon$4) {
+ // Apply rounded corners?
+ if (rc > epsilon$5) {
var x11 = r1 * cos$2(a11),
- y11 = r1 * sin$2(a11),
- x00 = r0 * cos$2(a00),
- y00 = r0 * sin$2(a00),
- oc; // Restrict the corner radius according to the sector angle.
+ y11 = r1 * sin$2(a11),
+ x00 = r0 * cos$2(a00),
+ y00 = r0 * sin$2(a00),
+ oc;
- if (da < pi$2 && (oc = intersect$3(x01, y01, x00, y00, x11, y11, x10, y10))) {
- var ax = x01 - oc[0],
+ // Restrict the corner radius according to the sector angle. If this
+ // intersection fails, it’s probably because the arc is too small, so
+ // disable the corner radius entirely.
+ if (da < pi$3) {
+ if (oc = intersect$3(x01, y01, x00, y00, x11, y11, x10, y10)) {
+ var ax = x01 - oc[0],
ay = y01 - oc[1],
bx = x11 - oc[0],
by = y11 - oc[1],
kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$3(ax * ax + ay * ay) * sqrt$3(bx * bx + by * by))) / 2),
lc = sqrt$3(oc[0] * oc[0] + oc[1] * oc[1]);
- rc0 = min$1(rc, (r0 - lc) / (kc - 1));
- rc1 = min$1(rc, (r1 - lc) / (kc + 1));
+ rc0 = min$1(rc, (r0 - lc) / (kc - 1));
+ rc1 = min$1(rc, (r1 - lc) / (kc + 1));
+ } else {
+ rc0 = rc1 = 0;
+ }
}
- } // Is the sector collapsed to a line?
+ }
+ // Is the sector collapsed to a line?
+ if (!(da1 > epsilon$5)) context.moveTo(x01, y01);
- if (!(da1 > epsilon$4)) context.moveTo(x01, y01); // Does the sector’s outer ring have rounded corners?
- else if (rc1 > epsilon$4) {
+ // Does the sector’s outer ring have rounded corners?
+ else if (rc1 > epsilon$5) {
t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
- context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); // Have the corners merged?
+ context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
- if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); // Otherwise, draw the two corners and the ring.
+ // Have the corners merged?
+ if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
+
+ // Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
}
- } // Or is the outer ring just a circular arc?
- else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); // Is there no inner ring, and it’s a circular sector?
+ }
+
+ // Or is the outer ring just a circular arc?
+ else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
+
+ // Is there no inner ring, and it’s a circular sector?
// Or perhaps it’s an annular sector collapsed due to padding?
+ if (!(r0 > epsilon$5) || !(da0 > epsilon$5)) context.lineTo(x10, y10);
- if (!(r0 > epsilon$4) || !(da0 > epsilon$4)) context.lineTo(x10, y10); // Does the sector’s inner ring (or point) have rounded corners?
- else if (rc0 > epsilon$4) {
+ // Does the sector’s inner ring (or point) have rounded corners?
+ else if (rc0 > epsilon$5) {
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
- context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); // Have the corners merged?
+ context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
- if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); // Otherwise, draw the two corners and the ring.
+ // Have the corners merged?
+ if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
+
+ // Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw);
context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
}
- } // Or is the inner ring just a circular arc?
+ }
+
+ // Or is the inner ring just a circular arc?
else context.arc(0, 0, r0, a10, a00, cw);
}
context.closePath();
if (buffer) return context = null, buffer + "" || null;
}
-
arc.centroid = function () {
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
- a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$2 / 2;
+ a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$3 / 2;
return [cos$2(a) * r, sin$2(a) * r];
};
-
arc.innerRadius = function (_) {
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$3(+_), arc) : innerRadius;
};
-
arc.outerRadius = function (_) {
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$3(+_), arc) : outerRadius;
};
-
arc.cornerRadius = function (_) {
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$3(+_), arc) : cornerRadius;
};
-
arc.padRadius = function (_) {
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$3(+_), arc) : padRadius;
};
-
arc.startAngle = function (_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$3(+_), arc) : startAngle;
};
-
arc.endAngle = function (_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$3(+_), arc) : endAngle;
};
-
arc.padAngle = function (_) {
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$3(+_), arc) : padAngle;
};
-
arc.context = function (_) {
return arguments.length ? (context = _ == null ? null : _, arc) : context;
};
-
return arc;
}
function array$4 (x) {
return typeof x === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
@@ -12305,11 +11081,10 @@
}
function Linear$1(context) {
this._context = context;
}
-
Linear$1.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -12322,24 +11097,20 @@
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
// falls through
-
default:
this._context.lineTo(x, y);
-
break;
}
}
};
function curveLinear (context) {
@@ -12353,196 +11124,163 @@
return p[1];
}
function line$2$1 (x, y) {
var defined = constant$3(true),
- context = null,
- curve = curveLinear,
- output = null;
+ context = null,
+ curve = curveLinear,
+ output = null,
+ path = withPath(line);
x = typeof x === "function" ? x : x === undefined ? x$3 : constant$3(x);
y = typeof y === "function" ? y : y === undefined ? y$3 : constant$3(y);
-
function line(data) {
var i,
- n = (data = array$4(data)).length,
- d,
- defined0 = false,
- buffer;
- if (context == null) output = curve(buffer = path$3());
-
+ n = (data = array$4(data)).length,
+ d,
+ defined0 = false,
+ buffer;
+ if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) output.lineStart();else output.lineEnd();
}
-
if (defined0) output.point(+x(d, i, data), +y(d, i, data));
}
-
if (buffer) return output = null, buffer + "" || null;
}
-
line.x = function (_) {
return arguments.length ? (x = typeof _ === "function" ? _ : constant$3(+_), line) : x;
};
-
line.y = function (_) {
return arguments.length ? (y = typeof _ === "function" ? _ : constant$3(+_), line) : y;
};
-
line.defined = function (_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$3(!!_), line) : defined;
};
-
line.curve = function (_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
};
-
line.context = function (_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
};
-
return line;
}
function area$2$1 (x0, y0, y1) {
var x1 = null,
- defined = constant$3(true),
- context = null,
- curve = curveLinear,
- output = null;
+ defined = constant$3(true),
+ context = null,
+ curve = curveLinear,
+ output = null,
+ path = withPath(area);
x0 = typeof x0 === "function" ? x0 : x0 === undefined ? x$3 : constant$3(+x0);
y0 = typeof y0 === "function" ? y0 : y0 === undefined ? constant$3(0) : constant$3(+y0);
y1 = typeof y1 === "function" ? y1 : y1 === undefined ? y$3 : constant$3(+y1);
-
function area(data) {
var i,
- j,
- k,
- n = (data = array$4(data)).length,
- d,
- defined0 = false,
- buffer,
- x0z = new Array(n),
- y0z = new Array(n);
- if (context == null) output = curve(buffer = path$3());
-
+ j,
+ k,
+ n = (data = array$4(data)).length,
+ d,
+ defined0 = false,
+ buffer,
+ x0z = new Array(n),
+ y0z = new Array(n);
+ if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) {
j = i;
output.areaStart();
output.lineStart();
} else {
output.lineEnd();
output.lineStart();
-
for (k = i - 1; k >= j; --k) {
output.point(x0z[k], y0z[k]);
}
-
output.lineEnd();
output.areaEnd();
}
}
-
if (defined0) {
x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
}
}
-
if (buffer) return output = null, buffer + "" || null;
}
-
function arealine() {
return line$2$1().defined(defined).curve(curve).context(context);
}
-
area.x = function (_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$3(+_), x1 = null, area) : x0;
};
-
area.x0 = function (_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$3(+_), area) : x0;
};
-
area.x1 = function (_) {
return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$3(+_), area) : x1;
};
-
area.y = function (_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$3(+_), y1 = null, area) : y0;
};
-
area.y0 = function (_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$3(+_), area) : y0;
};
-
area.y1 = function (_) {
return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$3(+_), area) : y1;
};
-
area.lineX0 = area.lineY0 = function () {
return arealine().x(x0).y(y0);
};
-
area.lineY1 = function () {
return arealine().x(x0).y(y1);
};
-
area.lineX1 = function () {
return arealine().x(x1).y(y0);
};
-
area.defined = function (_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$3(!!_), area) : defined;
};
-
area.curve = function (_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
};
-
area.context = function (_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
};
-
return area;
}
- var circle$1 = {
+ var circle = {
draw(context, size) {
- const r = sqrt$3(size / pi$2);
+ const r = sqrt$3(size / pi$3);
context.moveTo(r, 0);
- context.arc(0, 0, r, 0, tau$2);
+ context.arc(0, 0, r, 0, tau$3);
}
-
};
function Symbol$1(type, size) {
- let context = null;
- type = typeof type === "function" ? type : constant$3(type || circle$1);
+ let context = null,
+ path = withPath(symbol);
+ type = typeof type === "function" ? type : constant$3(type || circle);
size = typeof size === "function" ? size : constant$3(size === undefined ? 64 : +size);
-
function symbol() {
let buffer;
- if (!context) context = buffer = path$3();
+ if (!context) context = buffer = path();
type.apply(this, arguments).draw(context, +size.apply(this, arguments));
if (buffer) return context = null, buffer + "" || null;
}
-
symbol.type = function (_) {
return arguments.length ? (type = typeof _ === "function" ? _ : constant$3(_), symbol) : type;
};
-
symbol.size = function (_) {
return arguments.length ? (size = typeof _ === "function" ? _ : constant$3(+_), symbol) : size;
};
-
symbol.context = function (_) {
return arguments.length ? (context = _ == null ? null : _, symbol) : context;
};
-
return symbol;
}
function noop$3 () {}
@@ -12566,45 +11304,35 @@
lineEnd: function () {
switch (this._point) {
case 3:
point$5(this, this._x1, this._y1);
// falls through
-
case 2:
this._context.lineTo(this._x1, this._y1);
-
break;
}
-
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
-
this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6);
-
// falls through
-
default:
point$5(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function curveBasis (context) {
@@ -12612,11 +11340,10 @@
}
function BasisClosed(context) {
this._context = context;
}
-
BasisClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function () {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
@@ -12625,27 +11352,20 @@
lineEnd: function () {
switch (this._point) {
case 1:
{
this._context.moveTo(this._x2, this._y2);
-
this._context.closePath();
-
break;
}
-
case 2:
{
this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
-
this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
-
this._context.closePath();
-
break;
}
-
case 3:
{
this.point(this._x2, this._y2);
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
@@ -12653,35 +11373,28 @@
}
}
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._x2 = x, this._y2 = y;
break;
-
case 1:
this._point = 2;
this._x3 = x, this._y3 = y;
break;
-
case 2:
this._point = 3;
this._x4 = x, this._y4 = y;
-
this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6);
-
break;
-
default:
point$5(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function curveBasisClosed (context) {
@@ -12689,11 +11402,10 @@
}
function BasisOpen(context) {
this._context = context;
}
-
BasisOpen.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -12707,36 +11419,30 @@
if (this._line || this._line !== 0 && this._point === 3) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
var x0 = (this._x0 + 4 * this._x1 + x) / 6,
- y0 = (this._y0 + 4 * this._y1 + y) / 6;
+ y0 = (this._y0 + 4 * this._y1 + y) / 6;
this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0);
break;
-
case 3:
this._point = 4;
// falls through
-
default:
point$5(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function curveBasisOpen (context) {
@@ -12745,57 +11451,47 @@
function Bundle(context, beta) {
this._basis = new Basis(context);
this._beta = beta;
}
-
Bundle.prototype = {
lineStart: function () {
this._x = [];
this._y = [];
-
this._basis.lineStart();
},
lineEnd: function () {
var x = this._x,
- y = this._y,
- j = x.length - 1;
-
+ y = this._y,
+ j = x.length - 1;
if (j > 0) {
var x0 = x[0],
- y0 = y[0],
- dx = x[j] - x0,
- dy = y[j] - y0,
- i = -1,
- t;
-
+ y0 = y[0],
+ dx = x[j] - x0,
+ dy = y[j] - y0,
+ i = -1,
+ t;
while (++i <= j) {
t = i / j;
-
this._basis.point(this._beta * x[i] + (1 - this._beta) * (x0 + t * dx), this._beta * y[i] + (1 - this._beta) * (y0 + t * dy));
}
}
-
this._x = this._y = null;
-
this._basis.lineEnd();
},
point: function (x, y) {
this._x.push(+x);
-
this._y.push(+y);
}
};
var curveBundle = (function custom(beta) {
function bundle(context) {
return beta === 1 ? new Basis(context) : new Bundle(context, beta);
}
-
bundle.beta = function (beta) {
return custom(+beta);
};
-
return bundle;
})(0.85);
function point$4(that, x, y) {
that._context.bezierCurveTo(that._x1 + that._k * (that._x2 - that._x0), that._y1 + that._k * (that._y2 - that._y0), that._x2 + that._k * (that._x1 - x), that._y2 + that._k * (that._y1 - y), that._x2, that._y2);
@@ -12817,57 +11513,47 @@
},
lineEnd: function () {
switch (this._point) {
case 2:
this._context.lineTo(this._x2, this._y2);
-
break;
-
case 3:
point$4(this, this._x1, this._y1);
break;
}
-
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
this._x1 = x, this._y1 = y;
break;
-
case 2:
this._point = 3;
// falls through
-
default:
point$4(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinal = (function custom(tension) {
function cardinal(context) {
return new Cardinal(context, tension);
}
-
cardinal.tension = function (tension) {
return custom(+tension);
};
-
return cardinal;
})(0);
function CardinalClosed(context, tension) {
this._context = context;
@@ -12883,25 +11569,19 @@
lineEnd: function () {
switch (this._point) {
case 1:
{
this._context.moveTo(this._x3, this._y3);
-
this._context.closePath();
-
break;
}
-
case 2:
{
this._context.lineTo(this._x3, this._y3);
-
this._context.closePath();
-
break;
}
-
case 3:
{
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
@@ -12909,47 +11589,38 @@
}
}
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._x3 = x, this._y3 = y;
break;
-
case 1:
this._point = 2;
-
this._context.moveTo(this._x4 = x, this._y4 = y);
-
break;
-
case 2:
this._point = 3;
this._x5 = x, this._y5 = y;
break;
-
default:
point$4(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinalClosed = (function custom(tension) {
function cardinal(context) {
return new CardinalClosed(context, tension);
}
-
cardinal.tension = function (tension) {
return custom(+tension);
};
-
return cardinal;
})(0);
function CardinalOpen(context, tension) {
this._context = context;
@@ -12970,78 +11641,65 @@
if (this._line || this._line !== 0 && this._point === 3) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2);
break;
-
case 3:
this._point = 4;
// falls through
-
default:
point$4(this, x, y);
break;
}
-
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCardinalOpen = (function custom(tension) {
function cardinal(context) {
return new CardinalOpen(context, tension);
}
-
cardinal.tension = function (tension) {
return custom(+tension);
};
-
return cardinal;
})(0);
function point$3(that, x, y) {
var x1 = that._x1,
- y1 = that._y1,
- x2 = that._x2,
- y2 = that._y2;
-
- if (that._l01_a > epsilon$4) {
+ y1 = that._y1,
+ x2 = that._x2,
+ y2 = that._y2;
+ if (that._l01_a > epsilon$5) {
var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
- n = 3 * that._l01_a * (that._l01_a + that._l12_a);
+ n = 3 * that._l01_a * (that._l01_a + that._l12_a);
x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
}
-
- if (that._l23_a > epsilon$4) {
+ if (that._l23_a > epsilon$5) {
var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
- m = 3 * that._l23_a * (that._l23_a + that._l12_a);
+ m = 3 * that._l23_a * (that._l23_a + that._l12_a);
x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
}
-
that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
}
-
function CatmullRom(context, alpha) {
this._context = context;
this._alpha = alpha;
}
-
CatmullRom.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -13053,72 +11711,60 @@
},
lineEnd: function () {
switch (this._point) {
case 2:
this._context.lineTo(this._x2, this._y2);
-
break;
-
case 3:
this.point(this._x2, this._y2);
break;
}
-
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
if (this._point) {
var x23 = this._x2 - x,
- y23 = this._y2 - y;
+ y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
// falls through
-
default:
point$3(this, x, y);
break;
}
-
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRom = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
}
-
catmullRom.alpha = function (alpha) {
return custom(+alpha);
};
-
return catmullRom;
})(0.5);
function CatmullRomClosed(context, alpha) {
this._context = context;
this._alpha = alpha;
}
-
CatmullRomClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function () {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
@@ -13127,25 +11773,19 @@
lineEnd: function () {
switch (this._point) {
case 1:
{
this._context.moveTo(this._x3, this._y3);
-
this._context.closePath();
-
break;
}
-
case 2:
{
this._context.lineTo(this._x3, this._y3);
-
this._context.closePath();
-
break;
}
-
case 3:
{
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
@@ -13153,63 +11793,52 @@
}
}
},
point: function (x, y) {
x = +x, y = +y;
-
if (this._point) {
var x23 = this._x2 - x,
- y23 = this._y2 - y;
+ y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
-
switch (this._point) {
case 0:
this._point = 1;
this._x3 = x, this._y3 = y;
break;
-
case 1:
this._point = 2;
-
this._context.moveTo(this._x4 = x, this._y4 = y);
-
break;
-
case 2:
this._point = 3;
this._x5 = x, this._y5 = y;
break;
-
default:
point$3(this, x, y);
break;
}
-
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRomClosed = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
}
-
catmullRom.alpha = function (alpha) {
return custom(+alpha);
};
-
return catmullRom;
})(0.5);
function CatmullRomOpen(context, alpha) {
this._context = context;
this._alpha = alpha;
}
-
CatmullRomOpen.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -13223,62 +11852,52 @@
if (this._line || this._line !== 0 && this._point === 3) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
if (this._point) {
var x23 = this._x2 - x,
- y23 = this._y2 - y;
+ y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
-
switch (this._point) {
case 0:
this._point = 1;
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2);
break;
-
case 3:
this._point = 4;
// falls through
-
default:
point$3(this, x, y);
break;
}
-
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var curveCatmullRomOpen = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
}
-
catmullRom.alpha = function (alpha) {
return custom(+alpha);
};
-
return catmullRom;
})(0.5);
function LinearClosed(context) {
this._context = context;
}
-
LinearClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function () {
this._point = 0;
@@ -13295,48 +11914,45 @@
return new LinearClosed(context);
}
function sign$1(x) {
return x < 0 ? -1 : 1;
- } // Calculate the slopes of the tangents (Hermite-type interpolation) based on
+ }
+
+ // Calculate the slopes of the tangents (Hermite-type interpolation) based on
// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
// NOV(II), P. 443, 1990.
-
-
function slope3(that, x2, y2) {
var h0 = that._x1 - that._x0,
- h1 = x2 - that._x1,
- s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
- s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
- p = (s0 * h1 + s1 * h0) / (h0 + h1);
+ h1 = x2 - that._x1,
+ s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
+ s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
+ p = (s0 * h1 + s1 * h0) / (h0 + h1);
return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
- } // Calculate a one-sided slope.
+ }
-
+ // Calculate a one-sided slope.
function slope2(that, t) {
var h = that._x1 - that._x0;
return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
- } // According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
+ }
+
+ // According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
-
-
function point$2(that, t0, t1) {
var x0 = that._x0,
- y0 = that._y0,
- x1 = that._x1,
- y1 = that._y1,
- dx = (x1 - x0) / 3;
-
+ y0 = that._y0,
+ x1 = that._x1,
+ y1 = that._y1,
+ dx = (x1 - x0) / 3;
that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
}
-
function MonotoneX(context) {
this._context = context;
}
-
MonotoneX.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -13348,64 +11964,52 @@
},
lineEnd: function () {
switch (this._point) {
case 2:
this._context.lineTo(this._x1, this._y1);
-
break;
-
case 3:
point$2(this, this._t0, slope2(this, this._t0));
break;
}
-
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
this._line = 1 - this._line;
},
point: function (x, y) {
var t1 = NaN;
x = +x, y = +y;
if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
break;
-
case 2:
this._point = 3;
point$2(this, slope2(this, t1 = slope3(this, x, y)), t1);
break;
-
default:
point$2(this, this._t0, t1 = slope3(this, x, y));
break;
}
-
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
this._t0 = t1;
}
};
-
function MonotoneY(context) {
this._context = new ReflectContext(context);
}
-
(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function (x, y) {
MonotoneX.prototype.point.call(this, y, x);
};
-
function ReflectContext(context) {
this._context = context;
}
-
ReflectContext.prototype = {
moveTo: function (x, y) {
this._context.moveTo(y, x);
},
closePath: function () {
@@ -13426,11 +12030,10 @@
}
function Natural(context) {
this._context = context;
}
-
Natural.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -13440,74 +12043,60 @@
this._x = [];
this._y = [];
},
lineEnd: function () {
var x = this._x,
- y = this._y,
- n = x.length;
-
+ y = this._y,
+ n = x.length;
if (n) {
this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
-
if (n === 2) {
this._context.lineTo(x[1], y[1]);
} else {
var px = controlPoints(x),
- py = controlPoints(y);
-
+ py = controlPoints(y);
for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
}
}
}
-
if (this._line || this._line !== 0 && n === 1) this._context.closePath();
this._line = 1 - this._line;
this._x = this._y = null;
},
point: function (x, y) {
this._x.push(+x);
-
this._y.push(+y);
}
- }; // See https://www.particleincell.com/2012/bezier-splines/ for derivation.
+ };
+ // See https://www.particleincell.com/2012/bezier-splines/ for derivation.
function controlPoints(x) {
var i,
- n = x.length - 1,
- m,
- a = new Array(n),
- b = new Array(n),
- r = new Array(n);
+ n = x.length - 1,
+ m,
+ a = new Array(n),
+ b = new Array(n),
+ r = new Array(n);
a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
-
for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
-
a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
-
for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
-
a[n - 1] = r[n - 1] / b[n - 1];
-
for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
-
b[n - 1] = (x[n] + a[n - 1]) / 2;
-
for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
-
return [a, b];
}
-
function curveNatural (context) {
return new Natural(context);
}
function Step(context, t) {
this._context = context;
this._t = t;
}
-
Step.prototype = {
areaStart: function () {
this._line = 0;
},
areaEnd: function () {
@@ -13522,39 +12111,31 @@
if (this._line || this._line !== 0 && this._point === 1) this._context.closePath();
if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
},
point: function (x, y) {
x = +x, y = +y;
-
switch (this._point) {
case 0:
this._point = 1;
this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
break;
-
case 1:
this._point = 2;
// falls through
-
default:
{
if (this._t <= 0) {
this._context.lineTo(this._x, y);
-
this._context.lineTo(x, y);
} else {
var x1 = this._x * (1 - this._t) + x * this._t;
-
this._context.lineTo(x1, this._y);
-
this._context.lineTo(x1, y);
}
-
break;
}
}
-
this._x = x, this._y = y;
}
};
function curveStep (context) {
return new Step(context, 0.5);
@@ -13567,132 +12148,111 @@
}
function domCanvas(w, h) {
if (typeof document !== 'undefined' && document.createElement) {
const c = document.createElement('canvas');
-
if (c && c.getContext) {
c.width = w;
c.height = h;
return c;
}
}
-
return null;
}
-
const domImage = () => typeof Image !== 'undefined' ? Image : null;
function initRange(domain, range) {
switch (arguments.length) {
case 0:
break;
-
case 1:
this.range(domain);
break;
-
default:
this.range(range).domain(domain);
break;
}
-
return this;
}
function initInterpolator(domain, interpolator) {
switch (arguments.length) {
case 0:
break;
-
case 1:
{
if (typeof domain === "function") this.interpolator(domain);else this.range(domain);
break;
}
-
default:
{
this.domain(domain);
if (typeof interpolator === "function") this.interpolator(interpolator);else this.range(interpolator);
break;
}
}
-
return this;
}
const implicit = Symbol("implicit");
function ordinal() {
var index = new InternMap(),
- domain = [],
- range = [],
- unknown = implicit;
-
+ domain = [],
+ range = [],
+ unknown = implicit;
function scale(d) {
let i = index.get(d);
-
if (i === undefined) {
if (unknown !== implicit) return unknown;
index.set(d, i = domain.push(d) - 1);
}
-
return range[i % range.length];
}
-
scale.domain = function (_) {
if (!arguments.length) return domain.slice();
domain = [], index = new InternMap();
-
for (const value of _) {
if (index.has(value)) continue;
index.set(value, domain.push(value) - 1);
}
-
return scale;
};
-
scale.range = function (_) {
return arguments.length ? (range = Array.from(_), scale) : range.slice();
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
scale.copy = function () {
return ordinal(domain, range).unknown(unknown);
};
-
initRange.apply(scale, arguments);
return scale;
}
function define (constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
}
function extend(parent, definition) {
var prototype = Object.create(parent.prototype);
-
for (var key in definition) prototype[key] = definition[key];
-
return prototype;
}
function Color() {}
var darker = 0.7;
var brighter = 1 / darker;
var reI = "\\s*([+-]?\\d+)\\s*",
- reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
- reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
- reHex = /^#([0-9a-f]{3,8})$/,
- reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
- reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
- reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
- reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
- reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
- reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
+ reN = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",
+ reP = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
+ reHex = /^#([0-9a-f]{3,8})$/,
+ reRgbInteger = new RegExp(`^rgb\\(${reI},${reI},${reI}\\)$`),
+ reRgbPercent = new RegExp(`^rgb\\(${reP},${reP},${reP}\\)$`),
+ reRgbaInteger = new RegExp(`^rgba\\(${reI},${reI},${reI},${reN}\\)$`),
+ reRgbaPercent = new RegExp(`^rgba\\(${reP},${reP},${reP},${reN}\\)$`),
+ reHslPercent = new RegExp(`^hsl\\(${reN},${reP},${reP}\\)$`),
+ reHslaPercent = new RegExp(`^hsla\\(${reN},${reP},${reP},${reN}\\)$`);
var named = {
aliceblue: 0xf0f8ff,
antiquewhite: 0xfaebd7,
aqua: 0x00ffff,
aquamarine: 0x7fffd4,
@@ -13840,36 +12400,36 @@
whitesmoke: 0xf5f5f5,
yellow: 0xffff00,
yellowgreen: 0x9acd32
};
define(Color, color$2, {
- copy: function (channels) {
+ copy(channels) {
return Object.assign(new this.constructor(), this, channels);
},
- displayable: function () {
+ displayable() {
return this.rgb().displayable();
},
hex: color_formatHex,
// Deprecated! Use color.formatHex.
formatHex: color_formatHex,
+ formatHex8: color_formatHex8,
formatHsl: color_formatHsl,
formatRgb: color_formatRgb,
toString: color_formatRgb
});
-
function color_formatHex() {
return this.rgb().formatHex();
}
-
+ function color_formatHex8() {
+ return this.rgb().formatHex8();
+ }
function color_formatHsl() {
return hslConvert(this).formatHsl();
}
-
function color_formatRgb() {
return this.rgb().formatRgb();
}
-
function color$2(format) {
var m, l;
format = (format + "").trim().toLowerCase();
return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
: l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
@@ -13883,20 +12443,17 @@
: (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
: (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
: named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
: format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
}
-
function rgbn(n) {
return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
}
-
function rgba(r, g, b, a) {
if (a <= 0) r = g = b = NaN;
return new Rgb(r, g, b, a);
}
-
function rgbConvert(o) {
if (!(o instanceof Color)) o = color$2(o);
if (!o) return new Rgb();
o = o.rgb();
return new Rgb(o.r, o.g, o.b, o.opacity);
@@ -13909,141 +12466,153 @@
this.g = +g;
this.b = +b;
this.opacity = +opacity;
}
define(Rgb, rgb$1, extend(Color, {
- brighter: function (k) {
+ brighter(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
- darker: function (k) {
+ darker(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
- rgb: function () {
+ rgb() {
return this;
},
- displayable: function () {
+ clamp() {
+ return new Rgb(clampi(this.r), clampi(this.g), clampi(this.b), clampa(this.opacity));
+ },
+ displayable() {
return -0.5 <= this.r && this.r < 255.5 && -0.5 <= this.g && this.g < 255.5 && -0.5 <= this.b && this.b < 255.5 && 0 <= this.opacity && this.opacity <= 1;
},
hex: rgb_formatHex,
// Deprecated! Use color.formatHex.
formatHex: rgb_formatHex,
+ formatHex8: rgb_formatHex8,
formatRgb: rgb_formatRgb,
toString: rgb_formatRgb
}));
-
function rgb_formatHex() {
- return "#" + hex(this.r) + hex(this.g) + hex(this.b);
+ return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}`;
}
-
+ function rgb_formatHex8() {
+ return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}${hex((isNaN(this.opacity) ? 1 : this.opacity) * 255)}`;
+ }
function rgb_formatRgb() {
- var a = this.opacity;
- a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
- return (a === 1 ? "rgb(" : "rgba(") + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + (a === 1 ? ")" : ", " + a + ")");
+ const a = clampa(this.opacity);
+ return `${a === 1 ? "rgb(" : "rgba("}${clampi(this.r)}, ${clampi(this.g)}, ${clampi(this.b)}${a === 1 ? ")" : `, ${a})`}`;
}
-
+ function clampa(opacity) {
+ return isNaN(opacity) ? 1 : Math.max(0, Math.min(1, opacity));
+ }
+ function clampi(value) {
+ return Math.max(0, Math.min(255, Math.round(value) || 0));
+ }
function hex(value) {
- value = Math.max(0, Math.min(255, Math.round(value) || 0));
+ value = clampi(value);
return (value < 16 ? "0" : "") + value.toString(16);
}
-
function hsla(h, s, l, a) {
if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
return new Hsl(h, s, l, a);
}
-
function hslConvert(o) {
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Color)) o = color$2(o);
if (!o) return new Hsl();
if (o instanceof Hsl) return o;
o = o.rgb();
var r = o.r / 255,
- g = o.g / 255,
- b = o.b / 255,
- min = Math.min(r, g, b),
- max = Math.max(r, g, b),
- h = NaN,
- s = max - min,
- l = (max + min) / 2;
-
+ g = o.g / 255,
+ b = o.b / 255,
+ min = Math.min(r, g, b),
+ max = Math.max(r, g, b),
+ h = NaN,
+ s = max - min,
+ l = (max + min) / 2;
if (s) {
if (r === max) h = (g - b) / s + (g < b) * 6;else if (g === max) h = (b - r) / s + 2;else h = (r - g) / s + 4;
s /= l < 0.5 ? max + min : 2 - max - min;
h *= 60;
} else {
s = l > 0 && l < 1 ? 0 : h;
}
-
return new Hsl(h, s, l, o.opacity);
}
function hsl$2(h, s, l, opacity) {
return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
}
-
function Hsl(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
-
define(Hsl, hsl$2, extend(Color, {
- brighter: function (k) {
+ brighter(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
- darker: function (k) {
+ darker(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
- rgb: function () {
+ rgb() {
var h = this.h % 360 + (this.h < 0) * 360,
- s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
- l = this.l,
- m2 = l + (l < 0.5 ? l : 1 - l) * s,
- m1 = 2 * l - m2;
+ s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
+ l = this.l,
+ m2 = l + (l < 0.5 ? l : 1 - l) * s,
+ m1 = 2 * l - m2;
return new Rgb(hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), hsl2rgb(h, m1, m2), hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), this.opacity);
},
- displayable: function () {
+ clamp() {
+ return new Hsl(clamph(this.h), clampt(this.s), clampt(this.l), clampa(this.opacity));
+ },
+ displayable() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
},
- formatHsl: function () {
- var a = this.opacity;
- a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
- return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
+ formatHsl() {
+ const a = clampa(this.opacity);
+ return `${a === 1 ? "hsl(" : "hsla("}${clamph(this.h)}, ${clampt(this.s) * 100}%, ${clampt(this.l) * 100}%${a === 1 ? ")" : `, ${a})`}`;
}
}));
- /* From FvD 13.37, CSS Color Module Level 3 */
+ function clamph(value) {
+ value = (value || 0) % 360;
+ return value < 0 ? value + 360 : value;
+ }
+ function clampt(value) {
+ return Math.max(0, Math.min(1, value || 0));
+ }
+ /* From FvD 13.37, CSS Color Module Level 3 */
function hsl2rgb(h, m1, m2) {
return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
}
const radians$1 = Math.PI / 180;
const degrees$2 = 180 / Math.PI;
+ // https://observablehq.com/@mbostock/lab-and-rgb
const K = 18,
- Xn = 0.96422,
- Yn = 1,
- Zn = 0.82521,
- t0 = 4 / 29,
- t1 = 6 / 29,
- t2 = 3 * t1 * t1,
- t3 = t1 * t1 * t1;
-
+ Xn = 0.96422,
+ Yn = 1,
+ Zn = 0.82521,
+ t0 = 4 / 29,
+ t1 = 6 / 29,
+ t2 = 3 * t1 * t1,
+ t3 = t1 * t1 * t1;
function labConvert(o) {
if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
if (o instanceof Hcl) return hcl2lab(o);
if (!(o instanceof Rgb)) o = rgbConvert(o);
var r = rgb2lrgb(o.r),
- g = rgb2lrgb(o.g),
- b = rgb2lrgb(o.b),
- y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn),
- x,
- z;
+ g = rgb2lrgb(o.g),
+ b = rgb2lrgb(o.b),
+ y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn),
+ x,
+ z;
if (r === g && g === b) x = z = y;else {
x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
}
return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
@@ -14056,43 +12625,38 @@
this.a = +a;
this.b = +b;
this.opacity = +opacity;
}
define(Lab, lab$1, extend(Color, {
- brighter: function (k) {
+ brighter(k) {
return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
- darker: function (k) {
+ darker(k) {
return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
- rgb: function () {
+ rgb() {
var y = (this.l + 16) / 116,
- x = isNaN(this.a) ? y : y + this.a / 500,
- z = isNaN(this.b) ? y : y - this.b / 200;
+ x = isNaN(this.a) ? y : y + this.a / 500,
+ z = isNaN(this.b) ? y : y - this.b / 200;
x = Xn * lab2xyz(x);
y = Yn * lab2xyz(y);
z = Zn * lab2xyz(z);
return new Rgb(lrgb2rgb(3.1338561 * x - 1.6168667 * y - 0.4906146 * z), lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z), lrgb2rgb(0.0719453 * x - 0.2289914 * y + 1.4052427 * z), this.opacity);
}
}));
-
function xyz2lab(t) {
return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
}
-
function lab2xyz(t) {
return t > t1 ? t * t * t : t2 * (t - t0);
}
-
function lrgb2rgb(x) {
return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
}
-
function rgb2lrgb(x) {
return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}
-
function hclConvert(o) {
if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
if (!(o instanceof Lab)) o = labConvert(o);
if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0 < o.l && o.l < 100 ? 0 : NaN, o.l, o.opacity);
var h = Math.atan2(o.b, o.a) * degrees$2;
@@ -14105,106 +12669,102 @@
this.h = +h;
this.c = +c;
this.l = +l;
this.opacity = +opacity;
}
-
function hcl2lab(o) {
if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
var h = o.h * radians$1;
return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
}
-
define(Hcl, hcl$2, extend(Color, {
- brighter: function (k) {
+ brighter(k) {
return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
},
- darker: function (k) {
+ darker(k) {
return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
},
- rgb: function () {
+ rgb() {
return hcl2lab(this).rgb();
}
}));
var A = -0.14861,
- B$1 = +1.78277,
- C$1 = -0.29227,
- D$1 = -0.90649,
- E = +1.97294,
- ED = E * D$1,
- EB = E * B$1,
- BC_DA = B$1 * C$1 - D$1 * A;
-
+ B$1 = +1.78277,
+ C$1 = -0.29227,
+ D$1 = -0.90649,
+ E = +1.97294,
+ ED = E * D$1,
+ EB = E * B$1,
+ BC_DA = B$1 * C$1 - D$1 * A;
function cubehelixConvert(o) {
if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Rgb)) o = rgbConvert(o);
var r = o.r / 255,
- g = o.g / 255,
- b = o.b / 255,
- l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
- bl = b - l,
- k = (E * (g - l) - C$1 * bl) / D$1,
- s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)),
- // NaN if l=0 or l=1
- h = s ? Math.atan2(k, bl) * degrees$2 - 120 : NaN;
+ g = o.g / 255,
+ b = o.b / 255,
+ l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
+ bl = b - l,
+ k = (E * (g - l) - C$1 * bl) / D$1,
+ s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)),
+ // NaN if l=0 or l=1
+ h = s ? Math.atan2(k, bl) * degrees$2 - 120 : NaN;
return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
}
-
function cubehelix$2(h, s, l, opacity) {
return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
}
function Cubehelix(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define(Cubehelix, cubehelix$2, extend(Color, {
- brighter: function (k) {
+ brighter(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
- darker: function (k) {
+ darker(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
- rgb: function () {
+ rgb() {
var h = isNaN(this.h) ? 0 : (this.h + 120) * radians$1,
- l = +this.l,
- a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
- cosh = Math.cos(h),
- sinh = Math.sin(h);
+ l = +this.l,
+ a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
+ cosh = Math.cos(h),
+ sinh = Math.sin(h);
return new Rgb(255 * (l + a * (A * cosh + B$1 * sinh)), 255 * (l + a * (C$1 * cosh + D$1 * sinh)), 255 * (l + a * (E * cosh)), this.opacity);
}
}));
function basis(t1, v0, v1, v2, v3) {
var t2 = t1 * t1,
- t3 = t2 * t1;
+ t3 = t2 * t1;
return ((1 - 3 * t1 + 3 * t2 - t3) * v0 + (4 - 6 * t2 + 3 * t3) * v1 + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2 + t3 * v3) / 6;
}
function basis$1 (values) {
var n = values.length - 1;
return function (t) {
var i = t <= 0 ? t = 0 : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),
- v1 = values[i],
- v2 = values[i + 1],
- v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
- v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
+ v1 = values[i],
+ v2 = values[i + 1],
+ v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
+ v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
return basis((t - i / n) * n, v0, v1, v2, v3);
};
}
function basisClosed (values) {
var n = values.length;
return function (t) {
var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),
- v0 = values[(i + n - 1) % n],
- v1 = values[i % n],
- v2 = values[(i + 1) % n],
- v3 = values[(i + 2) % n];
+ v0 = values[(i + n - 1) % n],
+ v1 = values[i % n],
+ v2 = values[(i + 1) % n],
+ v3 = values[(i + 2) % n];
return basis((t - i / n) * n, v0, v1, v2, v3);
};
}
var constant$2 = (x => () => x);
@@ -14212,17 +12772,15 @@
function linear$1(a, d) {
return function (t) {
return a + t * d;
};
}
-
function exponential(a, b, y) {
return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
return Math.pow(a + t * b, y);
};
}
-
function hue$1(a, b) {
var d = b - a;
return d ? linear$1(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$2(isNaN(a) ? b : a);
}
function gamma(y) {
@@ -14235,45 +12793,40 @@
return d ? linear$1(a, d) : constant$2(isNaN(a) ? b : a);
}
var rgb = (function rgbGamma(y) {
var color = gamma(y);
-
function rgb(start, end) {
var r = color((start = rgb$1(start)).r, (end = rgb$1(end)).r),
- g = color(start.g, end.g),
- b = color(start.b, end.b),
- opacity = nogamma(start.opacity, end.opacity);
+ g = color(start.g, end.g),
+ b = color(start.b, end.b),
+ opacity = nogamma(start.opacity, end.opacity);
return function (t) {
start.r = r(t);
start.g = g(t);
start.b = b(t);
start.opacity = opacity(t);
return start + "";
};
}
-
rgb.gamma = rgbGamma;
return rgb;
})(1);
-
function rgbSpline(spline) {
return function (colors) {
var n = colors.length,
- r = new Array(n),
- g = new Array(n),
- b = new Array(n),
- i,
- color;
-
+ r = new Array(n),
+ g = new Array(n),
+ b = new Array(n),
+ i,
+ color;
for (i = 0; i < n; ++i) {
color = rgb$1(colors[i]);
r[i] = color.r || 0;
g[i] = color.g || 0;
b[i] = color.b || 0;
}
-
r = spline(r);
g = spline(g);
b = spline(b);
color.opacity = 1;
return function (t) {
@@ -14282,22 +12835,20 @@
color.b = b(t);
return color + "";
};
};
}
-
var rgbBasis = rgbSpline(basis$1);
var rgbBasisClosed = rgbSpline(basisClosed);
function numberArray (a, b) {
if (!b) b = [];
var n = a ? Math.min(b.length, a.length) : 0,
- c = b.slice(),
- i;
+ c = b.slice(),
+ i;
return function (t) {
for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;
-
return c;
};
}
function isNumberArray(x) {
return ArrayBuffer.isView(x) && !(x instanceof DataView);
@@ -14306,22 +12857,18 @@
function array$3 (a, b) {
return (isNumberArray(b) ? numberArray : genericArray)(a, b);
}
function genericArray(a, b) {
var nb = b ? b.length : 0,
- na = a ? Math.min(nb, a.length) : 0,
- x = new Array(na),
- c = new Array(nb),
- i;
-
+ na = a ? Math.min(nb, a.length) : 0,
+ x = new Array(na),
+ c = new Array(nb),
+ i;
for (i = 0; i < na; ++i) x[i] = interpolate$1(a[i], b[i]);
-
for (; i < nb; ++i) c[i] = b[i];
-
return function (t) {
for (i = 0; i < na; ++i) c[i] = x[i](t);
-
return c;
};
}
function date$1 (a, b) {
@@ -14337,71 +12884,65 @@
};
}
function object (a, b) {
var i = {},
- c = {},
- k;
+ c = {},
+ k;
if (a === null || typeof a !== "object") a = {};
if (b === null || typeof b !== "object") b = {};
-
for (k in b) {
if (k in a) {
i[k] = interpolate$1(a[k], b[k]);
} else {
c[k] = b[k];
}
}
-
return function (t) {
for (k in i) c[k] = i[k](t);
-
return c;
};
}
var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
- reB = new RegExp(reA.source, "g");
-
+ reB = new RegExp(reA.source, "g");
function zero$1(b) {
return function () {
return b;
};
}
-
function one$1(b) {
return function (t) {
return b(t) + "";
};
}
-
function string (a, b) {
var bi = reA.lastIndex = reB.lastIndex = 0,
- // scan index for next number in b
- am,
- // current match in a
- bm,
- // current match in b
- bs,
- // string preceding current number in b, if any
- i = -1,
- // index in s
- s = [],
- // string constants and placeholders
- q = []; // number interpolators
+ // scan index for next number in b
+ am,
+ // current match in a
+ bm,
+ // current match in b
+ bs,
+ // string preceding current number in b, if any
+ i = -1,
+ // index in s
+ s = [],
+ // string constants and placeholders
+ q = []; // number interpolators
+
// Coerce inputs to strings.
+ a = a + "", b = b + "";
- a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
-
+ // Interpolate pairs of numbers in a & b.
while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
if ((bs = bm.index) > bi) {
// a string precedes the next number in b
bs = b.slice(bi, bs);
if (s[i]) s[i] += bs; // coalesce with previous string
else s[++i] = bs;
}
-
if ((am = am[0]) === (bm = bm[0])) {
// numbers in a & b match
if (s[i]) s[i] += bm; // coalesce with previous string
else s[++i] = bm;
} else {
@@ -14410,33 +12951,31 @@
q.push({
i: i,
x: interpolateNumber(am, bm)
});
}
-
bi = reB.lastIndex;
- } // Add remains of b.
+ }
-
+ // Add remains of b.
if (bi < b.length) {
bs = b.slice(bi);
if (s[i]) s[i] += bs; // coalesce with previous string
else s[++i] = bs;
- } // Special optimization for only a single match.
- // Otherwise, interpolate each of the numbers and rejoin the string.
+ }
-
+ // Special optimization for only a single match.
+ // Otherwise, interpolate each of the numbers and rejoin the string.
return s.length < 2 ? q[0] ? one$1(q[0].x) : zero$1(b) : (b = q.length, function (t) {
for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
-
return s.join("");
});
}
function interpolate$1 (a, b) {
var t = typeof b,
- c;
+ c;
return b == null || t === "boolean" ? constant$2(b) : (t === "number" ? interpolateNumber : t === "string" ? (c = color$2(b)) ? (b = c, rgb) : string : b instanceof color$2 ? rgb : b instanceof Date ? date$1 : isNumberArray(b) ? numberArray : Array.isArray(b) ? genericArray : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object : interpolateNumber)(a, b);
}
function discrete$1 (range) {
var n = range.length;
@@ -14483,12 +13022,12 @@
scaleY: scaleY
};
}
var svgNode;
- /* eslint-disable no-undef */
+ /* eslint-disable no-undef */
function parseCss(value) {
const m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
return m.isIdentity ? identity$3 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
}
function parseSvg(value) {
@@ -14502,11 +13041,10 @@
function interpolateTransform(parse, pxComma, pxParen, degParen) {
function pop(s) {
return s.length ? s.pop() + " " : "";
}
-
function translate(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i = s.push("translate(", null, pxComma, null, pxParen);
q.push({
i: i - 4,
@@ -14517,35 +13055,31 @@
});
} else if (xb || yb) {
s.push("translate(" + xb + pxComma + yb + pxParen);
}
}
-
function rotate(a, b, s, q) {
if (a !== b) {
if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
-
q.push({
i: s.push(pop(s) + "rotate(", null, degParen) - 2,
x: interpolateNumber(a, b)
});
} else if (b) {
s.push(pop(s) + "rotate(" + b + degParen);
}
}
-
function skewX(a, b, s, q) {
if (a !== b) {
q.push({
i: s.push(pop(s) + "skewX(", null, degParen) - 2,
x: interpolateNumber(a, b)
});
} else if (b) {
s.push(pop(s) + "skewX(" + b + degParen);
}
}
-
function scale(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i = s.push(pop(s) + "scale(", null, ",", null, ")");
q.push({
i: i - 4,
@@ -14556,130 +13090,116 @@
});
} else if (xb !== 1 || yb !== 1) {
s.push(pop(s) + "scale(" + xb + "," + yb + ")");
}
}
-
return function (a, b) {
var s = [],
- // string constants and placeholders
- q = []; // number interpolators
-
+ // string constants and placeholders
+ q = []; // number interpolators
a = parse(a), b = parse(b);
translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
rotate(a.rotate, b.rotate, s, q);
skewX(a.skewX, b.skewX, s, q);
scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
a = b = null; // gc
-
return function (t) {
var i = -1,
- n = q.length,
- o;
-
+ n = q.length,
+ o;
while (++i < n) s[(o = q[i]).i] = o.x(t);
-
return s.join("");
};
};
}
-
var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
var epsilon2$1 = 1e-12;
-
function cosh(x) {
return ((x = Math.exp(x)) + 1 / x) / 2;
}
-
function sinh(x) {
return ((x = Math.exp(x)) - 1 / x) / 2;
}
-
function tanh(x) {
return ((x = Math.exp(2 * x)) - 1) / (x + 1);
}
-
var zoom = (function zoomRho(rho, rho2, rho4) {
// p0 = [ux0, uy0, w0]
// p1 = [ux1, uy1, w1]
function zoom(p0, p1) {
var ux0 = p0[0],
- uy0 = p0[1],
- w0 = p0[2],
- ux1 = p1[0],
- uy1 = p1[1],
- w1 = p1[2],
- dx = ux1 - ux0,
- dy = uy1 - uy0,
- d2 = dx * dx + dy * dy,
- i,
- S; // Special case for u0 ≅ u1.
+ uy0 = p0[1],
+ w0 = p0[2],
+ ux1 = p1[0],
+ uy1 = p1[1],
+ w1 = p1[2],
+ dx = ux1 - ux0,
+ dy = uy1 - uy0,
+ d2 = dx * dx + dy * dy,
+ i,
+ S;
+ // Special case for u0 ≅ u1.
if (d2 < epsilon2$1) {
S = Math.log(w1 / w0) / rho;
-
i = function (t) {
return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
};
- } // General case.
+ }
+
+ // General case.
else {
var d1 = Math.sqrt(d2),
- b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
- b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
- r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
- r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
+ b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
+ b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
+ r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
+ r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
S = (r1 - r0) / rho;
-
i = function (t) {
var s = t * S,
- coshr0 = cosh(r0),
- u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
+ coshr0 = cosh(r0),
+ u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
};
}
-
i.duration = S * 1000 * rho / Math.SQRT2;
return i;
}
-
zoom.rho = function (_) {
var _1 = Math.max(1e-3, +_),
- _2 = _1 * _1,
- _4 = _2 * _2;
-
+ _2 = _1 * _1,
+ _4 = _2 * _2;
return zoomRho(_1, _2, _4);
};
-
return zoom;
})(Math.SQRT2, 2, 4);
function hsl(hue) {
return function (start, end) {
var h = hue((start = hsl$2(start)).h, (end = hsl$2(end)).h),
- s = nogamma(start.s, end.s),
- l = nogamma(start.l, end.l),
- opacity = nogamma(start.opacity, end.opacity);
+ s = nogamma(start.s, end.s),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
return function (t) {
start.h = h(t);
start.s = s(t);
start.l = l(t);
start.opacity = opacity(t);
return start + "";
};
};
}
-
var hsl$1 = hsl(hue$1);
var hslLong = hsl(nogamma);
function lab(start, end) {
var l = nogamma((start = lab$1(start)).l, (end = lab$1(end)).l),
- a = nogamma(start.a, end.a),
- b = nogamma(start.b, end.b),
- opacity = nogamma(start.opacity, end.opacity);
+ a = nogamma(start.a, end.a),
+ b = nogamma(start.b, end.b),
+ opacity = nogamma(start.opacity, end.opacity);
return function (t) {
start.l = l(t);
start.a = a(t);
start.b = b(t);
start.opacity = opacity(t);
@@ -14688,102 +13208,94 @@
}
function hcl(hue) {
return function (start, end) {
var h = hue((start = hcl$2(start)).h, (end = hcl$2(end)).h),
- c = nogamma(start.c, end.c),
- l = nogamma(start.l, end.l),
- opacity = nogamma(start.opacity, end.opacity);
+ c = nogamma(start.c, end.c),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
return function (t) {
start.h = h(t);
start.c = c(t);
start.l = l(t);
start.opacity = opacity(t);
return start + "";
};
};
}
-
var hcl$1 = hcl(hue$1);
var hclLong = hcl(nogamma);
function cubehelix(hue) {
return function cubehelixGamma(y) {
y = +y;
-
function cubehelix(start, end) {
var h = hue((start = cubehelix$2(start)).h, (end = cubehelix$2(end)).h),
- s = nogamma(start.s, end.s),
- l = nogamma(start.l, end.l),
- opacity = nogamma(start.opacity, end.opacity);
+ s = nogamma(start.s, end.s),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
return function (t) {
start.h = h(t);
start.s = s(t);
start.l = l(Math.pow(t, y));
start.opacity = opacity(t);
return start + "";
};
}
-
cubehelix.gamma = cubehelixGamma;
return cubehelix;
}(1);
}
-
var cubehelix$1 = cubehelix(hue$1);
var cubehelixLong = cubehelix(nogamma);
function piecewise(interpolate, values) {
if (values === undefined) values = interpolate, interpolate = interpolate$1;
var i = 0,
- n = values.length - 1,
- v = values[0],
- I = new Array(n < 0 ? 0 : n);
-
+ n = values.length - 1,
+ v = values[0],
+ I = new Array(n < 0 ? 0 : n);
while (i < n) I[i] = interpolate(v, v = values[++i]);
-
return function (t) {
var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));
return I[i](t - i);
};
}
function quantize$2 (interpolator, n) {
var samples = new Array(n);
-
for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
-
return samples;
}
var $$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
interpolate: interpolate$1,
interpolateArray: array$3,
interpolateBasis: basis$1,
interpolateBasisClosed: basisClosed,
+ interpolateCubehelix: cubehelix$1,
+ interpolateCubehelixLong: cubehelixLong,
interpolateDate: date$1,
interpolateDiscrete: discrete$1,
+ interpolateHcl: hcl$1,
+ interpolateHclLong: hclLong,
+ interpolateHsl: hsl$1,
+ interpolateHslLong: hslLong,
interpolateHue: hue,
+ interpolateLab: lab,
interpolateNumber: interpolateNumber,
interpolateNumberArray: numberArray,
interpolateObject: object,
+ interpolateRgb: rgb,
+ interpolateRgbBasis: rgbBasis,
+ interpolateRgbBasisClosed: rgbBasisClosed,
interpolateRound: interpolateRound,
interpolateString: string,
interpolateTransformCss: interpolateTransformCss,
interpolateTransformSvg: interpolateTransformSvg,
interpolateZoom: zoom,
- interpolateRgb: rgb,
- interpolateRgbBasis: rgbBasis,
- interpolateRgbBasisClosed: rgbBasisClosed,
- interpolateHsl: hsl$1,
- interpolateHslLong: hslLong,
- interpolateLab: lab,
- interpolateHcl: hcl$1,
- interpolateHclLong: hclLong,
- interpolateCubehelix: cubehelix$1,
- interpolateCubehelixLong: cubehelixLong,
piecewise: piecewise,
quantize: quantize$2
});
function constants(x) {
@@ -14798,115 +13310,100 @@
var unit = [0, 1];
function identity$2(x) {
return x;
}
-
function normalize$1(a, b) {
return (b -= a = +a) ? function (x) {
return (x - a) / b;
} : constants(isNaN(b) ? NaN : 0.5);
}
-
function clamper(a, b) {
var t;
if (a > b) t = a, a = b, b = t;
return function (x) {
return Math.max(a, Math.min(b, x));
};
- } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
- // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
+ }
-
+ // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
+ // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
function bimap(domain, range, interpolate) {
var d0 = domain[0],
- d1 = domain[1],
- r0 = range[0],
- r1 = range[1];
+ d1 = domain[1],
+ r0 = range[0],
+ r1 = range[1];
if (d1 < d0) d0 = normalize$1(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize$1(d0, d1), r0 = interpolate(r0, r1);
return function (x) {
return r0(d0(x));
};
}
-
function polymap(domain, range, interpolate) {
var j = Math.min(domain.length, range.length) - 1,
- d = new Array(j),
- r = new Array(j),
- i = -1; // Reverse descending domains.
+ d = new Array(j),
+ r = new Array(j),
+ i = -1;
+ // Reverse descending domains.
if (domain[j] < domain[0]) {
domain = domain.slice().reverse();
range = range.slice().reverse();
}
-
while (++i < j) {
d[i] = normalize$1(domain[i], domain[i + 1]);
r[i] = interpolate(range[i], range[i + 1]);
}
-
return function (x) {
- var i = bisect$1(domain, x, 1, j) - 1;
+ var i = bisectRight$1(domain, x, 1, j) - 1;
return r[i](d[i](x));
};
}
-
function copy$2(source, target) {
return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
}
function transformer$3() {
var domain = unit,
- range = unit,
- interpolate = interpolate$1,
- transform,
- untransform,
- unknown,
- clamp = identity$2,
- piecewise,
- output,
- input;
-
+ range = unit,
+ interpolate = interpolate$1,
+ transform,
+ untransform,
+ unknown,
+ clamp = identity$2,
+ piecewise,
+ output,
+ input;
function rescale() {
var n = Math.min(domain.length, range.length);
if (clamp !== identity$2) clamp = clamper(domain[0], domain[n - 1]);
piecewise = n > 2 ? polymap : bimap;
output = input = null;
return scale;
}
-
function scale(x) {
return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
}
-
scale.invert = function (y) {
return clamp(untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y)));
};
-
scale.domain = function (_) {
return arguments.length ? (domain = Array.from(_, number$5), rescale()) : domain.slice();
};
-
scale.range = function (_) {
return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
};
-
scale.rangeRound = function (_) {
return range = Array.from(_), interpolate = interpolateRound, rescale();
};
-
scale.clamp = function (_) {
return arguments.length ? (clamp = _ ? true : identity$2, rescale()) : clamp !== identity$2;
};
-
scale.interpolate = function (_) {
return arguments.length ? (interpolate = _, rescale()) : interpolate;
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
return function (t, u) {
transform = t, untransform = u;
return rescale();
};
}
@@ -14914,74 +13411,64 @@
return transformer$3()(identity$2, identity$2);
}
function tickFormat$1(start, stop, count, specifier) {
var step = tickStep(start, stop, count),
- precision;
+ precision;
specifier = formatSpecifier(specifier == null ? ",f" : specifier);
-
switch (specifier.type) {
case "s":
{
var value = Math.max(Math.abs(start), Math.abs(stop));
if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
return formatPrefix(specifier, value);
}
-
case "":
case "e":
case "g":
case "p":
case "r":
{
if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
break;
}
-
case "f":
case "%":
{
if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
break;
}
}
-
return format$3(specifier);
}
function linearish(scale) {
var domain = scale.domain;
-
scale.ticks = function (count) {
var d = domain();
return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
};
-
scale.tickFormat = function (count, specifier) {
var d = domain();
return tickFormat$1(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
};
-
scale.nice = function (count) {
if (count == null) count = 10;
var d = domain();
var i0 = 0;
var i1 = d.length - 1;
var start = d[i0];
var stop = d[i1];
var prestep;
var step;
var maxIter = 10;
-
if (stop < start) {
step = start, start = stop, stop = step;
step = i0, i0 = i1, i1 = step;
}
-
while (maxIter-- > 0) {
step = tickIncrement(start, stop, count);
-
if (step === prestep) {
d[i0] = start;
d[i1] = stop;
return domain(d);
} else if (step > 0) {
@@ -14991,133 +13478,106 @@
start = Math.ceil(start * step) / step;
stop = Math.floor(stop * step) / step;
} else {
break;
}
-
prestep = step;
}
-
return scale;
};
-
return scale;
}
function linear() {
var scale = continuous$1();
-
scale.copy = function () {
return copy$2(scale, linear());
};
-
initRange.apply(scale, arguments);
return linearish(scale);
}
function identity$1(domain) {
var unknown;
-
function scale(x) {
return x == null || isNaN(x = +x) ? unknown : x;
}
-
scale.invert = scale;
-
scale.domain = scale.range = function (_) {
return arguments.length ? (domain = Array.from(_, number$5), scale) : domain.slice();
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
scale.copy = function () {
return identity$1(domain).unknown(unknown);
};
-
domain = arguments.length ? Array.from(domain, number$5) : [0, 1];
return linearish(scale);
}
function nice(domain, interval) {
domain = domain.slice();
var i0 = 0,
- i1 = domain.length - 1,
- x0 = domain[i0],
- x1 = domain[i1],
- t;
-
+ i1 = domain.length - 1,
+ x0 = domain[i0],
+ x1 = domain[i1],
+ t;
if (x1 < x0) {
t = i0, i0 = i1, i1 = t;
t = x0, x0 = x1, x1 = t;
}
-
domain[i0] = interval.floor(x0);
domain[i1] = interval.ceil(x1);
return domain;
}
function transformLog(x) {
return Math.log(x);
}
-
function transformExp(x) {
return Math.exp(x);
}
-
function transformLogn(x) {
return -Math.log(-x);
}
-
function transformExpn(x) {
return -Math.exp(-x);
}
-
function pow10(x) {
return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x;
}
-
function powp(base) {
return base === 10 ? pow10 : base === Math.E ? Math.exp : x => Math.pow(base, x);
}
-
function logp(base) {
return base === Math.E ? Math.log : base === 10 && Math.log10 || base === 2 && Math.log2 || (base = Math.log(base), x => Math.log(x) / base);
}
-
function reflect(f) {
return (x, k) => -f(-x, k);
}
-
function loggish(transform) {
const scale = transform(transformLog, transformExp);
const domain = scale.domain;
let base = 10;
let logs;
let pows;
-
function rescale() {
logs = logp(base), pows = powp(base);
-
if (domain()[0] < 0) {
logs = reflect(logs), pows = reflect(pows);
transform(transformLogn, transformExpn);
} else {
transform(transformLog, transformExp);
}
-
return scale;
}
-
scale.base = function (_) {
return arguments.length ? (base = +_, rescale()) : base;
};
-
scale.domain = function (_) {
return arguments.length ? (domain(_), rescale()) : domain();
};
-
scale.ticks = count => {
const d = domain();
let u = d[0];
let v = d[d.length - 1];
const r = v < u;
@@ -15126,11 +13586,10 @@
let j = logs(v);
let k;
let t;
const n = count == null ? 10 : +count;
let z = [];
-
if (!(base % 1) && j - i < n) {
i = Math.floor(i), j = Math.ceil(j);
if (u > 0) for (; i <= j; ++i) {
for (k = 1; k < base; ++k) {
t = i < 0 ? k / pows(-i) : k * pows(i);
@@ -15148,510 +13607,439 @@
}
if (z.length * 2 < n) z = ticks(u, v, n);
} else {
z = ticks(i, j, Math.min(j - i, n)).map(pows);
}
-
return r ? z.reverse() : z;
};
-
scale.tickFormat = (count, specifier) => {
if (count == null) count = 10;
if (specifier == null) specifier = base === 10 ? "s" : ",";
-
if (typeof specifier !== "function") {
if (!(base % 1) && (specifier = formatSpecifier(specifier)).precision == null) specifier.trim = true;
specifier = format$3(specifier);
}
-
if (count === Infinity) return specifier;
const k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
-
return d => {
let i = d / pows(Math.round(logs(d)));
if (i * base < base - 0.5) i *= base;
return i <= k ? specifier(d) : "";
};
};
-
scale.nice = () => {
return domain(nice(domain(), {
floor: x => pows(Math.floor(logs(x))),
ceil: x => pows(Math.ceil(logs(x)))
}));
};
-
return scale;
}
function log$2() {
const scale = loggish(transformer$3()).domain([1, 10]);
-
scale.copy = () => copy$2(scale, log$2()).base(scale.base());
-
initRange.apply(scale, arguments);
return scale;
}
function transformSymlog(c) {
return function (x) {
return Math.sign(x) * Math.log1p(Math.abs(x / c));
};
}
-
function transformSymexp(c) {
return function (x) {
return Math.sign(x) * Math.expm1(Math.abs(x)) * c;
};
}
-
function symlogish(transform) {
var c = 1,
- scale = transform(transformSymlog(c), transformSymexp(c));
-
+ scale = transform(transformSymlog(c), transformSymexp(c));
scale.constant = function (_) {
return arguments.length ? transform(transformSymlog(c = +_), transformSymexp(c)) : c;
};
-
return linearish(scale);
}
function symlog() {
var scale = symlogish(transformer$3());
-
scale.copy = function () {
return copy$2(scale, symlog()).constant(scale.constant());
};
-
return initRange.apply(scale, arguments);
}
function transformPow(exponent) {
return function (x) {
return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
};
}
-
function transformSqrt(x) {
return x < 0 ? -Math.sqrt(-x) : Math.sqrt(x);
}
-
function transformSquare(x) {
return x < 0 ? -x * x : x * x;
}
-
function powish(transform) {
var scale = transform(identity$2, identity$2),
- exponent = 1;
-
+ exponent = 1;
function rescale() {
return exponent === 1 ? transform(identity$2, identity$2) : exponent === 0.5 ? transform(transformSqrt, transformSquare) : transform(transformPow(exponent), transformPow(1 / exponent));
}
-
scale.exponent = function (_) {
return arguments.length ? (exponent = +_, rescale()) : exponent;
};
-
return linearish(scale);
}
function pow$2() {
var scale = powish(transformer$3());
-
scale.copy = function () {
return copy$2(scale, pow$2()).exponent(scale.exponent());
};
-
initRange.apply(scale, arguments);
return scale;
}
function sqrt$2() {
return pow$2.apply(null, arguments).exponent(0.5);
}
function quantile() {
var domain = [],
- range = [],
- thresholds = [],
- unknown;
-
+ range = [],
+ thresholds = [],
+ unknown;
function rescale() {
var i = 0,
- n = Math.max(1, range.length);
+ n = Math.max(1, range.length);
thresholds = new Array(n - 1);
-
while (++i < n) thresholds[i - 1] = quantileSorted(domain, i / n);
-
return scale;
}
-
function scale(x) {
- return x == null || isNaN(x = +x) ? unknown : range[bisect$1(thresholds, x)];
+ return x == null || isNaN(x = +x) ? unknown : range[bisectRight$1(thresholds, x)];
}
-
scale.invertExtent = function (y) {
var i = range.indexOf(y);
return i < 0 ? [NaN, NaN] : [i > 0 ? thresholds[i - 1] : domain[0], i < thresholds.length ? thresholds[i] : domain[domain.length - 1]];
};
-
scale.domain = function (_) {
if (!arguments.length) return domain.slice();
domain = [];
-
for (let d of _) if (d != null && !isNaN(d = +d)) domain.push(d);
-
domain.sort(ascending$1);
return rescale();
};
-
scale.range = function (_) {
return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
scale.quantiles = function () {
return thresholds.slice();
};
-
scale.copy = function () {
return quantile().domain(domain).range(range).unknown(unknown);
};
-
return initRange.apply(scale, arguments);
}
function quantize$1() {
var x0 = 0,
- x1 = 1,
- n = 1,
- domain = [0.5],
- range = [0, 1],
- unknown;
-
+ x1 = 1,
+ n = 1,
+ domain = [0.5],
+ range = [0, 1],
+ unknown;
function scale(x) {
- return x != null && x <= x ? range[bisect$1(domain, x, 0, n)] : unknown;
+ return x != null && x <= x ? range[bisectRight$1(domain, x, 0, n)] : unknown;
}
-
function rescale() {
var i = -1;
domain = new Array(n);
-
while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
-
return scale;
}
-
scale.domain = function (_) {
return arguments.length ? ([x0, x1] = _, x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
};
-
scale.range = function (_) {
return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
};
-
scale.invertExtent = function (y) {
var i = range.indexOf(y);
return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : scale;
};
-
scale.thresholds = function () {
return domain.slice();
};
-
scale.copy = function () {
return quantize$1().domain([x0, x1]).range(range).unknown(unknown);
};
-
return initRange.apply(linearish(scale), arguments);
}
function threshold() {
var domain = [0.5],
- range = [0, 1],
- unknown,
- n = 1;
-
+ range = [0, 1],
+ unknown,
+ n = 1;
function scale(x) {
- return x != null && x <= x ? range[bisect$1(domain, x, 0, n)] : unknown;
+ return x != null && x <= x ? range[bisectRight$1(domain, x, 0, n)] : unknown;
}
-
scale.domain = function (_) {
return arguments.length ? (domain = Array.from(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();
};
-
scale.range = function (_) {
return arguments.length ? (range = Array.from(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();
};
-
scale.invertExtent = function (y) {
var i = range.indexOf(y);
return [domain[i - 1], domain[i]];
};
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
scale.copy = function () {
return threshold().domain(domain).range(range).unknown(unknown);
};
-
return initRange.apply(scale, arguments);
}
function date(t) {
return new Date(t);
}
-
function number$4(t) {
return t instanceof Date ? +t : +new Date(+t);
}
-
function calendar(ticks, tickInterval, year, month, week, day, hour, minute, second, format) {
var scale = continuous$1(),
- invert = scale.invert,
- domain = scale.domain;
+ invert = scale.invert,
+ domain = scale.domain;
var formatMillisecond = format(".%L"),
- formatSecond = format(":%S"),
- formatMinute = format("%I:%M"),
- formatHour = format("%I %p"),
- formatDay = format("%a %d"),
- formatWeek = format("%b %d"),
- formatMonth = format("%B"),
- formatYear = format("%Y");
-
+ formatSecond = format(":%S"),
+ formatMinute = format("%I:%M"),
+ formatHour = format("%I %p"),
+ formatDay = format("%a %d"),
+ formatWeek = format("%b %d"),
+ formatMonth = format("%B"),
+ formatYear = format("%Y");
function tickFormat(date) {
return (second(date) < date ? formatMillisecond : minute(date) < date ? formatSecond : hour(date) < date ? formatMinute : day(date) < date ? formatHour : month(date) < date ? week(date) < date ? formatDay : formatWeek : year(date) < date ? formatMonth : formatYear)(date);
}
-
scale.invert = function (y) {
return new Date(invert(y));
};
-
scale.domain = function (_) {
return arguments.length ? domain(Array.from(_, number$4)) : domain().map(date);
};
-
scale.ticks = function (interval) {
var d = domain();
return ticks(d[0], d[d.length - 1], interval == null ? 10 : interval);
};
-
scale.tickFormat = function (count, specifier) {
return specifier == null ? tickFormat : format(specifier);
};
-
scale.nice = function (interval) {
var d = domain();
if (!interval || typeof interval.range !== "function") interval = tickInterval(d[0], d[d.length - 1], interval == null ? 10 : interval);
return interval ? domain(nice(d, interval)) : scale;
};
-
scale.copy = function () {
return copy$2(scale, calendar(ticks, tickInterval, year, month, week, day, hour, minute, second, format));
};
-
return scale;
}
function time$1() {
- return initRange.apply(calendar(timeTicks, timeTickInterval, timeYear, timeMonth, sunday, timeDay, timeHour, timeMinute, utcSecond, timeFormat$1).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]), arguments);
+ return initRange.apply(calendar(timeTicks, timeTickInterval, timeYear, timeMonth, timeSunday, timeDay, timeHour, timeMinute, second, timeFormat$1).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]), arguments);
}
function utcTime() {
- return initRange.apply(calendar(utcTicks, utcTickInterval, utcYear$1, utcMonth$1, utcSunday, utcDay$1, utcHour$1, utcMinute$1, utcSecond, utcFormat$1).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]), arguments);
+ return initRange.apply(calendar(utcTicks, utcTickInterval, utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, utcFormat$1).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]), arguments);
}
function transformer$2() {
var x0 = 0,
- x1 = 1,
- t0,
- t1,
- k10,
- transform,
- interpolator = identity$2,
- clamp = false,
- unknown;
-
+ x1 = 1,
+ t0,
+ t1,
+ k10,
+ transform,
+ interpolator = identity$2,
+ clamp = false,
+ unknown;
function scale(x) {
return x == null || isNaN(x = +x) ? unknown : interpolator(k10 === 0 ? 0.5 : (x = (transform(x) - t0) * k10, clamp ? Math.max(0, Math.min(1, x)) : x));
}
-
scale.domain = function (_) {
return arguments.length ? ([x0, x1] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0), scale) : [x0, x1];
};
-
scale.clamp = function (_) {
return arguments.length ? (clamp = !!_, scale) : clamp;
};
-
scale.interpolator = function (_) {
return arguments.length ? (interpolator = _, scale) : interpolator;
};
-
function range(interpolate) {
return function (_) {
var r0, r1;
return arguments.length ? ([r0, r1] = _, interpolator = interpolate(r0, r1), scale) : [interpolator(0), interpolator(1)];
};
}
-
scale.range = range(interpolate$1);
scale.rangeRound = range(interpolateRound);
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
return function (t) {
transform = t, t0 = t(x0), t1 = t(x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0);
return scale;
};
}
-
function copy$1(source, target) {
return target.domain(source.domain()).interpolator(source.interpolator()).clamp(source.clamp()).unknown(source.unknown());
}
function sequential() {
var scale = linearish(transformer$2()(identity$2));
-
scale.copy = function () {
return copy$1(scale, sequential());
};
-
return initInterpolator.apply(scale, arguments);
}
function sequentialLog() {
var scale = loggish(transformer$2()).domain([1, 10]);
-
scale.copy = function () {
return copy$1(scale, sequentialLog()).base(scale.base());
};
-
return initInterpolator.apply(scale, arguments);
}
function sequentialSymlog() {
var scale = symlogish(transformer$2());
-
scale.copy = function () {
return copy$1(scale, sequentialSymlog()).constant(scale.constant());
};
-
return initInterpolator.apply(scale, arguments);
}
function sequentialPow() {
var scale = powish(transformer$2());
-
scale.copy = function () {
return copy$1(scale, sequentialPow()).exponent(scale.exponent());
};
-
return initInterpolator.apply(scale, arguments);
}
function sequentialSqrt() {
return sequentialPow.apply(null, arguments).exponent(0.5);
}
function transformer$1() {
var x0 = 0,
- x1 = 0.5,
- x2 = 1,
- s = 1,
- t0,
- t1,
- t2,
- k10,
- k21,
- interpolator = identity$2,
- transform,
- clamp = false,
- unknown;
-
+ x1 = 0.5,
+ x2 = 1,
+ s = 1,
+ t0,
+ t1,
+ t2,
+ k10,
+ k21,
+ interpolator = identity$2,
+ transform,
+ clamp = false,
+ unknown;
function scale(x) {
return isNaN(x = +x) ? unknown : (x = 0.5 + ((x = +transform(x)) - t1) * (s * x < s * t1 ? k10 : k21), interpolator(clamp ? Math.max(0, Math.min(1, x)) : x));
}
-
scale.domain = function (_) {
return arguments.length ? ([x0, x1, x2] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), t2 = transform(x2 = +x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1, scale) : [x0, x1, x2];
};
-
scale.clamp = function (_) {
return arguments.length ? (clamp = !!_, scale) : clamp;
};
-
scale.interpolator = function (_) {
return arguments.length ? (interpolator = _, scale) : interpolator;
};
-
function range(interpolate) {
return function (_) {
var r0, r1, r2;
return arguments.length ? ([r0, r1, r2] = _, interpolator = piecewise(interpolate, [r0, r1, r2]), scale) : [interpolator(0), interpolator(0.5), interpolator(1)];
};
}
-
scale.range = range(interpolate$1);
scale.rangeRound = range(interpolateRound);
-
scale.unknown = function (_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
-
return function (t) {
transform = t, t0 = t(x0), t1 = t(x1), t2 = t(x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1;
return scale;
};
}
-
function diverging() {
var scale = linearish(transformer$1()(identity$2));
-
scale.copy = function () {
return copy$1(scale, diverging());
};
-
return initInterpolator.apply(scale, arguments);
}
function divergingLog() {
var scale = loggish(transformer$1()).domain([0.1, 1, 10]);
-
scale.copy = function () {
return copy$1(scale, divergingLog()).base(scale.base());
};
-
return initInterpolator.apply(scale, arguments);
}
function divergingSymlog() {
var scale = symlogish(transformer$1());
-
scale.copy = function () {
return copy$1(scale, divergingSymlog()).constant(scale.constant());
};
-
return initInterpolator.apply(scale, arguments);
}
function divergingPow() {
var scale = powish(transformer$1());
-
scale.copy = function () {
return copy$1(scale, divergingPow()).exponent(scale.exponent());
};
-
return initInterpolator.apply(scale, arguments);
}
function divergingSqrt() {
return divergingPow.apply(null, arguments).exponent(0.5);
}
+ function colors$1 (specifier) {
+ var n = specifier.length / 6 | 0,
+ colors = new Array(n),
+ i = 0;
+ while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
+ return colors;
+ }
+
+ var schemeCategory10 = colors$1("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf");
+
+ var schemeAccent = colors$1("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666");
+
+ var schemeDark2 = colors$1("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666");
+
+ var schemeObservable10 = colors$1("4269d0efb118ff725c6cc5b03ca951ff8ab7a463f297bbf59c6b4e9498a0");
+
+ var schemePaired = colors$1("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928");
+
+ var schemePastel1 = colors$1("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2");
+
+ var schemePastel2 = colors$1("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc");
+
+ var schemeSet1 = colors$1("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999");
+
+ var schemeSet2 = colors$1("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3");
+
+ var schemeSet3 = colors$1("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f");
+
function bandSpace(count, paddingInner, paddingOuter) {
const space = count - paddingInner + paddingOuter * 2;
return count ? space > 0 ? space : 1 : 0;
}
-
const Identity = 'identity';
const Linear = 'linear';
const Log = 'log';
const Pow = 'pow';
const Sqrt = 'sqrt';
@@ -15664,438 +14052,409 @@
const Quantize = 'quantize';
const Threshold = 'threshold';
const Ordinal = 'ordinal';
const Point = 'point';
const Band = 'band';
- const BinOrdinal = 'bin-ordinal'; // categories
+ const BinOrdinal = 'bin-ordinal';
+ // categories
const Continuous = 'continuous';
const Discrete$1 = 'discrete';
const Discretizing = 'discretizing';
const Interpolating = 'interpolating';
const Temporal = 'temporal';
-
function invertRange(scale) {
return function (_) {
let lo = _[0],
- hi = _[1],
- t;
-
+ hi = _[1],
+ t;
if (hi < lo) {
t = lo;
lo = hi;
hi = t;
}
-
return [scale.invert(lo), scale.invert(hi)];
};
}
-
function invertRangeExtent(scale) {
return function (_) {
const range = scale.range();
let lo = _[0],
- hi = _[1],
- min = -1,
- max,
- t,
- i,
- n;
-
+ hi = _[1],
+ min = -1,
+ max,
+ t,
+ i,
+ n;
if (hi < lo) {
t = lo;
lo = hi;
hi = t;
}
-
for (i = 0, n = range.length; i < n; ++i) {
if (range[i] >= lo && range[i] <= hi) {
if (min < 0) min = i;
max = i;
}
}
-
if (min < 0) return undefined;
lo = scale.invertExtent(range[min]);
hi = scale.invertExtent(range[max]);
return [lo[0] === undefined ? lo[1] : lo[0], hi[1] === undefined ? hi[0] : hi[1]];
};
}
-
function band() {
const scale = ordinal().unknown(undefined),
- domain = scale.domain,
- ordinalRange = scale.range;
+ domain = scale.domain,
+ ordinalRange = scale.range;
let range$1 = [0, 1],
- step,
- bandwidth,
- round = false,
- paddingInner = 0,
- paddingOuter = 0,
- align = 0.5;
+ step,
+ bandwidth,
+ round = false,
+ paddingInner = 0,
+ paddingOuter = 0,
+ align = 0.5;
delete scale.unknown;
-
function rescale() {
const n = domain().length,
- reverse = range$1[1] < range$1[0],
- stop = range$1[1 - reverse],
- space = bandSpace(n, paddingInner, paddingOuter);
+ reverse = range$1[1] < range$1[0],
+ stop = range$1[1 - reverse],
+ space = bandSpace(n, paddingInner, paddingOuter);
let start = range$1[reverse - 0];
step = (stop - start) / (space || 1);
-
if (round) {
step = Math.floor(step);
}
-
start += (stop - start - step * (n - paddingInner)) * align;
bandwidth = step * (1 - paddingInner);
-
if (round) {
start = Math.round(start);
bandwidth = Math.round(bandwidth);
}
-
const values = range$3(n).map(i => start + step * i);
return ordinalRange(reverse ? values.reverse() : values);
}
-
scale.domain = function (_) {
if (arguments.length) {
domain(_);
return rescale();
} else {
return domain();
}
};
-
scale.range = function (_) {
if (arguments.length) {
range$1 = [+_[0], +_[1]];
return rescale();
} else {
return range$1.slice();
}
};
-
scale.rangeRound = function (_) {
range$1 = [+_[0], +_[1]];
round = true;
return rescale();
};
-
scale.bandwidth = function () {
return bandwidth;
};
-
scale.step = function () {
return step;
};
-
scale.round = function (_) {
if (arguments.length) {
round = !!_;
return rescale();
} else {
return round;
}
};
-
scale.padding = function (_) {
if (arguments.length) {
paddingOuter = Math.max(0, Math.min(1, _));
paddingInner = paddingOuter;
return rescale();
} else {
return paddingInner;
}
};
-
scale.paddingInner = function (_) {
if (arguments.length) {
paddingInner = Math.max(0, Math.min(1, _));
return rescale();
} else {
return paddingInner;
}
};
-
scale.paddingOuter = function (_) {
if (arguments.length) {
paddingOuter = Math.max(0, Math.min(1, _));
return rescale();
} else {
return paddingOuter;
}
};
-
scale.align = function (_) {
if (arguments.length) {
align = Math.max(0, Math.min(1, _));
return rescale();
} else {
return align;
}
};
-
scale.invertRange = function (_) {
// bail if range has null or undefined values
if (_[0] == null || _[1] == null) return;
const reverse = range$1[1] < range$1[0],
- values = reverse ? ordinalRange().reverse() : ordinalRange(),
- n = values.length - 1;
+ values = reverse ? ordinalRange().reverse() : ordinalRange(),
+ n = values.length - 1;
let lo = +_[0],
- hi = +_[1],
- a,
- b,
- t; // bail if either range endpoint is invalid
+ hi = +_[1],
+ a,
+ b,
+ t;
- if (lo !== lo || hi !== hi) return; // order range inputs, bail if outside of scale range
+ // bail if either range endpoint is invalid
+ if (lo !== lo || hi !== hi) return;
+ // order range inputs, bail if outside of scale range
if (hi < lo) {
t = lo;
lo = hi;
hi = t;
}
+ if (hi < values[0] || lo > range$1[1 - reverse]) return;
- if (hi < values[0] || lo > range$1[1 - reverse]) return; // binary search to index into scale range
-
+ // binary search to index into scale range
a = Math.max(0, bisectRight$1(values, lo) - 1);
- b = lo === hi ? a : bisectRight$1(values, hi) - 1; // increment index a if lo is within padding gap
+ b = lo === hi ? a : bisectRight$1(values, hi) - 1;
+ // increment index a if lo is within padding gap
if (lo - values[a] > bandwidth + 1e-10) ++a;
-
if (reverse) {
// map + swap
t = a;
a = n - b;
b = n - t;
}
-
return a > b ? undefined : domain().slice(a, b + 1);
};
-
scale.invert = function (_) {
const value = scale.invertRange([_, _]);
return value ? value[0] : value;
};
-
scale.copy = function () {
return band().domain(domain()).range(range$1).round(round).paddingInner(paddingInner).paddingOuter(paddingOuter).align(align);
};
-
return rescale();
}
-
function pointish(scale) {
const copy = scale.copy;
scale.padding = scale.paddingOuter;
delete scale.paddingInner;
-
scale.copy = function () {
return pointish(copy());
};
-
return scale;
}
-
function point$1() {
return pointish(band().paddingInner(1));
}
-
var map = Array.prototype.map;
-
function numbers(_) {
return map.call(_, toNumber);
}
-
const slice$1 = Array.prototype.slice;
-
function scaleBinOrdinal() {
let domain = [],
- range = [];
-
+ range = [];
function scale(x) {
- return x == null || x !== x ? undefined : range[(bisect$1(domain, x) - 1) % range.length];
+ return x == null || x !== x ? undefined : range[(bisectRight$1(domain, x) - 1) % range.length];
}
-
scale.domain = function (_) {
if (arguments.length) {
domain = numbers(_);
return scale;
} else {
return domain.slice();
}
};
-
scale.range = function (_) {
if (arguments.length) {
range = slice$1.call(_);
return scale;
} else {
return range.slice();
}
};
-
scale.tickFormat = function (count, specifier) {
return tickFormat$1(domain[0], peek$1(domain), count == null ? 10 : count, specifier);
};
-
scale.copy = function () {
return scaleBinOrdinal().domain(scale.domain()).range(scale.range());
};
+ return scale;
+ }
+ /** Private scale registry: should not be exported */
+ const scales = new Map();
+ const VEGA_SCALE = Symbol('vega_scale');
+ function registerScale(scale) {
+ scale[VEGA_SCALE] = true;
return scale;
}
- const scales = {};
/**
- * Augment scales with their type and needed inverse methods.
+ * Return true if object was created by a constructor from the vega-scale `scale` function.
*/
+ function isRegisteredScale(scale) {
+ return scale && scale[VEGA_SCALE] === true;
+ }
+ /**
+ * Augment scales with their type and needed inverse methods.
+ */
function create$2(type, constructor, metadata) {
const ctr = function scale() {
const s = constructor();
-
if (!s.invertRange) {
s.invertRange = s.invert ? invertRange(s) : s.invertExtent ? invertRangeExtent(s) : undefined;
}
-
s.type = type;
- return s;
+ return registerScale(s);
};
-
ctr.metadata = toSet(array$5(metadata));
return ctr;
}
+ /**
+ * Registry function for adding and accessing scale constructor functions.
+ * The *type* argument is a String indicating the name of the scale type.
+ *
+ * If the *scale* argument is not specified, this method returns the matching scale constructor in the registry, or `null` if not found.
+ * If the *scale* argument is provided, it must be a scale constructor function to add to the registry under the given *type* name.
+ * The *metadata* argument provides additional information to guide appropriate use of scales within Vega.
+ *
+ * *metadata* can be either a string or string array. The valid string values are:
+ * - `"continuous"` - the scale is defined over a continuous-valued domain.
+ * - `"discrete"` - the scale is defined over a discrete domain and range.
+ * - `"discretizing"` - the scale discretizes a continuous domain to a discrete range.
+ * - `"interpolating"` - the scale range is defined using a color interpolator.
+ * - `"log"` - the scale performs a logarithmic transform of the continuous domain.
+ * - `"temporal"` - the scale domain is defined over date-time values.
+ */
function scale$4(type, scale, metadata) {
if (arguments.length > 1) {
- scales[type] = create$2(type, scale, metadata);
+ scales.set(type, create$2(type, scale, metadata));
return this;
} else {
- return isValidScaleType(type) ? scales[type] : undefined;
+ return isValidScaleType(type) ? scales.get(type) : undefined;
}
- } // identity scale
+ }
+ // identity scale
+ scale$4(Identity, identity$1);
- scale$4(Identity, identity$1); // continuous scales
-
+ // continuous scales
scale$4(Linear, linear, Continuous);
scale$4(Log, log$2, [Continuous, Log]);
scale$4(Pow, pow$2, Continuous);
scale$4(Sqrt, sqrt$2, Continuous);
scale$4(Symlog, symlog, Continuous);
scale$4(Time, time$1, [Continuous, Temporal]);
- scale$4(UTC, utcTime, [Continuous, Temporal]); // sequential scales
+ scale$4(UTC, utcTime, [Continuous, Temporal]);
+ // sequential scales
scale$4(Sequential, sequential, [Continuous, Interpolating]); // backwards compat
+ scale$4(`${Sequential}-${Linear}`, sequential, [Continuous, Interpolating]);
+ scale$4(`${Sequential}-${Log}`, sequentialLog, [Continuous, Interpolating, Log]);
+ scale$4(`${Sequential}-${Pow}`, sequentialPow, [Continuous, Interpolating]);
+ scale$4(`${Sequential}-${Sqrt}`, sequentialSqrt, [Continuous, Interpolating]);
+ scale$4(`${Sequential}-${Symlog}`, sequentialSymlog, [Continuous, Interpolating]);
- scale$4("".concat(Sequential, "-").concat(Linear), sequential, [Continuous, Interpolating]);
- scale$4("".concat(Sequential, "-").concat(Log), sequentialLog, [Continuous, Interpolating, Log]);
- scale$4("".concat(Sequential, "-").concat(Pow), sequentialPow, [Continuous, Interpolating]);
- scale$4("".concat(Sequential, "-").concat(Sqrt), sequentialSqrt, [Continuous, Interpolating]);
- scale$4("".concat(Sequential, "-").concat(Symlog), sequentialSymlog, [Continuous, Interpolating]); // diverging scales
+ // diverging scales
+ scale$4(`${Diverging}-${Linear}`, diverging, [Continuous, Interpolating]);
+ scale$4(`${Diverging}-${Log}`, divergingLog, [Continuous, Interpolating, Log]);
+ scale$4(`${Diverging}-${Pow}`, divergingPow, [Continuous, Interpolating]);
+ scale$4(`${Diverging}-${Sqrt}`, divergingSqrt, [Continuous, Interpolating]);
+ scale$4(`${Diverging}-${Symlog}`, divergingSymlog, [Continuous, Interpolating]);
- scale$4("".concat(Diverging, "-").concat(Linear), diverging, [Continuous, Interpolating]);
- scale$4("".concat(Diverging, "-").concat(Log), divergingLog, [Continuous, Interpolating, Log]);
- scale$4("".concat(Diverging, "-").concat(Pow), divergingPow, [Continuous, Interpolating]);
- scale$4("".concat(Diverging, "-").concat(Sqrt), divergingSqrt, [Continuous, Interpolating]);
- scale$4("".concat(Diverging, "-").concat(Symlog), divergingSymlog, [Continuous, Interpolating]); // discretizing scales
-
+ // discretizing scales
scale$4(Quantile, quantile, [Discretizing, Quantile]);
scale$4(Quantize, quantize$1, Discretizing);
- scale$4(Threshold, threshold, Discretizing); // discrete scales
+ scale$4(Threshold, threshold, Discretizing);
+ // discrete scales
scale$4(BinOrdinal, scaleBinOrdinal, [Discrete$1, Discretizing]);
scale$4(Ordinal, ordinal, Discrete$1);
scale$4(Band, band, Discrete$1);
scale$4(Point, point$1, Discrete$1);
-
function isValidScaleType(type) {
- return has$1(scales, type);
+ return scales.has(type);
}
-
function hasType(key, type) {
- const s = scales[key];
+ const s = scales.get(key);
return s && s.metadata[type];
}
-
function isContinuous(key) {
return hasType(key, Continuous);
}
-
function isDiscrete(key) {
return hasType(key, Discrete$1);
}
-
function isDiscretizing(key) {
return hasType(key, Discretizing);
}
-
function isLogarithmic(key) {
return hasType(key, Log);
}
-
function isTemporal(key) {
return hasType(key, Temporal);
}
-
function isInterpolating(key) {
return hasType(key, Interpolating);
}
-
function isQuantile(key) {
return hasType(key, Quantile);
}
-
const scaleProps = ['clamp', 'base', 'constant', 'exponent'];
-
function interpolateRange(interpolator, range) {
const start = range[0],
- span = peek$1(range) - start;
+ span = peek$1(range) - start;
return function (i) {
return interpolator(start + i * span);
};
}
-
function interpolateColors(colors, type, gamma) {
return piecewise(interpolate(type || 'rgb', gamma), colors);
}
-
function quantizeInterpolator(interpolator, count) {
const samples = new Array(count),
- n = count + 1;
-
+ n = count + 1;
for (let i = 0; i < count;) samples[i] = interpolator(++i / n);
-
return samples;
}
-
function scaleFraction(scale$1, min, max) {
const delta = max - min;
let i, t, s;
-
if (!delta || !Number.isFinite(delta)) {
- return constant$4(0.5);
+ return constant$5(0.5);
} else {
i = (t = scale$1.type).indexOf('-');
t = i < 0 ? t : t.slice(i + 1);
s = scale$4(t)().domain([min, max]).range([0, 1]);
scaleProps.forEach(m => scale$1[m] ? s[m](scale$1[m]()) : 0);
return s;
}
}
-
function interpolate(type, gamma) {
const interp = $$1[method(type)];
return gamma != null && interp && interp.gamma ? interp.gamma(gamma) : interp;
}
-
function method(type) {
return 'interpolate' + type.toLowerCase().split('-').map(s => s[0].toUpperCase() + s.slice(1)).join('');
}
-
const continuous = {
blues: 'cfe1f2bed8eca8cee58fc1de74b2d75ba3cf4592c63181bd206fb2125ca40a4a90',
greens: 'd3eecdc0e6baabdda594d3917bc77d60ba6c46ab5e329a512089430e7735036429',
greys: 'e2e2e2d4d4d4c4c4c4b1b1b19d9d9d8888887575756262624d4d4d3535351e1e1e',
oranges: 'fdd8b3fdc998fdb87bfda55efc9244f87f2cf06b18e4580bd14904b93d029f3303',
@@ -16148,156 +14507,135 @@
darkGreen: '3a3a3a215748006f4d048942489e4276b340a6c63dd2d836ffeb2cffffaa',
darkMulti: '3737371f5287197d8c29a86995ce3fffe800ffffff',
darkRed: '3434347036339e3c38cc4037e75d1eec8620eeab29f0ce32ffeb2c'
};
const discrete = {
- category10: '1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf',
+ accent: schemeAccent,
+ category10: schemeCategory10,
category20: '1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5',
category20b: '393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6',
category20c: '3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9',
+ dark2: schemeDark2,
+ observable10: schemeObservable10,
+ paired: schemePaired,
+ pastel1: schemePastel1,
+ pastel2: schemePastel2,
+ set1: schemeSet1,
+ set2: schemeSet2,
+ set3: schemeSet3,
tableau10: '4c78a8f58518e4575672b7b254a24beeca3bb279a2ff9da69d755dbab0ac',
- tableau20: '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5',
- accent: '7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666',
- dark2: '1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666',
- paired: 'a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928',
- pastel1: 'fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2',
- pastel2: 'b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc',
- set1: 'e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999',
- set2: '66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3',
- set3: '8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f'
+ tableau20: '4c78a89ecae9f58518ffbf7954a24b88d27ab79a20f2cf5b43989483bcb6e45756ff9d9879706ebab0acd67195fcbfd2b279a2d6a5c99e765fd8b5a5'
};
-
function colors(palette) {
+ if (isArray(palette)) return palette;
const n = palette.length / 6 | 0,
- c = new Array(n);
-
+ c = new Array(n);
for (let i = 0; i < n;) {
c[i] = '#' + palette.slice(i * 6, ++i * 6);
}
-
return c;
}
-
function apply(_, f) {
for (const k in _) scheme(k, f(_[k]));
}
-
const schemes = {};
apply(discrete, colors);
apply(continuous, _ => interpolateColors(colors(_)));
-
function scheme(name, scheme) {
name = name && name.toLowerCase();
-
if (arguments.length > 1) {
schemes[name] = scheme;
return this;
} else {
return schemes[name];
}
}
-
const SymbolLegend = 'symbol';
const DiscreteLegend = 'discrete';
const GradientLegend = 'gradient';
-
const defaultFormatter = value => isArray(value) ? value.map(v => String(v)) : String(value);
-
const ascending = (a, b) => a[1] - b[1];
-
const descending = (a, b) => b[1] - a[1];
+
/**
* Determine the tick count or interval function.
* @param {Scale} scale - The scale for which to generate tick values.
* @param {*} count - The desired tick count or interval specifier.
* @param {number} minStep - The desired minimum step between tick values.
* @return {*} - The tick count or interval function.
*/
-
-
function tickCount(scale, count, minStep) {
let step;
-
if (isNumber$1(count)) {
if (scale.bins) {
count = Math.max(count, scale.bins.length);
}
-
if (minStep != null) {
- count = Math.min(count, Math.floor(span(scale.domain()) / minStep || 1));
+ count = Math.min(count, Math.floor(span(scale.domain()) / minStep || 1) + 1);
}
}
-
if (isObject(count)) {
step = count.step;
count = count.interval;
}
-
if (isString(count)) {
count = scale.type === Time ? timeInterval(count) : scale.type == UTC ? utcInterval(count) : error('Only time and utc scales accept interval strings.');
if (step) count = count.every(step);
}
-
return count;
}
+
/**
* Filter a set of candidate tick values, ensuring that only tick values
* that lie within the scale range are included.
* @param {Scale} scale - The scale for which to generate tick values.
* @param {Array<*>} ticks - The candidate tick values.
* @param {*} count - The tick count or interval function.
* @return {Array<*>} - The filtered tick values.
*/
-
-
function validTicks(scale, ticks, count) {
let range = scale.range(),
- lo = range[0],
- hi = peek$1(range),
- cmp = ascending;
-
+ lo = range[0],
+ hi = peek$1(range),
+ cmp = ascending;
if (lo > hi) {
range = hi;
hi = lo;
lo = range;
cmp = descending;
}
-
lo = Math.floor(lo);
- hi = Math.ceil(hi); // filter ticks to valid values within the range
- // additionally sort ticks in range order (#2579)
+ hi = Math.ceil(hi);
+ // filter ticks to valid values within the range
+ // additionally sort ticks in range order (#2579)
ticks = ticks.map(v => [v, scale(v)]).filter(_ => lo <= _[1] && _[1] <= hi).sort(cmp).map(_ => _[0]);
-
if (count > 0 && ticks.length > 1) {
const endpoints = [ticks[0], peek$1(ticks)];
-
while (ticks.length > count && ticks.length >= 3) {
ticks = ticks.filter((_, i) => !(i % 2));
}
-
if (ticks.length < 3) {
ticks = endpoints;
}
}
-
return ticks;
}
+
/**
* Generate tick values for the given scale and approximate tick count or
* interval value. If the scale has a 'ticks' method, it will be used to
* generate the ticks, with the count argument passed as a parameter. If the
* scale lacks a 'ticks' method, the full scale domain will be returned.
* @param {Scale} scale - The scale for which to generate tick values.
* @param {*} [count] - The approximate number of desired ticks.
* @return {Array<*>} - The generated tick values.
*/
-
-
function tickValues(scale, count) {
- return scale.bins ? validTicks(scale, scale.bins) : scale.ticks ? scale.ticks(count) : scale.domain();
+ return scale.bins ? validTicks(scale, scale.bins, count) : scale.ticks ? scale.ticks(count) : scale.domain();
}
+
/**
* Generate a label format function for a scale. If the scale has a
* 'tickFormat' method, it will be used to generate the formatter, with the
* count and specifier arguments passed as parameters. If the scale lacks a
* 'tickFormat' method, the returned formatter performs simple string coercion.
@@ -16309,188 +14647,163 @@
* @param {string} [specifier] - The format specifier. Must be a legal d3
* specifier string (see https://github.com/d3/d3-format#formatSpecifier) or
* time multi-format specifier object.
* @return {function(*):string} - The generated label formatter.
*/
-
-
function tickFormat(locale, scale, count, specifier, formatType, noSkip) {
const type = scale.type;
let format = defaultFormatter;
-
if (type === Time || formatType === Time) {
format = locale.timeFormat(specifier);
} else if (type === UTC || formatType === UTC) {
format = locale.utcFormat(specifier);
} else if (isLogarithmic(type)) {
const varfmt = locale.formatFloat(specifier);
-
if (noSkip || scale.bins) {
format = varfmt;
} else {
const test = tickLog(scale, count, false);
-
format = _ => test(_) ? varfmt(_) : '';
}
} else if (scale.tickFormat) {
// if d3 scale has tickFormat, it must be continuous
const d = scale.domain();
format = locale.formatSpan(d[0], d[d.length - 1], count, specifier);
} else if (specifier) {
format = locale.format(specifier);
}
-
return format;
}
-
function tickLog(scale, count, values) {
const ticks = tickValues(scale, count),
- base = scale.base(),
- logb = Math.log(base),
- k = Math.max(1, base * count / ticks.length); // apply d3-scale's log format filter criteria
+ base = scale.base(),
+ logb = Math.log(base),
+ k = Math.max(1, base * count / ticks.length);
+ // apply d3-scale's log format filter criteria
const test = d => {
let i = d / Math.pow(base, Math.round(Math.log(d) / logb));
if (i * base < base - 0.5) i *= base;
return i <= k;
};
-
return values ? ticks.filter(test) : test;
}
-
const symbols$1 = {
[Quantile]: 'quantiles',
[Quantize]: 'thresholds',
[Threshold]: 'domain'
};
const formats = {
[Quantile]: 'quantiles',
[Quantize]: 'domain'
};
-
function labelValues(scale, count) {
return scale.bins ? binValues(scale.bins) : scale.type === Log ? tickLog(scale, count, true) : symbols$1[scale.type] ? thresholdValues(scale[symbols$1[scale.type]]()) : tickValues(scale, count);
}
-
function thresholdFormat(locale, scale, specifier) {
const _ = scale[formats[scale.type]](),
- n = _.length;
-
+ n = _.length;
let d = n > 1 ? _[1] - _[0] : _[0],
- i;
-
+ i;
for (i = 1; i < n; ++i) {
d = Math.min(d, _[i] - _[i - 1]);
- } // tickCount = 3 ticks times 10 for increased resolution
+ }
-
+ // tickCount = 3 ticks times 10 for increased resolution
return locale.formatSpan(0, d, 3 * 10, specifier);
}
-
function thresholdValues(thresholds) {
const values = [-Infinity].concat(thresholds);
values.max = +Infinity;
return values;
}
-
function binValues(bins) {
const values = bins.slice(0, -1);
values.max = peek$1(bins);
return values;
}
-
const isDiscreteRange = scale => symbols$1[scale.type] || scale.bins;
-
function labelFormat(locale, scale, count, type, specifier, formatType, noSkip) {
const format = formats[scale.type] && formatType !== Time && formatType !== UTC ? thresholdFormat(locale, scale, specifier) : tickFormat(locale, scale, count, specifier, formatType, noSkip);
return type === SymbolLegend && isDiscreteRange(scale) ? formatRange(format) : type === DiscreteLegend ? formatDiscrete(format) : formatPoint(format);
}
-
const formatRange = format => (value, index, array) => {
const limit = get$3(array[index + 1], get$3(array.max, +Infinity)),
- lo = formatValue$1(value, format),
- hi = formatValue$1(limit, format);
+ lo = formatValue$1(value, format),
+ hi = formatValue$1(limit, format);
return lo && hi ? lo + ' \u2013 ' + hi : hi ? '< ' + hi : '\u2265 ' + lo;
};
-
const get$3 = (value, dflt) => value != null ? value : dflt;
-
const formatDiscrete = format => (value, index) => index ? format(value) : null;
-
const formatPoint = format => value => format(value);
-
const formatValue$1 = (value, format) => Number.isFinite(value) ? format(value) : null;
-
function labelFraction(scale) {
const domain = scale.domain(),
- count = domain.length - 1;
+ count = domain.length - 1;
let lo = +domain[0],
- hi = +peek$1(domain),
- span = hi - lo;
-
+ hi = +peek$1(domain),
+ span = hi - lo;
if (scale.type === Threshold) {
const adjust = count ? span / count : 0.1;
lo -= adjust;
hi += adjust;
span = hi - lo;
}
-
return value => (value - lo) / span;
}
-
function format$1(locale, scale, specifier, formatType) {
- const type = formatType || scale.type; // replace abbreviated time specifiers to improve screen reader experience
+ const type = formatType || scale.type;
+ // replace abbreviated time specifiers to improve screen reader experience
if (isString(specifier) && isTemporal(type)) {
specifier = specifier.replace(/%a/g, '%A').replace(/%b/g, '%B');
}
-
return !specifier && type === Time ? locale.timeFormat('%A, %d %B %Y, %X') : !specifier && type === UTC ? locale.utcFormat('%A, %d %B %Y, %X UTC') : labelFormat(locale, scale, 5, null, specifier, formatType, true);
}
-
function domainCaption(locale, scale, opt) {
opt = opt || {};
const max = Math.max(3, opt.maxlen || 7),
- fmt = format$1(locale, scale, opt.format, opt.formatType); // if scale breaks domain into bins, describe boundaries
+ fmt = format$1(locale, scale, opt.format, opt.formatType);
+ // if scale breaks domain into bins, describe boundaries
if (isDiscretizing(scale.type)) {
const v = labelValues(scale).slice(1).map(fmt),
- n = v.length;
- return "".concat(n, " boundar").concat(n === 1 ? 'y' : 'ies', ": ").concat(v.join(', '));
- } // if scale domain is discrete, list values
+ n = v.length;
+ return `${n} boundar${n === 1 ? 'y' : 'ies'}: ${v.join(', ')}`;
+ }
+
+ // if scale domain is discrete, list values
else if (isDiscrete(scale.type)) {
const d = scale.domain(),
- n = d.length,
- v = n > max ? d.slice(0, max - 2).map(fmt).join(', ') + ', ending with ' + d.slice(-1).map(fmt) : d.map(fmt).join(', ');
- return "".concat(n, " value").concat(n === 1 ? '' : 's', ": ").concat(v);
- } // if scale domain is continuous, describe value range
+ n = d.length,
+ v = n > max ? d.slice(0, max - 2).map(fmt).join(', ') + ', ending with ' + d.slice(-1).map(fmt) : d.map(fmt).join(', ');
+ return `${n} value${n === 1 ? '' : 's'}: ${v}`;
+ }
+
+ // if scale domain is continuous, describe value range
else {
const d = scale.domain();
- return "values from ".concat(fmt(d[0]), " to ").concat(fmt(peek$1(d)));
+ return `values from ${fmt(d[0])} to ${fmt(peek$1(d))}`;
}
}
let gradient_id = 0;
-
function resetSVGGradientId() {
gradient_id = 0;
}
-
const patternPrefix = 'p_';
-
function isGradient(value) {
return value && value.gradient;
}
-
function gradientRef(g, defs, base) {
const type = g.gradient;
let id = g.id,
- prefix = type === 'radial' ? patternPrefix : ''; // check id, assign default values as needed
+ prefix = type === 'radial' ? patternPrefix : '';
+ // check id, assign default values as needed
if (!id) {
id = g.id = 'gradient_' + gradient_id++;
-
if (type === 'radial') {
g.x1 = get$2(g.x1, 0.5);
g.y1 = get$2(g.y1, 0.5);
g.r1 = get$2(g.r1, 0);
g.x2 = get$2(g.x2, 0.5);
@@ -16501,25 +14814,24 @@
g.x1 = get$2(g.x1, 0);
g.y1 = get$2(g.y1, 0);
g.x2 = get$2(g.x2, 1);
g.y2 = get$2(g.y2, 0);
}
- } // register definition
+ }
+ // register definition
+ defs[id] = g;
- defs[id] = g; // return url reference
-
+ // return url reference
return 'url(' + (base || '') + '#' + prefix + id + ')';
}
-
function get$2(val, def) {
return val != null ? val : def;
}
-
function Gradient$1(p0, p1) {
var stops = [],
- gradient;
+ gradient;
return gradient = {
gradient: 'linear',
x1: p0 ? p0[0] : 0,
y1: p0 ? p0[1] : 0,
x2: p1 ? p1[0] : 1,
@@ -16532,11 +14844,10 @@
});
return gradient;
}
};
}
-
const lookup$4 = {
'basis': {
curve: curveBasis
},
'basis-closed': {
@@ -16601,26 +14912,21 @@
},
'step-before': {
curve: stepBefore
}
};
-
function curves(type, orientation, tension) {
var entry = has$1(lookup$4, type) && lookup$4[type],
- curve = null;
-
+ curve = null;
if (entry) {
curve = entry.curve || entry[orientation || 'vertical'];
-
if (entry.tension && tension != null) {
curve = curve[entry.tension](tension);
}
}
-
return curve;
}
-
const paramCounts = {
m: 2,
l: 2,
h: 1,
v: 1,
@@ -16633,102 +14939,94 @@
};
const commandPattern = /[mlhvzcsqta]([^mlhvzcsqta]+|$)/gi;
const numberPattern = /^[+-]?(([0-9]*\.[0-9]+)|([0-9]+\.)|([0-9]+))([eE][+-]?[0-9]+)?/;
const spacePattern = /^((\s+,?\s*)|(,\s*))/;
const flagPattern = /^[01]/;
-
function parse$3(path) {
const commands = [];
const matches = path.match(commandPattern) || [];
matches.forEach(str => {
let cmd = str[0];
- const type = cmd.toLowerCase(); // parse parameters
+ const type = cmd.toLowerCase();
+ // parse parameters
const paramCount = paramCounts[type];
const params = parseParams(type, paramCount, str.slice(1).trim());
- const count = params.length; // error checking based on parameter count
+ const count = params.length;
+ // error checking based on parameter count
if (count < paramCount || count && count % paramCount !== 0) {
throw Error('Invalid SVG path, incorrect parameter count');
- } // register the command
+ }
+ // register the command
+ commands.push([cmd, ...params.slice(0, paramCount)]);
- commands.push([cmd, ...params.slice(0, paramCount)]); // exit now if we're done, also handles zero-param 'z'
-
+ // exit now if we're done, also handles zero-param 'z'
if (count === paramCount) {
return;
- } // handle implicit line-to
+ }
-
+ // handle implicit line-to
if (type === 'm') {
cmd = cmd === 'M' ? 'L' : 'l';
- } // repeat command when given extended param list
+ }
-
+ // repeat command when given extended param list
for (let i = paramCount; i < count; i += paramCount) {
commands.push([cmd, ...params.slice(i, i + paramCount)]);
}
});
return commands;
}
-
function parseParams(type, paramCount, segment) {
const params = [];
-
for (let index = 0; paramCount && index < segment.length;) {
for (let i = 0; i < paramCount; ++i) {
const pattern = type === 'a' && (i === 3 || i === 4) ? flagPattern : numberPattern;
const match = segment.slice(index).match(pattern);
-
if (match === null) {
throw Error('Invalid SVG path, incorrect parameter type');
}
-
index += match[0].length;
params.push(+match[0]);
const ws = segment.slice(index).match(spacePattern);
-
if (ws !== null) {
index += ws[0].length;
}
}
}
-
return params;
}
-
const DegToRad = Math.PI / 180;
const Epsilon = 1e-14;
const HalfPi = Math.PI / 2;
const Tau = Math.PI * 2;
const HalfSqrt3 = Math.sqrt(3) / 2;
var segmentCache = {};
var bezierCache = {};
- var join$1 = [].join; // Copied from Inkscape svgtopdf, thanks!
+ var join$1 = [].join;
+ // Copied from Inkscape svgtopdf, thanks!
function segments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
const key = join$1.call(arguments);
-
if (segmentCache[key]) {
return segmentCache[key];
}
-
const th = rotateX * DegToRad;
const sin_th = Math.sin(th);
const cos_th = Math.cos(th);
rx = Math.abs(rx);
ry = Math.abs(ry);
const px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
const py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
let pl = px * px / (rx * rx) + py * py / (ry * ry);
-
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
}
-
const a00 = cos_th / rx;
const a01 = sin_th / rx;
const a10 = -sin_th / ry;
const a11 = cos_th / ry;
const x0 = a00 * ox + a01 * oy;
@@ -16743,44 +15041,37 @@
const xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
const yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
const th0 = Math.atan2(y0 - yc, x0 - xc);
const th1 = Math.atan2(y1 - yc, x1 - xc);
let th_arc = th1 - th0;
-
if (th_arc < 0 && sweep === 1) {
th_arc += Tau;
} else if (th_arc > 0 && sweep === 0) {
th_arc -= Tau;
}
-
const segs = Math.ceil(Math.abs(th_arc / (HalfPi + 0.001)));
const result = [];
-
for (let i = 0; i < segs; ++i) {
const th2 = th0 + i * th_arc / segs;
const th3 = th0 + (i + 1) * th_arc / segs;
result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
}
-
return segmentCache[key] = result;
}
-
function bezier(params) {
const key = join$1.call(params);
-
if (bezierCache[key]) {
return bezierCache[key];
}
-
var cx = params[0],
- cy = params[1],
- th0 = params[2],
- th1 = params[3],
- rx = params[4],
- ry = params[5],
- sin_th = params[6],
- cos_th = params[7];
+ cy = params[1],
+ th0 = params[2],
+ th1 = params[3],
+ rx = params[4],
+ ry = params[5],
+ sin_th = params[6],
+ cos_th = params[7];
const a00 = cos_th * rx;
const a01 = -sin_th * ry;
const a10 = sin_th * rx;
const a11 = cos_th * ry;
const cos_th0 = Math.cos(th0);
@@ -16796,16 +15087,13 @@
const y3 = cy + sin_th1;
const x2 = x3 + t * sin_th1;
const y2 = y3 - t * cos_th1;
return bezierCache[key] = [a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3];
}
-
const temp = ['l', 0, 0, 0, 0, 0, 0, 0];
-
function scale$1$1(current, sX, sY) {
const c = temp[0] = current[0];
-
if (c === 'a' || c === 'A') {
temp[1] = sX * current[1];
temp[2] = sY * current[2];
temp[3] = current[3];
temp[4] = current[4];
@@ -16819,164 +15107,153 @@
} else {
for (var i = 1, n = current.length; i < n; ++i) {
temp[i] = (i % 2 == 1 ? sX : sY) * current[i];
}
}
-
return temp;
}
-
function pathRender(context, path, l, t, sX, sY) {
var current,
- // current instruction
- previous = null,
- x = 0,
- // current x
- y = 0,
- // current y
- controlX = 0,
- // current control point x
- controlY = 0,
- // current control point y
- tempX,
- tempY,
- tempControlX,
- tempControlY,
- anchorX = 0,
- anchorY = 0;
+ // current instruction
+ previous = null,
+ x = 0,
+ // current x
+ y = 0,
+ // current y
+ controlX = 0,
+ // current control point x
+ controlY = 0,
+ // current control point y
+ tempX,
+ tempY,
+ tempControlX,
+ tempControlY,
+ anchorX = 0,
+ anchorY = 0;
if (l == null) l = 0;
if (t == null) t = 0;
if (sX == null) sX = 1;
if (sY == null) sY = sX;
if (context.beginPath) context.beginPath();
-
for (var i = 0, len = path.length; i < len; ++i) {
current = path[i];
-
if (sX !== 1 || sY !== 1) {
current = scale$1$1(current, sX, sY);
}
-
switch (current[0]) {
// first letter
+
case 'l':
// lineto, relative
x += current[1];
y += current[2];
context.lineTo(x + l, y + t);
break;
-
case 'L':
// lineto, absolute
x = current[1];
y = current[2];
context.lineTo(x + l, y + t);
break;
-
case 'h':
// horizontal lineto, relative
x += current[1];
context.lineTo(x + l, y + t);
break;
-
case 'H':
// horizontal lineto, absolute
x = current[1];
context.lineTo(x + l, y + t);
break;
-
case 'v':
// vertical lineto, relative
y += current[1];
context.lineTo(x + l, y + t);
break;
-
case 'V':
// verical lineto, absolute
y = current[1];
context.lineTo(x + l, y + t);
break;
-
case 'm':
// moveTo, relative
x += current[1];
y += current[2];
anchorX = x;
anchorY = y;
context.moveTo(x + l, y + t);
break;
-
case 'M':
// moveTo, absolute
x = current[1];
y = current[2];
anchorX = x;
anchorY = y;
context.moveTo(x + l, y + t);
break;
-
case 'c':
// bezierCurveTo, relative
tempX = x + current[5];
tempY = y + current[6];
controlX = x + current[3];
controlY = y + current[4];
- context.bezierCurveTo(x + current[1] + l, // x1
- y + current[2] + t, // y1
- controlX + l, // x2
- controlY + t, // y2
+ context.bezierCurveTo(x + current[1] + l,
+ // x1
+ y + current[2] + t,
+ // y1
+ controlX + l,
+ // x2
+ controlY + t,
+ // y2
tempX + l, tempY + t);
x = tempX;
y = tempY;
break;
-
case 'C':
// bezierCurveTo, absolute
x = current[5];
y = current[6];
controlX = current[3];
controlY = current[4];
context.bezierCurveTo(current[1] + l, current[2] + t, controlX + l, controlY + t, x + l, y + t);
break;
-
case 's':
// shorthand cubic bezierCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
- tempY = y + current[4]; // calculate reflection of previous control points
-
+ tempY = y + current[4];
+ // calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
- context.bezierCurveTo(controlX + l, controlY + t, x + current[1] + l, y + current[2] + t, tempX + l, tempY + t); // set control point to 2nd one of this command
+ context.bezierCurveTo(controlX + l, controlY + t, x + current[1] + l, y + current[2] + t, tempX + l, tempY + t);
+
+ // set control point to 2nd one of this command
// the first control point is assumed to be the reflection of
// the second control point on the previous command relative
// to the current point.
-
controlX = x + current[1];
controlY = y + current[2];
x = tempX;
y = tempY;
break;
-
case 'S':
// shorthand cubic bezierCurveTo, absolute
tempX = current[3];
- tempY = current[4]; // calculate reflection of previous control points
-
+ tempY = current[4];
+ // calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
context.bezierCurveTo(controlX + l, controlY + t, current[1] + l, current[2] + t, tempX + l, tempY + t);
x = tempX;
- y = tempY; // set control point to 2nd one of this command
+ y = tempY;
+ // set control point to 2nd one of this command
// the first control point is assumed to be the reflection of
// the second control point on the previous command relative
// to the current point.
-
controlX = current[1];
controlY = current[2];
break;
-
case 'q':
// quadraticCurveTo, relative
// transform to absolute x,y
tempX = x + current[3];
tempY = y + current[4];
@@ -16984,28 +15261,26 @@
controlY = y + current[2];
context.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t);
x = tempX;
y = tempY;
break;
-
case 'Q':
// quadraticCurveTo, absolute
tempX = current[3];
tempY = current[4];
context.quadraticCurveTo(current[1] + l, current[2] + t, tempX + l, tempY + t);
x = tempX;
y = tempY;
controlX = current[1];
controlY = current[2];
break;
-
case 't':
// shorthand quadraticCurveTo, relative
+
// transform to absolute x,y
tempX = x + current[1];
tempY = y + current[2];
-
if (previous[0].match(/[QqTt]/) === null) {
// If there is no previous command or if the previous command was not a Q, q, T or t,
// assume the control point is coincident with the current point
controlX = x;
controlY = y;
@@ -17016,71 +15291,70 @@
} else if (previous[0] === 'q') {
// calculate reflection of previous control points for q
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
}
-
tempControlX = controlX;
tempControlY = controlY;
context.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t);
x = tempX;
y = tempY;
controlX = x + current[1];
controlY = y + current[2];
break;
-
case 'T':
tempX = current[1];
- tempY = current[2]; // calculate reflection of previous control points
+ tempY = current[2];
+ // calculate reflection of previous control points
controlX = 2 * x - controlX;
controlY = 2 * y - controlY;
context.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t);
x = tempX;
y = tempY;
break;
-
case 'a':
drawArc(context, x + l, y + t, [current[1], current[2], current[3], current[4], current[5], current[6] + x + l, current[7] + y + t]);
x += current[6];
y += current[7];
break;
-
case 'A':
drawArc(context, x + l, y + t, [current[1], current[2], current[3], current[4], current[5], current[6] + l, current[7] + t]);
x = current[6];
y = current[7];
break;
-
case 'z':
case 'Z':
x = anchorX;
y = anchorY;
context.closePath();
break;
}
-
previous = current;
}
}
-
function drawArc(context, x, y, coords) {
- const seg = segments(coords[5], // end x
- coords[6], // end y
- coords[0], // radius x
- coords[1], // radius y
- coords[3], // large flag
- coords[4], // sweep flag
- coords[2], // rotation
+ const seg = segments(coords[5],
+ // end x
+ coords[6],
+ // end y
+ coords[0],
+ // radius x
+ coords[1],
+ // radius y
+ coords[3],
+ // large flag
+ coords[4],
+ // sweep flag
+ coords[2],
+ // rotation
x, y);
-
for (let i = 0; i < seg.length; ++i) {
const bez = bezier(seg[i]);
context.bezierCurveTo(bez[0], bez[1], bez[2], bez[3], bez[4], bez[5]);
}
}
-
const Tan30 = 0.5773502691896257;
const builtins = {
'circle': {
draw: function (context, size) {
const r = Math.sqrt(size) / 2;
@@ -17089,11 +15363,11 @@
}
},
'cross': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- s = r / 2.5;
+ s = r / 2.5;
context.moveTo(-r, -s);
context.lineTo(-r, s);
context.lineTo(-s, s);
context.lineTo(-s, r);
context.lineTo(s, r);
@@ -17118,20 +15392,20 @@
}
},
'square': {
draw: function (context, size) {
var w = Math.sqrt(size),
- x = -w / 2;
+ x = -w / 2;
context.rect(x, x, w, w);
}
},
'arrow': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- s = r / 7,
- t = r / 2.5,
- v = r / 8;
+ s = r / 7,
+ t = r / 2.5,
+ v = r / 8;
context.moveTo(-s, r);
context.lineTo(s, r);
context.lineTo(s, -v);
context.lineTo(t, -v);
context.lineTo(0, -r);
@@ -17141,64 +15415,64 @@
}
},
'wedge': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r,
- o = h - r * Tan30,
- b = r / 4;
+ h = HalfSqrt3 * r,
+ o = h - r * Tan30,
+ b = r / 4;
context.moveTo(0, -h - o);
context.lineTo(-b, h - o);
context.lineTo(b, h - o);
context.closePath();
}
},
'triangle': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r,
- o = h - r * Tan30;
+ h = HalfSqrt3 * r,
+ o = h - r * Tan30;
context.moveTo(0, -h - o);
context.lineTo(-r, h - o);
context.lineTo(r, h - o);
context.closePath();
}
},
'triangle-up': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r;
+ h = HalfSqrt3 * r;
context.moveTo(0, -h);
context.lineTo(-r, h);
context.lineTo(r, h);
context.closePath();
}
},
'triangle-down': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r;
+ h = HalfSqrt3 * r;
context.moveTo(0, h);
context.lineTo(-r, -h);
context.lineTo(r, -h);
context.closePath();
}
},
'triangle-right': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r;
+ h = HalfSqrt3 * r;
context.moveTo(h, 0);
context.lineTo(-h, -r);
context.lineTo(-h, r);
context.closePath();
}
},
'triangle-left': {
draw: function (context, size) {
var r = Math.sqrt(size) / 2,
- h = HalfSqrt3 * r;
+ h = HalfSqrt3 * r;
context.moveTo(-h, 0);
context.lineTo(h, -r);
context.lineTo(h, r);
context.closePath();
}
@@ -17209,85 +15483,74 @@
context.moveTo(-r, 0);
context.lineTo(r, 0);
}
}
};
-
function symbols(_) {
return has$1(builtins, _) ? builtins[_] : customSymbol(_);
}
-
var custom = {};
-
function customSymbol(path) {
if (!has$1(custom, path)) {
const parsed = parse$3(path);
custom[path] = {
draw: function (context, size) {
pathRender(context, parsed, 0, 0, Math.sqrt(size) / 2);
}
};
}
-
return custom[path];
}
+ // See http://spencermortensen.com/articles/bezier-circle/
const C = 0.448084975506; // C = 1 - c
function rectangleX(d) {
return d.x;
}
-
function rectangleY(d) {
return d.y;
}
-
function rectangleWidth(d) {
return d.width;
}
-
function rectangleHeight(d) {
return d.height;
}
-
function number$3(_) {
return typeof _ === 'function' ? _ : () => +_;
}
-
function clamp(value, min, max) {
return Math.max(min, Math.min(value, max));
}
-
function vg_rect() {
var x = rectangleX,
- y = rectangleY,
- width = rectangleWidth,
- height = rectangleHeight,
- crTL = number$3(0),
- crTR = crTL,
- crBL = crTL,
- crBR = crTL,
- context = null;
-
+ y = rectangleY,
+ width = rectangleWidth,
+ height = rectangleHeight,
+ crTL = number$3(0),
+ crTR = crTL,
+ crBL = crTL,
+ crBR = crTL,
+ context = null;
function rectangle(_, x0, y0) {
var buffer,
- x1 = x0 != null ? x0 : +x.call(this, _),
- y1 = y0 != null ? y0 : +y.call(this, _),
- w = +width.call(this, _),
- h = +height.call(this, _),
- s = Math.min(w, h) / 2,
- tl = clamp(+crTL.call(this, _), 0, s),
- tr = clamp(+crTR.call(this, _), 0, s),
- bl = clamp(+crBL.call(this, _), 0, s),
- br = clamp(+crBR.call(this, _), 0, s);
+ x1 = x0 != null ? x0 : +x.call(this, _),
+ y1 = y0 != null ? y0 : +y.call(this, _),
+ w = +width.call(this, _),
+ h = +height.call(this, _),
+ s = Math.min(w, h) / 2,
+ tl = clamp(+crTL.call(this, _), 0, s),
+ tr = clamp(+crTR.call(this, _), 0, s),
+ bl = clamp(+crBL.call(this, _), 0, s),
+ br = clamp(+crBR.call(this, _), 0, s);
if (!context) context = buffer = path$3();
-
if (tl <= 0 && tr <= 0 && bl <= 0 && br <= 0) {
context.rect(x1, y1, w, h);
} else {
var x2 = x1 + w,
- y2 = y1 + h;
+ y2 = y1 + h;
context.moveTo(x1 + tl, y1);
context.lineTo(x2 - tr, y1);
context.bezierCurveTo(x2 - C * tr, y1, x2, y1 + C * tr, x2, y1 + tr);
context.lineTo(x2, y2 - br);
context.bezierCurveTo(x2, y2 - C * br, x2 - C * br, y2, x2 - br, y2);
@@ -17295,53 +15558,47 @@
context.bezierCurveTo(x1 + C * bl, y2, x1, y2 - C * bl, x1, y2 - bl);
context.lineTo(x1, y1 + tl);
context.bezierCurveTo(x1, y1 + C * tl, x1 + C * tl, y1, x1 + tl, y1);
context.closePath();
}
-
if (buffer) {
context = null;
return buffer + '' || null;
}
}
-
rectangle.x = function (_) {
if (arguments.length) {
x = number$3(_);
return rectangle;
} else {
return x;
}
};
-
rectangle.y = function (_) {
if (arguments.length) {
y = number$3(_);
return rectangle;
} else {
return y;
}
};
-
rectangle.width = function (_) {
if (arguments.length) {
width = number$3(_);
return rectangle;
} else {
return width;
}
};
-
rectangle.height = function (_) {
if (arguments.length) {
height = number$3(_);
return rectangle;
} else {
return height;
}
};
-
rectangle.cornerRadius = function (tl, tr, br, bl) {
if (arguments.length) {
crTL = number$3(tl);
crTR = tr != null ? number$3(tr) : crTL;
crBR = br != null ? number$3(br) : crTL;
@@ -17349,414 +15606,342 @@
return rectangle;
} else {
return crTL;
}
};
-
rectangle.context = function (_) {
if (arguments.length) {
context = _ == null ? null : _;
return rectangle;
} else {
return context;
}
};
-
return rectangle;
}
-
function vg_trail() {
var x,
- y,
- size,
- defined,
- context = null,
- ready,
- x1,
- y1,
- r1;
-
+ y,
+ size,
+ defined,
+ context = null,
+ ready,
+ x1,
+ y1,
+ r1;
function point(x2, y2, w2) {
const r2 = w2 / 2;
-
if (ready) {
var ux = y1 - y2,
- uy = x2 - x1;
-
+ uy = x2 - x1;
if (ux || uy) {
// get normal vector
- var ud = Math.sqrt(ux * ux + uy * uy),
- rx = (ux /= ud) * r1,
- ry = (uy /= ud) * r1,
- t = Math.atan2(uy, ux); // draw segment
+ var ud = Math.hypot(ux, uy),
+ rx = (ux /= ud) * r1,
+ ry = (uy /= ud) * r1,
+ t = Math.atan2(uy, ux);
+ // draw segment
context.moveTo(x1 - rx, y1 - ry);
context.lineTo(x2 - ux * r2, y2 - uy * r2);
context.arc(x2, y2, r2, t - Math.PI, t);
context.lineTo(x1 + rx, y1 + ry);
context.arc(x1, y1, r1, t, t + Math.PI);
} else {
context.arc(x2, y2, r2, 0, Tau);
}
-
context.closePath();
} else {
ready = 1;
}
-
x1 = x2;
y1 = y2;
r1 = r2;
}
-
function trail(data) {
var i,
- n = data.length,
- d,
- defined0 = false,
- buffer;
+ n = data.length,
+ d,
+ defined0 = false,
+ buffer;
if (context == null) context = buffer = path$3();
-
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) ready = 0;
}
-
if (defined0) point(+x(d, i, data), +y(d, i, data), +size(d, i, data));
}
-
if (buffer) {
context = null;
return buffer + '' || null;
}
}
-
trail.x = function (_) {
if (arguments.length) {
x = _;
return trail;
} else {
return x;
}
};
-
trail.y = function (_) {
if (arguments.length) {
y = _;
return trail;
} else {
return y;
}
};
-
trail.size = function (_) {
if (arguments.length) {
size = _;
return trail;
} else {
return size;
}
};
-
trail.defined = function (_) {
if (arguments.length) {
defined = _;
return trail;
} else {
return defined;
}
};
-
trail.context = function (_) {
if (arguments.length) {
if (_ == null) {
context = null;
} else {
context = _;
}
-
return trail;
} else {
return context;
}
};
-
return trail;
}
-
function value$1(a, b) {
return a != null ? a : b;
}
-
const x$2 = item => item.x || 0,
- y$2 = item => item.y || 0,
- w = item => item.width || 0,
- h = item => item.height || 0,
- xw = item => (item.x || 0) + (item.width || 0),
- yh = item => (item.y || 0) + (item.height || 0),
- sa = item => item.startAngle || 0,
- ea = item => item.endAngle || 0,
- pa = item => item.padAngle || 0,
- ir = item => item.innerRadius || 0,
- or = item => item.outerRadius || 0,
- cr = item => item.cornerRadius || 0,
- tl = item => value$1(item.cornerRadiusTopLeft, item.cornerRadius) || 0,
- tr = item => value$1(item.cornerRadiusTopRight, item.cornerRadius) || 0,
- br = item => value$1(item.cornerRadiusBottomRight, item.cornerRadius) || 0,
- bl = item => value$1(item.cornerRadiusBottomLeft, item.cornerRadius) || 0,
- sz = item => value$1(item.size, 64),
- ts = item => item.size || 1,
- def = item => !(item.defined === false),
- type = item => symbols(item.shape || 'circle');
-
+ y$2 = item => item.y || 0,
+ w = item => item.width || 0,
+ h = item => item.height || 0,
+ xw = item => (item.x || 0) + (item.width || 0),
+ yh = item => (item.y || 0) + (item.height || 0),
+ sa = item => item.startAngle || 0,
+ ea = item => item.endAngle || 0,
+ pa = item => item.padAngle || 0,
+ ir = item => item.innerRadius || 0,
+ or = item => item.outerRadius || 0,
+ cr = item => item.cornerRadius || 0,
+ tl = item => value$1(item.cornerRadiusTopLeft, item.cornerRadius) || 0,
+ tr = item => value$1(item.cornerRadiusTopRight, item.cornerRadius) || 0,
+ br = item => value$1(item.cornerRadiusBottomRight, item.cornerRadius) || 0,
+ bl = item => value$1(item.cornerRadiusBottomLeft, item.cornerRadius) || 0,
+ sz = item => value$1(item.size, 64),
+ ts = item => item.size || 1,
+ def = item => !(item.defined === false),
+ type = item => symbols(item.shape || 'circle');
const arcShape = arc$2$1().startAngle(sa).endAngle(ea).padAngle(pa).innerRadius(ir).outerRadius(or).cornerRadius(cr),
- areavShape = area$2$1().x(x$2).y1(y$2).y0(yh).defined(def),
- areahShape = area$2$1().y(y$2).x1(x$2).x0(xw).defined(def),
- lineShape = line$2$1().x(x$2).y(y$2).defined(def),
- rectShape = vg_rect().x(x$2).y(y$2).width(w).height(h).cornerRadius(tl, tr, br, bl),
- symbolShape = Symbol$1().type(type).size(sz),
- trailShape = vg_trail().x(x$2).y(y$2).defined(def).size(ts);
-
+ areavShape = area$2$1().x(x$2).y1(y$2).y0(yh).defined(def),
+ areahShape = area$2$1().y(y$2).x1(x$2).x0(xw).defined(def),
+ lineShape = line$2$1().x(x$2).y(y$2).defined(def),
+ rectShape = vg_rect().x(x$2).y(y$2).width(w).height(h).cornerRadius(tl, tr, br, bl),
+ symbolShape = Symbol$1().type(type).size(sz),
+ trailShape = vg_trail().x(x$2).y(y$2).defined(def).size(ts);
function hasCornerRadius(item) {
return item.cornerRadius || item.cornerRadiusTopLeft || item.cornerRadiusTopRight || item.cornerRadiusBottomRight || item.cornerRadiusBottomLeft;
}
-
function arc$1(context, item) {
return arcShape.context(context)(item);
}
-
function area$1(context, items) {
const item = items[0],
- interp = item.interpolate || 'linear';
+ interp = item.interpolate || 'linear';
return (item.orient === 'horizontal' ? areahShape : areavShape).curve(curves(interp, item.orient, item.tension)).context(context)(items);
}
-
function line$1(context, items) {
const item = items[0],
- interp = item.interpolate || 'linear';
+ interp = item.interpolate || 'linear';
return lineShape.curve(curves(interp, item.orient, item.tension)).context(context)(items);
}
-
function rectangle(context, item, x, y) {
return rectShape.context(context)(item, x, y);
}
-
function shape$1(context, item) {
return (item.mark.shape || item.shape).context(context)(item);
}
-
function symbol$1(context, item) {
return symbolShape.context(context)(item);
}
-
function trail$1(context, items) {
return trailShape.context(context)(items);
}
-
var clip_id = 1;
-
function resetSVGClipId() {
clip_id = 1;
}
-
function clip$1$1(renderer, item, size) {
var clip = item.clip,
- defs = renderer._defs,
- id = item.clip_id || (item.clip_id = 'clip' + clip_id++),
- c = defs.clipping[id] || (defs.clipping[id] = {
- id: id
- });
-
+ defs = renderer._defs,
+ id = item.clip_id || (item.clip_id = 'clip' + clip_id++),
+ c = defs.clipping[id] || (defs.clipping[id] = {
+ id: id
+ });
if (isFunction(clip)) {
c.path = clip(null);
} else if (hasCornerRadius(size)) {
c.path = rectangle(null, size, 0, 0);
} else {
c.width = size.width || 0;
c.height = size.height || 0;
}
-
return 'url(#' + id + ')';
}
-
function Bounds(b) {
this.clear();
if (b) this.union(b);
}
-
Bounds.prototype = {
clone() {
return new Bounds(this);
},
-
clear() {
this.x1 = +Number.MAX_VALUE;
this.y1 = +Number.MAX_VALUE;
this.x2 = -Number.MAX_VALUE;
this.y2 = -Number.MAX_VALUE;
return this;
},
-
empty() {
return this.x1 === +Number.MAX_VALUE && this.y1 === +Number.MAX_VALUE && this.x2 === -Number.MAX_VALUE && this.y2 === -Number.MAX_VALUE;
},
-
equals(b) {
return this.x1 === b.x1 && this.y1 === b.y1 && this.x2 === b.x2 && this.y2 === b.y2;
},
-
set(x1, y1, x2, y2) {
if (x2 < x1) {
this.x2 = x1;
this.x1 = x2;
} else {
this.x1 = x1;
this.x2 = x2;
}
-
if (y2 < y1) {
this.y2 = y1;
this.y1 = y2;
} else {
this.y1 = y1;
this.y2 = y2;
}
-
return this;
},
-
add(x, y) {
if (x < this.x1) this.x1 = x;
if (y < this.y1) this.y1 = y;
if (x > this.x2) this.x2 = x;
if (y > this.y2) this.y2 = y;
return this;
},
-
expand(d) {
this.x1 -= d;
this.y1 -= d;
this.x2 += d;
this.y2 += d;
return this;
},
-
round() {
this.x1 = Math.floor(this.x1);
this.y1 = Math.floor(this.y1);
this.x2 = Math.ceil(this.x2);
this.y2 = Math.ceil(this.y2);
return this;
},
-
scale(s) {
this.x1 *= s;
this.y1 *= s;
this.x2 *= s;
this.y2 *= s;
return this;
},
-
translate(dx, dy) {
this.x1 += dx;
this.x2 += dx;
this.y1 += dy;
this.y2 += dy;
return this;
},
-
rotate(angle, x, y) {
const p = this.rotatedPoints(angle, x, y);
return this.clear().add(p[0], p[1]).add(p[2], p[3]).add(p[4], p[5]).add(p[6], p[7]);
},
-
rotatedPoints(angle, x, y) {
var {
- x1,
- y1,
- x2,
- y2
- } = this,
- cos = Math.cos(angle),
- sin = Math.sin(angle),
- cx = x - x * cos + y * sin,
- cy = y - x * sin - y * cos;
+ x1,
+ y1,
+ x2,
+ y2
+ } = this,
+ cos = Math.cos(angle),
+ sin = Math.sin(angle),
+ cx = x - x * cos + y * sin,
+ cy = y - x * sin - y * cos;
return [cos * x1 - sin * y1 + cx, sin * x1 + cos * y1 + cy, cos * x1 - sin * y2 + cx, sin * x1 + cos * y2 + cy, cos * x2 - sin * y1 + cx, sin * x2 + cos * y1 + cy, cos * x2 - sin * y2 + cx, sin * x2 + cos * y2 + cy];
},
-
union(b) {
if (b.x1 < this.x1) this.x1 = b.x1;
if (b.y1 < this.y1) this.y1 = b.y1;
if (b.x2 > this.x2) this.x2 = b.x2;
if (b.y2 > this.y2) this.y2 = b.y2;
return this;
},
-
intersect(b) {
if (b.x1 > this.x1) this.x1 = b.x1;
if (b.y1 > this.y1) this.y1 = b.y1;
if (b.x2 < this.x2) this.x2 = b.x2;
if (b.y2 < this.y2) this.y2 = b.y2;
return this;
},
-
encloses(b) {
return b && this.x1 <= b.x1 && this.x2 >= b.x2 && this.y1 <= b.y1 && this.y2 >= b.y2;
},
-
alignsWith(b) {
return b && (this.x1 == b.x1 || this.x2 == b.x2 || this.y1 == b.y1 || this.y2 == b.y2);
},
-
intersects(b) {
return b && !(this.x2 < b.x1 || this.x1 > b.x2 || this.y2 < b.y1 || this.y1 > b.y2);
},
-
contains(x, y) {
return !(x < this.x1 || x > this.x2 || y < this.y1 || y > this.y2);
},
-
width() {
return this.x2 - this.x1;
},
-
height() {
return this.y2 - this.y1;
}
-
};
-
function Item(mark) {
this.mark = mark;
this.bounds = this.bounds || new Bounds();
}
-
function GroupItem(mark) {
Item.call(this, mark);
this.items = this.items || [];
}
-
inherits(GroupItem, Item);
-
- function ResourceLoader(customLoader) {
- this._pending = 0;
- this._loader = customLoader || loader();
- }
-
- function increment(loader) {
- loader._pending += 1;
- }
-
- function decrement(loader) {
- loader._pending -= 1;
- }
-
- ResourceLoader.prototype = {
+ class ResourceLoader {
+ constructor(customLoader) {
+ this._pending = 0;
+ this._loader = customLoader || loader();
+ }
pending() {
return this._pending;
- },
-
+ }
sanitizeURL(uri) {
const loader = this;
increment(loader);
return loader._loader.sanitize(uri, {
context: 'href'
@@ -17765,33 +15950,32 @@
return opt;
}).catch(() => {
decrement(loader);
return null;
});
- },
-
+ }
loadImage(uri) {
const loader = this,
- Image = domImage();
+ Image = domImage();
increment(loader);
return loader._loader.sanitize(uri, {
context: 'image'
}).then(opt => {
const url = opt.href;
if (!url || !Image) throw {
url: url
};
- const img = new Image(); // set crossOrigin only if cors is defined; empty string sets anonymous mode
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin
+ const img = new Image();
+ // set crossOrigin only if cors is defined; empty string sets anonymous mode
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin
const cors = has$1(opt, 'crossOrigin') ? opt.crossOrigin : 'anonymous';
- if (cors != null) img.crossOrigin = cors; // attempt to load image resource
+ if (cors != null) img.crossOrigin = cors;
+ // attempt to load image resource
img.onload = () => decrement(loader);
-
img.onerror = () => decrement(loader);
-
img.src = url;
return img;
}).catch(e => {
decrement(loader);
return {
@@ -17799,84 +15983,68 @@
width: 0,
height: 0,
src: e && e.url || ''
};
});
- },
-
+ }
ready() {
const loader = this;
return new Promise(accept => {
function poll(value) {
if (!loader.pending()) accept(value);else setTimeout(() => {
poll(true);
}, 10);
}
-
poll(false);
});
}
-
- };
-
+ }
+ function increment(loader) {
+ loader._pending += 1;
+ }
+ function decrement(loader) {
+ loader._pending -= 1;
+ }
function boundStroke(bounds, item, miter) {
if (item.stroke && item.opacity !== 0 && item.strokeOpacity !== 0) {
const sw = item.strokeWidth != null ? +item.strokeWidth : 1;
bounds.expand(sw + (miter ? miterAdjustment(item, sw) : 0));
}
-
return bounds;
}
-
function miterAdjustment(item, strokeWidth) {
// TODO: more sophisticated adjustment? Or miter support in boundContext?
return item.strokeJoin && item.strokeJoin !== 'miter' ? 0 : strokeWidth;
}
-
const circleThreshold = Tau - 1e-8;
let bounds, lx, ly, rot, ma, mb, mc, md;
-
const add$1 = (x, y) => bounds.add(x, y);
-
const addL = (x, y) => add$1(lx = x, ly = y);
-
const addX = x => add$1(x, bounds.y1);
-
const addY = y => add$1(bounds.x1, y);
-
const px = (x, y) => ma * x + mc * y;
-
const py = (x, y) => mb * x + md * y;
-
const addp = (x, y) => add$1(px(x, y), py(x, y));
-
const addpL = (x, y) => addL(px(x, y), py(x, y));
-
function boundContext(_, deg) {
bounds = _;
-
if (deg) {
rot = deg * DegToRad;
ma = md = Math.cos(rot);
mb = Math.sin(rot);
mc = -mb;
} else {
ma = md = 1;
rot = mb = mc = 0;
}
-
return context$1;
}
-
const context$1 = {
beginPath() {},
-
closePath() {},
-
moveTo: addpL,
lineTo: addpL,
-
rect(x, y, w, h) {
if (rot) {
addp(x + w, y);
addp(x + w, y + h);
addp(x, y + h);
@@ -17884,498 +16052,428 @@
} else {
add$1(x + w, y + h);
addL(x, y);
}
},
-
quadraticCurveTo(x1, y1, x2, y2) {
const px1 = px(x1, y1),
- py1 = py(x1, y1),
- px2 = px(x2, y2),
- py2 = py(x2, y2);
+ py1 = py(x1, y1),
+ px2 = px(x2, y2),
+ py2 = py(x2, y2);
quadExtrema(lx, px1, px2, addX);
quadExtrema(ly, py1, py2, addY);
addL(px2, py2);
},
-
bezierCurveTo(x1, y1, x2, y2, x3, y3) {
const px1 = px(x1, y1),
- py1 = py(x1, y1),
- px2 = px(x2, y2),
- py2 = py(x2, y2),
- px3 = px(x3, y3),
- py3 = py(x3, y3);
+ py1 = py(x1, y1),
+ px2 = px(x2, y2),
+ py2 = py(x2, y2),
+ px3 = px(x3, y3),
+ py3 = py(x3, y3);
cubicExtrema(lx, px1, px2, px3, addX);
cubicExtrema(ly, py1, py2, py3, addY);
addL(px3, py3);
},
-
arc(cx, cy, r, sa, ea, ccw) {
sa += rot;
- ea += rot; // store last point on path
+ ea += rot;
+ // store last point on path
lx = r * Math.cos(ea) + cx;
ly = r * Math.sin(ea) + cy;
-
if (Math.abs(ea - sa) > circleThreshold) {
// treat as full circle
add$1(cx - r, cy - r);
add$1(cx + r, cy + r);
} else {
const update = a => add$1(r * Math.cos(a) + cx, r * Math.sin(a) + cy);
+ let s, i;
- let s, i; // sample end points
-
+ // sample end points
update(sa);
- update(ea); // sample interior points aligned with 90 degrees
+ update(ea);
+ // sample interior points aligned with 90 degrees
if (ea !== sa) {
sa = sa % Tau;
if (sa < 0) sa += Tau;
ea = ea % Tau;
if (ea < 0) ea += Tau;
-
if (ea < sa) {
ccw = !ccw; // flip direction
-
s = sa;
sa = ea;
ea = s; // swap end-points
}
-
if (ccw) {
ea -= Tau;
s = sa - sa % HalfPi;
-
for (i = 0; i < 4 && s > ea; ++i, s -= HalfPi) update(s);
} else {
s = sa - sa % HalfPi + HalfPi;
-
for (i = 0; i < 4 && s < ea; ++i, s = s + HalfPi) update(s);
}
}
}
}
-
};
-
function quadExtrema(x0, x1, x2, cb) {
const t = (x0 - x1) / (x0 + x2 - 2 * x1);
if (0 < t && t < 1) cb(x0 + (x1 - x0) * t);
}
-
function cubicExtrema(x0, x1, x2, x3, cb) {
const a = x3 - x0 + 3 * x1 - 3 * x2,
- b = x0 + x2 - 2 * x1,
- c = x0 - x1;
+ b = x0 + x2 - 2 * x1,
+ c = x0 - x1;
let t0 = 0,
- t1 = 0,
- r; // solve for parameter t
+ t1 = 0,
+ r;
+ // solve for parameter t
if (Math.abs(a) > Epsilon) {
// quadratic equation
r = b * b + c * a;
-
if (r >= 0) {
r = Math.sqrt(r);
t0 = (-b + r) / a;
t1 = (-b - r) / a;
}
} else {
// linear equation
t0 = 0.5 * c / b;
- } // calculate position
+ }
-
+ // calculate position
if (0 < t0 && t0 < 1) cb(cubic(t0, x0, x1, x2, x3));
if (0 < t1 && t1 < 1) cb(cubic(t1, x0, x1, x2, x3));
}
-
function cubic(t, x0, x1, x2, x3) {
const s = 1 - t,
- s2 = s * s,
- t2 = t * t;
+ s2 = s * s,
+ t2 = t * t;
return s2 * s * x0 + 3 * s2 * t * x1 + 3 * s * t2 * x2 + t2 * t * x3;
}
-
var context$2 = (context$2 = domCanvas(1, 1)) ? context$2.getContext('2d') : null;
const b = new Bounds();
-
function intersectPath(draw) {
return function (item, brush) {
// rely on (inaccurate) bounds intersection if no context
- if (!context$2) return true; // add path to offscreen graphics context
+ if (!context$2) return true;
- draw(context$2, item); // get bounds intersection region
+ // add path to offscreen graphics context
+ draw(context$2, item);
+ // get bounds intersection region
b.clear().union(item.bounds).intersect(brush).round();
const {
x1,
y1,
x2,
y2
- } = b; // iterate over intersection region
- // perform fine grained inclusion test
+ } = b;
+ // iterate over intersection region
+ // perform fine grained inclusion test
for (let y = y1; y <= y2; ++y) {
for (let x = x1; x <= x2; ++x) {
if (context$2.isPointInPath(x, y)) {
return true;
}
}
- } // false if no hits in intersection region
+ }
-
+ // false if no hits in intersection region
return false;
};
}
-
function intersectPoint(item, box) {
return box.contains(item.x || 0, item.y || 0);
}
-
function intersectRect(item, box) {
const x = item.x || 0,
- y = item.y || 0,
- w = item.width || 0,
- h = item.height || 0;
+ y = item.y || 0,
+ w = item.width || 0,
+ h = item.height || 0;
return box.intersects(b.set(x, y, x + w, y + h));
}
-
function intersectRule(item, box) {
const x = item.x || 0,
- y = item.y || 0,
- x2 = item.x2 != null ? item.x2 : x,
- y2 = item.y2 != null ? item.y2 : y;
+ y = item.y || 0,
+ x2 = item.x2 != null ? item.x2 : x,
+ y2 = item.y2 != null ? item.y2 : y;
return intersectBoxLine(box, x, y, x2, y2);
}
-
function intersectBoxLine(box, x, y, u, v) {
const {
- x1,
- y1,
- x2,
- y2
- } = box,
- dx = u - x,
- dy = v - y;
+ x1,
+ y1,
+ x2,
+ y2
+ } = box,
+ dx = u - x,
+ dy = v - y;
let t0 = 0,
- t1 = 1,
- p,
- q,
- r,
- e;
-
+ t1 = 1,
+ p,
+ q,
+ r,
+ e;
for (e = 0; e < 4; ++e) {
if (e === 0) {
p = -dx;
q = -(x1 - x);
}
-
if (e === 1) {
p = dx;
q = x2 - x;
}
-
if (e === 2) {
p = -dy;
q = -(y1 - y);
}
-
if (e === 3) {
p = dy;
q = y2 - y;
}
-
if (Math.abs(p) < 1e-10 && q < 0) return false;
r = q / p;
-
if (p < 0) {
if (r > t1) return false;else if (r > t0) t0 = r;
} else if (p > 0) {
if (r < t0) return false;else if (r < t1) t1 = r;
}
}
-
return true;
}
-
function blend(context, item) {
context.globalCompositeOperation = item.blend || 'source-over';
}
-
function value$2(value, dflt) {
return value == null ? dflt : value;
}
-
function addStops(gradient, stops) {
const n = stops.length;
-
for (let i = 0; i < n; ++i) {
gradient.addColorStop(stops[i].offset, stops[i].color);
}
-
return gradient;
}
-
function gradient$1(context, spec, bounds) {
const w = bounds.width(),
- h = bounds.height();
+ h = bounds.height();
let gradient;
-
if (spec.gradient === 'radial') {
gradient = context.createRadialGradient(bounds.x1 + value$2(spec.x1, 0.5) * w, bounds.y1 + value$2(spec.y1, 0.5) * h, Math.max(w, h) * value$2(spec.r1, 0), bounds.x1 + value$2(spec.x2, 0.5) * w, bounds.y1 + value$2(spec.y2, 0.5) * h, Math.max(w, h) * value$2(spec.r2, 0.5));
} else {
// linear gradient
const x1 = value$2(spec.x1, 0),
- y1 = value$2(spec.y1, 0),
- x2 = value$2(spec.x2, 1),
- y2 = value$2(spec.y2, 0);
-
+ y1 = value$2(spec.y1, 0),
+ x2 = value$2(spec.x2, 1),
+ y2 = value$2(spec.y2, 0);
if (x1 === x2 || y1 === y2 || w === h) {
// axis aligned: use normal gradient
gradient = context.createLinearGradient(bounds.x1 + x1 * w, bounds.y1 + y1 * h, bounds.x1 + x2 * w, bounds.y1 + y2 * h);
} else {
// not axis aligned: render gradient into a pattern (#2365)
// this allows us to use normalized bounding box coordinates
const image = domCanvas(Math.ceil(w), Math.ceil(h)),
- ictx = image.getContext('2d');
+ ictx = image.getContext('2d');
ictx.scale(w, h);
ictx.fillStyle = addStops(ictx.createLinearGradient(x1, y1, x2, y2), spec.stops);
ictx.fillRect(0, 0, w, h);
return context.createPattern(image, 'no-repeat');
}
}
-
return addStops(gradient, spec.stops);
}
-
function color$1(context, item, value) {
return isGradient(value) ? gradient$1(context, value, item.bounds) : value;
}
-
function fill(context, item, opacity) {
opacity *= item.fillOpacity == null ? 1 : item.fillOpacity;
-
if (opacity > 0) {
context.globalAlpha = opacity;
context.fillStyle = color$1(context, item, item.fill);
return true;
} else {
return false;
}
}
-
var Empty = [];
-
function stroke(context, item, opacity) {
var lw = (lw = item.strokeWidth) != null ? lw : 1;
if (lw <= 0) return false;
opacity *= item.strokeOpacity == null ? 1 : item.strokeOpacity;
-
if (opacity > 0) {
context.globalAlpha = opacity;
context.strokeStyle = color$1(context, item, item.stroke);
context.lineWidth = lw;
context.lineCap = item.strokeCap || 'butt';
context.lineJoin = item.strokeJoin || 'miter';
context.miterLimit = item.strokeMiterLimit || 10;
-
if (context.setLineDash) {
context.setLineDash(item.strokeDash || Empty);
context.lineDashOffset = item.strokeDashOffset || 0;
}
-
return true;
} else {
return false;
}
}
-
function compare(a, b) {
return a.zindex - b.zindex || a.index - b.index;
}
-
function zorder(scene) {
if (!scene.zdirty) return scene.zitems;
var items = scene.items,
- output = [],
- item,
- i,
- n;
-
+ output = [],
+ item,
+ i,
+ n;
for (i = 0, n = items.length; i < n; ++i) {
item = items[i];
item.index = i;
if (item.zindex) output.push(item);
}
-
scene.zdirty = false;
return scene.zitems = output.sort(compare);
}
-
function visit(scene, visitor) {
var items = scene.items,
- i,
- n;
+ i,
+ n;
if (!items || !items.length) return;
const zitems = zorder(scene);
-
if (zitems && zitems.length) {
for (i = 0, n = items.length; i < n; ++i) {
if (!items[i].zindex) visitor(items[i]);
}
-
items = zitems;
}
-
for (i = 0, n = items.length; i < n; ++i) {
visitor(items[i]);
}
}
-
function pickVisit(scene, visitor) {
var items = scene.items,
- hit,
- i;
+ hit,
+ i;
if (!items || !items.length) return null;
const zitems = zorder(scene);
if (zitems && zitems.length) items = zitems;
-
for (i = items.length; --i >= 0;) {
if (hit = visitor(items[i])) return hit;
}
-
if (items === zitems) {
for (items = scene.items, i = items.length; --i >= 0;) {
if (!items[i].zindex) {
if (hit = visitor(items[i])) return hit;
}
}
}
-
return null;
}
-
function drawAll(path) {
return function (context, scene, bounds) {
visit(scene, item => {
if (!bounds || bounds.intersects(item.bounds)) {
drawPath(path, context, item, item);
}
});
};
}
-
function drawOne(path) {
return function (context, scene, bounds) {
if (scene.items.length && (!bounds || bounds.intersects(scene.bounds))) {
drawPath(path, context, scene.items[0], scene.items);
}
};
}
-
function drawPath(path, context, item, items) {
var opacity = item.opacity == null ? 1 : item.opacity;
if (opacity === 0) return;
if (path(context, items)) return;
blend(context, item);
-
if (item.fill && fill(context, item, opacity)) {
context.fill();
}
-
if (item.stroke && stroke(context, item, opacity)) {
context.stroke();
}
}
-
function pick$1(test) {
test = test || truthy;
return function (context, scene, x, y, gx, gy) {
x *= context.pixelRatio;
y *= context.pixelRatio;
return pickVisit(scene, item => {
- const b = item.bounds; // first hit test against bounding box
-
- if (b && !b.contains(gx, gy) || !b) return; // if in bounding box, perform more careful test
-
+ const b = item.bounds;
+ // first hit test against bounding box
+ if (b && !b.contains(gx, gy) || !b) return;
+ // if in bounding box, perform more careful test
if (test(context, item, x, y, gx, gy)) return item;
});
};
}
-
function hitPath(path, filled) {
return function (context, o, x, y) {
var item = Array.isArray(o) ? o[0] : o,
- fill = filled == null ? item.fill : filled,
- stroke = item.stroke && context.isPointInStroke,
- lw,
- lc;
-
+ fill = filled == null ? item.fill : filled,
+ stroke = item.stroke && context.isPointInStroke,
+ lw,
+ lc;
if (stroke) {
lw = item.strokeWidth;
lc = item.strokeCap;
context.lineWidth = lw != null ? lw : 1;
context.lineCap = lc != null ? lc : 'butt';
}
-
return path(context, o) ? false : fill && context.isPointInPath(x, y) || stroke && context.isPointInStroke(x, y);
};
}
-
function pickPath(path) {
return pick$1(hitPath(path));
}
-
function translate$1(x, y) {
return 'translate(' + x + ',' + y + ')';
}
-
function rotate(a) {
return 'rotate(' + a + ')';
}
-
function scale$3(scaleX, scaleY) {
return 'scale(' + scaleX + ',' + scaleY + ')';
}
-
function translateItem(item) {
return translate$1(item.x || 0, item.y || 0);
}
-
function rotateItem(item) {
return translate$1(item.x || 0, item.y || 0) + (item.angle ? ' ' + rotate(item.angle) : '');
}
-
function transformItem(item) {
return translate$1(item.x || 0, item.y || 0) + (item.angle ? ' ' + rotate(item.angle) : '') + (item.scaleX || item.scaleY ? ' ' + scale$3(item.scaleX || 1, item.scaleY || 1) : '');
}
-
function markItemPath(type, shape, isect) {
function attr(emit, item) {
emit('transform', rotateItem(item));
emit('d', shape(null, item));
}
-
function bound(bounds, item) {
shape(boundContext(bounds, item.angle), item);
return boundStroke(bounds, item).translate(item.x || 0, item.y || 0);
}
-
function draw(context, item) {
var x = item.x || 0,
- y = item.y || 0,
- a = item.angle || 0;
+ y = item.y || 0,
+ a = item.angle || 0;
context.translate(x, y);
if (a) context.rotate(a *= DegToRad);
context.beginPath();
shape(context, item);
if (a) context.rotate(-a);
context.translate(-x, -y);
}
-
return {
type: type,
tag: 'path',
nested: false,
attr: attr,
@@ -18383,107 +16481,87 @@
draw: drawAll(draw),
pick: pickPath(draw),
isect: isect || intersectPath(draw)
};
}
-
var arc$2 = markItemPath('arc', arc$1);
-
function pickArea(a, p) {
var v = a[0].orient === 'horizontal' ? p[1] : p[0],
- z = a[0].orient === 'horizontal' ? 'y' : 'x',
- i = a.length,
- min = +Infinity,
- hit,
- d;
-
+ z = a[0].orient === 'horizontal' ? 'y' : 'x',
+ i = a.length,
+ min = +Infinity,
+ hit,
+ d;
while (--i >= 0) {
if (a[i].defined === false) continue;
d = Math.abs(a[i][z] - v);
-
if (d < min) {
min = d;
hit = a[i];
}
}
-
return hit;
}
-
function pickLine(a, p) {
var t = Math.pow(a[0].strokeWidth || 1, 2),
- i = a.length,
- dx,
- dy,
- dd;
-
+ i = a.length,
+ dx,
+ dy,
+ dd;
while (--i >= 0) {
if (a[i].defined === false) continue;
dx = a[i].x - p[0];
dy = a[i].y - p[1];
dd = dx * dx + dy * dy;
if (dd < t) return a[i];
}
-
return null;
}
-
function pickTrail(a, p) {
var i = a.length,
- dx,
- dy,
- dd;
-
+ dx,
+ dy,
+ dd;
while (--i >= 0) {
if (a[i].defined === false) continue;
dx = a[i].x - p[0];
dy = a[i].y - p[1];
dd = dx * dx + dy * dy;
dx = a[i].size || 1;
if (dd < dx * dx) return a[i];
}
-
return null;
}
-
function markMultiItemPath(type, shape, tip) {
function attr(emit, item) {
var items = item.mark.items;
if (items.length) emit('d', shape(null, items));
}
-
function bound(bounds, mark) {
var items = mark.items;
-
if (items.length === 0) {
return bounds;
} else {
shape(boundContext(bounds), items);
return boundStroke(bounds, items[0]);
}
}
-
function draw(context, items) {
context.beginPath();
shape(context, items);
}
-
const hit = hitPath(draw);
-
function pick(context, scene, x, y, gx, gy) {
var items = scene.items,
- b = scene.bounds;
-
+ b = scene.bounds;
if (!items || !items.length || b && !b.contains(gx, gy)) {
return null;
}
-
x *= context.pixelRatio;
y *= context.pixelRatio;
return hit(context, items, x, y) ? items[0] : null;
}
-
return {
type: type,
tag: 'path',
nested: true,
attr: attr,
@@ -18492,195 +16570,181 @@
pick: pick,
isect: intersectPoint,
tip: tip
};
}
-
var area$2 = markMultiItemPath('area', area$1, pickArea);
-
function clip$2(context, scene) {
var clip = scene.clip;
context.save();
-
if (isFunction(clip)) {
context.beginPath();
clip(context);
context.clip();
} else {
clipGroup(context, scene.group);
}
}
-
function clipGroup(context, group) {
context.beginPath();
hasCornerRadius(group) ? rectangle(context, group, 0, 0) : context.rect(0, 0, group.width || 0, group.height || 0);
context.clip();
}
-
function offset$1(item) {
const sw = value$2(item.strokeWidth, 1);
return item.strokeOffset != null ? item.strokeOffset : item.stroke && sw > 0.5 && sw < 1.5 ? 0.5 - Math.abs(sw - 1) : 0;
}
-
function attr$5(emit, item) {
emit('transform', translateItem(item));
}
-
function emitRectangle(emit, item) {
const off = offset$1(item);
emit('d', rectangle(null, item, off, off));
}
-
function background$1(emit, item) {
emit('class', 'background');
emit('aria-hidden', true);
emitRectangle(emit, item);
}
-
function foreground(emit, item) {
emit('class', 'foreground');
emit('aria-hidden', true);
-
if (item.strokeForeground) {
emitRectangle(emit, item);
} else {
emit('d', '');
}
}
-
function content(emit, item, renderer) {
const url = item.clip ? clip$1$1(renderer, item, item) : null;
emit('clip-path', url);
}
-
function bound$5(bounds, group) {
if (!group.clip && group.items) {
const items = group.items,
- m = items.length;
-
+ m = items.length;
for (let j = 0; j < m; ++j) {
bounds.union(items[j].bounds);
}
}
-
if ((group.clip || group.width || group.height) && !group.noBound) {
bounds.add(0, 0).add(group.width || 0, group.height || 0);
}
-
boundStroke(bounds, group);
return bounds.translate(group.x || 0, group.y || 0);
}
-
function rectanglePath(context, group, x, y) {
const off = offset$1(group);
context.beginPath();
rectangle(context, group, (x || 0) + off, (y || 0) + off);
}
-
const hitBackground = hitPath(rectanglePath);
const hitForeground = hitPath(rectanglePath, false);
const hitCorner = hitPath(rectanglePath, true);
-
- function draw$4(context, scene, bounds) {
+ function draw$4(context, scene, bounds, markTypes) {
visit(scene, group => {
const gx = group.x || 0,
- gy = group.y || 0,
- fore = group.strokeForeground,
- opacity = group.opacity == null ? 1 : group.opacity; // draw group background
+ gy = group.y || 0,
+ fore = group.strokeForeground,
+ opacity = group.opacity == null ? 1 : group.opacity;
+ // draw group background
if ((group.stroke || group.fill) && opacity) {
rectanglePath(context, group, gx, gy);
blend(context, group);
-
if (group.fill && fill(context, group, opacity)) {
context.fill();
}
-
if (group.stroke && !fore && stroke(context, group, opacity)) {
context.stroke();
}
- } // setup graphics context, set clip and bounds
+ }
-
+ // setup graphics context, set clip and bounds
context.save();
context.translate(gx, gy);
if (group.clip) clipGroup(context, group);
- if (bounds) bounds.translate(-gx, -gy); // draw group contents
+ if (bounds) bounds.translate(-gx, -gy);
+ // draw group contents
visit(group, item => {
- this.draw(context, item, bounds);
- }); // restore graphics context
+ if (item.marktype === 'group' || markTypes == null || markTypes.includes(item.marktype)) {
+ this.draw(context, item, bounds, markTypes);
+ }
+ });
+ // restore graphics context
if (bounds) bounds.translate(gx, gy);
- context.restore(); // draw group foreground
+ context.restore();
+ // draw group foreground
if (fore && group.stroke && opacity) {
rectanglePath(context, group, gx, gy);
blend(context, group);
-
if (stroke(context, group, opacity)) {
context.stroke();
}
}
});
}
-
function pick(context, scene, x, y, gx, gy) {
if (scene.bounds && !scene.bounds.contains(gx, gy) || !scene.items) {
return null;
}
-
const cx = x * context.pixelRatio,
- cy = y * context.pixelRatio;
+ cy = y * context.pixelRatio;
return pickVisit(scene, group => {
- let hit, dx, dy; // first hit test bounding box
+ let hit, dx, dy;
+ // first hit test bounding box
const b = group.bounds;
- if (b && !b.contains(gx, gy)) return; // passed bounds check, test rectangular clip
+ if (b && !b.contains(gx, gy)) return;
+ // passed bounds check, test rectangular clip
dx = group.x || 0;
dy = group.y || 0;
const dw = dx + (group.width || 0),
- dh = dy + (group.height || 0),
- c = group.clip;
- if (c && (gx < dx || gx > dw || gy < dy || gy > dh)) return; // adjust coordinate system
+ dh = dy + (group.height || 0),
+ c = group.clip;
+ if (c && (gx < dx || gx > dw || gy < dy || gy > dh)) return;
+ // adjust coordinate system
context.save();
context.translate(dx, dy);
dx = gx - dx;
- dy = gy - dy; // test background for rounded corner clip
+ dy = gy - dy;
+ // test background for rounded corner clip
if (c && hasCornerRadius(group) && !hitCorner(context, group, cx, cy)) {
context.restore();
return null;
}
-
const fore = group.strokeForeground,
- ix = scene.interactive !== false; // hit test against group foreground
+ ix = scene.interactive !== false;
+ // hit test against group foreground
if (ix && fore && group.stroke && hitForeground(context, group, cx, cy)) {
context.restore();
return group;
- } // hit test against contained marks
+ }
+ // hit test against contained marks
+ hit = pickVisit(group, mark => pickMark(mark, dx, dy) ? this.pick(mark, x, y, dx, dy) : null);
- hit = pickVisit(group, mark => pickMark(mark, dx, dy) ? this.pick(mark, x, y, dx, dy) : null); // hit test against group background
-
+ // hit test against group background
if (!hit && ix && (group.fill || !fore && group.stroke) && hitBackground(context, group, cx, cy)) {
hit = group;
- } // restore state and return
+ }
-
+ // restore state and return
context.restore();
return hit || null;
});
}
-
function pickMark(mark, x, y) {
return (mark.interactive !== false || mark.marktype === 'group') && mark.bounds && mark.bounds.contains(x, y);
}
-
var group = {
type: 'group',
tag: 'g',
nested: false,
attr: attr$5,
@@ -18695,14 +16759,12 @@
var metadata = {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'version': '1.1'
};
-
function getImage(item, renderer) {
var image = item.image;
-
if (!image || item.url && item.url !== image.url) {
image = {
complete: false,
width: 0,
height: 0
@@ -18710,73 +16772,63 @@
renderer.loadImage(item.url).then(image => {
item.image = image;
item.image.url = item.url;
});
}
-
return image;
}
-
function imageWidth(item, image) {
return item.width != null ? item.width : !image || !image.width ? 0 : item.aspect !== false && item.height ? item.height * image.width / image.height : image.width;
}
-
function imageHeight(item, image) {
return item.height != null ? item.height : !image || !image.height ? 0 : item.aspect !== false && item.width ? item.width * image.height / image.width : image.height;
}
-
function imageXOffset(align, w) {
return align === 'center' ? w / 2 : align === 'right' ? w : 0;
}
-
function imageYOffset(baseline, h) {
return baseline === 'middle' ? h / 2 : baseline === 'bottom' ? h : 0;
}
-
function attr$4(emit, item, renderer) {
const img = getImage(item, renderer),
- w = imageWidth(item, img),
- h = imageHeight(item, img),
- x = (item.x || 0) - imageXOffset(item.align, w),
- y = (item.y || 0) - imageYOffset(item.baseline, h),
- i = !img.src && img.toDataURL ? img.toDataURL() : img.src || '';
+ w = imageWidth(item, img),
+ h = imageHeight(item, img),
+ x = (item.x || 0) - imageXOffset(item.align, w),
+ y = (item.y || 0) - imageYOffset(item.baseline, h),
+ i = !img.src && img.toDataURL ? img.toDataURL() : img.src || '';
emit('href', i, metadata['xmlns:xlink'], 'xlink:href');
emit('transform', translate$1(x, y));
emit('width', w);
emit('height', h);
emit('preserveAspectRatio', item.aspect === false ? 'none' : 'xMidYMid');
}
-
function bound$4(bounds, item) {
const img = item.image,
- w = imageWidth(item, img),
- h = imageHeight(item, img),
- x = (item.x || 0) - imageXOffset(item.align, w),
- y = (item.y || 0) - imageYOffset(item.baseline, h);
+ w = imageWidth(item, img),
+ h = imageHeight(item, img),
+ x = (item.x || 0) - imageXOffset(item.align, w),
+ y = (item.y || 0) - imageYOffset(item.baseline, h);
return bounds.set(x, y, x + w, y + h);
}
-
function draw$3(context, scene, bounds) {
visit(scene, item => {
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
const img = getImage(item, this);
let w = imageWidth(item, img);
let h = imageHeight(item, img);
if (w === 0 || h === 0) return; // early exit
let x = (item.x || 0) - imageXOffset(item.align, w),
- y = (item.y || 0) - imageYOffset(item.baseline, h),
- opacity,
- ar0,
- ar1,
- t;
-
+ y = (item.y || 0) - imageYOffset(item.baseline, h),
+ opacity,
+ ar0,
+ ar1,
+ t;
if (item.aspect !== false) {
ar0 = img.width / img.height;
ar1 = item.width / item.height;
-
if (ar0 === ar0 && ar1 === ar1 && ar0 !== ar1) {
if (ar1 < ar0) {
t = w / ar0;
y += (h - t) / 2;
h = t;
@@ -18785,20 +16837,18 @@
x += (w - t) / 2;
w = t;
}
}
}
-
if (img.complete || img.toDataURL) {
blend(context, item);
context.globalAlpha = (opacity = item.opacity) != null ? opacity : 1;
context.imageSmoothingEnabled = item.smooth !== false;
context.drawImage(img, x, y, w, h);
}
});
}
-
var image = {
type: 'image',
tag: 'image',
nested: false,
attr: attr$4,
@@ -18810,134 +16860,112 @@
get: getImage,
xOffset: imageXOffset,
yOffset: imageYOffset
};
var line$2 = markMultiItemPath('line', line$1, pickLine);
-
function attr$3(emit, item) {
var sx = item.scaleX || 1,
- sy = item.scaleY || 1;
-
+ sy = item.scaleY || 1;
if (sx !== 1 || sy !== 1) {
emit('vector-effect', 'non-scaling-stroke');
}
-
emit('transform', transformItem(item));
emit('d', item.path);
}
-
function path$1(context, item) {
var path = item.path;
if (path == null) return true;
var x = item.x || 0,
- y = item.y || 0,
- sx = item.scaleX || 1,
- sy = item.scaleY || 1,
- a = (item.angle || 0) * DegToRad,
- cache = item.pathCache;
-
+ y = item.y || 0,
+ sx = item.scaleX || 1,
+ sy = item.scaleY || 1,
+ a = (item.angle || 0) * DegToRad,
+ cache = item.pathCache;
if (!cache || cache.path !== path) {
(item.pathCache = cache = parse$3(path)).path = path;
}
-
if (a && context.rotate && context.translate) {
context.translate(x, y);
context.rotate(a);
pathRender(context, cache, 0, 0, sx, sy);
context.rotate(-a);
context.translate(-x, -y);
} else {
pathRender(context, cache, x, y, sx, sy);
}
}
-
function bound$3(bounds, item) {
return path$1(boundContext(bounds, item.angle), item) ? bounds.set(0, 0, 0, 0) : boundStroke(bounds, item, true);
}
-
var path$2 = {
type: 'path',
tag: 'path',
nested: false,
attr: attr$3,
bound: bound$3,
draw: drawAll(path$1),
pick: pickPath(path$1),
isect: intersectPath(path$1)
};
-
function attr$2(emit, item) {
emit('d', rectangle(null, item));
}
-
function bound$2(bounds, item) {
var x, y;
return boundStroke(bounds.set(x = item.x || 0, y = item.y || 0, x + item.width || 0, y + item.height || 0), item);
}
-
function draw$2(context, item) {
context.beginPath();
rectangle(context, item);
}
-
var rect = {
type: 'rect',
tag: 'path',
nested: false,
attr: attr$2,
bound: bound$2,
draw: drawAll(draw$2),
pick: pickPath(draw$2),
isect: intersectRect
};
-
function attr$1(emit, item) {
emit('transform', translateItem(item));
emit('x2', item.x2 != null ? item.x2 - (item.x || 0) : 0);
emit('y2', item.y2 != null ? item.y2 - (item.y || 0) : 0);
}
-
function bound$1(bounds, item) {
var x1, y1;
return boundStroke(bounds.set(x1 = item.x || 0, y1 = item.y || 0, item.x2 != null ? item.x2 : x1, item.y2 != null ? item.y2 : y1), item);
}
-
function path(context, item, opacity) {
var x1, y1, x2, y2;
-
if (item.stroke && stroke(context, item, opacity)) {
x1 = item.x || 0;
y1 = item.y || 0;
x2 = item.x2 != null ? item.x2 : x1;
y2 = item.y2 != null ? item.y2 : y1;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
return true;
}
-
return false;
}
-
function draw$1(context, scene, bounds) {
visit(scene, item => {
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
-
var opacity = item.opacity == null ? 1 : item.opacity;
-
if (opacity && path(context, item, opacity)) {
blend(context, item);
context.stroke();
}
});
}
-
function hit$1(context, item, x, y) {
if (!context.isPointInStroke) return false;
return path(context, item, 1) && context.isPointInStroke(x, y);
}
-
var rule$1 = {
type: 'rule',
tag: 'line',
nested: false,
attr: attr$1,
@@ -18946,301 +16974,264 @@
pick: pick$1(hit$1),
isect: intersectRule
};
var shape = markItemPath('shape', shape$1);
var symbol = markItemPath('symbol', symbol$1, intersectPoint);
+
+ // memoize text width measurement
const widthCache = lruCache();
var textMetrics = {
height: fontSize,
measureWidth: measureWidth,
estimateWidth: estimateWidth,
width: estimateWidth,
canvas: useCanvas
};
useCanvas(true);
-
function useCanvas(use) {
textMetrics.width = use && context$2 ? measureWidth : estimateWidth;
- } // make simple estimate if no canvas is available
+ }
-
+ // make simple estimate if no canvas is available
function estimateWidth(item, text) {
return _estimateWidth(textValue(item, text), fontSize(item));
}
-
function _estimateWidth(text, currentFontHeight) {
return ~~(0.8 * text.length * currentFontHeight);
- } // measure text width if canvas is available
+ }
-
+ // measure text width if canvas is available
function measureWidth(item, text) {
return fontSize(item) <= 0 || !(text = textValue(item, text)) ? 0 : _measureWidth(text, font(item));
}
-
function _measureWidth(text, currentFont) {
- const key = "(".concat(currentFont, ") ").concat(text);
+ const key = `(${currentFont}) ${text}`;
let width = widthCache.get(key);
-
if (width === undefined) {
context$2.font = currentFont;
width = context$2.measureText(text).width;
widthCache.set(key, width);
}
-
return width;
}
-
function fontSize(item) {
return item.fontSize != null ? +item.fontSize || 0 : 11;
}
-
function lineHeight(item) {
return item.lineHeight != null ? item.lineHeight : fontSize(item) + 2;
}
-
function lineArray(_) {
return isArray(_) ? _.length > 1 ? _ : _[0] : _;
}
-
function textLines(item) {
return lineArray(item.lineBreak && item.text && !isArray(item.text) ? item.text.split(item.lineBreak) : item.text);
}
-
function multiLineOffset(item) {
const tl = textLines(item);
return (isArray(tl) ? tl.length - 1 : 0) * lineHeight(item);
}
-
function textValue(item, line) {
const text = line == null ? '' : (line + '').trim();
return item.limit > 0 && text.length ? truncate(item, text) : text;
}
-
function widthGetter(item) {
if (textMetrics.width === measureWidth) {
// we are using canvas
const currentFont = font(item);
return text => _measureWidth(text, currentFont);
- } else {
+ } else if (textMetrics.width === estimateWidth) {
// we are relying on estimates
const currentFontHeight = fontSize(item);
return text => _estimateWidth(text, currentFontHeight);
+ } else {
+ // User defined textMetrics.width function in use (e.g. vl-convert)
+ return text => textMetrics.width(item, text);
}
}
-
function truncate(item, text) {
var limit = +item.limit,
- width = widthGetter(item);
+ width = widthGetter(item);
if (width(text) < limit) return text;
var ellipsis = item.ellipsis || '\u2026',
- rtl = item.dir === 'rtl',
- lo = 0,
- hi = text.length,
- mid;
+ rtl = item.dir === 'rtl',
+ lo = 0,
+ hi = text.length,
+ mid;
limit -= width(ellipsis);
-
if (rtl) {
while (lo < hi) {
mid = lo + hi >>> 1;
if (width(text.slice(mid)) > limit) lo = mid + 1;else hi = mid;
}
-
return ellipsis + text.slice(lo);
} else {
while (lo < hi) {
mid = 1 + (lo + hi >>> 1);
if (width(text.slice(0, mid)) < limit) lo = mid;else hi = mid - 1;
}
-
return text.slice(0, lo) + ellipsis;
}
}
-
function fontFamily(item, quote) {
var font = item.font;
return (quote && font ? String(font).replace(/"/g, '\'') : font) || 'sans-serif';
}
-
function font(item, quote) {
return '' + (item.fontStyle ? item.fontStyle + ' ' : '') + (item.fontVariant ? item.fontVariant + ' ' : '') + (item.fontWeight ? item.fontWeight + ' ' : '') + fontSize(item) + 'px ' + fontFamily(item, quote);
}
-
function offset$2(item) {
// perform our own font baseline calculation
// why? not all browsers support SVG 1.1 'alignment-baseline' :(
// this also ensures consistent layout across renderers
var baseline = item.baseline,
- h = fontSize(item);
+ h = fontSize(item);
return Math.round(baseline === 'top' ? 0.79 * h : baseline === 'middle' ? 0.30 * h : baseline === 'bottom' ? -0.21 * h : baseline === 'line-top' ? 0.29 * h + 0.5 * lineHeight(item) : baseline === 'line-bottom' ? 0.29 * h - 0.5 * lineHeight(item) : 0);
}
-
const textAlign = {
'left': 'start',
'center': 'middle',
'right': 'end'
};
const tempBounds$1 = new Bounds();
-
function anchorPoint(item) {
var x = item.x || 0,
- y = item.y || 0,
- r = item.radius || 0,
- t;
-
+ y = item.y || 0,
+ r = item.radius || 0,
+ t;
if (r) {
t = (item.theta || 0) - HalfPi;
x += r * Math.cos(t);
y += r * Math.sin(t);
}
-
tempBounds$1.x1 = x;
tempBounds$1.y1 = y;
return tempBounds$1;
}
-
function attr(emit, item) {
var dx = item.dx || 0,
- dy = (item.dy || 0) + offset$2(item),
- p = anchorPoint(item),
- x = p.x1,
- y = p.y1,
- a = item.angle || 0,
- t;
+ dy = (item.dy || 0) + offset$2(item),
+ p = anchorPoint(item),
+ x = p.x1,
+ y = p.y1,
+ a = item.angle || 0,
+ t;
emit('text-anchor', textAlign[item.align] || 'start');
-
if (a) {
t = translate$1(x, y) + ' ' + rotate(a);
if (dx || dy) t += ' ' + translate$1(dx, dy);
} else {
t = translate$1(x + dx, y + dy);
}
-
emit('transform', t);
}
-
function bound(bounds, item, mode) {
var h = textMetrics.height(item),
- a = item.align,
- p = anchorPoint(item),
- x = p.x1,
- y = p.y1,
- dx = item.dx || 0,
- dy = (item.dy || 0) + offset$2(item) - Math.round(0.8 * h),
- // use 4/5 offset
- tl = textLines(item),
- w; // get dimensions
+ a = item.align,
+ p = anchorPoint(item),
+ x = p.x1,
+ y = p.y1,
+ dx = item.dx || 0,
+ dy = (item.dy || 0) + offset$2(item) - Math.round(0.8 * h),
+ // use 4/5 offset
+ tl = textLines(item),
+ w;
+ // get dimensions
if (isArray(tl)) {
// multi-line text
h += lineHeight(item) * (tl.length - 1);
w = tl.reduce((w, t) => Math.max(w, textMetrics.width(item, t)), 0);
} else {
// single-line text
w = textMetrics.width(item, tl);
- } // horizontal alignment
+ }
-
+ // horizontal alignment
if (a === 'center') {
dx -= w / 2;
} else if (a === 'right') {
dx -= w;
} else ;
-
bounds.set(dx += x, dy += y, dx + w, dy + h);
-
if (item.angle && !mode) {
bounds.rotate(item.angle * DegToRad, x, y);
} else if (mode === 2) {
return bounds.rotatedPoints(item.angle * DegToRad, x, y);
}
-
return bounds;
}
-
function draw$5(context, scene, bounds) {
visit(scene, item => {
var opacity = item.opacity == null ? 1 : item.opacity,
- p,
- x,
- y,
- i,
- lh,
- tl,
- str;
- if (bounds && !bounds.intersects(item.bounds) || // bounds check
+ p,
+ x,
+ y,
+ i,
+ lh,
+ tl,
+ str;
+ if (bounds && !bounds.intersects(item.bounds) ||
+ // bounds check
opacity === 0 || item.fontSize <= 0 || item.text == null || item.text.length === 0) return;
context.font = font(item);
context.textAlign = item.align || 'left';
p = anchorPoint(item);
x = p.x1, y = p.y1;
-
if (item.angle) {
context.save();
context.translate(x, y);
context.rotate(item.angle * DegToRad);
x = y = 0; // reset x, y
}
-
x += item.dx || 0;
y += (item.dy || 0) + offset$2(item);
tl = textLines(item);
blend(context, item);
-
if (isArray(tl)) {
lh = lineHeight(item);
-
for (i = 0; i < tl.length; ++i) {
str = textValue(item, tl[i]);
-
if (item.fill && fill(context, item, opacity)) {
context.fillText(str, x, y);
}
-
if (item.stroke && stroke(context, item, opacity)) {
context.strokeText(str, x, y);
}
-
y += lh;
}
} else {
str = textValue(item, tl);
-
if (item.fill && fill(context, item, opacity)) {
context.fillText(str, x, y);
}
-
if (item.stroke && stroke(context, item, opacity)) {
context.strokeText(str, x, y);
}
}
-
if (item.angle) context.restore();
});
}
-
function hit(context, item, x, y, gx, gy) {
if (item.fontSize <= 0) return false;
if (!item.angle) return true; // bounds sufficient if no rotation
- // project point into space of unrotated bounds
+ // project point into space of unrotated bounds
var p = anchorPoint(item),
- ax = p.x1,
- ay = p.y1,
- b = bound(tempBounds$1, item, 1),
- a = -item.angle * DegToRad,
- cos = Math.cos(a),
- sin = Math.sin(a),
- px = cos * gx - sin * gy + (ax - cos * ax + sin * ay),
- py = sin * gx + cos * gy + (ay - sin * ax - cos * ay);
+ ax = p.x1,
+ ay = p.y1,
+ b = bound(tempBounds$1, item, 1),
+ a = -item.angle * DegToRad,
+ cos = Math.cos(a),
+ sin = Math.sin(a),
+ px = cos * gx - sin * gy + (ax - cos * ax + sin * ay),
+ py = sin * gx + cos * gy + (ay - sin * ax - cos * ay);
return b.contains(px, py);
}
-
function intersectText(item, box) {
const p = bound(tempBounds$1, item, 2);
return intersectBoxLine(box, p[0], p[1], p[2], p[3]) || intersectBoxLine(box, p[0], p[1], p[4], p[5]) || intersectBoxLine(box, p[4], p[5], p[6], p[7]) || intersectBoxLine(box, p[2], p[3], p[6], p[7]);
}
-
var text = {
type: 'text',
tag: 'text',
nested: false,
attr: attr,
@@ -19262,132 +17253,128 @@
shape: shape,
symbol: symbol,
text: text,
trail: trail
};
-
function boundItem$1(item, func, opt) {
var type = Marks[item.mark.marktype],
- bound = func || type.bound;
+ bound = func || type.bound;
if (type.nested) item = item.mark;
return bound(item.bounds || (item.bounds = new Bounds()), item, opt);
}
-
var DUMMY = {
mark: null
};
-
function boundMark(mark, bounds, opt) {
var type = Marks[mark.marktype],
- bound = type.bound,
- items = mark.items,
- hasItems = items && items.length,
- i,
- n,
- item,
- b;
-
+ bound = type.bound,
+ items = mark.items,
+ hasItems = items && items.length,
+ i,
+ n,
+ item,
+ b;
if (type.nested) {
if (hasItems) {
item = items[0];
} else {
// no items, fake it
DUMMY.mark = mark;
item = DUMMY;
}
-
b = boundItem$1(item, bound, opt);
bounds = bounds && bounds.union(b) || b;
return bounds;
}
-
bounds = bounds || mark.bounds && mark.bounds.clear() || new Bounds();
-
if (hasItems) {
for (i = 0, n = items.length; i < n; ++i) {
bounds.union(boundItem$1(items[i], bound, opt));
}
}
-
return mark.bounds = bounds;
}
-
- const keys$1 = ['marktype', 'name', 'role', 'interactive', 'clip', 'items', 'zindex', 'x', 'y', 'width', 'height', 'align', 'baseline', // layout
- 'fill', 'fillOpacity', 'opacity', 'blend', // fill
- 'stroke', 'strokeOpacity', 'strokeWidth', 'strokeCap', // stroke
- 'strokeDash', 'strokeDashOffset', // stroke dash
- 'strokeForeground', 'strokeOffset', // group
- 'startAngle', 'endAngle', 'innerRadius', 'outerRadius', // arc
- 'cornerRadius', 'padAngle', // arc, rect
- 'cornerRadiusTopLeft', 'cornerRadiusTopRight', // rect, group
- 'cornerRadiusBottomLeft', 'cornerRadiusBottomRight', 'interpolate', 'tension', 'orient', 'defined', // area, line
- 'url', 'aspect', 'smooth', // image
- 'path', 'scaleX', 'scaleY', // path
- 'x2', 'y2', // rule
- 'size', 'shape', // symbol
- 'text', 'angle', 'theta', 'radius', 'dir', 'dx', 'dy', // text
- 'ellipsis', 'limit', 'lineBreak', 'lineHeight', 'font', 'fontSize', 'fontWeight', 'fontStyle', 'fontVariant', // font
+ const keys$1 = ['marktype', 'name', 'role', 'interactive', 'clip', 'items', 'zindex', 'x', 'y', 'width', 'height', 'align', 'baseline',
+ // layout
+ 'fill', 'fillOpacity', 'opacity', 'blend',
+ // fill
+ 'stroke', 'strokeOpacity', 'strokeWidth', 'strokeCap',
+ // stroke
+ 'strokeDash', 'strokeDashOffset',
+ // stroke dash
+ 'strokeForeground', 'strokeOffset',
+ // group
+ 'startAngle', 'endAngle', 'innerRadius', 'outerRadius',
+ // arc
+ 'cornerRadius', 'padAngle',
+ // arc, rect
+ 'cornerRadiusTopLeft', 'cornerRadiusTopRight',
+ // rect, group
+ 'cornerRadiusBottomLeft', 'cornerRadiusBottomRight', 'interpolate', 'tension', 'orient', 'defined',
+ // area, line
+ 'url', 'aspect', 'smooth',
+ // image
+ 'path', 'scaleX', 'scaleY',
+ // path
+ 'x2', 'y2',
+ // rule
+ 'size', 'shape',
+ // symbol
+ 'text', 'angle', 'theta', 'radius', 'dir', 'dx', 'dy',
+ // text
+ 'ellipsis', 'limit', 'lineBreak', 'lineHeight', 'font', 'fontSize', 'fontWeight', 'fontStyle', 'fontVariant',
+ // font
'description', 'aria', 'ariaRole', 'ariaRoleDescription' // aria
];
-
function sceneToJSON(scene, indent) {
return JSON.stringify(scene, keys$1, indent);
}
-
function sceneFromJSON(json) {
const scene = typeof json === 'string' ? JSON.parse(json) : json;
return initialize$1(scene);
}
-
function initialize$1(scene) {
var type = scene.marktype,
- items = scene.items,
- parent,
- i,
- n;
-
+ items = scene.items,
+ parent,
+ i,
+ n;
if (items) {
for (i = 0, n = items.length; i < n; ++i) {
parent = type ? 'mark' : 'group';
items[i][parent] = scene;
if (items[i].zindex) items[i][parent].zdirty = true;
if ('group' === (type || parent)) initialize$1(items[i]);
}
}
-
if (type) boundMark(scene);
return scene;
}
-
- function Scenegraph(scene) {
- if (arguments.length) {
- this.root = sceneFromJSON(scene);
- } else {
- this.root = createMark({
- marktype: 'group',
- name: 'root',
- role: 'frame'
- });
- this.root.items = [new GroupItem(this.root)];
+ class Scenegraph {
+ constructor(scene) {
+ if (arguments.length) {
+ this.root = sceneFromJSON(scene);
+ } else {
+ this.root = createMark({
+ marktype: 'group',
+ name: 'root',
+ role: 'frame'
+ });
+ this.root.items = [new GroupItem(this.root)];
+ }
}
- }
-
- Scenegraph.prototype = {
toJSON(indent) {
return sceneToJSON(this.root, indent || 0);
- },
-
+ }
mark(markdef, group, index) {
group = group || this.root.items[0];
const mark = createMark(markdef, group);
group.items[index] = mark;
if (mark.zindex) mark.group.zdirty = true;
return mark;
}
-
- };
-
+ }
function createMark(def, group) {
const mark = {
bounds: new Bounds(),
clip: !!def.clip,
group: group,
@@ -19395,124 +17382,103 @@
items: [],
marktype: def.marktype,
name: def.name || undefined,
role: def.role || undefined,
zindex: def.zindex || 0
- }; // add accessibility properties if defined
+ };
+ // add accessibility properties if defined
if (def.aria != null) {
mark.aria = def.aria;
}
-
if (def.description) {
mark.description = def.description;
}
-
return mark;
- } // create a new DOM element
+ }
-
+ // create a new DOM element
function domCreate(doc, tag, ns) {
if (!doc && typeof document !== 'undefined' && document.createElement) {
doc = document;
}
-
return doc ? ns ? doc.createElementNS(ns, tag) : doc.createElement(tag) : null;
- } // find first child element with matching tag
+ }
-
+ // find first child element with matching tag
function domFind(el, tag) {
tag = tag.toLowerCase();
var nodes = el.childNodes,
- i = 0,
- n = nodes.length;
-
+ i = 0,
+ n = nodes.length;
for (; i < n; ++i) if (nodes[i].tagName.toLowerCase() === tag) {
return nodes[i];
}
- } // retrieve child element at given index
- // create & insert if doesn't exist or if tags do not match
+ }
-
+ // retrieve child element at given index
+ // create & insert if doesn't exist or if tags do not match
function domChild(el, index, tag, ns) {
var a = el.childNodes[index],
- b;
-
+ b;
if (!a || a.tagName.toLowerCase() !== tag.toLowerCase()) {
b = a || null;
a = domCreate(el.ownerDocument, tag, ns);
el.insertBefore(a, b);
}
-
return a;
- } // remove all child elements at or above the given index
+ }
-
+ // remove all child elements at or above the given index
function domClear(el, index) {
var nodes = el.childNodes,
- curr = nodes.length;
-
+ curr = nodes.length;
while (curr > index) el.removeChild(nodes[--curr]);
-
return el;
- } // generate css class name for mark
+ }
-
+ // generate css class name for mark
function cssClass(mark) {
return 'mark-' + mark.marktype + (mark.role ? ' role-' + mark.role : '') + (mark.name ? ' ' + mark.name : '');
}
-
function point(event, el) {
const rect = el.getBoundingClientRect();
return [event.clientX - rect.left - (el.clientLeft || 0), event.clientY - rect.top - (el.clientTop || 0)];
}
-
function resolveItem(item, event, el, origin) {
var mark = item && item.mark,
- mdef,
- p;
-
+ mdef,
+ p;
if (mark && (mdef = Marks[mark.marktype]).tip) {
p = point(event, el);
p[0] -= origin[0];
p[1] -= origin[1];
-
while (item = item.mark.group) {
p[0] -= item.x || 0;
p[1] -= item.y || 0;
}
-
item = mdef.tip(mark.items, p);
}
-
return item;
}
- /**
- * Create a new Handler instance.
- * @param {object} [customLoader] - Optional loader instance for
- * href URL sanitization. If not specified, a standard loader
- * instance will be generated.
- * @param {function} [customTooltip] - Optional tooltip handler
- * function for custom tooltip display.
- * @constructor
- */
+ class Handler {
+ /**
+ * Create a new Handler instance.
+ * @param {object} [customLoader] - Optional loader instance for
+ * href URL sanitization. If not specified, a standard loader
+ * instance will be generated.
+ * @param {function} [customTooltip] - Optional tooltip handler
+ * function for custom tooltip display.
+ * @constructor
+ */
+ constructor(customLoader, customTooltip) {
+ this._active = null;
+ this._handlers = {};
+ this._loader = customLoader || loader();
+ this._tooltip = customTooltip || defaultTooltip$1;
+ }
-
- function Handler(customLoader, customTooltip) {
- this._active = null;
- this._handlers = {};
- this._loader = customLoader || loader();
- this._tooltip = customTooltip || defaultTooltip$1;
- } // The default tooltip display handler.
- // Sets the HTML title attribute on the visualization container.
-
-
- function defaultTooltip$1(handler, event, item, value) {
- handler.element().setAttribute('title', value || '');
- }
-
- Handler.prototype = {
/**
* Initialize a new Handler instance.
* @param {DOMElement} el - The containing DOM element for the display.
* @param {Array<number>} origin - The origin of the display, in pixels.
* The coordinate system will be translated to this point.
@@ -19522,28 +17488,28 @@
*/
initialize(el, origin, obj) {
this._el = el;
this._obj = obj || null;
return this.origin(origin);
- },
+ }
/**
* Returns the parent container element for a visualization.
* @return {DOMElement} - The containing DOM element.
*/
element() {
return this._el;
- },
+ }
/**
* Returns the scene element (e.g., canvas or SVG) of the visualization
* Subclasses must override if the first child is not the scene element.
* @return {DOMElement} - The scene (e.g., canvas or SVG) element.
*/
canvas() {
return this._el && this._el.firstChild;
- },
+ }
/**
* Get / set the origin coordinates of the visualization.
*/
origin(origin) {
@@ -19551,30 +17517,30 @@
this._origin = origin || [0, 0];
return this;
} else {
return this._origin.slice();
}
- },
+ }
/**
* Get / set the scenegraph root.
*/
scene(scene) {
if (!arguments.length) return this._scene;
this._scene = scene;
return this;
- },
+ }
/**
* Add an event handler. Subclasses should override this method.
*/
- on() {},
+ on( /*type, handler*/) {}
/**
* Remove an event handler. Subclasses should override this method.
*/
- off() {},
+ off( /*type, handler*/) {}
/**
* Utility method for finding the array index of an event handler.
* @param {Array} h - An array of registered event handlers.
* @param {string} type - The event type.
@@ -19585,13 +17551,12 @@
for (let i = h ? h.length : 0; --i >= 0;) {
if (h[i].type === type && (!handler || h[i].handler === handler)) {
return i;
}
}
-
return -1;
- },
+ }
/**
* Returns an array with registered event handlers.
* @param {string} [type] - The event type to query. Any annotations
* are ignored; for example, for the argument "click.foo", ".foo" will
@@ -19599,33 +17564,31 @@
* null or unspecified, this method returns handlers for all types.
* @return {Array} - A new array containing all registered event handlers.
*/
handlers(type) {
const h = this._handlers,
- a = [];
-
+ a = [];
if (type) {
a.push(...h[this.eventName(type)]);
} else {
for (const k in h) {
a.push(...h[k]);
}
}
-
return a;
- },
+ }
/**
* Parses an event name string to return the specific event type.
* For example, given "click.foo" returns "click"
* @param {string} name - The input event type string.
* @return {string} - A string with the event type only.
*/
eventName(name) {
const i = name.indexOf('.');
return i < 0 ? name : name.slice(0, i);
- },
+ }
/**
* Handle hyperlink navigation in response to an item.href value.
* @param {Event} event - The event triggering hyperlink navigation.
* @param {Item} item - The scenegraph item.
@@ -19634,19 +17597,15 @@
handleHref(event, item, href) {
this._loader.sanitize(href, {
context: 'href'
}).then(opt => {
const e = new MouseEvent(event.type, event),
- a = domCreate(null, 'a');
-
+ a = domCreate(null, 'a');
for (const name in opt) a.setAttribute(name, opt[name]);
-
a.dispatchEvent(e);
- }).catch(() => {
- /* do nothing */
- });
- },
+ }).catch(() => {});
+ }
/**
* Handle tooltip display in response to an item.tooltip value.
* @param {Event} event - The event triggering tooltip display.
* @param {Item} item - The scenegraph item.
@@ -19655,14 +17614,13 @@
*/
handleTooltip(event, item, show) {
if (item && item.tooltip != null) {
item = resolveItem(item, event, this.canvas(), this._origin);
const value = show && item && item.tooltip || null;
-
this._tooltip.call(this._obj, this, event, item, value);
}
- },
+ }
/**
* Returns the size of a scenegraph item and its position relative
* to the viewport.
* @param {Item} item - The scenegraph item.
@@ -19672,23 +17630,24 @@
*/
getItemBoundingClientRect(item) {
const el = this.canvas();
if (!el) return;
const rect = el.getBoundingClientRect(),
- origin = this._origin,
- bounds = item.bounds,
- width = bounds.width(),
- height = bounds.height();
+ origin = this._origin,
+ bounds = item.bounds,
+ width = bounds.width(),
+ height = bounds.height();
let x = bounds.x1 + origin[0] + rect.left,
- y = bounds.y1 + origin[1] + rect.top; // translate coordinate for each parent group
+ y = bounds.y1 + origin[1] + rect.top;
+ // translate coordinate for each parent group
while (item.mark && (item = item.mark.group)) {
x += item.x || 0;
y += item.y || 0;
- } // return DOMRect-compatible bounding box
+ }
-
+ // return DOMRect-compatible bounding box
return {
x,
y,
width,
height,
@@ -19696,27 +17655,31 @@
top: y,
right: x + width,
bottom: y + height
};
}
+ }
- };
- /**
- * Create a new Renderer instance.
- * @param {object} [loader] - Optional loader instance for
- * image and href URL sanitization. If not specified, a
- * standard loader instance will be generated.
- * @constructor
- */
-
- function Renderer(loader) {
- this._el = null;
- this._bgcolor = null;
- this._loader = new ResourceLoader(loader);
+ // The default tooltip display handler.
+ // Sets the HTML title attribute on the visualization container.
+ function defaultTooltip$1(handler, event, item, value) {
+ handler.element().setAttribute('title', value || '');
}
+ class Renderer {
+ /**
+ * Create a new Renderer instance.
+ * @param {object} [loader] - Optional loader instance for
+ * image and href URL sanitization. If not specified, a
+ * standard loader instance will be generated.
+ * @constructor
+ */
+ constructor(loader) {
+ this._el = null;
+ this._bgcolor = null;
+ this._loader = new ResourceLoader(loader);
+ }
- Renderer.prototype = {
/**
* Initialize a new Renderer instance.
* @param {DOMElement} el - The containing DOM element for the display.
* @param {number} width - The coordinate width of the display, in pixels.
* @param {number} height - The coordinate height of the display, in pixels.
@@ -19727,37 +17690,37 @@
* @return {Renderer} - This renderer instance.
*/
initialize(el, width, height, origin, scaleFactor) {
this._el = el;
return this.resize(width, height, origin, scaleFactor);
- },
+ }
/**
* Returns the parent container element for a visualization.
* @return {DOMElement} - The containing DOM element.
*/
element() {
return this._el;
- },
+ }
/**
* Returns the scene element (e.g., canvas or SVG) of the visualization
* Subclasses must override if the first child is not the scene element.
* @return {DOMElement} - The scene (e.g., canvas or SVG) element.
*/
canvas() {
return this._el && this._el.firstChild;
- },
+ }
/**
* Get / set the background color.
*/
background(bgcolor) {
if (arguments.length === 0) return this._bgcolor;
this._bgcolor = bgcolor;
return this;
- },
+ }
/**
* Resize the display.
* @param {number} width - The new coordinate width of the display, in pixels.
* @param {number} height - The new coordinate height of the display, in pixels.
@@ -19771,67 +17734,76 @@
this._width = width;
this._height = height;
this._origin = origin || [0, 0];
this._scale = scaleFactor || 1;
return this;
- },
+ }
/**
* Report a dirty item whose bounds should be redrawn.
* This base class method does nothing. Subclasses that perform
* incremental should implement this method.
* @param {Item} item - The dirty item whose bounds should be redrawn.
*/
- dirty() {},
+ dirty( /*item*/) {}
/**
* Render an input scenegraph, potentially with a set of dirty items.
* This method will perform an immediate rendering with available resources.
* The renderer may also need to perform image loading to perform a complete
* render. This process can lead to asynchronous re-rendering of the scene
* after this method returns. To receive notification when rendering is
* complete, use the renderAsync method instead.
* @param {object} scene - The root mark of a scenegraph to render.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
* @return {Renderer} - This renderer instance.
*/
- render(scene) {
- const r = this; // bind arguments into a render call, and cache it
- // this function may be subsequently called for async redraw
+ render(scene, markTypes) {
+ const r = this;
+ // bind arguments into a render call, and cache it
+ // this function may be subsequently called for async redraw
r._call = function () {
- r._render(scene);
- }; // invoke the renderer
+ r._render(scene, markTypes);
+ };
+ // invoke the renderer
+ r._call();
- r._call(); // clear the cached call for garbage collection
+ // clear the cached call for garbage collection
// async redraws will stash their own copy
-
-
r._call = null;
return r;
- },
+ }
/**
* Internal rendering method. Renderer subclasses should override this
* method to actually perform rendering.
* @param {object} scene - The root mark of a scenegraph to render.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
*/
- _render() {// subclasses to override
- },
+ _render( /*scene, markTypes*/
+ ) {
+ // subclasses to override
+ }
/**
* Asynchronous rendering method. Similar to render, but returns a Promise
* that resolves when all rendering is completed. Sometimes a renderer must
* perform image loading to get a complete rendering. The returned
* Promise will not resolve until this process completes.
* @param {object} scene - The root mark of a scenegraph to render.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
* @return {Promise} - A Promise that resolves when rendering is complete.
*/
- renderAsync(scene) {
- const r = this.render(scene);
+ renderAsync(scene, markTypes) {
+ const r = this.render(scene, markTypes);
return this._ready ? this._ready.then(() => r) : Promise.resolve(r);
- },
+ }
/**
* Internal method for asynchronous resource loading.
* Proxies method calls to the ImageLoader, and tracks loading
* progress to invoke a re-render once complete.
@@ -19839,34 +17811,32 @@
* @param {string} uri - The URI for the requested resource.
* @return {Promise} - A Promise that resolves to the requested resource.
*/
_load(method, uri) {
var r = this,
- p = r._loader[method](uri);
-
+ p = r._loader[method](uri);
if (!r._ready) {
// re-render the scene when loading completes
const call = r._call;
r._ready = r._loader.ready().then(redraw => {
if (redraw) call();
r._ready = null;
});
}
-
return p;
- },
+ }
/**
* Sanitize a URL to include as a hyperlink in the rendered scene.
* This method proxies a call to ImageLoader.sanitizeURL, but also tracks
* image loading progress and invokes a re-render once complete.
* @param {string} uri - The URI string to sanitize.
* @return {Promise} - A Promise that resolves to the sanitized URL.
*/
sanitizeURL(uri) {
return this._load('sanitizeURL', uri);
- },
+ }
/**
* Requests an image to include in the rendered scene.
* This method proxies a call to ImageLoader.loadImage, but also tracks
* image loading progress and invokes a re-render once complete.
@@ -19874,18 +17844,22 @@
* @return {Promise} - A Promise that resolves to the loaded Image.
*/
loadImage(uri) {
return this._load('loadImage', uri);
}
-
- };
+ }
const KeyDownEvent = 'keydown';
const KeyPressEvent = 'keypress';
const KeyUpEvent = 'keyup';
const DragEnterEvent = 'dragenter';
const DragLeaveEvent = 'dragleave';
const DragOverEvent = 'dragover';
+ const PointerDownEvent = 'pointerdown';
+ const PointerUpEvent = 'pointerup';
+ const PointerMoveEvent = 'pointermove';
+ const PointerOutEvent = 'pointerout';
+ const PointerOverEvent = 'pointerover';
const MouseDownEvent = 'mousedown';
const MouseUpEvent = 'mouseup';
const MouseMoveEvent = 'mousemove';
const MouseOutEvent = 'mouseout';
const MouseOverEvent = 'mouseover';
@@ -19894,457 +17868,406 @@
const WheelEvent = 'wheel';
const MouseWheelEvent = 'mousewheel';
const TouchStartEvent = 'touchstart';
const TouchMoveEvent = 'touchmove';
const TouchEndEvent = 'touchend';
- const Events = [KeyDownEvent, KeyPressEvent, KeyUpEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, MouseDownEvent, MouseUpEvent, MouseMoveEvent, MouseOutEvent, MouseOverEvent, ClickEvent, DoubleClickEvent, WheelEvent, MouseWheelEvent, TouchStartEvent, TouchMoveEvent, TouchEndEvent];
- const TooltipShowEvent = MouseMoveEvent;
+ const Events = [KeyDownEvent, KeyPressEvent, KeyUpEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, PointerDownEvent, PointerUpEvent, PointerMoveEvent, PointerOutEvent, PointerOverEvent, MouseDownEvent, MouseUpEvent, MouseMoveEvent, MouseOutEvent, MouseOverEvent, ClickEvent, DoubleClickEvent, WheelEvent, MouseWheelEvent, TouchStartEvent, TouchMoveEvent, TouchEndEvent];
+ const TooltipShowEvent = PointerMoveEvent;
const TooltipHideEvent = MouseOutEvent;
const HrefEvent = ClickEvent;
+ class CanvasHandler extends Handler {
+ constructor(loader, tooltip) {
+ super(loader, tooltip);
+ this._down = null;
+ this._touch = null;
+ this._first = true;
+ this._events = {};
- function CanvasHandler(loader, tooltip) {
- Handler.call(this, loader, tooltip);
- this._down = null;
- this._touch = null;
- this._first = true;
- this._events = {};
- }
-
- const eventBundle = type => type === TouchStartEvent || type === TouchMoveEvent || type === TouchEndEvent ? [TouchStartEvent, TouchMoveEvent, TouchEndEvent] : [type]; // lazily add listeners to the canvas as needed
-
-
- function eventListenerCheck(handler, type) {
- eventBundle(type).forEach(_ => addEventListener(handler, _));
- }
-
- function addEventListener(handler, type) {
- const canvas = handler.canvas();
-
- if (canvas && !handler._events[type]) {
- handler._events[type] = 1;
- canvas.addEventListener(type, handler[type] ? evt => handler[type](evt) : evt => handler.fire(type, evt));
+ // supported events
+ this.events = Events;
+ this.pointermove = move([PointerMoveEvent, MouseMoveEvent], [PointerOverEvent, MouseOverEvent], [PointerOutEvent, MouseOutEvent]);
+ this.dragover = move([DragOverEvent], [DragEnterEvent], [DragLeaveEvent]), this.pointerout = inactive([PointerOutEvent, MouseOutEvent]);
+ this.dragleave = inactive([DragLeaveEvent]);
}
- }
-
- function move(moveEvent, overEvent, outEvent) {
- return function (evt) {
- const a = this._active,
- p = this.pickEvent(evt);
-
- if (p === a) {
- // active item and picked item are the same
- this.fire(moveEvent, evt); // fire move
- } else {
- // active item and picked item are different
- if (!a || !a.exit) {
- // fire out for prior active item
- // suppress if active item was removed from scene
- this.fire(outEvent, evt);
- }
-
- this._active = p; // set new active item
-
- this.fire(overEvent, evt); // fire over for new active item
-
- this.fire(moveEvent, evt); // fire move for new active item
- }
- };
- }
-
- function inactive(type) {
- return function (evt) {
- this.fire(type, evt);
- this._active = null;
- };
- }
-
- inherits(CanvasHandler, Handler, {
initialize(el, origin, obj) {
- this._canvas = el && domFind(el, 'canvas'); // add minimal events required for proper state management
+ this._canvas = el && domFind(el, 'canvas');
- [ClickEvent, MouseDownEvent, MouseMoveEvent, MouseOutEvent, DragLeaveEvent].forEach(type => eventListenerCheck(this, type));
- return Handler.prototype.initialize.call(this, el, origin, obj);
- },
+ // add minimal events required for proper state management
+ [ClickEvent, MouseDownEvent, PointerDownEvent, PointerMoveEvent, PointerOutEvent, DragLeaveEvent].forEach(type => eventListenerCheck(this, type));
+ return super.initialize(el, origin, obj);
+ }
// return the backing canvas instance
canvas() {
return this._canvas;
- },
+ }
// retrieve the current canvas context
context() {
return this._canvas.getContext('2d');
- },
+ }
- // supported events
- events: Events,
-
// to keep old versions of firefox happy
DOMMouseScroll(evt) {
this.fire(MouseWheelEvent, evt);
- },
-
- mousemove: move(MouseMoveEvent, MouseOverEvent, MouseOutEvent),
- dragover: move(DragOverEvent, DragEnterEvent, DragLeaveEvent),
- mouseout: inactive(MouseOutEvent),
- dragleave: inactive(DragLeaveEvent),
-
+ }
+ pointerdown(evt) {
+ this._down = this._active;
+ this.fire(PointerDownEvent, evt);
+ }
mousedown(evt) {
this._down = this._active;
this.fire(MouseDownEvent, evt);
- },
-
+ }
click(evt) {
if (this._down === this._active) {
this.fire(ClickEvent, evt);
this._down = null;
}
- },
-
+ }
touchstart(evt) {
this._touch = this.pickEvent(evt.changedTouches[0]);
-
if (this._first) {
this._active = this._touch;
this._first = false;
}
-
this.fire(TouchStartEvent, evt, true);
- },
-
+ }
touchmove(evt) {
this.fire(TouchMoveEvent, evt, true);
- },
-
+ }
touchend(evt) {
this.fire(TouchEndEvent, evt, true);
this._touch = null;
- },
+ }
// fire an event
fire(type, evt, touch) {
const a = touch ? this._touch : this._active,
- h = this._handlers[type]; // set event type relative to scenegraph items
+ h = this._handlers[type];
- evt.vegaType = type; // handle hyperlinks and tooltips first
+ // set event type relative to scenegraph items
+ evt.vegaType = type;
+ // handle hyperlinks and tooltips first
if (type === HrefEvent && a && a.href) {
this.handleHref(evt, a, a.href);
} else if (type === TooltipShowEvent || type === TooltipHideEvent) {
this.handleTooltip(evt, a, type !== TooltipHideEvent);
- } // invoke all registered handlers
+ }
-
+ // invoke all registered handlers
if (h) {
for (let i = 0, len = h.length; i < len; ++i) {
h[i].handler.call(this._obj, evt, a);
}
}
- },
+ }
// add an event handler
on(type, handler) {
const name = this.eventName(type),
- h = this._handlers,
- i = this._handlerIndex(h[name], type, handler);
-
+ h = this._handlers,
+ i = this._handlerIndex(h[name], type, handler);
if (i < 0) {
eventListenerCheck(this, type);
(h[name] || (h[name] = [])).push({
type: type,
handler: handler
});
}
-
return this;
- },
+ }
// remove an event handler
off(type, handler) {
const name = this.eventName(type),
- h = this._handlers[name],
- i = this._handlerIndex(h, type, handler);
-
+ h = this._handlers[name],
+ i = this._handlerIndex(h, type, handler);
if (i >= 0) {
h.splice(i, 1);
}
-
return this;
- },
-
+ }
pickEvent(evt) {
const p = point(evt, this._canvas),
- o = this._origin;
+ o = this._origin;
return this.pick(this._scene, p[0], p[1], p[0] - o[0], p[1] - o[1]);
- },
+ }
- // find the scenegraph item at the current mouse position
- // x, y -- the absolute x, y mouse coordinates on the canvas element
+ // find the scenegraph item at the current pointer position
+ // x, y -- the absolute x, y pointer coordinates on the canvas element
// gx, gy -- the relative coordinates within the current group
pick(scene, x, y, gx, gy) {
const g = this.context(),
- mark = Marks[scene.marktype];
+ mark = Marks[scene.marktype];
return mark.pick.call(this, g, scene, x, y, gx, gy);
}
+ }
+ const eventBundle = type => type === TouchStartEvent || type === TouchMoveEvent || type === TouchEndEvent ? [TouchStartEvent, TouchMoveEvent, TouchEndEvent] : [type];
- });
-
+ // lazily add listeners to the canvas as needed
+ function eventListenerCheck(handler, type) {
+ eventBundle(type).forEach(_ => addEventListener(handler, _));
+ }
+ function addEventListener(handler, type) {
+ const canvas = handler.canvas();
+ if (canvas && !handler._events[type]) {
+ handler._events[type] = 1;
+ canvas.addEventListener(type, handler[type] ? evt => handler[type](evt) : evt => handler.fire(type, evt));
+ }
+ }
+ function fireAll(handler, types, event) {
+ types.forEach(type => handler.fire(type, event));
+ }
+ function move(moveEvents, overEvents, outEvents) {
+ return function (evt) {
+ const a = this._active,
+ p = this.pickEvent(evt);
+ if (p === a) {
+ // active item and picked item are the same
+ fireAll(this, moveEvents, evt); // fire move
+ } else {
+ // active item and picked item are different
+ if (!a || !a.exit) {
+ // fire out for prior active item
+ // suppress if active item was removed from scene
+ fireAll(this, outEvents, evt);
+ }
+ this._active = p; // set new active item
+ fireAll(this, overEvents, evt); // fire over for new active item
+ fireAll(this, moveEvents, evt); // fire move for new active item
+ }
+ };
+ }
+ function inactive(types) {
+ return function (evt) {
+ fireAll(this, types, evt);
+ this._active = null;
+ };
+ }
function devicePixelRatio() {
return typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
}
-
- var pixelRatio = devicePixelRatio();
-
function resize(canvas, width, height, origin, scaleFactor, opt) {
const inDOM = typeof HTMLElement !== 'undefined' && canvas instanceof HTMLElement && canvas.parentNode != null,
- context = canvas.getContext('2d'),
- ratio = inDOM ? pixelRatio : scaleFactor;
+ context = canvas.getContext('2d'),
+ ratio = inDOM ? devicePixelRatio() : scaleFactor;
canvas.width = width * ratio;
canvas.height = height * ratio;
-
for (const key in opt) {
context[key] = opt[key];
}
-
if (inDOM && ratio !== 1) {
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
}
-
context.pixelRatio = ratio;
context.setTransform(ratio, 0, 0, ratio, ratio * origin[0], ratio * origin[1]);
return canvas;
}
-
- function CanvasRenderer(loader) {
- Renderer.call(this, loader);
- this._options = {};
- this._redraw = false;
- this._dirty = new Bounds();
- this._tempb = new Bounds();
- }
-
- const base$1 = Renderer.prototype;
-
- const viewBounds = (origin, width, height) => new Bounds().set(0, 0, width, height).translate(-origin[0], -origin[1]);
-
- function clipToBounds(g, b, origin) {
- // expand bounds by 1 pixel, then round to pixel boundaries
- b.expand(1).round(); // align to base pixel grid in case of non-integer scaling (#2425)
-
- if (g.pixelRatio % 1) {
- b.scale(g.pixelRatio).round().scale(1 / g.pixelRatio);
- } // to avoid artifacts translate if origin has fractional pixels
-
-
- b.translate(-(origin[0] % 1), -(origin[1] % 1)); // set clip path
-
- g.beginPath();
- g.rect(b.x1, b.y1, b.width(), b.height());
- g.clip();
- return b;
- }
-
- inherits(CanvasRenderer, Renderer, {
+ class CanvasRenderer extends Renderer {
+ constructor(loader) {
+ super(loader);
+ this._options = {};
+ this._redraw = false;
+ this._dirty = new Bounds();
+ this._tempb = new Bounds();
+ }
initialize(el, width, height, origin, scaleFactor, options) {
this._options = options || {};
this._canvas = this._options.externalContext ? null : domCanvas(1, 1, this._options.type); // instantiate a small canvas
if (el && this._canvas) {
domClear(el, 0).appendChild(this._canvas);
-
this._canvas.setAttribute('class', 'marks');
- } // this method will invoke resize to size the canvas appropriately
+ }
-
- return base$1.initialize.call(this, el, width, height, origin, scaleFactor);
- },
-
+ // this method will invoke resize to size the canvas appropriately
+ return super.initialize(el, width, height, origin, scaleFactor);
+ }
resize(width, height, origin, scaleFactor) {
- base$1.resize.call(this, width, height, origin, scaleFactor);
-
+ super.resize(width, height, origin, scaleFactor);
if (this._canvas) {
// configure canvas size and transform
resize(this._canvas, this._width, this._height, this._origin, this._scale, this._options.context);
} else {
// external context needs to be scaled and positioned to origin
const ctx = this._options.externalContext;
if (!ctx) error('CanvasRenderer is missing a valid canvas or context');
ctx.scale(this._scale, this._scale);
ctx.translate(this._origin[0], this._origin[1]);
}
-
this._redraw = true;
return this;
- },
-
+ }
canvas() {
return this._canvas;
- },
-
+ }
context() {
return this._options.externalContext || (this._canvas ? this._canvas.getContext('2d') : null);
- },
-
+ }
dirty(item) {
const b = this._tempb.clear().union(item.bounds);
-
let g = item.mark.group;
-
while (g) {
b.translate(g.x || 0, g.y || 0);
g = g.mark.group;
}
-
this._dirty.union(b);
- },
-
- _render(scene) {
+ }
+ _render(scene, markTypes) {
const g = this.context(),
- o = this._origin,
- w = this._width,
- h = this._height,
- db = this._dirty,
- vb = viewBounds(o, w, h); // setup
+ o = this._origin,
+ w = this._width,
+ h = this._height,
+ db = this._dirty,
+ vb = viewBounds(o, w, h);
+ // setup
g.save();
const b = this._redraw || db.empty() ? (this._redraw = false, vb.expand(1)) : clipToBounds(g, vb.intersect(db), o);
- this.clear(-o[0], -o[1], w, h); // render
+ this.clear(-o[0], -o[1], w, h);
- this.draw(g, scene, b); // takedown
+ // render
+ this.draw(g, scene, b, markTypes);
+ // takedown
g.restore();
db.clear();
return this;
- },
-
- draw(ctx, scene, bounds) {
+ }
+ draw(ctx, scene, bounds, markTypes) {
+ if (scene.marktype !== 'group' && markTypes != null && !markTypes.includes(scene.marktype)) {
+ return;
+ }
const mark = Marks[scene.marktype];
if (scene.clip) clip$2(ctx, scene);
- mark.draw.call(this, ctx, scene, bounds);
+ mark.draw.call(this, ctx, scene, bounds, markTypes);
if (scene.clip) ctx.restore();
- },
-
+ }
clear(x, y, w, h) {
const opt = this._options,
- g = this.context();
-
+ g = this.context();
if (opt.type !== 'pdf' && !opt.externalContext) {
// calling clear rect voids vector output in pdf mode
// and could remove external context content (#2615)
g.clearRect(x, y, w, h);
}
-
if (this._bgcolor != null) {
g.fillStyle = this._bgcolor;
g.fillRect(x, y, w, h);
}
}
+ }
+ const viewBounds = (origin, width, height) => new Bounds().set(0, 0, width, height).translate(-origin[0], -origin[1]);
+ function clipToBounds(g, b, origin) {
+ // expand bounds by 1 pixel, then round to pixel boundaries
+ b.expand(1).round();
- });
+ // align to base pixel grid in case of non-integer scaling (#2425)
+ if (g.pixelRatio % 1) {
+ b.scale(g.pixelRatio).round().scale(1 / g.pixelRatio);
+ }
- function SVGHandler(loader, tooltip) {
- Handler.call(this, loader, tooltip);
- const h = this;
- h._hrefHandler = listener(h, (evt, item) => {
- if (item && item.href) h.handleHref(evt, item, item.href);
- });
- h._tooltipHandler = listener(h, (evt, item) => {
- h.handleTooltip(evt, item, evt.type !== TooltipHideEvent);
- });
- } // wrap an event listener for the SVG DOM
+ // to avoid artifacts translate if origin has fractional pixels
+ b.translate(-(origin[0] % 1), -(origin[1] % 1));
-
- const listener = (context, handler) => evt => {
- let item = evt.target.__data__;
- item = Array.isArray(item) ? item[0] : item;
- evt.vegaType = evt.type;
- handler.call(context._obj, evt, item);
- };
-
- inherits(SVGHandler, Handler, {
+ // set clip path
+ g.beginPath();
+ g.rect(b.x1, b.y1, b.width(), b.height());
+ g.clip();
+ return b;
+ }
+ class SVGHandler extends Handler {
+ constructor(loader, tooltip) {
+ super(loader, tooltip);
+ const h = this;
+ h._hrefHandler = listener(h, (evt, item) => {
+ if (item && item.href) h.handleHref(evt, item, item.href);
+ });
+ h._tooltipHandler = listener(h, (evt, item) => {
+ h.handleTooltip(evt, item, evt.type !== TooltipHideEvent);
+ });
+ }
initialize(el, origin, obj) {
let svg = this._svg;
-
if (svg) {
svg.removeEventListener(HrefEvent, this._hrefHandler);
svg.removeEventListener(TooltipShowEvent, this._tooltipHandler);
svg.removeEventListener(TooltipHideEvent, this._tooltipHandler);
}
-
this._svg = svg = el && domFind(el, 'svg');
-
if (svg) {
svg.addEventListener(HrefEvent, this._hrefHandler);
svg.addEventListener(TooltipShowEvent, this._tooltipHandler);
svg.addEventListener(TooltipHideEvent, this._tooltipHandler);
}
-
- return Handler.prototype.initialize.call(this, el, origin, obj);
- },
-
+ return super.initialize(el, origin, obj);
+ }
canvas() {
return this._svg;
- },
+ }
// add an event handler
on(type, handler) {
const name = this.eventName(type),
- h = this._handlers,
- i = this._handlerIndex(h[name], type, handler);
-
+ h = this._handlers,
+ i = this._handlerIndex(h[name], type, handler);
if (i < 0) {
const x = {
type,
handler,
listener: listener(this, handler)
};
(h[name] || (h[name] = [])).push(x);
-
if (this._svg) {
this._svg.addEventListener(name, x.listener);
}
}
-
return this;
- },
+ }
// remove an event handler
off(type, handler) {
const name = this.eventName(type),
- h = this._handlers[name],
- i = this._handlerIndex(h, type, handler);
-
+ h = this._handlers[name],
+ i = this._handlerIndex(h, type, handler);
if (i >= 0) {
if (this._svg) {
this._svg.removeEventListener(name, h[i].listener);
}
-
h.splice(i, 1);
}
-
return this;
}
+ }
- });
+ // wrap an event listener for the SVG DOM
+ const listener = (context, handler) => evt => {
+ let item = evt.target.__data__;
+ item = Array.isArray(item) ? item[0] : item;
+ evt.vegaType = evt.type;
+ handler.call(context._obj, evt, item);
+ };
const ARIA_HIDDEN = 'aria-hidden';
const ARIA_LABEL = 'aria-label';
const ARIA_ROLE = 'role';
const ARIA_ROLEDESCRIPTION = 'aria-roledescription';
const GRAPHICS_OBJECT = 'graphics-object';
const GRAPHICS_SYMBOL = 'graphics-symbol';
-
const bundle = (role, roledesc, label) => ({
[ARIA_ROLE]: role,
[ARIA_ROLEDESCRIPTION]: roledesc,
[ARIA_LABEL]: label || undefined
- }); // these roles are covered by related roles
+ });
+
+ // these roles are covered by related roles
// we can ignore them, no need to generate attributes
+ const AriaIgnore = toSet(['axis-domain', 'axis-grid', 'axis-label', 'axis-tick', 'axis-title', 'legend-band', 'legend-entry', 'legend-gradient', 'legend-label', 'legend-title', 'legend-symbol', 'title']);
-
- const AriaIgnore = toSet(['axis-domain', 'axis-grid', 'axis-label', 'axis-tick', 'axis-title', 'legend-band', 'legend-entry', 'legend-gradient', 'legend-label', 'legend-title', 'legend-symbol', 'title']); // aria attribute generators for guide roles
-
+ // aria attribute generators for guide roles
const AriaGuides = {
'axis': {
desc: 'axis',
caption: axisCaption
},
@@ -20352,194 +18275,162 @@
desc: 'legend',
caption: legendCaption
},
'title-text': {
desc: 'title',
- caption: item => "Title text '".concat(titleCaption(item), "'")
+ caption: item => `Title text '${titleCaption(item)}'`
},
'title-subtitle': {
desc: 'subtitle',
- caption: item => "Subtitle text '".concat(titleCaption(item), "'")
+ caption: item => `Subtitle text '${titleCaption(item)}'`
}
- }; // aria properties generated for mark item encoding channels
+ };
+ // aria properties generated for mark item encoding channels
const AriaEncode = {
ariaRole: ARIA_ROLE,
ariaRoleDescription: ARIA_ROLEDESCRIPTION,
description: ARIA_LABEL
};
-
function ariaItemAttributes(emit, item) {
const hide = item.aria === false;
emit(ARIA_HIDDEN, hide || undefined);
-
if (hide || item.description == null) {
for (const prop in AriaEncode) {
emit(AriaEncode[prop], undefined);
}
} else {
const type = item.mark.marktype;
emit(ARIA_LABEL, item.description);
emit(ARIA_ROLE, item.ariaRole || (type === 'group' ? GRAPHICS_OBJECT : GRAPHICS_SYMBOL));
- emit(ARIA_ROLEDESCRIPTION, item.ariaRoleDescription || "".concat(type, " mark"));
+ emit(ARIA_ROLEDESCRIPTION, item.ariaRoleDescription || `${type} mark`);
}
}
-
function ariaMarkAttributes(mark) {
return mark.aria === false ? {
[ARIA_HIDDEN]: true
} : AriaIgnore[mark.role] ? null : AriaGuides[mark.role] ? ariaGuide(mark, AriaGuides[mark.role]) : ariaMark(mark);
}
-
function ariaMark(mark) {
const type = mark.marktype;
const recurse = type === 'group' || type === 'text' || mark.items.some(_ => _.description != null && _.aria !== false);
- return bundle(recurse ? GRAPHICS_OBJECT : GRAPHICS_SYMBOL, "".concat(type, " mark container"), mark.description);
+ return bundle(recurse ? GRAPHICS_OBJECT : GRAPHICS_SYMBOL, `${type} mark container`, mark.description);
}
-
function ariaGuide(mark, opt) {
try {
const item = mark.items[0],
- caption = opt.caption || (() => '');
-
+ caption = opt.caption || (() => '');
return bundle(opt.role || GRAPHICS_SYMBOL, opt.desc, item.description || caption(item));
} catch (err) {
return null;
}
}
-
function titleCaption(item) {
return array$5(item.text).join(' ');
}
-
function axisCaption(item) {
const datum = item.datum,
- orient = item.orient,
- title = datum.title ? extractTitle(item) : null,
- ctx = item.context,
- scale = ctx.scales[datum.scale].value,
- locale = ctx.dataflow.locale(),
- type = scale.type,
- xy = orient === 'left' || orient === 'right' ? 'Y' : 'X';
- return "".concat(xy, "-axis") + (title ? " titled '".concat(title, "'") : '') + " for a ".concat(isDiscrete(type) ? 'discrete' : type, " scale") + " with ".concat(domainCaption(locale, scale, item));
+ orient = item.orient,
+ title = datum.title ? extractTitle(item) : null,
+ ctx = item.context,
+ scale = ctx.scales[datum.scale].value,
+ locale = ctx.dataflow.locale(),
+ type = scale.type,
+ xy = orient === 'left' || orient === 'right' ? 'Y' : 'X';
+ return `${xy}-axis` + (title ? ` titled '${title}'` : '') + ` for a ${isDiscrete(type) ? 'discrete' : type} scale` + ` with ${domainCaption(locale, scale, item)}`;
}
-
function legendCaption(item) {
const datum = item.datum,
- title = datum.title ? extractTitle(item) : null,
- type = "".concat(datum.type || '', " legend").trim(),
- scales = datum.scales,
- props = Object.keys(scales),
- ctx = item.context,
- scale = ctx.scales[scales[props[0]]].value,
- locale = ctx.dataflow.locale();
- return capitalize(type) + (title ? " titled '".concat(title, "'") : '') + " for ".concat(channelCaption(props)) + " with ".concat(domainCaption(locale, scale, item));
+ title = datum.title ? extractTitle(item) : null,
+ type = `${datum.type || ''} legend`.trim(),
+ scales = datum.scales,
+ props = Object.keys(scales),
+ ctx = item.context,
+ scale = ctx.scales[scales[props[0]]].value,
+ locale = ctx.dataflow.locale();
+ return capitalize(type) + (title ? ` titled '${title}'` : '') + ` for ${channelCaption(props)}` + ` with ${domainCaption(locale, scale, item)}`;
}
-
function extractTitle(item) {
try {
return array$5(peek$1(item.items).items[0].text).join(' ');
} catch (err) {
return null;
}
}
-
function channelCaption(props) {
props = props.map(p => p + (p === 'fill' || p === 'stroke' ? ' color' : ''));
return props.length < 2 ? props[0] : props.slice(0, -1).join(', ') + ' and ' + peek$1(props);
}
-
function capitalize(s) {
return s.length ? s[0].toUpperCase() + s.slice(1) : s;
}
-
const innerText = val => (val + '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
-
const attrText = val => innerText(val).replace(/"/g, '"').replace(/\t/g, '	').replace(/\n/g, '
').replace(/\r/g, '
');
-
function markup() {
let buf = '',
- outer = '',
- inner = '';
-
+ outer = '',
+ inner = '';
const stack = [],
- clear = () => outer = inner = '',
- push = tag => {
- if (outer) {
- buf += "".concat(outer, ">").concat(inner);
- clear();
- }
-
- stack.push(tag);
- },
- attr = (name, value) => {
- if (value != null) outer += " ".concat(name, "=\"").concat(attrText(value), "\"");
- return m;
- },
- m = {
- open(tag) {
- push(tag);
- outer = '<' + tag;
-
- for (var _len = arguments.length, attrs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- attrs[_key - 1] = arguments[_key];
- }
-
- for (const set of attrs) {
- for (const key in set) attr(key, set[key]);
- }
-
- return m;
- },
-
- close() {
- const tag = stack.pop();
-
+ clear = () => outer = inner = '',
+ push = tag => {
if (outer) {
- buf += outer + (inner ? ">".concat(inner, "</").concat(tag, ">") : '/>');
- } else {
- buf += "</".concat(tag, ">");
+ buf += `${outer}>${inner}`;
+ clear();
}
-
- clear();
+ stack.push(tag);
+ },
+ attr = (name, value) => {
+ if (value != null) outer += ` ${name}="${attrText(value)}"`;
return m;
},
-
- attr,
- text: t => (inner += innerText(t), m),
- toString: () => buf
- };
-
+ m = {
+ open(tag) {
+ push(tag);
+ outer = '<' + tag;
+ for (var _len = arguments.length, attrs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ attrs[_key - 1] = arguments[_key];
+ }
+ for (const set of attrs) {
+ for (const key in set) attr(key, set[key]);
+ }
+ return m;
+ },
+ close() {
+ const tag = stack.pop();
+ if (outer) {
+ buf += outer + (inner ? `>${inner}</${tag}>` : '/>');
+ } else {
+ buf += `</${tag}>`;
+ }
+ clear();
+ return m;
+ },
+ attr,
+ text: t => (inner += innerText(t), m),
+ toString: () => buf
+ };
return m;
}
-
const serializeXML = node => _serialize(markup(), node) + '';
-
function _serialize(m, node) {
m.open(node.tagName);
-
if (node.hasAttributes()) {
const attrs = node.attributes,
- n = attrs.length;
-
+ n = attrs.length;
for (let i = 0; i < n; ++i) {
m.attr(attrs[i].name, attrs[i].value);
}
}
-
if (node.hasChildNodes()) {
const children = node.childNodes;
-
for (const child of children) {
child.nodeType === 3 // text node
? m.text(child.nodeValue) : _serialize(m, child);
}
}
-
return m.close();
}
-
const stylesAttr = {
fill: 'fill',
fillOpacity: 'fill-opacity',
stroke: 'stroke',
strokeOpacity: 'stroke-opacity',
@@ -20551,31 +18442,30 @@
strokeMiterLimit: 'stroke-miterlimit',
opacity: 'opacity'
};
const stylesCss = {
blend: 'mix-blend-mode'
- }; // ensure miter limit default is consistent with canvas (#2498)
+ };
+ // ensure miter limit default is consistent with canvas (#2498)
const rootAttributes = {
'fill': 'none',
'stroke-miterlimit': 10
};
const RootIndex = 0,
- xmlns = 'http://www.w3.org/2000/xmlns/',
- svgns = metadata.xmlns;
+ xmlns = 'http://www.w3.org/2000/xmlns/',
+ svgns = metadata.xmlns;
+ class SVGRenderer extends Renderer {
+ constructor(loader) {
+ super(loader);
+ this._dirtyID = 0;
+ this._dirty = [];
+ this._svg = null;
+ this._root = null;
+ this._defs = null;
+ }
- function SVGRenderer(loader) {
- Renderer.call(this, loader);
- this._dirtyID = 0;
- this._dirty = [];
- this._svg = null;
- this._root = null;
- this._defs = null;
- }
-
- const base = Renderer.prototype;
- inherits(SVGRenderer, Renderer, {
/**
* Initialize a new SVGRenderer instance.
* @param {DOMElement} el - The containing DOM element for the display.
* @param {number} width - The coordinate width of the display, in pixels.
* @param {number} height - The coordinate height of the display, in pixels.
@@ -20586,48 +18476,42 @@
* @return {SVGRenderer} - This renderer instance.
*/
initialize(el, width, height, origin, scaleFactor) {
// create the svg definitions cache
this._defs = {};
-
this._clearDefs();
-
if (el) {
this._svg = domChild(el, 0, 'svg', svgns);
-
this._svg.setAttributeNS(xmlns, 'xmlns', svgns);
-
this._svg.setAttributeNS(xmlns, 'xmlns:xlink', metadata['xmlns:xlink']);
-
this._svg.setAttribute('version', metadata['version']);
-
this._svg.setAttribute('class', 'marks');
+ domClear(el, 1);
- domClear(el, 1); // set the svg root group
-
+ // set the svg root group
this._root = domChild(this._svg, RootIndex, 'g', svgns);
- setAttributes(this._root, rootAttributes); // ensure no additional child elements
+ setAttributes(this._root, rootAttributes);
+ // ensure no additional child elements
domClear(this._svg, RootIndex + 1);
- } // set background color if defined
+ }
-
+ // set background color if defined
this.background(this._bgcolor);
- return base.initialize.call(this, el, width, height, origin, scaleFactor);
- },
+ return super.initialize(el, width, height, origin, scaleFactor);
+ }
/**
* Get / set the background color.
*/
background(bgcolor) {
if (arguments.length && this._svg) {
this._svg.style.setProperty('background-color', bgcolor);
}
+ return super.background(...arguments);
+ }
- return base.background.apply(this, arguments);
- },
-
/**
* Resize the display.
* @param {number} width - The new coordinate width of the display, in pixels.
* @param {number} height - The new coordinate height of the display, in pixels.
* @param {Array<number>} origin - The new origin of the display, in pixels.
@@ -20635,104 +18519,96 @@
* @param {number} [scaleFactor=1] - Optional scaleFactor by which to multiply
* the width and height to determine the final pixel size.
* @return {SVGRenderer} - This renderer instance;
*/
resize(width, height, origin, scaleFactor) {
- base.resize.call(this, width, height, origin, scaleFactor);
-
+ super.resize(width, height, origin, scaleFactor);
if (this._svg) {
setAttributes(this._svg, {
width: this._width * this._scale,
height: this._height * this._scale,
- viewBox: "0 0 ".concat(this._width, " ").concat(this._height)
+ viewBox: `0 0 ${this._width} ${this._height}`
});
-
- this._root.setAttribute('transform', "translate(".concat(this._origin, ")"));
+ this._root.setAttribute('transform', `translate(${this._origin})`);
}
-
this._dirty = [];
return this;
- },
+ }
/**
* Returns the SVG element of the visualization.
* @return {DOMElement} - The SVG element.
*/
canvas() {
return this._svg;
- },
+ }
/**
* Returns an SVG text string for the rendered content,
* or null if this renderer is currently headless.
*/
svg() {
const svg = this._svg,
- bg = this._bgcolor;
+ bg = this._bgcolor;
if (!svg) return null;
let node;
-
if (bg) {
svg.removeAttribute('style');
node = domChild(svg, RootIndex, 'rect', svgns);
setAttributes(node, {
width: this._width,
height: this._height,
fill: bg
});
}
-
const text = serializeXML(svg);
-
if (bg) {
svg.removeChild(node);
-
this._svg.style.setProperty('background-color', bg);
}
-
return text;
- },
+ }
/**
* Internal rendering method.
* @param {object} scene - The root mark of a scenegraph to render.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
*/
- _render(scene) {
+ _render(scene, markTypes) {
// perform spot updates and re-render markup
if (this._dirtyCheck()) {
if (this._dirtyAll) this._clearDefs();
- this.mark(this._root, scene);
+ this.mark(this._root, scene, undefined, markTypes);
domClear(this._root, 1);
}
-
this.defs();
this._dirty = [];
++this._dirtyID;
return this;
- },
+ }
// -- Manage rendering of items marked as dirty --
/**
* Flag a mark item as dirty.
* @param {Item} item - The mark item.
*/
dirty(item) {
if (item.dirty !== this._dirtyID) {
item.dirty = this._dirtyID;
-
this._dirty.push(item);
}
- },
+ }
/**
* Check if a mark item is considered dirty.
* @param {Item} item - The mark item.
*/
isDirty(item) {
return this._dirtyAll || !item._svg || !item._svg.ownerSVGElement || item.dirty === this._dirtyID;
- },
+ }
/**
* Internal method to check dirty status and, if possible,
* make targetted updates without a full rendering pass.
*/
@@ -20740,29 +18616,25 @@
this._dirtyAll = true;
const items = this._dirty;
if (!items.length || !this._dirtyID) return true;
const id = ++this._dirtyID;
let item, mark, type, mdef, i, n, o;
-
for (i = 0, n = items.length; i < n; ++i) {
item = items[i];
mark = item.mark;
-
if (mark.marktype !== type) {
// memoize mark instance lookup
type = mark.marktype;
mdef = Marks[type];
}
-
if (mark.zdirty && mark.dirty !== id) {
this._dirtyAll = false;
dirtyParents(item, id);
mark.items.forEach(i => {
i.dirty = id;
});
}
-
if (mark.zdirty) continue; // handle in standard drawing pass
if (item.exit) {
// EXIT
if (mdef.nested && mark.items.length) {
@@ -20772,15 +18644,13 @@
} else if (item._svg) {
// otherwise remove from DOM
o = item._svg.parentNode;
if (o) o.removeChild(item._svg);
}
-
item._svg = null;
continue;
}
-
item = mdef.nested ? mark.items[0] : item;
if (item._update === id) continue; // already visited
if (!item._svg || !item._svg.ownerSVGElement) {
// ENTER
@@ -20788,72 +18658,68 @@
dirtyParents(item, id);
} else {
// IN-PLACE UPDATE
this._update(mdef, item._svg, item);
}
-
item._update = id;
}
-
return !this._dirtyAll;
- },
+ }
// -- Construct & maintain scenegraph to SVG mapping ---
/**
* Render a set of mark items.
* @param {SVGElement} el - The parent element in the SVG tree.
* @param {object} scene - The mark parent to render.
* @param {SVGElement} prev - The previous sibling in the SVG tree.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
*/
- mark(el, scene, prev) {
+ mark(el, scene, prev, markTypes) {
if (!this.isDirty(scene)) {
return scene._svg;
}
-
const svg = this._svg,
- mdef = Marks[scene.marktype],
- events = scene.interactive === false ? 'none' : null,
- isGroup = mdef.tag === 'g';
+ markType = scene.marktype,
+ mdef = Marks[markType],
+ events = scene.interactive === false ? 'none' : null,
+ isGroup = mdef.tag === 'g';
const parent = bind$1(scene, el, prev, 'g', svg);
- parent.setAttribute('class', cssClass(scene)); // apply aria attributes to parent container element
+ if (markType !== 'group' && markTypes != null && !markTypes.includes(markType)) {
+ domClear(parent, 0);
+ return scene._svg;
+ }
+ parent.setAttribute('class', cssClass(scene));
+ // apply aria attributes to parent container element
const aria = ariaMarkAttributes(scene);
-
for (const key in aria) setAttribute(parent, key, aria[key]);
-
if (!isGroup) {
setAttribute(parent, 'pointer-events', events);
}
-
setAttribute(parent, 'clip-path', scene.clip ? clip$1$1(this, scene, scene.group) : null);
let sibling = null,
- i = 0;
-
+ i = 0;
const process = item => {
const dirty = this.isDirty(item),
- node = bind$1(item, parent, sibling, mdef.tag, svg);
-
+ node = bind$1(item, parent, sibling, mdef.tag, svg);
if (dirty) {
this._update(mdef, node, item);
-
- if (isGroup) recurse(this, node, item);
+ if (isGroup) recurse(this, node, item, markTypes);
}
-
sibling = node;
++i;
};
-
if (mdef.nested) {
if (scene.items.length) process(scene.items[0]);
} else {
visit(scene, process);
}
-
domClear(parent, i);
return parent;
- },
+ }
/**
* Update the attributes of an SVG element for a mark item.
* @param {object} mdef - The mark definition object
* @param {SVGElement} el - The SVG element.
@@ -20861,106 +18727,101 @@
*/
_update(mdef, el, item) {
// set dom element and values cache
// provides access to emit method
element$1 = el;
- values = el.__values__; // apply aria-specific properties
+ values = el.__values__;
- ariaItemAttributes(emit, item); // apply svg attributes
+ // apply aria-specific properties
+ ariaItemAttributes(emit, item);
- mdef.attr(emit, item, this); // some marks need special treatment
+ // apply svg attributes
+ mdef.attr(emit, item, this);
+ // some marks need special treatment
const extra = mark_extras[mdef.type];
- if (extra) extra.call(this, mdef, el, item); // apply svg style attributes
- // note: element state may have been modified by 'extra' method
+ if (extra) extra.call(this, mdef, el, item);
+ // apply svg style attributes
+ // note: element state may have been modified by 'extra' method
if (element$1) this.style(element$1, item);
- },
+ }
/**
* Update the presentation attributes of an SVG element for a mark item.
* @param {SVGElement} el - The SVG element.
* @param {Item} item - The mark item.
*/
style(el, item) {
if (item == null) return;
-
for (const prop in stylesAttr) {
let value = prop === 'font' ? fontFamily(item) : item[prop];
if (value === values[prop]) continue;
const name = stylesAttr[prop];
-
if (value == null) {
el.removeAttribute(name);
} else {
if (isGradient(value)) {
value = gradientRef(value, this._defs.gradient, href());
}
-
el.setAttribute(name, value + '');
}
-
values[prop] = value;
}
-
for (const prop in stylesCss) {
setStyle(el, stylesCss[prop], item[prop]);
}
- },
+ }
/**
* Render SVG defs, as needed.
* Must be called *after* marks have been processed to ensure the
* collected state is current and accurate.
*/
defs() {
const svg = this._svg,
- defs = this._defs;
+ defs = this._defs;
let el = defs.el,
- index = 0;
-
+ index = 0;
for (const id in defs.gradient) {
if (!el) defs.el = el = domChild(svg, RootIndex + 1, 'defs', svgns);
index = updateGradient(el, defs.gradient[id], index);
}
-
for (const id in defs.clipping) {
if (!el) defs.el = el = domChild(svg, RootIndex + 1, 'defs', svgns);
index = updateClipping(el, defs.clipping[id], index);
- } // clean-up
+ }
-
+ // clean-up
if (el) {
index === 0 ? (svg.removeChild(el), defs.el = null) : domClear(el, index);
}
- },
+ }
/**
* Clear defs caches.
*/
_clearDefs() {
const def = this._defs;
def.gradient = {};
def.clipping = {};
}
+ }
- }); // mark ancestor chain with a dirty id
-
+ // mark ancestor chain with a dirty id
function dirtyParents(item, id) {
for (; item && item.dirty !== id; item = item.mark.group) {
item.dirty = id;
-
if (item.mark && item.mark.dirty !== id) {
item.mark.dirty = id;
} else return;
}
- } // update gradient definitions
+ }
-
+ // update gradient definitions
function updateGradient(el, grad, index) {
let i, n, stop;
-
if (grad.gradient === 'radial') {
// SVG radial gradients automatically transform to normalized bbox
// coordinates, in a way that is cumbersome to replicate in canvas.
// We wrap the radial gradient in a pattern element, allowing us to
// maintain a circular gradient that matches what canvas provides.
@@ -20974,11 +18835,11 @@
});
pt = domChild(pt, 0, 'rect', svgns);
setAttributes(pt, {
width: 1,
height: 1,
- fill: "url(".concat(href(), "#").concat(grad.id, ")")
+ fill: `url(${href()}#${grad.id})`
});
el = domChild(el, index++, 'radialGradient', svgns);
setAttributes(el, {
id: grad.id,
fx: grad.x1,
@@ -20996,27 +18857,24 @@
x2: grad.x2,
y1: grad.y1,
y2: grad.y2
});
}
-
for (i = 0, n = grad.stops.length; i < n; ++i) {
stop = domChild(el, i, 'stop', svgns);
stop.setAttribute('offset', grad.stops[i].offset);
stop.setAttribute('stop-color', grad.stops[i].color);
}
-
domClear(el, i);
return index;
- } // update clipping path definitions
+ }
-
+ // update clipping path definitions
function updateClipping(el, clip, index) {
let mask;
el = domChild(el, index, 'clipPath', svgns);
el.setAttribute('id', clip.id);
-
if (clip.path) {
mask = domChild(el, 0, 'path', svgns);
mask.setAttribute('d', clip.path);
} else {
mask = domChild(el, 0, 'rect', svgns);
@@ -21025,47 +18883,48 @@
y: 0,
width: clip.width,
height: clip.height
});
}
-
domClear(el, 1);
return index + 1;
- } // Recursively process group contents.
+ }
-
- function recurse(renderer, el, group) {
+ // Recursively process group contents.
+ function recurse(renderer, el, group, markTypes) {
// child 'g' element is second to last among children (path, g, path)
// other children here are foreground and background path elements
el = el.lastChild.previousSibling;
let prev,
- idx = 0;
+ idx = 0;
visit(group, item => {
- prev = renderer.mark(el, item, prev);
+ prev = renderer.mark(el, item, prev, markTypes);
++idx;
- }); // remove any extraneous DOM elements
+ });
+ // remove any extraneous DOM elements
domClear(el, 1 + idx);
- } // Bind a scenegraph item to an SVG DOM element.
- // Create new SVG elements as needed.
+ }
-
+ // Bind a scenegraph item to an SVG DOM element.
+ // Create new SVG elements as needed.
function bind$1(item, el, sibling, tag, svg) {
let node = item._svg,
- doc; // create a new dom node if needed
+ doc;
+ // create a new dom node if needed
if (!node) {
doc = el.ownerDocument;
node = domCreate(doc, tag, svgns);
item._svg = node;
-
if (item.mark) {
node.__data__ = item;
node.__values__ = {
fill: 'default'
- }; // if group, create background, content, and foreground elements
+ };
+ // if group, create background, content, and foreground elements
if (tag === 'g') {
const bg = domCreate(doc, 'path', svgns);
node.appendChild(bg);
bg.__data__ = item;
const cg = domCreate(doc, 'g', svgns);
@@ -21077,82 +18936,79 @@
fg.__values__ = {
fill: 'default'
};
}
}
- } // (re-)insert if (a) not contained in SVG or (b) sibling order has changed
+ }
-
+ // (re-)insert if (a) not contained in SVG or (b) sibling order has changed
if (node.ownerSVGElement !== svg || siblingCheck(node, sibling)) {
el.insertBefore(node, sibling ? sibling.nextSibling : el.firstChild);
}
-
return node;
- } // check if two nodes are ordered siblings
+ }
-
+ // check if two nodes are ordered siblings
function siblingCheck(node, sibling) {
return node.parentNode && node.parentNode.childNodes.length > 1 && node.previousSibling != sibling; // treat null/undefined the same
- } // -- Set attributes & styles on SVG elements ---
+ }
+ // -- Set attributes & styles on SVG elements ---
let element$1 = null,
- // temp var for current SVG element
- values = null; // temp var for current values hash
- // Extra configuration for certain mark types
+ // temp var for current SVG element
+ values = null; // temp var for current values hash
+ // Extra configuration for certain mark types
const mark_extras = {
group(mdef, el, item) {
const fg = element$1 = el.childNodes[2];
values = fg.__values__;
mdef.foreground(emit, item, this);
values = el.__values__; // use parent's values hash
-
element$1 = el.childNodes[1];
mdef.content(emit, item, this);
const bg = element$1 = el.childNodes[0];
mdef.background(emit, item, this);
const value = item.mark.interactive === false ? 'none' : null;
-
if (value !== values.events) {
setAttribute(fg, 'pointer-events', value);
setAttribute(bg, 'pointer-events', value);
values.events = value;
}
-
if (item.strokeForeground && item.stroke) {
const fill = item.fill;
- setAttribute(fg, 'display', null); // set style of background
+ setAttribute(fg, 'display', null);
+ // set style of background
this.style(bg, item);
- setAttribute(bg, 'stroke', null); // set style of foreground
+ setAttribute(bg, 'stroke', null);
+ // set style of foreground
if (fill) item.fill = null;
values = fg.__values__;
this.style(fg, item);
- if (fill) item.fill = fill; // leave element null to prevent downstream styling
+ if (fill) item.fill = fill;
+ // leave element null to prevent downstream styling
element$1 = null;
} else {
// ensure foreground is ignored
setAttribute(fg, 'display', 'none');
}
},
-
image(mdef, el, item) {
if (item.smooth === false) {
setStyle(el, 'image-rendering', 'optimizeSpeed');
setStyle(el, 'image-rendering', 'pixelated');
} else {
setStyle(el, 'image-rendering', null);
}
},
-
text(mdef, el, item) {
const tl = textLines(item);
let key, value, doc, lh;
-
if (isArray(tl)) {
// multi-line text
value = tl.map(_ => textValue(item, _));
key = value.join('\n'); // content cache key
@@ -21161,241 +19017,223 @@
doc = el.ownerDocument;
lh = lineHeight(item);
value.forEach((t, i) => {
const ts = domCreate(doc, 'tspan', svgns);
ts.__data__ = item; // data binding
-
ts.textContent = t;
-
if (i) {
ts.setAttribute('x', 0);
ts.setAttribute('dy', lh);
}
-
el.appendChild(ts);
});
values.text = key;
}
} else {
// single-line text
value = textValue(item, tl);
-
if (value !== values.text) {
el.textContent = value;
values.text = value;
}
}
-
setAttribute(el, 'font-family', fontFamily(item));
setAttribute(el, 'font-size', fontSize(item) + 'px');
setAttribute(el, 'font-style', item.fontStyle);
setAttribute(el, 'font-variant', item.fontVariant);
setAttribute(el, 'font-weight', item.fontWeight);
}
-
};
-
function emit(name, value, ns) {
// early exit if value is unchanged
- if (value === values[name]) return; // use appropriate method given namespace (ns)
+ if (value === values[name]) return;
+ // use appropriate method given namespace (ns)
if (ns) {
setAttributeNS(element$1, name, value, ns);
} else {
setAttribute(element$1, name, value);
- } // note current value for future comparison
+ }
-
+ // note current value for future comparison
values[name] = value;
}
-
function setStyle(el, name, value) {
if (value !== values[name]) {
if (value == null) {
el.style.removeProperty(name);
} else {
el.style.setProperty(name, value + '');
}
-
values[name] = value;
}
}
-
function setAttributes(el, attrs) {
for (const key in attrs) {
setAttribute(el, key, attrs[key]);
}
}
-
function setAttribute(el, name, value) {
if (value != null) {
// if value is provided, update DOM attribute
el.setAttribute(name, value);
} else {
// else remove DOM attribute
el.removeAttribute(name);
}
}
-
function setAttributeNS(el, name, value, ns) {
if (value != null) {
// if value is provided, update DOM attribute
el.setAttributeNS(ns, name, value);
} else {
// else remove DOM attribute
el.removeAttributeNS(ns, name);
}
}
-
function href() {
let loc;
return typeof window === 'undefined' ? '' : (loc = window.location).hash ? loc.href.slice(0, -loc.hash.length) : loc.href;
}
+ class SVGStringRenderer extends Renderer {
+ constructor(loader) {
+ super(loader);
+ this._text = null;
+ this._defs = {
+ gradient: {},
+ clipping: {}
+ };
+ }
- function SVGStringRenderer(loader) {
- Renderer.call(this, loader);
- this._text = null;
- this._defs = {
- gradient: {},
- clipping: {}
- };
- }
-
- inherits(SVGStringRenderer, Renderer, {
/**
* Returns the rendered SVG text string,
* or null if rendering has not yet occurred.
*/
svg() {
return this._text;
- },
+ }
/**
* Internal rendering method.
* @param {object} scene - The root mark of a scenegraph to render.
*/
_render(scene) {
- const m = markup(); // svg tag
+ const m = markup();
+ // svg tag
m.open('svg', extend$1({}, metadata, {
class: 'marks',
width: this._width * this._scale,
height: this._height * this._scale,
- viewBox: "0 0 ".concat(this._width, " ").concat(this._height)
- })); // background, if defined
+ viewBox: `0 0 ${this._width} ${this._height}`
+ }));
+ // background, if defined
const bg = this._bgcolor;
-
if (bg && bg !== 'transparent' && bg !== 'none') {
m.open('rect', {
width: this._width,
height: this._height,
fill: bg
}).close();
- } // root content group
+ }
-
+ // root content group
m.open('g', rootAttributes, {
transform: 'translate(' + this._origin + ')'
});
this.mark(m, scene);
m.close(); // </g>
+
// defs
+ this.defs(m);
- this.defs(m); // get SVG text string
-
+ // get SVG text string
this._text = m.close() + '';
return this;
- },
+ }
/**
* Render a set of mark items.
* @param {object} m - The markup context.
* @param {object} scene - The mark parent to render.
*/
mark(m, scene) {
const mdef = Marks[scene.marktype],
- tag = mdef.tag,
- attrList = [ariaItemAttributes, mdef.attr]; // render opening group tag
+ tag = mdef.tag,
+ attrList = [ariaItemAttributes, mdef.attr];
+ // render opening group tag
m.open('g', {
'class': cssClass(scene),
'clip-path': scene.clip ? clip$1$1(this, scene, scene.group) : null
}, ariaMarkAttributes(scene), {
'pointer-events': tag !== 'g' && scene.interactive === false ? 'none' : null
- }); // render contained elements
+ });
+ // render contained elements
const process = item => {
const href = this.href(item);
if (href) m.open('a', href);
m.open(tag, this.attr(scene, item, attrList, tag !== 'g' ? tag : null));
-
if (tag === 'text') {
const tl = textLines(item);
-
if (isArray(tl)) {
// multi-line text
const attrs = {
x: 0,
dy: lineHeight(item)
};
-
for (let i = 0; i < tl.length; ++i) {
m.open('tspan', i ? attrs : null).text(textValue(item, tl[i])).close();
}
} else {
// single-line text
m.text(textValue(item, tl));
}
} else if (tag === 'g') {
const fore = item.strokeForeground,
- fill = item.fill,
- stroke = item.stroke;
-
+ fill = item.fill,
+ stroke = item.stroke;
if (fore && stroke) {
item.stroke = null;
}
+ m.open('path', this.attr(scene, item, mdef.background, 'bgrect')).close();
- m.open('path', this.attr(scene, item, mdef.background, 'bgrect')).close(); // recurse for group content
-
+ // recurse for group content
m.open('g', this.attr(scene, item, mdef.content));
visit(item, scene => this.mark(m, scene));
m.close();
-
if (fore && stroke) {
if (fill) item.fill = null;
item.stroke = stroke;
m.open('path', this.attr(scene, item, mdef.foreground, 'bgrect')).close();
if (fill) item.fill = fill;
} else {
m.open('path', this.attr(scene, item, mdef.foreground, 'bgfore')).close();
}
}
-
m.close(); // </tag>
-
if (href) m.close(); // </a>
};
-
if (mdef.nested) {
if (scene.items && scene.items.length) process(scene.items[0]);
} else {
visit(scene, process);
- } // render closing group tag
+ }
-
+ // render closing group tag
return m.close(); // </g>
- },
+ }
/**
* Get href attributes for a hyperlinked mark item.
* @param {Item} item - The mark item.
*/
href(item) {
const href = item.href;
let attr;
-
if (href) {
if (attr = this._hrefs && this._hrefs[href]) {
return attr;
} else {
this.sanitizeURL(href).then(attr => {
@@ -21404,65 +19242,62 @@
attr.href = null;
(this._hrefs || (this._hrefs = {}))[href] = attr;
});
}
}
-
return null;
- },
+ }
/**
* Get an object of SVG attributes for a mark item.
* @param {object} scene - The mark parent.
* @param {Item} item - The mark item.
* @param {array|function} attrs - One or more attribute emitters.
* @param {string} tag - The tag being rendered.
*/
attr(scene, item, attrs, tag) {
const object = {},
- emit = (name, value, ns, prefixed) => {
- object[prefixed || name] = value;
- }; // apply mark specific attributes
+ emit = (name, value, ns, prefixed) => {
+ object[prefixed || name] = value;
+ };
-
+ // apply mark specific attributes
if (Array.isArray(attrs)) {
attrs.forEach(fn => fn(emit, item, this));
} else {
attrs(emit, item, this);
- } // apply style attributes
+ }
-
+ // apply style attributes
if (tag) {
style(object, item, scene, tag, this._defs);
}
-
return object;
- },
+ }
/**
* Render SVG defs, as needed.
* Must be called *after* marks have been processed to ensure the
* collected state is current and accurate.
* @param {object} m - The markup context.
*/
defs(m) {
const gradient = this._defs.gradient,
- clipping = this._defs.clipping,
- count = Object.keys(gradient).length + Object.keys(clipping).length;
+ clipping = this._defs.clipping,
+ count = Object.keys(gradient).length + Object.keys(clipping).length;
if (count === 0) return; // nothing to do
m.open('defs');
-
for (const id in gradient) {
const def = gradient[id],
- stops = def.stops;
-
+ stops = def.stops;
if (def.gradient === 'radial') {
// SVG radial gradients automatically transform to normalized bbox
// coordinates, in a way that is cumbersome to replicate in canvas.
// We wrap the radial gradient in a pattern element, allowing us to
// maintain a circular gradient that matches what canvas provides.
+
m.open('pattern', {
id: patternPrefix + id,
viewBox: '0,0,1,1',
width: '100%',
height: '100%',
@@ -21491,27 +19326,23 @@
x2: def.x2,
y1: def.y1,
y2: def.y2
});
}
-
for (let i = 0; i < stops.length; ++i) {
m.open('stop', {
offset: stops[i].offset,
'stop-color': stops[i].color
}).close();
}
-
m.close();
}
-
for (const id in clipping) {
const def = clipping[id];
m.open('clipPath', {
id: id
});
-
if (def.path) {
m.open('path', {
d: def.path
}).close();
} else {
@@ -21520,84 +19351,206 @@
y: 0,
width: def.width,
height: def.height
}).close();
}
-
m.close();
}
-
m.close();
}
+ }
- }); // Helper function for attr for style presentation attributes
-
+ // Helper function for attr for style presentation attributes
function style(s, item, scene, tag, defs) {
let styleList;
if (item == null) return s;
-
if (tag === 'bgrect' && scene.interactive === false) {
s['pointer-events'] = 'none';
}
-
if (tag === 'bgfore') {
if (scene.interactive === false) {
s['pointer-events'] = 'none';
}
-
s.display = 'none';
if (item.fill !== null) return s;
}
-
if (tag === 'image' && item.smooth === false) {
styleList = ['image-rendering: optimizeSpeed;', 'image-rendering: pixelated;'];
}
-
if (tag === 'text') {
s['font-family'] = fontFamily(item);
s['font-size'] = fontSize(item) + 'px';
s['font-style'] = item.fontStyle;
s['font-variant'] = item.fontVariant;
s['font-weight'] = item.fontWeight;
}
-
for (const prop in stylesAttr) {
let value = item[prop];
const name = stylesAttr[prop];
if (value === 'transparent' && (name === 'fill' || name === 'stroke')) ;else if (value != null) {
if (isGradient(value)) {
value = gradientRef(value, defs.gradient, '');
}
-
s[name] = value;
}
}
-
for (const prop in stylesCss) {
const value = item[prop];
-
if (value != null) {
styleList = styleList || [];
- styleList.push("".concat(stylesCss[prop], ": ").concat(value, ";"));
+ styleList.push(`${stylesCss[prop]}: ${value};`);
}
}
-
if (styleList) {
s.style = styleList.join(' ');
}
-
return s;
}
+ /**
+ * @typedef {Object} HybridRendererOptions
+ *
+ * @property {string[]} [svgMarkTypes=['text']] - An array of SVG mark types to render
+ * in the SVG layer. All other mark types
+ * will be rendered in the Canvas layer.
+ * @property {boolean} [svgOnTop=true] - Flag to determine if SVG should be rendered on top.
+ * @property {boolean} [debug=false] - Flag to enable or disable debugging mode. When true,
+ * the top layer will be stacked below the bottom layer
+ * rather than overlaid on top.
+ */
+
+ /** @type {HybridRendererOptions} */
+ const OPTS = {
+ svgMarkTypes: ['text'],
+ svgOnTop: true,
+ debug: false
+ };
+
+ /**
+ * Configure the HybridRenderer
+ *
+ * @param {HybridRendererOptions} options - HybridRenderer configuration options.
+ */
+ function setHybridRendererOptions(options) {
+ OPTS['svgMarkTypes'] = options.svgMarkTypes ?? ['text'];
+ OPTS['svgOnTop'] = options.svgOnTop ?? true;
+ OPTS['debug'] = options.debug ?? false;
+ }
+ class HybridRenderer extends Renderer {
+ constructor(loader) {
+ super(loader);
+ this._svgRenderer = new SVGRenderer(loader);
+ this._canvasRenderer = new CanvasRenderer(loader);
+ }
+
+ /**
+ * Initialize a new HybridRenderer instance.
+ * @param {DOMElement} el - The containing DOM element for the display.
+ * @param {number} width - The coordinate width of the display, in pixels.
+ * @param {number} height - The coordinate height of the display, in pixels.
+ * @param {Array<number>} origin - The origin of the display, in pixels.
+ * The coordinate system will be translated to this point.
+ * @param {number} [scaleFactor=1] - Optional scaleFactor by which to multiply
+ * the width and height to determine the final pixel size.
+ * @return {HybridRenderer} - This renderer instance.
+ */
+ initialize(el, width, height, origin, scaleFactor) {
+ this._root_el = domChild(el, 0, 'div');
+ const bottomEl = domChild(this._root_el, 0, 'div');
+ const topEl = domChild(this._root_el, 1, 'div');
+ this._root_el.style.position = 'relative';
+
+ // Set position absolute to overlay svg on top of canvas
+ if (!OPTS.debug) {
+ bottomEl.style.height = '100%';
+ topEl.style.position = 'absolute';
+ topEl.style.top = '0';
+ topEl.style.left = '0';
+ topEl.style.height = '100%';
+ topEl.style.width = '100%';
+ }
+ this._svgEl = OPTS.svgOnTop ? topEl : bottomEl;
+ this._canvasEl = OPTS.svgOnTop ? bottomEl : topEl;
+
+ // pointer-events to none on SVG layer so that canvas gets all events
+ this._svgEl.style.pointerEvents = 'none';
+ this._canvasRenderer.initialize(this._canvasEl, width, height, origin, scaleFactor);
+ this._svgRenderer.initialize(this._svgEl, width, height, origin, scaleFactor);
+ return super.initialize(el, width, height, origin, scaleFactor);
+ }
+
+ /**
+ * Flag a mark item as dirty.
+ * @param {Item} item - The mark item.
+ */
+ dirty(item) {
+ if (OPTS.svgMarkTypes.includes(item.mark.marktype)) {
+ this._svgRenderer.dirty(item);
+ } else {
+ this._canvasRenderer.dirty(item);
+ }
+ return this;
+ }
+
+ /**
+ * Internal rendering method.
+ * @param {object} scene - The root mark of a scenegraph to render.
+ * @param {Array} markTypes - Array of the mark types to render.
+ * If undefined, render all mark types
+ */
+ _render(scene, markTypes) {
+ const allMarkTypes = markTypes ?? ['arc', 'area', 'image', 'line', 'path', 'rect', 'rule', 'shape', 'symbol', 'text', 'trail'];
+ const canvasMarkTypes = allMarkTypes.filter(m => !OPTS.svgMarkTypes.includes(m));
+ this._svgRenderer.render(scene, OPTS.svgMarkTypes);
+ this._canvasRenderer.render(scene, canvasMarkTypes);
+ }
+
+ /**
+ * Resize the display.
+ * @param {number} width - The new coordinate width of the display, in pixels.
+ * @param {number} height - The new coordinate height of the display, in pixels.
+ * @param {Array<number>} origin - The new origin of the display, in pixels.
+ * The coordinate system will be translated to this point.
+ * @param {number} [scaleFactor=1] - Optional scaleFactor by which to multiply
+ * the width and height to determine the final pixel size.
+ * @return {SVGRenderer} - This renderer instance;
+ */
+ resize(width, height, origin, scaleFactor) {
+ super.resize(width, height, origin, scaleFactor);
+ this._svgRenderer.resize(width, height, origin, scaleFactor);
+ this._canvasRenderer.resize(width, height, origin, scaleFactor);
+ return this;
+ }
+ background(bgcolor) {
+ // Propagate background color to lower canvas renderer
+ if (OPTS.svgOnTop) {
+ this._canvasRenderer.background(bgcolor);
+ } else {
+ this._svgRenderer.background(bgcolor);
+ }
+ return this;
+ }
+ }
+ class HybridHandler extends CanvasHandler {
+ constructor(loader, tooltip) {
+ super(loader, tooltip);
+ }
+ initialize(el, origin, obj) {
+ const canvas = domChild(domChild(el, 0, 'div'), OPTS.svgOnTop ? 0 : 1, 'div');
+ return super.initialize(canvas, origin, obj);
+ }
+ }
const Canvas = 'canvas';
+ const Hybrid = 'hybrid';
const PNG = 'png';
const SVG = 'svg';
const None$1 = 'none';
const RenderType = {
Canvas: Canvas,
PNG: PNG,
SVG: SVG,
+ Hybrid: Hybrid,
None: None$1
};
const modules = {};
modules[Canvas] = modules[PNG] = {
renderer: CanvasRenderer,
@@ -21607,39 +19560,39 @@
modules[SVG] = {
renderer: SVGRenderer,
headless: SVGStringRenderer,
handler: SVGHandler
};
+ modules[Hybrid] = {
+ renderer: HybridRenderer,
+ headless: HybridRenderer,
+ handler: HybridHandler
+ };
modules[None$1] = {};
-
function renderModule(name, _) {
name = String(name || '').toLowerCase();
-
if (arguments.length > 1) {
modules[name] = _;
return this;
} else {
return modules[name];
}
}
-
function intersect$2(scene, bounds, filter) {
const hits = [],
- // intersection results
- box = new Bounds().union(bounds),
- // defensive copy
- type = scene.marktype;
+ // intersection results
+ box = new Bounds().union(bounds),
+ // defensive copy
+ type = scene.marktype;
return type ? intersectMark(scene, box, filter, hits) : type === 'group' ? intersectGroup(scene, box, filter, hits) : error('Intersect scene must be mark node or group item.');
}
-
function intersectMark(mark, box, filter, hits) {
if (visitMark(mark, box, filter)) {
const items = mark.items,
- type = mark.marktype,
- n = items.length;
+ type = mark.marktype,
+ n = items.length;
let i = 0;
-
if (type === 'group') {
for (; i < n; ++i) {
intersectGroup(items[i], box, filter, hits);
}
} else {
@@ -21647,99 +19600,79 @@
const item = items[i];
if (intersectItem(item, box, test)) hits.push(item);
}
}
}
-
return hits;
}
-
function visitMark(mark, box, filter) {
// process if bounds intersect and if
// (1) mark is a group mark (so we must recurse), or
// (2) mark is interactive and passes filter
return mark.bounds && box.intersects(mark.bounds) && (mark.marktype === 'group' || mark.interactive !== false && (!filter || filter(mark)));
}
-
function intersectGroup(group, box, filter, hits) {
// test intersect against group
// skip groups by default unless filter says otherwise
if (filter && filter(group.mark) && intersectItem(group, box, Marks.group.isect)) {
hits.push(group);
- } // recursively test children marks
- // translate box to group coordinate space
+ }
-
+ // recursively test children marks
+ // translate box to group coordinate space
const marks = group.items,
- n = marks && marks.length;
-
+ n = marks && marks.length;
if (n) {
const x = group.x || 0,
- y = group.y || 0;
+ y = group.y || 0;
box.translate(-x, -y);
-
for (let i = 0; i < n; ++i) {
intersectMark(marks[i], box, filter, hits);
}
-
box.translate(x, y);
}
-
return hits;
}
-
function intersectItem(item, box, test) {
// test bounds enclosure, bounds intersection, then detailed test
const bounds = item.bounds;
return box.encloses(bounds) || box.intersects(bounds) && test(item, box);
}
-
const clipBounds = new Bounds();
-
function boundClip(mark) {
const clip = mark.clip;
-
if (isFunction(clip)) {
clip(boundContext(clipBounds.clear()));
} else if (clip) {
clipBounds.set(0, 0, mark.group.width, mark.group.height);
} else return;
-
mark.bounds.intersect(clipBounds);
}
-
const TOLERANCE = 1e-9;
-
function sceneEqual(a, b, key) {
return a === b ? true : key === 'path' ? pathEqual(a, b) : a instanceof Date && b instanceof Date ? +a === +b : isNumber$1(a) && isNumber$1(b) ? Math.abs(a - b) <= TOLERANCE : !a || !b || !isObject(a) && !isObject(b) ? a == b : objectEqual(a, b);
}
-
function pathEqual(a, b) {
return sceneEqual(parse$3(a), parse$3(b));
}
-
function objectEqual(a, b) {
var ka = Object.keys(a),
- kb = Object.keys(b),
- key,
- i;
+ kb = Object.keys(b),
+ key,
+ i;
if (ka.length !== kb.length) return false;
ka.sort();
kb.sort();
-
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i]) return false;
}
-
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!sceneEqual(a[key], b[key], key)) return false;
}
-
return typeof a === typeof b;
}
-
function resetSVGDefIds() {
resetSVGClipId();
resetSVGGradientId();
}
@@ -21778,31 +19711,29 @@
const All = 'all';
const Each = 'each';
const Flush = 'flush';
const Column = 'column';
const Row = 'row';
+
/**
* Calculate bounding boxes for scenegraph items.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.mark - The scenegraph mark instance to bound.
*/
-
function Bound$1(params) {
Transform.call(this, null, params);
}
-
inherits(Bound$1, Transform, {
transform(_, pulse) {
const view = pulse.dataflow,
- mark = _.mark,
- type = mark.marktype,
- entry = Marks[type],
- bound = entry.bound;
+ mark = _.mark,
+ type = mark.marktype,
+ entry = Marks[type],
+ bound = entry.bound;
let markBounds = mark.bounds,
- rebound;
-
+ rebound;
if (entry.nested) {
// multi-item marks have a single bounds instance
if (mark.items.length) view.dirty(mark.items[0]);
markBounds = boundItem(mark, bound);
mark.items.forEach(item => {
@@ -21811,12 +19742,13 @@
} else if (type === Group || _.modified()) {
// operator parameters modified -> re-bound all items
// updates group bounds in response to modified group content
pulse.visit(pulse.MOD, item => view.dirty(item));
markBounds.clear();
- mark.items.forEach(item => markBounds.union(boundItem(item, bound))); // force reflow for axes/legends/titles to propagate any layout changes
+ mark.items.forEach(item => markBounds.union(boundItem(item, bound)));
+ // force reflow for axes/legends/titles to propagate any layout changes
switch (mark.role) {
case AxisRole$1:
case LegendRole$1:
case TitleRole$1:
pulse.reflow();
@@ -21830,29 +19762,26 @@
pulse.visit(pulse.MOD, item => {
rebound = rebound || markBounds.alignsWith(item.bounds);
view.dirty(item);
markBounds.union(boundItem(item, bound));
});
-
if (rebound) {
markBounds.clear();
mark.items.forEach(item => markBounds.union(item.bounds));
}
- } // ensure mark bounds do not exceed any clipping region
+ }
-
+ // ensure mark bounds do not exceed any clipping region
boundClip(mark);
return pulse.modifies('bounds');
}
-
});
-
function boundItem(item, bound, opt) {
return bound(item.bounds.clear(), item, opt);
}
-
const COUNTER_NAME = ':vega_identifier:';
+
/**
* Adds a unique identifier to all added tuples.
* This transform creates a new signal that serves as an id counter.
* As a result, the id counter is shared across all instances of this
* transform, generating unique ids across multiple data streams. In
@@ -21860,15 +19789,13 @@
* dataflow state, enabling correct resumption of id allocation.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {string} params.as - The field name for the generated identifier.
*/
-
function Identifier$1(params) {
Transform.call(this, 0, params);
}
-
Identifier$1.Definition = {
'type': 'Identifier',
'metadata': {
'modifies': true
},
@@ -21879,75 +19806,70 @@
}]
};
inherits(Identifier$1, Transform, {
transform(_, pulse) {
const counter = getCounter(pulse.dataflow),
- as = _.as;
+ as = _.as;
let id = counter.value;
pulse.visit(pulse.ADD, t => t[as] = t[as] || ++id);
counter.set(this.value = id);
return pulse;
}
-
});
-
function getCounter(view) {
return view._signals[COUNTER_NAME] || (view._signals[COUNTER_NAME] = view.add(0));
}
+
/**
* Bind scenegraph items to a scenegraph mark instance.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.markdef - The mark definition for creating the mark.
* This is an object of legal scenegraph mark properties which *must* include
* the 'marktype' property.
*/
-
-
function Mark$1(params) {
Transform.call(this, null, params);
}
-
inherits(Mark$1, Transform, {
transform(_, pulse) {
- let mark = this.value; // acquire mark on first invocation, bind context and group
+ let mark = this.value;
+ // acquire mark on first invocation, bind context and group
if (!mark) {
mark = pulse.dataflow.scenegraph().mark(_.markdef, lookup$1$1(_), _.index);
mark.group.context = _.context;
if (!_.context.group) _.context.group = mark.group;
mark.source = this.source; // point to upstream collector
-
mark.clip = _.clip;
mark.interactive = _.interactive;
this.value = mark;
- } // initialize entering items
+ }
-
+ // initialize entering items
const Init = mark.marktype === Group ? GroupItem : Item;
- pulse.visit(pulse.ADD, item => Init.call(item, mark)); // update clipping and/or interactive status
+ pulse.visit(pulse.ADD, item => Init.call(item, mark));
+ // update clipping and/or interactive status
if (_.modified('clip') || _.modified('interactive')) {
mark.clip = _.clip;
mark.interactive = !!_.interactive;
mark.zdirty = true; // force scenegraph re-eval
-
pulse.reflow();
- } // bind items array to scenegraph mark
+ }
-
+ // bind items array to scenegraph mark
mark.items = pulse.source;
return pulse;
}
-
});
-
function lookup$1$1(_) {
const g = _.groups,
- p = _.parent;
+ p = _.parent;
return g && g.size === 1 ? g.get(Object.keys(g.object)[0]) : g && p ? g.lookup(p) : null;
}
+
/**
* Analyze items for overlap, changing opacity to hide items with
* overlapping bounding boxes. This transform will preserve at least
* two items (e.g., first and last) even if overlap persists.
* @param {object} params - The parameters for this operator.
@@ -21967,370 +19889,328 @@
* @param {object} [params.boundTolerance] - The tolerance in pixels for
* bound inclusion testing (default 1). This specifies by how many pixels
* an item's bounds may exceed the scale range bounds and not be culled.
* @constructor
*/
-
-
function Overlap$1(params) {
Transform.call(this, null, params);
}
-
const methods = {
parity: items => items.filter((item, i) => i % 2 ? item.opacity = 0 : 1),
greedy: (items, sep) => {
let a;
return items.filter((b, i) => !i || !intersect$1(a.bounds, b.bounds, sep) ? (a = b, 1) : b.opacity = 0);
}
- }; // compute bounding box intersection
- // including padding pixels of separation
+ };
+ // compute bounding box intersection
+ // including padding pixels of separation
const intersect$1 = (a, b, sep) => sep > Math.max(b.x1 - a.x2, a.x1 - b.x2, b.y1 - a.y2, a.y1 - b.y2);
-
const hasOverlap = (items, pad) => {
for (var i = 1, n = items.length, a = items[0].bounds, b; i < n; a = b, ++i) {
if (intersect$1(a, b = items[i].bounds, pad)) return true;
}
};
-
const hasBounds = item => {
const b = item.bounds;
return b.width() > 1 && b.height() > 1;
};
-
const boundTest = (scale, orient, tolerance) => {
var range = scale.range(),
- b = new Bounds();
-
+ b = new Bounds();
if (orient === Top$1 || orient === Bottom$1) {
b.set(range[0], -Infinity, range[1], +Infinity);
} else {
b.set(-Infinity, range[0], +Infinity, range[1]);
}
-
b.expand(tolerance || 1);
return item => b.encloses(item.bounds);
- }; // reset all items to be fully opaque
+ };
-
+ // reset all items to be fully opaque
const reset = source => {
source.forEach(item => item.opacity = 1);
return source;
- }; // add all tuples to mod, fork pulse if parameters were modified
- // fork prevents cross-stream tuple pollution (e.g., pulse from scale)
+ };
-
+ // add all tuples to mod, fork pulse if parameters were modified
+ // fork prevents cross-stream tuple pollution (e.g., pulse from scale)
const reflow = (pulse, _) => pulse.reflow(_.modified()).modifies('opacity');
-
inherits(Overlap$1, Transform, {
transform(_, pulse) {
const reduce = methods[_.method] || methods.parity,
- sep = _.separation || 0;
+ sep = _.separation || 0;
let source = pulse.materialize(pulse.SOURCE).source,
- items,
- test;
+ items,
+ test;
if (!source || !source.length) return;
-
if (!_.method) {
// early exit if method is falsy
if (_.modified('method')) {
reset(source);
pulse = reflow(pulse, _);
}
-
return pulse;
- } // skip labels with no content
+ }
+ // skip labels with no content
+ source = source.filter(hasBounds);
- source = source.filter(hasBounds); // early exit, nothing to do
-
+ // early exit, nothing to do
if (!source.length) return;
-
if (_.sort) {
source = source.slice().sort(_.sort);
}
-
items = reset(source);
pulse = reflow(pulse, _);
-
if (items.length >= 3 && hasOverlap(items, sep)) {
do {
items = reduce(items, sep);
} while (items.length >= 3 && hasOverlap(items, sep));
-
if (items.length < 3 && !peek$1(source).opacity) {
if (items.length > 1) peek$1(items).opacity = 0;
peek$1(source).opacity = 1;
}
}
-
if (_.boundScale && _.boundTolerance >= 0) {
test = boundTest(_.boundScale, _.boundOrient, +_.boundTolerance);
source.forEach(item => {
if (!test(item)) item.opacity = 0;
});
- } // re-calculate mark bounds
+ }
-
+ // re-calculate mark bounds
const bounds = items[0].mark.bounds.clear();
source.forEach(item => {
if (item.opacity) bounds.union(item.bounds);
});
return pulse;
}
-
});
+
/**
* Queue modified scenegraph items for rendering.
* @constructor
*/
-
function Render$1(params) {
Transform.call(this, null, params);
}
-
inherits(Render$1, Transform, {
transform(_, pulse) {
const view = pulse.dataflow;
- pulse.visit(pulse.ALL, item => view.dirty(item)); // set z-index dirty flag as needed
+ pulse.visit(pulse.ALL, item => view.dirty(item));
+ // set z-index dirty flag as needed
if (pulse.fields && pulse.fields['zindex']) {
const item = pulse.source && pulse.source[0];
if (item) item.mark.zdirty = true;
}
}
-
});
const tempBounds = new Bounds();
-
function set$2(item, property, value) {
return item[property] === value ? 0 : (item[property] = value, 1);
}
-
function isYAxis(mark) {
var orient = mark.items[0].orient;
return orient === Left$1 || orient === Right$1;
}
-
function axisIndices(datum) {
let index = +datum.grid;
- return [datum.ticks ? index++ : -1, // ticks index
- datum.labels ? index++ : -1, // labels index
+ return [datum.ticks ? index++ : -1,
+ // ticks index
+ datum.labels ? index++ : -1,
+ // labels index
index + +datum.domain // title index
];
}
-
function axisLayout(view, axis, width, height) {
var item = axis.items[0],
- datum = item.datum,
- delta = item.translate != null ? item.translate : 0.5,
- orient = item.orient,
- indices = axisIndices(datum),
- range = item.range,
- offset = item.offset,
- position = item.position,
- minExtent = item.minExtent,
- maxExtent = item.maxExtent,
- title = datum.title && item.items[indices[2]].items[0],
- titlePadding = item.titlePadding,
- bounds = item.bounds,
- dl = title && multiLineOffset(title),
- x = 0,
- y = 0,
- i,
- s;
+ datum = item.datum,
+ delta = item.translate != null ? item.translate : 0.5,
+ orient = item.orient,
+ indices = axisIndices(datum),
+ range = item.range,
+ offset = item.offset,
+ position = item.position,
+ minExtent = item.minExtent,
+ maxExtent = item.maxExtent,
+ title = datum.title && item.items[indices[2]].items[0],
+ titlePadding = item.titlePadding,
+ bounds = item.bounds,
+ dl = title && multiLineOffset(title),
+ x = 0,
+ y = 0,
+ i,
+ s;
tempBounds.clear().union(bounds);
bounds.clear();
if ((i = indices[0]) > -1) bounds.union(item.items[i].bounds);
- if ((i = indices[1]) > -1) bounds.union(item.items[i].bounds); // position axis group and title
+ if ((i = indices[1]) > -1) bounds.union(item.items[i].bounds);
+ // position axis group and title
switch (orient) {
case Top$1:
x = position || 0;
y = -offset;
s = Math.max(minExtent, Math.min(maxExtent, -bounds.y1));
bounds.add(0, -s).add(range, 0);
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 0, -1, bounds);
break;
-
case Left$1:
x = -offset;
y = position || 0;
s = Math.max(minExtent, Math.min(maxExtent, -bounds.x1));
bounds.add(-s, 0).add(0, range);
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 1, -1, bounds);
break;
-
case Right$1:
x = width + offset;
y = position || 0;
s = Math.max(minExtent, Math.min(maxExtent, bounds.x2));
bounds.add(0, 0).add(s, range);
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 1, 1, bounds);
break;
-
case Bottom$1:
x = position || 0;
y = height + offset;
s = Math.max(minExtent, Math.min(maxExtent, bounds.y2));
bounds.add(0, 0).add(range, s);
if (title) axisTitleLayout(view, title, s, titlePadding, 0, 0, 1, bounds);
break;
-
default:
x = item.x;
y = item.y;
- } // update bounds
+ }
-
+ // update bounds
boundStroke(bounds.translate(x, y), item);
-
if (set$2(item, 'x', x + delta) | set$2(item, 'y', y + delta)) {
item.bounds = tempBounds;
view.dirty(item);
item.bounds = bounds;
view.dirty(item);
}
-
return item.mark.bounds.clear().union(bounds);
}
-
function axisTitleLayout(view, title, offset, pad, dl, isYAxis, sign, bounds) {
const b = title.bounds;
-
if (title.auto) {
const v = sign * (offset + dl + pad);
let dx = 0,
- dy = 0;
+ dy = 0;
view.dirty(title);
isYAxis ? dx = (title.x || 0) - (title.x = v) : dy = (title.y || 0) - (title.y = v);
title.mark.bounds.clear().union(b.translate(-dx, -dy));
view.dirty(title);
}
-
bounds.union(b);
}
+ // aggregation functions for grid margin determination
const min = (a, b) => Math.floor(Math.min(a, b));
-
const max = (a, b) => Math.ceil(Math.max(a, b));
-
function gridLayoutGroups(group) {
var groups = group.items,
- n = groups.length,
- i = 0,
- mark,
- items;
+ n = groups.length,
+ i = 0,
+ mark,
+ items;
const views = {
marks: [],
rowheaders: [],
rowfooters: [],
colheaders: [],
colfooters: [],
rowtitle: null,
coltitle: null
- }; // layout axes, gather legends, collect bounds
+ };
+ // layout axes, gather legends, collect bounds
for (; i < n; ++i) {
mark = groups[i];
items = mark.items;
-
if (mark.marktype === Group) {
switch (mark.role) {
case AxisRole$1:
case LegendRole$1:
case TitleRole$1:
break;
-
case RowHeader:
views.rowheaders.push(...items);
break;
-
case RowFooter:
views.rowfooters.push(...items);
break;
-
case ColHeader:
views.colheaders.push(...items);
break;
-
case ColFooter:
views.colfooters.push(...items);
break;
-
case RowTitle:
views.rowtitle = items[0];
break;
-
case ColTitle:
views.coltitle = items[0];
break;
-
default:
views.marks.push(...items);
}
}
}
-
return views;
}
-
function bboxFlush(item) {
return new Bounds().set(0, 0, item.width || 0, item.height || 0);
}
-
function bboxFull(item) {
const b = item.bounds.clone();
return b.empty() ? b.set(0, 0, 0, 0) : b.translate(-(item.x || 0), -(item.y || 0));
}
-
function get$1(opt, key, d) {
const v = isObject(opt) ? opt[key] : opt;
return v != null ? v : d !== undefined ? d : 0;
}
-
function offsetValue$1(v) {
return v < 0 ? Math.ceil(-v) : 0;
}
-
function gridLayout(view, groups, opt) {
var dirty = !opt.nodirty,
- bbox = opt.bounds === Flush ? bboxFlush : bboxFull,
- bounds = tempBounds.set(0, 0, 0, 0),
- alignCol = get$1(opt.align, Column),
- alignRow = get$1(opt.align, Row),
- padCol = get$1(opt.padding, Column),
- padRow = get$1(opt.padding, Row),
- ncols = opt.columns || groups.length,
- nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
- n = groups.length,
- xOffset = Array(n),
- xExtent = Array(ncols),
- xMax = 0,
- yOffset = Array(n),
- yExtent = Array(nrows),
- yMax = 0,
- dx = Array(n),
- dy = Array(n),
- boxes = Array(n),
- m,
- i,
- c,
- r,
- b,
- g,
- px,
- py,
- x,
- y,
- offset;
-
+ bbox = opt.bounds === Flush ? bboxFlush : bboxFull,
+ bounds = tempBounds.set(0, 0, 0, 0),
+ alignCol = get$1(opt.align, Column),
+ alignRow = get$1(opt.align, Row),
+ padCol = get$1(opt.padding, Column),
+ padRow = get$1(opt.padding, Row),
+ ncols = opt.columns || groups.length,
+ nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
+ n = groups.length,
+ xOffset = Array(n),
+ xExtent = Array(ncols),
+ xMax = 0,
+ yOffset = Array(n),
+ yExtent = Array(nrows),
+ yMax = 0,
+ dx = Array(n),
+ dy = Array(n),
+ boxes = Array(n),
+ m,
+ i,
+ c,
+ r,
+ b,
+ g,
+ px,
+ py,
+ x,
+ y,
+ offset;
for (i = 0; i < ncols; ++i) xExtent[i] = 0;
+ for (i = 0; i < nrows; ++i) yExtent[i] = 0;
- for (i = 0; i < nrows; ++i) yExtent[i] = 0; // determine offsets for each group
-
-
+ // determine offsets for each group
for (i = 0; i < n; ++i) {
g = groups[i];
b = boxes[i] = bbox(g);
g.x = g.x || 0;
dx[i] = 0;
@@ -22343,702 +20223,645 @@
xExtent[c] = Math.max(xExtent[c], px);
yExtent[r] = Math.max(yExtent[r], py);
xOffset[i] = padCol + offsetValue$1(b.x1);
yOffset[i] = padRow + offsetValue$1(b.y1);
if (dirty) view.dirty(groups[i]);
- } // set initial alignment offsets
+ }
-
+ // set initial alignment offsets
for (i = 0; i < n; ++i) {
if (i % ncols === 0) xOffset[i] = 0;
if (i < ncols) yOffset[i] = 0;
- } // enforce column alignment constraints
+ }
-
+ // enforce column alignment constraints
if (alignCol === Each) {
for (c = 1; c < ncols; ++c) {
for (offset = 0, i = c; i < n; i += ncols) {
if (offset < xOffset[i]) offset = xOffset[i];
}
-
for (i = c; i < n; i += ncols) {
xOffset[i] = offset + xExtent[c - 1];
}
}
} else if (alignCol === All) {
for (offset = 0, i = 0; i < n; ++i) {
if (i % ncols && offset < xOffset[i]) offset = xOffset[i];
}
-
for (i = 0; i < n; ++i) {
if (i % ncols) xOffset[i] = offset + xMax;
}
} else {
for (alignCol = false, c = 1; c < ncols; ++c) {
for (i = c; i < n; i += ncols) {
xOffset[i] += xExtent[c - 1];
}
}
- } // enforce row alignment constraints
+ }
-
+ // enforce row alignment constraints
if (alignRow === Each) {
for (r = 1; r < nrows; ++r) {
for (offset = 0, i = r * ncols, m = i + ncols; i < m; ++i) {
if (offset < yOffset[i]) offset = yOffset[i];
}
-
for (i = r * ncols; i < m; ++i) {
yOffset[i] = offset + yExtent[r - 1];
}
}
} else if (alignRow === All) {
for (offset = 0, i = ncols; i < n; ++i) {
if (offset < yOffset[i]) offset = yOffset[i];
}
-
for (i = ncols; i < n; ++i) {
yOffset[i] = offset + yMax;
}
} else {
for (alignRow = false, r = 1; r < nrows; ++r) {
for (i = r * ncols, m = i + ncols; i < m; ++i) {
yOffset[i] += yExtent[r - 1];
}
}
- } // perform horizontal grid layout
+ }
-
+ // perform horizontal grid layout
for (x = 0, i = 0; i < n; ++i) {
x = xOffset[i] + (i % ncols ? x : 0);
dx[i] += x - groups[i].x;
- } // perform vertical grid layout
+ }
-
+ // perform vertical grid layout
for (c = 0; c < ncols; ++c) {
for (y = 0, i = c; i < n; i += ncols) {
y += yOffset[i];
dy[i] += y - groups[i].y;
}
- } // perform horizontal centering
+ }
-
+ // perform horizontal centering
if (alignCol && get$1(opt.center, Column) && nrows > 1) {
for (i = 0; i < n; ++i) {
b = alignCol === All ? xMax : xExtent[i % ncols];
x = b - boxes[i].x2 - groups[i].x - dx[i];
if (x > 0) dx[i] += x / 2;
}
- } // perform vertical centering
+ }
-
+ // perform vertical centering
if (alignRow && get$1(opt.center, Row) && ncols !== 1) {
for (i = 0; i < n; ++i) {
b = alignRow === All ? yMax : yExtent[~~(i / ncols)];
y = b - boxes[i].y2 - groups[i].y - dy[i];
if (y > 0) dy[i] += y / 2;
}
- } // position grid relative to anchor
+ }
-
+ // position grid relative to anchor
for (i = 0; i < n; ++i) {
bounds.union(boxes[i].translate(dx[i], dy[i]));
}
-
x = get$1(opt.anchor, X);
y = get$1(opt.anchor, Y);
-
switch (get$1(opt.anchor, Column)) {
case End$1:
x -= bounds.width();
break;
-
case Middle$1:
x -= bounds.width() / 2;
}
-
switch (get$1(opt.anchor, Row)) {
case End$1:
y -= bounds.height();
break;
-
case Middle$1:
y -= bounds.height() / 2;
}
-
x = Math.round(x);
- y = Math.round(y); // update mark positions, bounds, dirty
+ y = Math.round(y);
+ // update mark positions, bounds, dirty
bounds.clear();
-
for (i = 0; i < n; ++i) {
groups[i].mark.bounds.clear();
}
-
for (i = 0; i < n; ++i) {
g = groups[i];
g.x += dx[i] += x;
g.y += dy[i] += y;
bounds.union(g.mark.bounds.union(g.bounds.translate(dx[i], dy[i])));
if (dirty) view.dirty(g);
}
-
return bounds;
}
-
function trellisLayout(view, group, opt) {
var views = gridLayoutGroups(group),
- groups = views.marks,
- bbox = opt.bounds === Flush ? boundFlush : boundFull,
- off = opt.offset,
- ncols = opt.columns || groups.length,
- nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
- cells = nrows * ncols,
- x,
- y,
- x2,
- y2,
- anchor,
- band,
- offset; // -- initial grid layout
+ groups = views.marks,
+ bbox = opt.bounds === Flush ? boundFlush : boundFull,
+ off = opt.offset,
+ ncols = opt.columns || groups.length,
+ nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
+ cells = nrows * ncols,
+ x,
+ y,
+ x2,
+ y2,
+ anchor,
+ band,
+ offset;
+ // -- initial grid layout
const bounds = gridLayout(view, groups, opt);
if (bounds.empty()) bounds.set(0, 0, 0, 0); // empty grid
+
// -- layout grid headers and footers --
- // perform row header layout
+ // perform row header layout
if (views.rowheaders) {
band = get$1(opt.headerBand, Row, null);
x = layoutHeaders(view, views.rowheaders, groups, ncols, nrows, -get$1(off, 'rowHeader'), min, 0, bbox, 'x1', 0, ncols, 1, band);
- } // perform column header layout
+ }
-
+ // perform column header layout
if (views.colheaders) {
band = get$1(opt.headerBand, Column, null);
y = layoutHeaders(view, views.colheaders, groups, ncols, ncols, -get$1(off, 'columnHeader'), min, 1, bbox, 'y1', 0, 1, ncols, band);
- } // perform row footer layout
+ }
-
+ // perform row footer layout
if (views.rowfooters) {
band = get$1(opt.footerBand, Row, null);
x2 = layoutHeaders(view, views.rowfooters, groups, ncols, nrows, get$1(off, 'rowFooter'), max, 0, bbox, 'x2', ncols - 1, ncols, 1, band);
- } // perform column footer layout
+ }
-
+ // perform column footer layout
if (views.colfooters) {
band = get$1(opt.footerBand, Column, null);
y2 = layoutHeaders(view, views.colfooters, groups, ncols, ncols, get$1(off, 'columnFooter'), max, 1, bbox, 'y2', cells - ncols, 1, ncols, band);
- } // perform row title layout
+ }
-
+ // perform row title layout
if (views.rowtitle) {
anchor = get$1(opt.titleAnchor, Row);
offset = get$1(off, 'rowTitle');
offset = anchor === End$1 ? x2 + offset : x - offset;
band = get$1(opt.titleBand, Row, 0.5);
layoutTitle(view, views.rowtitle, offset, 0, bounds, band);
- } // perform column title layout
+ }
-
+ // perform column title layout
if (views.coltitle) {
anchor = get$1(opt.titleAnchor, Column);
offset = get$1(off, 'columnTitle');
offset = anchor === End$1 ? y2 + offset : y - offset;
band = get$1(opt.titleBand, Column, 0.5);
layoutTitle(view, views.coltitle, offset, 1, bounds, band);
}
}
-
function boundFlush(item, field) {
return field === 'x1' ? item.x || 0 : field === 'y1' ? item.y || 0 : field === 'x2' ? (item.x || 0) + (item.width || 0) : field === 'y2' ? (item.y || 0) + (item.height || 0) : undefined;
}
-
function boundFull(item, field) {
return item.bounds[field];
}
-
function layoutHeaders(view, headers, groups, ncols, limit, offset, agg, isX, bound, bf, start, stride, back, band) {
var n = groups.length,
- init = 0,
- edge = 0,
- i,
- j,
- k,
- m,
- b,
- h,
- g,
- x,
- y; // if no groups, early exit and return 0
+ init = 0,
+ edge = 0,
+ i,
+ j,
+ k,
+ m,
+ b,
+ h,
+ g,
+ x,
+ y;
- if (!n) return init; // compute margin
+ // if no groups, early exit and return 0
+ if (!n) return init;
+ // compute margin
for (i = start; i < n; i += stride) {
if (groups[i]) init = agg(init, bound(groups[i], bf));
- } // if no headers, return margin calculation
+ }
+ // if no headers, return margin calculation
+ if (!headers.length) return init;
- if (!headers.length) return init; // check if number of headers exceeds number of rows or columns
-
+ // check if number of headers exceeds number of rows or columns
if (headers.length > limit) {
view.warn('Grid headers exceed limit: ' + limit);
headers = headers.slice(0, limit);
- } // apply offset
+ }
+ // apply offset
+ init += offset;
- init += offset; // clear mark bounds for all headers
-
+ // clear mark bounds for all headers
for (j = 0, m = headers.length; j < m; ++j) {
view.dirty(headers[j]);
headers[j].mark.bounds.clear();
- } // layout each header
+ }
-
+ // layout each header
for (i = start, j = 0, m = headers.length; j < m; ++j, i += stride) {
h = headers[j];
- b = h.mark.bounds; // search for nearest group to align to
+ b = h.mark.bounds;
+
+ // search for nearest group to align to
// necessary if table has empty cells
+ for (k = i; k >= 0 && (g = groups[k]) == null; k -= back);
- for (k = i; k >= 0 && (g = groups[k]) == null; k -= back); // assign coordinates and update bounds
-
-
+ // assign coordinates and update bounds
if (isX) {
x = band == null ? g.x : Math.round(g.bounds.x1 + band * g.bounds.width());
y = init;
} else {
x = init;
y = band == null ? g.y : Math.round(g.bounds.y1 + band * g.bounds.height());
}
-
b.union(h.bounds.translate(x - (h.x || 0), y - (h.y || 0)));
h.x = x;
h.y = y;
- view.dirty(h); // update current edge of layout bounds
+ view.dirty(h);
+ // update current edge of layout bounds
edge = agg(edge, b[bf]);
}
-
return edge;
}
-
function layoutTitle(view, g, offset, isX, bounds, band) {
if (!g) return;
- view.dirty(g); // compute title coordinates
+ view.dirty(g);
+ // compute title coordinates
var x = offset,
- y = offset;
- isX ? x = Math.round(bounds.x1 + band * bounds.width()) : y = Math.round(bounds.y1 + band * bounds.height()); // assign coordinates and update bounds
+ y = offset;
+ isX ? x = Math.round(bounds.x1 + band * bounds.width()) : y = Math.round(bounds.y1 + band * bounds.height());
+ // assign coordinates and update bounds
g.bounds.translate(x - (g.x || 0), y - (g.y || 0));
g.mark.bounds.clear().union(g.bounds);
g.x = x;
- g.y = y; // queue title for redraw
+ g.y = y;
+ // queue title for redraw
view.dirty(g);
}
+ // utility for looking up legend layout configuration
function lookup$3(config, orient) {
const opt = config[orient] || {};
return (key, d) => opt[key] != null ? opt[key] : config[key] != null ? config[key] : d;
- } // if legends specify offset directly, use the maximum specified value
+ }
-
+ // if legends specify offset directly, use the maximum specified value
function offsets(legends, value) {
let max = -Infinity;
legends.forEach(item => {
if (item.offset != null) max = Math.max(max, item.offset);
});
return max > -Infinity ? max : value;
}
-
function legendParams(g, orient, config, xb, yb, w, h) {
const _ = lookup$3(config, orient),
- offset = offsets(g, _('offset', 0)),
- anchor = _('anchor', Start$1),
- mult = anchor === End$1 ? 1 : anchor === Middle$1 ? 0.5 : 0;
-
+ offset = offsets(g, _('offset', 0)),
+ anchor = _('anchor', Start$1),
+ mult = anchor === End$1 ? 1 : anchor === Middle$1 ? 0.5 : 0;
const p = {
align: Each,
bounds: _('bounds', Flush),
columns: _('direction') === 'vertical' ? 1 : g.length,
padding: _('margin', 8),
center: _('center'),
nodirty: true
};
-
switch (orient) {
case Left$1:
p.anchor = {
x: Math.floor(xb.x1) - offset,
column: End$1,
y: mult * (h || xb.height() + 2 * xb.y1),
row: anchor
};
break;
-
case Right$1:
p.anchor = {
x: Math.ceil(xb.x2) + offset,
y: mult * (h || xb.height() + 2 * xb.y1),
row: anchor
};
break;
-
case Top$1:
p.anchor = {
y: Math.floor(yb.y1) - offset,
row: End$1,
x: mult * (w || yb.width() + 2 * yb.x1),
column: anchor
};
break;
-
case Bottom$1:
p.anchor = {
y: Math.ceil(yb.y2) + offset,
x: mult * (w || yb.width() + 2 * yb.x1),
column: anchor
};
break;
-
case TopLeft:
p.anchor = {
x: offset,
y: offset
};
break;
-
case TopRight:
p.anchor = {
x: w - offset,
y: offset,
column: End$1
};
break;
-
case BottomLeft:
p.anchor = {
x: offset,
y: h - offset,
row: End$1
};
break;
-
case BottomRight:
p.anchor = {
x: w - offset,
y: h - offset,
column: End$1,
row: End$1
};
break;
}
-
return p;
}
-
function legendLayout(view, legend) {
var item = legend.items[0],
- datum = item.datum,
- orient = item.orient,
- bounds = item.bounds,
- x = item.x,
- y = item.y,
- w,
- h; // cache current bounds for later comparison
+ datum = item.datum,
+ orient = item.orient,
+ bounds = item.bounds,
+ x = item.x,
+ y = item.y,
+ w,
+ h;
+ // cache current bounds for later comparison
item._bounds ? item._bounds.clear().union(bounds) : item._bounds = bounds.clone();
- bounds.clear(); // adjust legend to accommodate padding and title
+ bounds.clear();
- legendGroupLayout(view, item, item.items[0].items[0]); // aggregate bounds to determine size, and include origin
+ // adjust legend to accommodate padding and title
+ legendGroupLayout(view, item, item.items[0].items[0]);
+ // aggregate bounds to determine size, and include origin
bounds = legendBounds(item, bounds);
w = 2 * item.padding;
h = 2 * item.padding;
-
if (!bounds.empty()) {
w = Math.ceil(bounds.width() + w);
h = Math.ceil(bounds.height() + h);
}
-
if (datum.type === Symbols$1) {
legendEntryLayout(item.items[0].items[0].items[0].items);
}
-
if (orient !== None) {
item.x = x = 0;
item.y = y = 0;
}
-
item.width = w;
item.height = h;
boundStroke(bounds.set(x, y, x + w, y + h), item);
item.mark.bounds.clear().union(bounds);
return item;
}
-
function legendBounds(item, b) {
// aggregate item bounds
- item.items.forEach(_ => b.union(_.bounds)); // anchor to legend origin
+ item.items.forEach(_ => b.union(_.bounds));
+ // anchor to legend origin
b.x1 = item.padding;
b.y1 = item.padding;
return b;
}
-
function legendGroupLayout(view, item, entry) {
var pad = item.padding,
- ex = pad - entry.x,
- ey = pad - entry.y;
-
+ ex = pad - entry.x,
+ ey = pad - entry.y;
if (!item.datum.title) {
if (ex || ey) translate(view, entry, ex, ey);
} else {
var title = item.items[1].items[0],
- anchor = title.anchor,
- tpad = item.titlePadding || 0,
- tx = pad - title.x,
- ty = pad - title.y;
-
+ anchor = title.anchor,
+ tpad = item.titlePadding || 0,
+ tx = pad - title.x,
+ ty = pad - title.y;
switch (title.orient) {
case Left$1:
ex += Math.ceil(title.bounds.width()) + tpad;
break;
-
case Right$1:
case Bottom$1:
break;
-
default:
ey += title.bounds.height() + tpad;
}
-
if (ex || ey) translate(view, entry, ex, ey);
-
switch (title.orient) {
case Left$1:
ty += legendTitleOffset(item, entry, title, anchor, 1, 1);
break;
-
case Right$1:
tx += legendTitleOffset(item, entry, title, End$1, 0, 0) + tpad;
ty += legendTitleOffset(item, entry, title, anchor, 1, 1);
break;
-
case Bottom$1:
tx += legendTitleOffset(item, entry, title, anchor, 0, 0);
ty += legendTitleOffset(item, entry, title, End$1, -1, 0, 1) + tpad;
break;
-
default:
tx += legendTitleOffset(item, entry, title, anchor, 0, 0);
}
+ if (tx || ty) translate(view, title, tx, ty);
- if (tx || ty) translate(view, title, tx, ty); // translate legend if title pushes into negative coordinates
-
+ // translate legend if title pushes into negative coordinates
if ((tx = Math.round(title.bounds.x1 - pad)) < 0) {
translate(view, entry, -tx, 0);
translate(view, title, -tx, 0);
}
}
}
-
function legendTitleOffset(item, entry, title, anchor, y, lr, noBar) {
const grad = item.datum.type !== 'symbol',
- vgrad = title.datum.vgrad,
- e = grad && (lr || !vgrad) && !noBar ? entry.items[0] : entry,
- s = e.bounds[y ? 'y2' : 'x2'] - item.padding,
- u = vgrad && lr ? s : 0,
- v = vgrad && lr ? 0 : s,
- o = y <= 0 ? 0 : multiLineOffset(title);
+ vgrad = title.datum.vgrad,
+ e = grad && (lr || !vgrad) && !noBar ? entry.items[0] : entry,
+ s = e.bounds[y ? 'y2' : 'x2'] - item.padding,
+ u = vgrad && lr ? s : 0,
+ v = vgrad && lr ? 0 : s,
+ o = y <= 0 ? 0 : multiLineOffset(title);
return Math.round(anchor === Start$1 ? u : anchor === End$1 ? v - o : 0.5 * (s - o));
}
-
function translate(view, item, dx, dy) {
item.x += dx;
item.y += dy;
item.bounds.translate(dx, dy);
item.mark.bounds.translate(dx, dy);
view.dirty(item);
}
-
function legendEntryLayout(entries) {
// get max widths for each column
const widths = entries.reduce((w, g) => {
w[g.column] = Math.max(g.bounds.x2 - g.x, w[g.column] || 0);
return w;
- }, {}); // set dimensions of legend entry groups
+ }, {});
+ // set dimensions of legend entry groups
entries.forEach(g => {
g.width = widths[g.column];
g.height = g.bounds.y2 - g.y;
});
}
-
function titleLayout(view, mark, width, height, viewBounds) {
var group = mark.items[0],
- frame = group.frame,
- orient = group.orient,
- anchor = group.anchor,
- offset = group.offset,
- padding = group.padding,
- title = group.items[0].items[0],
- subtitle = group.items[1] && group.items[1].items[0],
- end = orient === Left$1 || orient === Right$1 ? height : width,
- start = 0,
- x = 0,
- y = 0,
- sx = 0,
- sy = 0,
- pos;
-
+ frame = group.frame,
+ orient = group.orient,
+ anchor = group.anchor,
+ offset = group.offset,
+ padding = group.padding,
+ title = group.items[0].items[0],
+ subtitle = group.items[1] && group.items[1].items[0],
+ end = orient === Left$1 || orient === Right$1 ? height : width,
+ start = 0,
+ x = 0,
+ y = 0,
+ sx = 0,
+ sy = 0,
+ pos;
if (frame !== Group) {
orient === Left$1 ? (start = viewBounds.y2, end = viewBounds.y1) : orient === Right$1 ? (start = viewBounds.y1, end = viewBounds.y2) : (start = viewBounds.x1, end = viewBounds.x2);
} else if (orient === Left$1) {
start = height, end = 0;
}
-
pos = anchor === Start$1 ? start : anchor === End$1 ? end : (start + end) / 2;
-
if (subtitle && subtitle.text) {
// position subtitle
switch (orient) {
case Top$1:
case Bottom$1:
sy = title.bounds.height() + padding;
break;
-
case Left$1:
sx = title.bounds.width() + padding;
break;
-
case Right$1:
sx = -title.bounds.width() - padding;
break;
}
-
tempBounds.clear().union(subtitle.bounds);
tempBounds.translate(sx - (subtitle.x || 0), sy - (subtitle.y || 0));
-
if (set$2(subtitle, 'x', sx) | set$2(subtitle, 'y', sy)) {
view.dirty(subtitle);
subtitle.bounds.clear().union(tempBounds);
subtitle.mark.bounds.clear().union(tempBounds);
view.dirty(subtitle);
}
-
tempBounds.clear().union(subtitle.bounds);
} else {
tempBounds.clear();
}
+ tempBounds.union(title.bounds);
- tempBounds.union(title.bounds); // position title group
-
+ // position title group
switch (orient) {
case Top$1:
x = pos;
y = viewBounds.y1 - tempBounds.height() - offset;
break;
-
case Left$1:
x = viewBounds.x1 - tempBounds.width() - offset;
y = pos;
break;
-
case Right$1:
x = viewBounds.x2 + tempBounds.width() + offset;
y = pos;
break;
-
case Bottom$1:
x = pos;
y = viewBounds.y2 + offset;
break;
-
default:
x = group.x;
y = group.y;
}
-
if (set$2(group, 'x', x) | set$2(group, 'y', y)) {
tempBounds.translate(x, y);
view.dirty(group);
group.bounds.clear().union(tempBounds);
mark.bounds.clear().union(tempBounds);
view.dirty(group);
}
-
return group.bounds;
}
+
/**
* Layout view elements such as axes and legends.
* Also performs size adjustments.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.mark - Scenegraph mark of groups to layout.
*/
-
-
function ViewLayout$1(params) {
Transform.call(this, null, params);
}
-
inherits(ViewLayout$1, Transform, {
transform(_, pulse) {
const view = pulse.dataflow;
-
_.mark.items.forEach(group => {
if (_.layout) trellisLayout(view, group, _.layout);
layoutGroup(view, group, _);
});
-
return shouldReflow(_.mark.group) ? pulse.reflow() : pulse;
}
-
});
-
function shouldReflow(group) {
// We typically should reflow if layout is invoked (#2568), as child items
// may have resized and reflow ensures group bounds are re-calculated.
// However, legend entries have a special exception to avoid instability.
// For example, if a selected legend symbol gains a stroke on hover,
// we don't want to re-position subsequent elements in the legend.
return group && group.mark.role !== 'legend-entry';
}
-
function layoutGroup(view, group, _) {
var items = group.items,
- width = Math.max(0, group.width || 0),
- height = Math.max(0, group.height || 0),
- viewBounds = new Bounds().set(0, 0, width, height),
- xBounds = viewBounds.clone(),
- yBounds = viewBounds.clone(),
- legends = [],
- title,
- mark,
- orient,
- b,
- i,
- n; // layout axes, gather legends, collect bounds
+ width = Math.max(0, group.width || 0),
+ height = Math.max(0, group.height || 0),
+ viewBounds = new Bounds().set(0, 0, width, height),
+ xBounds = viewBounds.clone(),
+ yBounds = viewBounds.clone(),
+ legends = [],
+ title,
+ mark,
+ orient,
+ b,
+ i,
+ n;
+ // layout axes, gather legends, collect bounds
for (i = 0, n = items.length; i < n; ++i) {
mark = items[i];
-
switch (mark.role) {
case AxisRole$1:
b = isYAxis(mark) ? xBounds : yBounds;
b.union(axisLayout(view, mark, width, height));
break;
-
case TitleRole$1:
title = mark;
break;
-
case LegendRole$1:
legends.push(legendLayout(view, mark));
break;
-
case FrameRole$1:
case ScopeRole$1:
case RowHeader:
case RowFooter:
case RowTitle:
@@ -23046,97 +20869,91 @@
case ColFooter:
case ColTitle:
xBounds.union(mark.bounds);
yBounds.union(mark.bounds);
break;
-
default:
viewBounds.union(mark.bounds);
}
- } // layout legends, adjust viewBounds
+ }
-
+ // layout legends, adjust viewBounds
if (legends.length) {
// group legends by orient
const l = {};
legends.forEach(item => {
orient = item.orient || Right$1;
if (orient !== None) (l[orient] || (l[orient] = [])).push(item);
- }); // perform grid layout for each orient group
+ });
+ // perform grid layout for each orient group
for (const orient in l) {
const g = l[orient];
gridLayout(view, g, legendParams(g, orient, _.legends, xBounds, yBounds, width, height));
- } // update view bounds
+ }
-
+ // update view bounds
legends.forEach(item => {
const b = item.bounds;
-
if (!b.equals(item._bounds)) {
item.bounds = item._bounds;
view.dirty(item); // dirty previous location
-
item.bounds = b;
view.dirty(item);
}
-
- if (_.autosize && _.autosize.type === Fit) {
+ if (_.autosize && (_.autosize.type === Fit || _.autosize.type === FitX || _.autosize.type === FitY)) {
// For autosize fit, incorporate the orthogonal dimension only.
// Legends that overrun the chart area will then be clipped;
// otherwise the chart area gets reduced to nothing!
switch (item.orient) {
case Left$1:
case Right$1:
viewBounds.add(b.x1, 0).add(b.x2, 0);
break;
-
case Top$1:
case Bottom$1:
viewBounds.add(0, b.y1).add(0, b.y2);
}
} else {
viewBounds.union(b);
}
});
- } // combine bounding boxes
+ }
+ // combine bounding boxes
+ viewBounds.union(xBounds).union(yBounds);
- viewBounds.union(xBounds).union(yBounds); // layout title, adjust bounds
-
+ // layout title, adjust bounds
if (title) {
viewBounds.union(titleLayout(view, title, width, height, viewBounds));
- } // override aggregated view bounds if content is clipped
+ }
-
+ // override aggregated view bounds if content is clipped
if (group.clip) {
viewBounds.set(0, 0, group.width || 0, group.height || 0);
- } // perform size adjustment
+ }
-
+ // perform size adjustment
viewSizeLayout(view, group, viewBounds, _);
}
-
function viewSizeLayout(view, group, viewBounds, _) {
const auto = _.autosize || {},
- type = auto.type;
+ type = auto.type;
if (view._autosize < 1 || !type) return;
let viewWidth = view._width,
- viewHeight = view._height,
- width = Math.max(0, group.width || 0),
- left = Math.max(0, Math.ceil(-viewBounds.x1)),
- height = Math.max(0, group.height || 0),
- top = Math.max(0, Math.ceil(-viewBounds.y1));
+ viewHeight = view._height,
+ width = Math.max(0, group.width || 0),
+ left = Math.max(0, Math.ceil(-viewBounds.x1)),
+ height = Math.max(0, group.height || 0),
+ top = Math.max(0, Math.ceil(-viewBounds.y1));
const right = Math.max(0, Math.ceil(viewBounds.x2 - width)),
- bottom = Math.max(0, Math.ceil(viewBounds.y2 - height));
-
+ bottom = Math.max(0, Math.ceil(viewBounds.y2 - height));
if (auto.contains === Padding$1) {
const padding = view.padding();
viewWidth -= padding.left + padding.right;
viewHeight -= padding.top + padding.bottom;
}
-
if (type === None) {
left = 0;
top = 0;
width = viewWidth;
height = viewHeight;
@@ -23151,11 +20968,10 @@
height = Math.max(0, viewHeight - top - bottom);
} else if (type === Pad) {
viewWidth = width + left + right;
viewHeight = height + top + bottom;
}
-
view._resizeView(viewWidth, viewHeight, width, height, [left, top], auto.resize);
}
var vtx = /*#__PURE__*/Object.freeze({
__proto__: null,
@@ -23181,36 +20997,32 @@
* to use in conjunction with scale.tickFormat. Legal values are
* any valid d3 4.0 format specifier.
* @param {function(*):string} [params.format] - The format function to use.
* If provided, the formatSpecifier argument is ignored.
*/
-
function AxisTicks$1(params) {
Transform.call(this, null, params);
}
-
inherits(AxisTicks$1, Transform, {
transform(_, pulse) {
if (this.value && !_.modified()) {
return pulse.StopPropagation;
}
-
var locale = pulse.dataflow.locale(),
- out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- ticks = this.value,
- scale = _.scale,
- tally = _.count == null ? _.values ? _.values.length : 10 : _.count,
- count = tickCount(scale, tally, _.minstep),
- format = _.format || tickFormat(locale, scale, count, _.formatSpecifier, _.formatType, !!_.values),
- values = _.values ? validTicks(scale, _.values, count) : tickValues(scale, count);
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
+ ticks = this.value,
+ scale = _.scale,
+ tally = _.count == null ? _.values ? _.values.length : 10 : _.count,
+ count = tickCount(scale, tally, _.minstep),
+ format = _.format || tickFormat(locale, scale, count, _.formatSpecifier, _.formatType, !!_.values),
+ values = _.values ? validTicks(scale, _.values, count) : tickValues(scale, count);
if (ticks) out.rem = ticks;
ticks = values.map((value, i) => ingest$1({
index: i / (values.length - 1 || 1),
value: value,
label: format(value)
}));
-
if (_.extra && ticks.length) {
// add an extra tick pegged to the initial domain value
// this is used to generate axes with 'binned' domains
ticks.push(ingest$1({
index: -1,
@@ -23218,68 +21030,58 @@
value: ticks[0].value
},
label: ''
}));
}
-
out.source = ticks;
out.add = ticks;
this.value = ticks;
return out;
}
-
});
+
/**
* Joins a set of data elements against a set of visual items.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): object} [params.item] - An item generator function.
* @param {function(object): *} [params.key] - The key field associating data and visual items.
*/
-
function DataJoin$1(params) {
Transform.call(this, null, params);
}
-
function defaultItemCreate() {
return ingest$1({});
}
-
function newMap(key) {
const map = fastmap().test(t => t.exit);
-
map.lookup = t => map.get(key(t));
-
return map;
}
-
inherits(DataJoin$1, Transform, {
transform(_, pulse) {
var df = pulse.dataflow,
- out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- item = _.item || defaultItemCreate,
- key = _.key || tupleid,
- map = this.value; // prevent transient (e.g., hover) requests from
- // cascading across marks derived from marks
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
+ item = _.item || defaultItemCreate,
+ key = _.key || tupleid,
+ map = this.value;
+ // prevent transient (e.g., hover) requests from
+ // cascading across marks derived from marks
if (isArray(out.encode)) {
out.encode = null;
}
-
if (map && (_.modified('key') || pulse.modified(key))) {
error('DataJoin does not support modified key function or fields.');
}
-
if (!map) {
pulse = pulse.addAll();
this.value = map = newMap(key);
}
-
pulse.visit(pulse.ADD, t => {
const k = key(t);
let x = map.get(k);
-
if (x) {
if (x.exit) {
map.empty--;
out.add.push(x);
} else {
@@ -23288,43 +21090,38 @@
} else {
x = item(t);
map.set(k, x);
out.add.push(x);
}
-
x.datum = t;
x.exit = false;
});
pulse.visit(pulse.MOD, t => {
const k = key(t),
- x = map.get(k);
-
+ x = map.get(k);
if (x) {
x.datum = t;
out.mod.push(x);
}
});
pulse.visit(pulse.REM, t => {
const k = key(t),
- x = map.get(k);
-
+ x = map.get(k);
if (t === x.datum && !x.exit) {
out.rem.push(x);
x.exit = true;
++map.empty;
}
});
if (pulse.changed(pulse.ADD_MOD)) out.modifies('datum');
-
if (pulse.clean() || _.clean && map.empty > df.cleanThreshold) {
df.runAfter(map.clean);
}
-
return out;
}
-
});
+
/**
* Invokes encoding functions for visual items.
* @constructor
* @param {object} params - The parameters to the encoding functions. This
* parameter object will be passed through to all invoked encoding functions.
@@ -23333,66 +21130,60 @@
* @param {object} param.encoders - The encoding functions
* @param {function(object, object): boolean} [param.encoders.update] - Update encoding set
* @param {function(object, object): boolean} [param.encoders.enter] - Enter encoding set
* @param {function(object, object): boolean} [param.encoders.exit] - Exit encoding set
*/
-
function Encode$1(params) {
Transform.call(this, null, params);
}
-
inherits(Encode$1, Transform, {
transform(_, pulse) {
var out = pulse.fork(pulse.ADD_REM),
- fmod = _.mod || false,
- encoders = _.encoders,
- encode = pulse.encode; // if an array, the encode directive includes additional sets
+ fmod = _.mod || false,
+ encoders = _.encoders,
+ encode = pulse.encode;
+
+ // if an array, the encode directive includes additional sets
// that must be defined in order for the primary set to be invoked
// e.g., only run the update set if the hover set is defined
-
if (isArray(encode)) {
if (out.changed() || encode.every(e => encoders[e])) {
encode = encode[0];
out.encode = null; // consume targeted encode directive
} else {
return pulse.StopPropagation;
}
- } // marshall encoder functions
+ }
-
+ // marshall encoder functions
var reenter = encode === 'enter',
- update = encoders.update || falsy,
- enter = encoders.enter || falsy,
- exit = encoders.exit || falsy,
- set = (encode && !reenter ? encoders[encode] : update) || falsy;
-
+ update = encoders.update || falsy,
+ enter = encoders.enter || falsy,
+ exit = encoders.exit || falsy,
+ set = (encode && !reenter ? encoders[encode] : update) || falsy;
if (pulse.changed(pulse.ADD)) {
pulse.visit(pulse.ADD, t => {
enter(t, _);
update(t, _);
});
out.modifies(enter.output);
out.modifies(update.output);
-
if (set !== falsy && set !== update) {
pulse.visit(pulse.ADD, t => {
set(t, _);
});
out.modifies(set.output);
}
}
-
if (pulse.changed(pulse.REM) && exit !== falsy) {
pulse.visit(pulse.REM, t => {
exit(t, _);
});
out.modifies(exit.output);
}
-
if (reenter || set !== falsy) {
const flag = pulse.MOD | (_.modified() ? pulse.REFLOW : 0);
-
if (reenter) {
pulse.visit(flag, t => {
const mod = enter(t, _) || fmod;
if (set(t, _) || mod) out.mod.push(t);
});
@@ -23400,18 +21191,16 @@
} else {
pulse.visit(flag, t => {
if (set(t, _) || fmod) out.mod.push(t);
});
}
-
if (out.mod.length) out.modifies(set.output);
}
-
return out.changed() ? out : pulse.StopPropagation;
}
-
});
+
/**
* Generates legend entries for visualizing a scale.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Scale} params.scale - The scale to generate items for.
@@ -23426,85 +21215,77 @@
* to use in conjunction with scale.tickFormat. Legal values are
* any valid D3 format specifier string.
* @param {function(*):string} [params.format] - The format function to use.
* If provided, the formatSpecifier argument is ignored.
*/
-
function LegendEntries$1(params) {
Transform.call(this, [], params);
}
-
inherits(LegendEntries$1, Transform, {
transform(_, pulse) {
if (this.value != null && !_.modified()) {
return pulse.StopPropagation;
}
-
var locale = pulse.dataflow.locale(),
- out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- items = this.value,
- type = _.type || SymbolLegend,
- scale = _.scale,
- limit = +_.limit,
- count = tickCount(scale, _.count == null ? 5 : _.count, _.minstep),
- lskip = !!_.values || type === SymbolLegend,
- format = _.format || labelFormat(locale, scale, count, type, _.formatSpecifier, _.formatType, lskip),
- values = _.values || labelValues(scale, count),
- domain,
- fraction,
- size,
- offset,
- ellipsis;
+ out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
+ items = this.value,
+ type = _.type || SymbolLegend,
+ scale = _.scale,
+ limit = +_.limit,
+ count = tickCount(scale, _.count == null ? 5 : _.count, _.minstep),
+ lskip = !!_.values || type === SymbolLegend,
+ format = _.format || labelFormat(locale, scale, count, type, _.formatSpecifier, _.formatType, lskip),
+ values = _.values || labelValues(scale, count),
+ domain,
+ fraction,
+ size,
+ offset,
+ ellipsis;
if (items) out.rem = items;
-
if (type === SymbolLegend) {
if (limit && values.length > limit) {
pulse.dataflow.warn('Symbol legend count exceeds limit, filtering items.');
items = values.slice(0, limit - 1);
ellipsis = true;
} else {
items = values;
}
-
if (isFunction(size = _.size)) {
// if first value maps to size zero, remove from list (vega#717)
if (!_.values && scale(items[0]) === 0) {
items = items.slice(1);
- } // compute size offset for legend entries
-
-
+ }
+ // compute size offset for legend entries
offset = items.reduce((max, value) => Math.max(max, size(value, _)), 0);
} else {
- size = constant$4(offset = size || 8);
+ size = constant$5(offset = size || 8);
}
-
items = items.map((value, index) => ingest$1({
index: index,
label: format(value, index, items),
value: value,
offset: offset,
size: size(value, _)
}));
-
if (ellipsis) {
ellipsis = values[items.length];
items.push(ingest$1({
index: items.length,
- label: "\u2026".concat(values.length - items.length, " entries"),
+ label: `\u2026${values.length - items.length} entries`,
value: ellipsis,
offset: offset,
size: size(ellipsis, _)
}));
}
} else if (type === GradientLegend) {
- domain = scale.domain(), fraction = scaleFraction(scale, domain[0], peek$1(domain)); // if automatic label generation produces 2 or fewer values,
- // use the domain end points instead (fixes vega/vega#1364)
+ domain = scale.domain(), fraction = scaleFraction(scale, domain[0], peek$1(domain));
+ // if automatic label generation produces 2 or fewer values,
+ // use the domain end points instead (fixes vega/vega#1364)
if (values.length < 3 && !_.values && domain[0] !== peek$1(domain)) {
values = [domain[0], peek$1(domain)];
}
-
items = values.map((value, index) => ingest$1({
index: index,
label: format(value, index, values),
value: value,
perc: fraction(value)
@@ -23518,37 +21299,29 @@
value: value,
perc: index ? fraction(value) : 0,
perc2: index === size ? 1 : fraction(values[index + 1])
}));
}
-
out.source = items;
out.add = items;
this.value = items;
return out;
}
-
});
-
const sourceX = t => t.source.x;
-
const sourceY = t => t.source.y;
-
const targetX = t => t.target.x;
-
const targetY = t => t.target.y;
+
/**
* Layout paths linking source and target elements.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
-
function LinkPath(params) {
Transform.call(this, {}, params);
}
-
LinkPath.Definition = {
'type': 'LinkPath',
'metadata': {
'modifies': true
},
@@ -23588,86 +21361,70 @@
}]
};
inherits(LinkPath, Transform, {
transform(_, pulse) {
var sx = _.sourceX || sourceX,
- sy = _.sourceY || sourceY,
- tx = _.targetX || targetX,
- ty = _.targetY || targetY,
- as = _.as || 'path',
- orient = _.orient || 'vertical',
- shape = _.shape || 'line',
- path = Paths.get(shape + '-' + orient) || Paths.get(shape);
-
+ sy = _.sourceY || sourceY,
+ tx = _.targetX || targetX,
+ ty = _.targetY || targetY,
+ as = _.as || 'path',
+ orient = _.orient || 'vertical',
+ shape = _.shape || 'line',
+ path = Paths.get(shape + '-' + orient) || Paths.get(shape);
if (!path) {
error('LinkPath unsupported type: ' + _.shape + (_.orient ? '-' + _.orient : ''));
}
-
pulse.visit(pulse.SOURCE, t => {
t[as] = path(sx(t), sy(t), tx(t), ty(t));
});
return pulse.reflow(_.modified()).modifies(as);
}
-
});
-
const line = (sx, sy, tx, ty) => 'M' + sx + ',' + sy + 'L' + tx + ',' + ty;
-
const lineR = (sa, sr, ta, tr) => line(sr * Math.cos(sa), sr * Math.sin(sa), tr * Math.cos(ta), tr * Math.sin(ta));
-
const arc = (sx, sy, tx, ty) => {
var dx = tx - sx,
- dy = ty - sy,
- rr = Math.sqrt(dx * dx + dy * dy) / 2,
- ra = 180 * Math.atan2(dy, dx) / Math.PI;
+ dy = ty - sy,
+ rr = Math.hypot(dx, dy) / 2,
+ ra = 180 * Math.atan2(dy, dx) / Math.PI;
return 'M' + sx + ',' + sy + 'A' + rr + ',' + rr + ' ' + ra + ' 0 1' + ' ' + tx + ',' + ty;
};
-
const arcR = (sa, sr, ta, tr) => arc(sr * Math.cos(sa), sr * Math.sin(sa), tr * Math.cos(ta), tr * Math.sin(ta));
-
const curve = (sx, sy, tx, ty) => {
const dx = tx - sx,
- dy = ty - sy,
- ix = 0.2 * (dx + dy),
- iy = 0.2 * (dy - dx);
+ dy = ty - sy,
+ ix = 0.2 * (dx + dy),
+ iy = 0.2 * (dy - dx);
return 'M' + sx + ',' + sy + 'C' + (sx + ix) + ',' + (sy + iy) + ' ' + (tx + iy) + ',' + (ty - ix) + ' ' + tx + ',' + ty;
};
-
const curveR = (sa, sr, ta, tr) => curve(sr * Math.cos(sa), sr * Math.sin(sa), tr * Math.cos(ta), tr * Math.sin(ta));
-
const orthoX = (sx, sy, tx, ty) => 'M' + sx + ',' + sy + 'V' + ty + 'H' + tx;
-
const orthoY = (sx, sy, tx, ty) => 'M' + sx + ',' + sy + 'H' + tx + 'V' + ty;
-
const orthoR = (sa, sr, ta, tr) => {
const sc = Math.cos(sa),
- ss = Math.sin(sa),
- tc = Math.cos(ta),
- ts = Math.sin(ta),
- sf = Math.abs(ta - sa) > Math.PI ? ta <= sa : ta > sa;
+ ss = Math.sin(sa),
+ tc = Math.cos(ta),
+ ts = Math.sin(ta),
+ sf = Math.abs(ta - sa) > Math.PI ? ta <= sa : ta > sa;
return 'M' + sr * sc + ',' + sr * ss + 'A' + sr + ',' + sr + ' 0 0,' + (sf ? 1 : 0) + ' ' + sr * tc + ',' + sr * ts + 'L' + tr * tc + ',' + tr * ts;
};
-
const diagonalX = (sx, sy, tx, ty) => {
const m = (sx + tx) / 2;
return 'M' + sx + ',' + sy + 'C' + m + ',' + sy + ' ' + m + ',' + ty + ' ' + tx + ',' + ty;
};
-
const diagonalY = (sx, sy, tx, ty) => {
const m = (sy + ty) / 2;
return 'M' + sx + ',' + sy + 'C' + sx + ',' + m + ' ' + tx + ',' + m + ' ' + tx + ',' + ty;
};
-
const diagonalR = (sa, sr, ta, tr) => {
const sc = Math.cos(sa),
- ss = Math.sin(sa),
- tc = Math.cos(ta),
- ts = Math.sin(ta),
- mr = (sr + tr) / 2;
+ ss = Math.sin(sa),
+ tc = Math.cos(ta),
+ ts = Math.sin(ta),
+ mr = (sr + tr) / 2;
return 'M' + sr * sc + ',' + sr * ss + 'C' + mr * sc + ',' + mr * ss + ' ' + mr * tc + ',' + mr * ts + ' ' + tr * tc + ',' + tr * ts;
};
-
const Paths = fastmap({
'line': line,
'line-radial': lineR,
'arc': arc,
'arc-radial': arcR,
@@ -23678,24 +21435,23 @@
'orthogonal-radial': orthoR,
'diagonal-horizontal': diagonalX,
'diagonal-vertical': diagonalY,
'diagonal-radial': diagonalR
});
+
/**
* Pie and donut chart layout.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to size pie segments.
* @param {number} [params.startAngle=0] - The start angle (in radians) of the layout.
* @param {number} [params.endAngle=2π] - The end angle (in radians) of the layout.
* @param {boolean} [params.sort] - Boolean flag for sorting sectors by value.
*/
-
function Pie(params) {
Transform.call(this, null, params);
}
-
Pie.Definition = {
'type': 'Pie',
'metadata': {
'modifies': true
},
@@ -23723,347 +21479,314 @@
}]
};
inherits(Pie, Transform, {
transform(_, pulse) {
var as = _.as || ['startAngle', 'endAngle'],
- startAngle = as[0],
- endAngle = as[1],
- field = _.field || one$2,
- start = _.startAngle || 0,
- stop = _.endAngle != null ? _.endAngle : 2 * Math.PI,
- data = pulse.source,
- values = data.map(field),
- n = values.length,
- a = start,
- k = (stop - start) / sum$1(values),
- index = range$3(n),
- i,
- t,
- v;
-
+ startAngle = as[0],
+ endAngle = as[1],
+ field = _.field || one$2,
+ start = _.startAngle || 0,
+ stop = _.endAngle != null ? _.endAngle : 2 * Math.PI,
+ data = pulse.source,
+ values = data.map(field),
+ n = values.length,
+ a = start,
+ k = (stop - start) / sum$1(values),
+ index = range$3(n),
+ i,
+ t,
+ v;
if (_.sort) {
index.sort((a, b) => values[a] - values[b]);
}
-
for (i = 0; i < n; ++i) {
v = values[index[i]];
t = data[index[i]];
t[startAngle] = a;
t[endAngle] = a += v * k;
}
-
this.value = values;
return pulse.reflow(_.modified()).modifies(as);
}
-
});
const DEFAULT_COUNT = 5;
-
function includeZero(scale) {
const type = scale.type;
return !scale.bins && (type === Linear || type === Pow || type === Sqrt);
}
-
function includePad(type) {
return isContinuous(type) && type !== Sequential;
}
-
const SKIP$1 = toSet(['set', 'modified', 'clear', 'type', 'scheme', 'schemeExtent', 'schemeCount', 'domain', 'domainMin', 'domainMid', 'domainMax', 'domainRaw', 'domainImplicit', 'nice', 'zero', 'bins', 'range', 'rangeStep', 'round', 'reverse', 'interpolate', 'interpolateGamma']);
+
/**
* Maintains a scale function mapping data values to visual channels.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
function Scale$1(params) {
Transform.call(this, null, params);
this.modified(true); // always treat as modified
}
-
inherits(Scale$1, Transform, {
transform(_, pulse) {
var df = pulse.dataflow,
- scale$1 = this.value,
- key = scaleKey(_);
-
+ scale$1 = this.value,
+ key = scaleKey(_);
if (!scale$1 || key !== scale$1.type) {
this.value = scale$1 = scale$4(key)();
}
-
for (key in _) if (!SKIP$1[key]) {
// padding is a scale property for band/point but not others
- if (key === 'padding' && includePad(scale$1.type)) continue; // invoke scale property setter, raise warning if not found
-
+ if (key === 'padding' && includePad(scale$1.type)) continue;
+ // invoke scale property setter, raise warning if not found
isFunction(scale$1[key]) ? scale$1[key](_[key]) : df.warn('Unsupported scale property: ' + key);
}
-
configureRange(scale$1, _, configureBins(scale$1, _, configureDomain(scale$1, _, df)));
return pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
}
-
});
-
function scaleKey(_) {
var t = _.type,
- d = '',
- n; // backwards compatibility pre Vega 5.
+ d = '',
+ n;
+ // backwards compatibility pre Vega 5.
if (t === Sequential) return Sequential + '-' + Linear;
-
if (isContinuousColor(_)) {
n = _.rawDomain ? _.rawDomain.length : _.domain ? _.domain.length + +(_.domainMid != null) : 0;
d = n === 2 ? Sequential + '-' : n === 3 ? Diverging + '-' : '';
}
-
return (d + t || Linear).toLowerCase();
}
-
function isContinuousColor(_) {
const t = _.type;
return isContinuous(t) && t !== Time && t !== UTC && (_.scheme || _.range && _.range.length && _.range.every(isString));
}
-
function configureDomain(scale, _, df) {
// check raw domain, if provided use that and exit early
const raw = rawDomain(scale, _.domainRaw, df);
if (raw > -1) return raw;
var domain = _.domain,
- type = scale.type,
- zero = _.zero || _.zero === undefined && includeZero(scale),
- n,
- mid;
- if (!domain) return 0; // adjust continuous domain for minimum pixel padding
+ type = scale.type,
+ zero = _.zero || _.zero === undefined && includeZero(scale),
+ n,
+ mid;
+ if (!domain) return 0;
- if (includePad(type) && _.padding && domain[0] !== peek$1(domain)) {
- domain = padDomain(type, domain, _.range, _.padding, _.exponent, _.constant);
- } // adjust domain based on zero, min, max settings
-
-
+ // adjust domain based on zero, min, max settings
if (zero || _.domainMin != null || _.domainMax != null || _.domainMid != null) {
n = (domain = domain.slice()).length - 1 || 1;
-
if (zero) {
if (domain[0] > 0) domain[0] = 0;
if (domain[n] < 0) domain[n] = 0;
}
-
if (_.domainMin != null) domain[0] = _.domainMin;
if (_.domainMax != null) domain[n] = _.domainMax;
-
if (_.domainMid != null) {
mid = _.domainMid;
const i = mid > domain[n] ? n + 1 : mid < domain[0] ? 0 : n;
if (i !== n) df.warn('Scale domainMid exceeds domain min or max.', mid);
domain.splice(i, 0, mid);
}
- } // set the scale domain
+ }
+ // adjust continuous domain for minimum pixel padding
+ if (includePad(type) && _.padding && domain[0] !== peek$1(domain)) {
+ domain = padDomain(type, domain, _.range, _.padding, _.exponent, _.constant);
+ }
- scale.domain(domainCheck(type, domain, df)); // if ordinal scale domain is defined, prevent implicit
- // domain construction as side-effect of scale lookup
+ // set the scale domain
+ scale.domain(domainCheck(type, domain, df));
+ // if ordinal scale domain is defined, prevent implicit
+ // domain construction as side-effect of scale lookup
if (type === Ordinal) {
scale.unknown(_.domainImplicit ? implicit : undefined);
- } // perform 'nice' adjustment as requested
+ }
-
+ // perform 'nice' adjustment as requested
if (_.nice && scale.nice) {
scale.nice(_.nice !== true && tickCount(scale, _.nice) || null);
- } // return the cardinality of the domain
+ }
-
+ // return the cardinality of the domain
return domain.length;
}
-
function rawDomain(scale, raw, df) {
if (raw) {
scale.domain(domainCheck(scale.type, raw, df));
return raw.length;
} else {
return -1;
}
}
-
function padDomain(type, domain, range, pad, exponent, constant) {
var span = Math.abs(peek$1(range) - range[0]),
- frac = span / (span - 2 * pad),
- d = type === Log ? zoomLog(domain, null, frac) : type === Sqrt ? zoomPow(domain, null, frac, 0.5) : type === Pow ? zoomPow(domain, null, frac, exponent || 1) : type === Symlog ? zoomSymlog(domain, null, frac, constant || 1) : zoomLinear(domain, null, frac);
+ frac = span / (span - 2 * pad),
+ d = type === Log ? zoomLog(domain, null, frac) : type === Sqrt ? zoomPow(domain, null, frac, 0.5) : type === Pow ? zoomPow(domain, null, frac, exponent || 1) : type === Symlog ? zoomSymlog(domain, null, frac, constant || 1) : zoomLinear(domain, null, frac);
domain = domain.slice();
domain[0] = d[0];
domain[domain.length - 1] = d[1];
return domain;
}
-
function domainCheck(type, domain, df) {
if (isLogarithmic(type)) {
// sum signs of domain values
// if all pos or all neg, abs(sum) === domain.length
var s = Math.abs(domain.reduce((s, v) => s + (v < 0 ? -1 : v > 0 ? 1 : 0), 0));
-
if (s !== domain.length) {
df.warn('Log scale domain includes zero: ' + $(domain));
}
}
-
return domain;
}
-
function configureBins(scale, _, count) {
let bins = _.bins;
-
if (bins && !isArray(bins)) {
// generate bin boundary array
const domain = scale.domain(),
- lo = domain[0],
- hi = peek$1(domain),
- step = bins.step;
+ lo = domain[0],
+ hi = peek$1(domain),
+ step = bins.step;
let start = bins.start == null ? lo : bins.start,
- stop = bins.stop == null ? hi : bins.stop;
+ stop = bins.stop == null ? hi : bins.stop;
if (!step) error('Scale bins parameter missing step property.');
if (start < lo) start = step * Math.ceil(lo / step);
if (stop > hi) stop = step * Math.floor(hi / step);
bins = range$3(start, stop + step / 2, step);
}
-
if (bins) {
// assign bin boundaries to scale instance
scale.bins = bins;
} else if (scale.bins) {
// no current bins, remove bins if previously set
delete scale.bins;
- } // special handling for bin-ordinal scales
+ }
-
+ // special handling for bin-ordinal scales
if (scale.type === BinOrdinal) {
if (!bins) {
// the domain specifies the bins
scale.bins = scale.domain();
} else if (!_.domain && !_.domainRaw) {
// the bins specify the domain
scale.domain(bins);
count = bins.length;
}
- } // return domain cardinality
+ }
-
+ // return domain cardinality
return count;
}
-
function configureRange(scale, _, count) {
var type = scale.type,
- round = _.round || false,
- range = _.range; // if range step specified, calculate full range extent
+ round = _.round || false,
+ range = _.range;
+ // if range step specified, calculate full range extent
if (_.rangeStep != null) {
range = configureRangeStep(type, _, count);
- } // else if a range scheme is defined, use that
+ }
+
+ // else if a range scheme is defined, use that
else if (_.scheme) {
range = configureScheme(type, _, count);
-
if (isFunction(range)) {
if (scale.interpolator) {
return scale.interpolator(range);
} else {
- error("Scale type ".concat(type, " does not support interpolating color schemes."));
+ error(`Scale type ${type} does not support interpolating color schemes.`);
}
}
- } // given a range array for an interpolating scale, convert to interpolator
+ }
-
+ // given a range array for an interpolating scale, convert to interpolator
if (range && isInterpolating(type)) {
return scale.interpolator(interpolateColors(flip(range, _.reverse), _.interpolate, _.interpolateGamma));
- } // configure rounding / interpolation
+ }
-
+ // configure rounding / interpolation
if (range && _.interpolate && scale.interpolate) {
scale.interpolate(interpolate(_.interpolate, _.interpolateGamma));
} else if (isFunction(scale.round)) {
scale.round(round);
} else if (isFunction(scale.rangeRound)) {
scale.interpolate(round ? interpolateRound : interpolate$1);
}
-
if (range) scale.range(flip(range, _.reverse));
}
-
function configureRangeStep(type, _, count) {
if (type !== Band && type !== Point) {
error('Only band and point scales support rangeStep.');
- } // calculate full range based on requested step size and padding
+ }
-
+ // calculate full range based on requested step size and padding
var outer = (_.paddingOuter != null ? _.paddingOuter : _.padding) || 0,
- inner = type === Point ? 1 : (_.paddingInner != null ? _.paddingInner : _.padding) || 0;
+ inner = type === Point ? 1 : (_.paddingInner != null ? _.paddingInner : _.padding) || 0;
return [0, _.rangeStep * bandSpace(count, inner, outer)];
}
-
function configureScheme(type, _, count) {
var extent = _.schemeExtent,
- name,
- scheme$1;
-
+ name,
+ scheme$1;
if (isArray(_.scheme)) {
scheme$1 = interpolateColors(_.scheme, _.interpolate, _.interpolateGamma);
} else {
name = _.scheme.toLowerCase();
scheme$1 = scheme(name);
- if (!scheme$1) error("Unrecognized scheme name: ".concat(_.scheme));
- } // determine size for potential discrete range
+ if (!scheme$1) error(`Unrecognized scheme name: ${_.scheme}`);
+ }
+ // determine size for potential discrete range
+ count = type === Threshold ? count + 1 : type === BinOrdinal ? count - 1 : type === Quantile || type === Quantize ? +_.schemeCount || DEFAULT_COUNT : count;
- count = type === Threshold ? count + 1 : type === BinOrdinal ? count - 1 : type === Quantile || type === Quantize ? +_.schemeCount || DEFAULT_COUNT : count; // adjust and/or quantize scheme as appropriate
-
+ // adjust and/or quantize scheme as appropriate
return isInterpolating(type) ? adjustScheme(scheme$1, extent, _.reverse) : isFunction(scheme$1) ? quantizeInterpolator(adjustScheme(scheme$1, extent), count) : type === Ordinal ? scheme$1 : scheme$1.slice(0, count);
}
-
function adjustScheme(scheme, extent, reverse) {
return isFunction(scheme) && (extent || reverse) ? interpolateRange(scheme, flip(extent || [0, 1], reverse)) : scheme;
}
-
function flip(array, reverse) {
return reverse ? array.slice().reverse() : array;
}
+
/**
* Sorts scenegraph items in the pulse source array.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(*,*): number} [params.sort] - A comparator
* function for sorting tuples.
*/
-
-
function SortItems$1(params) {
Transform.call(this, null, params);
}
-
inherits(SortItems$1, Transform, {
transform(_, pulse) {
const mod = _.modified('sort') || pulse.changed(pulse.ADD) || pulse.modified(_.sort.fields) || pulse.modified('datum');
if (mod) pulse.source.sort(stableCompare(_.sort));
this.modified(mod);
return pulse;
}
-
});
const Zero = 'zero',
- Center$1 = 'center',
- Normalize = 'normalize',
- DefOutput = ['y0', 'y1'];
+ Center$1 = 'center',
+ Normalize = 'normalize',
+ DefOutput = ['y0', 'y1'];
+
/**
* Stack layout for visualization elements.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to stack.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors to groupby.
* @param {function(object,object): number} [params.sort] - A comparator for stack sorting.
* @param {string} [offset='zero'] - Stack baseline offset. One of 'zero', 'center', 'normalize'.
*/
-
function Stack(params) {
Transform.call(this, null, params);
}
-
Stack.Definition = {
'type': 'Stack',
'metadata': {
'modifies': true
},
@@ -24091,125 +21814,112 @@
}]
};
inherits(Stack, Transform, {
transform(_, pulse) {
var as = _.as || DefOutput,
- y0 = as[0],
- y1 = as[1],
- sort = stableCompare(_.sort),
- field = _.field || one$2,
- stack = _.offset === Center$1 ? stackCenter : _.offset === Normalize ? stackNormalize : stackZero,
- groups,
- i,
- n,
- max; // partition, sum, and sort the stack groups
+ y0 = as[0],
+ y1 = as[1],
+ sort = stableCompare(_.sort),
+ field = _.field || one$2,
+ stack = _.offset === Center$1 ? stackCenter : _.offset === Normalize ? stackNormalize : stackZero,
+ groups,
+ i,
+ n,
+ max;
- groups = partition$3(pulse.source, _.groupby, sort, field); // compute stack layouts per group
+ // partition, sum, and sort the stack groups
+ groups = partition$3(pulse.source, _.groupby, sort, field);
+ // compute stack layouts per group
for (i = 0, n = groups.length, max = groups.max; i < n; ++i) {
stack(groups[i], max, field, y0, y1);
}
-
return pulse.reflow(_.modified()).modifies(as);
}
-
});
-
function stackCenter(group, max, field, y0, y1) {
var last = (max - group.sum) / 2,
- m = group.length,
- j = 0,
- t;
-
+ m = group.length,
+ j = 0,
+ t;
for (; j < m; ++j) {
t = group[j];
t[y0] = last;
t[y1] = last += Math.abs(field(t));
}
}
-
function stackNormalize(group, max, field, y0, y1) {
var scale = 1 / group.sum,
- last = 0,
- m = group.length,
- j = 0,
- v = 0,
- t;
-
+ last = 0,
+ m = group.length,
+ j = 0,
+ v = 0,
+ t;
for (; j < m; ++j) {
t = group[j];
t[y0] = last;
t[y1] = last = scale * (v += Math.abs(field(t)));
}
}
-
function stackZero(group, max, field, y0, y1) {
var lastPos = 0,
- lastNeg = 0,
- m = group.length,
- j = 0,
- v,
- t;
-
+ lastNeg = 0,
+ m = group.length,
+ j = 0,
+ v,
+ t;
for (; j < m; ++j) {
t = group[j];
v = +field(t);
-
if (v < 0) {
t[y0] = lastNeg;
t[y1] = lastNeg += v;
} else {
t[y0] = lastPos;
t[y1] = lastPos += v;
}
}
}
-
function partition$3(data, groupby, sort, field) {
var groups = [],
- get = f => f(t),
- map,
- i,
- n,
- m,
- t,
- k,
- g,
- s,
- max; // partition data points into stack groups
+ get = f => f(t),
+ map,
+ i,
+ n,
+ m,
+ t,
+ k,
+ g,
+ s,
+ max;
-
+ // partition data points into stack groups
if (groupby == null) {
groups.push(data.slice());
} else {
for (map = {}, i = 0, n = data.length; i < n; ++i) {
t = data[i];
k = groupby.map(get);
g = map[k];
-
if (!g) {
map[k] = g = [];
groups.push(g);
}
-
g.push(t);
}
- } // compute sums of groups, sort groups as needed
+ }
-
+ // compute sums of groups, sort groups as needed
for (k = 0, max = 0, m = groups.length; k < m; ++k) {
g = groups[k];
-
for (i = 0, s = 0, n = g.length; i < n; ++i) {
s += Math.abs(field(g[i]));
}
-
g.sum = s;
if (s > max) max = s;
if (sort) g.sort(sort);
}
-
groups.max = max;
return groups;
}
var encode$1 = /*#__PURE__*/Object.freeze({
@@ -24260,20 +21970,18 @@
function streamGeometry(geometry, stream) {
if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
streamGeometryType[geometry.type](geometry, stream);
}
}
-
var streamObjectType = {
Feature: function (object, stream) {
streamGeometry(object.geometry, stream);
},
FeatureCollection: function (object, stream) {
var features = object.features,
- i = -1,
- n = features.length;
-
+ i = -1,
+ n = features.length;
while (++i < n) streamGeometry(features[i].geometry, stream);
}
};
var streamGeometryType = {
Sphere: function (object, stream) {
@@ -24283,81 +21991,72 @@
object = object.coordinates;
stream.point(object[0], object[1], object[2]);
},
MultiPoint: function (object, stream) {
var coordinates = object.coordinates,
- i = -1,
- n = coordinates.length;
-
+ i = -1,
+ n = coordinates.length;
while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
},
LineString: function (object, stream) {
streamLine(object.coordinates, stream, 0);
},
MultiLineString: function (object, stream) {
var coordinates = object.coordinates,
- i = -1,
- n = coordinates.length;
-
+ i = -1,
+ n = coordinates.length;
while (++i < n) streamLine(coordinates[i], stream, 0);
},
Polygon: function (object, stream) {
streamPolygon(object.coordinates, stream);
},
MultiPolygon: function (object, stream) {
var coordinates = object.coordinates,
- i = -1,
- n = coordinates.length;
-
+ i = -1,
+ n = coordinates.length;
while (++i < n) streamPolygon(coordinates[i], stream);
},
GeometryCollection: function (object, stream) {
var geometries = object.geometries,
- i = -1,
- n = geometries.length;
-
+ i = -1,
+ n = geometries.length;
while (++i < n) streamGeometry(geometries[i], stream);
}
};
-
function streamLine(coordinates, stream, closed) {
var i = -1,
- n = coordinates.length - closed,
- coordinate;
+ n = coordinates.length - closed,
+ coordinate;
stream.lineStart();
-
while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
-
stream.lineEnd();
}
-
function streamPolygon(coordinates, stream) {
var i = -1,
- n = coordinates.length;
+ n = coordinates.length;
stream.polygonStart();
-
while (++i < n) streamLine(coordinates[i], stream, 1);
-
stream.polygonEnd();
}
-
function geoStream (object, stream) {
if (object && streamObjectType.hasOwnProperty(object.type)) {
streamObjectType[object.type](object, stream);
} else {
streamGeometry(object, stream);
}
}
- var areaRingSum$1 = new Adder(); // hello?
+ var areaRingSum$1 = new Adder();
+ // hello?
+
var areaSum$1 = new Adder(),
- lambda00$2,
- phi00$2,
- lambda0$1,
- cosPhi0,
- sinPhi0;
+ lambda00$2,
+ phi00$2,
+ lambda0$1,
+ cosPhi0,
+ sinPhi0;
var areaStream$1 = {
point: noop$2,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: function () {
@@ -24372,46 +22071,42 @@
},
sphere: function () {
areaSum$1.add(tau$1);
}
};
-
function areaRingStart$1() {
areaStream$1.point = areaPointFirst$1;
}
-
function areaRingEnd$1() {
areaPoint$1(lambda00$2, phi00$2);
}
-
function areaPointFirst$1(lambda, phi) {
areaStream$1.point = areaPoint$1;
lambda00$2 = lambda, phi00$2 = phi;
lambda *= radians, phi *= radians;
lambda0$1 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi);
}
-
function areaPoint$1(lambda, phi) {
lambda *= radians, phi *= radians;
phi = phi / 2 + quarterPi; // half the angular distance from south pole
+
// Spherical excess E for a spherical triangle with vertices: south pole,
// previous point, current point. Uses a formula derived from Cagnoli’s
// theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
-
var dLambda = lambda - lambda0$1,
- sdLambda = dLambda >= 0 ? 1 : -1,
- adLambda = sdLambda * dLambda,
- cosPhi = cos$1(phi),
- sinPhi = sin$1(phi),
- k = sinPhi0 * sinPhi,
- u = cosPhi0 * cosPhi + k * cos$1(adLambda),
- v = k * sdLambda * sin$1(adLambda);
- areaRingSum$1.add(atan2(v, u)); // Advance the previous points.
+ sdLambda = dLambda >= 0 ? 1 : -1,
+ adLambda = sdLambda * dLambda,
+ cosPhi = cos$1(phi),
+ sinPhi = sin$1(phi),
+ k = sinPhi0 * sinPhi,
+ u = cosPhi0 * cosPhi + k * cos$1(adLambda),
+ v = k * sdLambda * sin$1(adLambda);
+ areaRingSum$1.add(atan2(v, u));
+ // Advance the previous points.
lambda0$1 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
}
-
function geoArea$1 (object) {
areaSum$1 = new Adder();
geoStream(object, areaStream$1);
return areaSum$1 * 2;
}
@@ -24419,94 +22114,95 @@
function spherical(cartesian) {
return [atan2(cartesian[1], cartesian[0]), asin$1(cartesian[2])];
}
function cartesian(spherical) {
var lambda = spherical[0],
- phi = spherical[1],
- cosPhi = cos$1(phi);
+ phi = spherical[1],
+ cosPhi = cos$1(phi);
return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)];
}
function cartesianDot(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
function cartesianCross(a, b) {
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
- } // TODO return a
+ }
+ // TODO return a
function cartesianAddInPlace(a, b) {
a[0] += b[0], a[1] += b[1], a[2] += b[2];
}
function cartesianScale(vector, k) {
return [vector[0] * k, vector[1] * k, vector[2] * k];
- } // TODO return d
+ }
+ // TODO return d
function cartesianNormalizeInPlace(d) {
var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
d[0] /= l, d[1] /= l, d[2] /= l;
}
- var lambda0, phi0, lambda1, phi1, // bounds
- lambda2, // previous lambda-coordinate
- lambda00$1, phi00$1, // first point
- p0, // previous 3D point
- deltaSum, ranges, range$2;
- var boundsStream$2 = {
+ var lambda0, phi0, lambda1, phi1,
+ // bounds
+ lambda2,
+ // previous lambda-coordinate
+ lambda00$1, phi00$1,
+ // first point
+ p0,
+ // previous 3D point
+ deltaSum, ranges, range$2;
+ var boundsStream$1 = {
point: boundsPoint$1,
lineStart: boundsLineStart,
lineEnd: boundsLineEnd,
polygonStart: function () {
- boundsStream$2.point = boundsRingPoint;
- boundsStream$2.lineStart = boundsRingStart;
- boundsStream$2.lineEnd = boundsRingEnd;
+ boundsStream$1.point = boundsRingPoint;
+ boundsStream$1.lineStart = boundsRingStart;
+ boundsStream$1.lineEnd = boundsRingEnd;
deltaSum = new Adder();
areaStream$1.polygonStart();
},
polygonEnd: function () {
areaStream$1.polygonEnd();
- boundsStream$2.point = boundsPoint$1;
- boundsStream$2.lineStart = boundsLineStart;
- boundsStream$2.lineEnd = boundsLineEnd;
+ boundsStream$1.point = boundsPoint$1;
+ boundsStream$1.lineStart = boundsLineStart;
+ boundsStream$1.lineEnd = boundsLineEnd;
if (areaRingSum$1 < 0) lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon$3) phi1 = 90;else if (deltaSum < -epsilon$3) phi0 = -90;
range$2[0] = lambda0, range$2[1] = lambda1;
},
sphere: function () {
lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);
}
};
-
function boundsPoint$1(lambda, phi) {
ranges.push(range$2 = [lambda0 = lambda, lambda1 = lambda]);
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
}
-
function linePoint(lambda, phi) {
var p = cartesian([lambda * radians, phi * radians]);
-
if (p0) {
var normal = cartesianCross(p0, p),
- equatorial = [normal[1], -normal[0], 0],
- inflection = cartesianCross(equatorial, normal);
+ equatorial = [normal[1], -normal[0], 0],
+ inflection = cartesianCross(equatorial, normal);
cartesianNormalizeInPlace(inflection);
inflection = spherical(inflection);
var delta = lambda - lambda2,
- sign = delta > 0 ? 1 : -1,
- lambdai = inflection[0] * degrees * sign,
- phii,
- antimeridian = abs$1(delta) > 180;
-
+ sign = delta > 0 ? 1 : -1,
+ lambdai = inflection[0] * degrees * sign,
+ phii,
+ antimeridian = abs$1(delta) > 180;
if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
phii = inflection[1] * degrees;
if (phii > phi1) phi1 = phii;
} else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
phii = -inflection[1] * degrees;
if (phii < phi0) phi0 = phii;
} else {
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
}
-
if (antimeridian) {
if (lambda < lambda2) {
if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;
} else {
if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;
@@ -24524,99 +22220,90 @@
}
}
} else {
ranges.push(range$2 = [lambda0 = lambda, lambda1 = lambda]);
}
-
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
p0 = p, lambda2 = lambda;
}
-
function boundsLineStart() {
- boundsStream$2.point = linePoint;
+ boundsStream$1.point = linePoint;
}
-
function boundsLineEnd() {
range$2[0] = lambda0, range$2[1] = lambda1;
- boundsStream$2.point = boundsPoint$1;
+ boundsStream$1.point = boundsPoint$1;
p0 = null;
}
-
function boundsRingPoint(lambda, phi) {
if (p0) {
var delta = lambda - lambda2;
deltaSum.add(abs$1(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
} else {
lambda00$1 = lambda, phi00$1 = phi;
}
-
areaStream$1.point(lambda, phi);
linePoint(lambda, phi);
}
-
function boundsRingStart() {
areaStream$1.lineStart();
}
-
function boundsRingEnd() {
boundsRingPoint(lambda00$1, phi00$1);
areaStream$1.lineEnd();
if (abs$1(deltaSum) > epsilon$3) lambda0 = -(lambda1 = 180);
range$2[0] = lambda0, range$2[1] = lambda1;
p0 = null;
- } // Finds the left-right distance between two longitudes.
+ }
+
+ // Finds the left-right distance between two longitudes.
// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
// the distance between ±180° to be 360°.
-
-
function angle(lambda0, lambda1) {
return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
}
-
function rangeCompare(a, b) {
return a[0] - b[0];
}
-
function rangeContains(range, x) {
return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
}
-
function geoBounds$1 (feature) {
var i, n, a, b, merged, deltaMax, delta;
phi1 = lambda1 = -(lambda0 = phi0 = Infinity);
ranges = [];
- geoStream(feature, boundsStream$2); // First, sort ranges by their minimum longitudes.
+ geoStream(feature, boundsStream$1);
+ // First, sort ranges by their minimum longitudes.
if (n = ranges.length) {
- ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
+ ranges.sort(rangeCompare);
+ // Then, merge any ranges that overlap.
for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
b = ranges[i];
-
if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
} else {
merged.push(a = b);
}
- } // Finally, find the largest gap between the merged ranges.
- // The final bounding box will be the inverse of this gap.
+ }
-
+ // Finally, find the largest gap between the merged ranges.
+ // The final bounding box will be the inverse of this gap.
for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
b = merged[i];
if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0 = b[0], lambda1 = a[1];
}
}
-
ranges = range$2 = null;
return lambda0 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0, phi0], [lambda1, phi1]];
}
- var W0, W1, X0$1, Y0$1, Z0$1, X1$1, Y1$1, Z1$1, X2$1, Y2$1, Z2$1, lambda00, phi00, // first point
- x0$4, y0$4, z0; // previous point
+ var W0, W1, X0$1, Y0$1, Z0$1, X1$1, Y1$1, Z1$1, X2$1, Y2$1, Z2$1, lambda00, phi00,
+ // first point
+ x0$4, y0$4, z0; // previous point
var centroidStream$1 = {
sphere: noop$2,
point: centroidPoint$1,
lineStart: centroidLineStart$1,
@@ -24627,232 +22314,214 @@
},
polygonEnd: function () {
centroidStream$1.lineStart = centroidLineStart$1;
centroidStream$1.lineEnd = centroidLineEnd$1;
}
- }; // Arithmetic mean of Cartesian vectors.
+ };
+ // Arithmetic mean of Cartesian vectors.
function centroidPoint$1(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi);
centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi));
}
-
function centroidPointCartesian(x, y, z) {
++W0;
X0$1 += (x - X0$1) / W0;
Y0$1 += (y - Y0$1) / W0;
Z0$1 += (z - Z0$1) / W0;
}
-
function centroidLineStart$1() {
centroidStream$1.point = centroidLinePointFirst;
}
-
function centroidLinePointFirst(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi);
x0$4 = cosPhi * cos$1(lambda);
y0$4 = cosPhi * sin$1(lambda);
z0 = sin$1(phi);
centroidStream$1.point = centroidLinePoint;
centroidPointCartesian(x0$4, y0$4, z0);
}
-
function centroidLinePoint(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi),
- x = cosPhi * cos$1(lambda),
- y = cosPhi * sin$1(lambda),
- z = sin$1(phi),
- w = atan2(sqrt$1((w = y0$4 * z - z0 * y) * w + (w = z0 * x - x0$4 * z) * w + (w = x0$4 * y - y0$4 * x) * w), x0$4 * x + y0$4 * y + z0 * z);
+ x = cosPhi * cos$1(lambda),
+ y = cosPhi * sin$1(lambda),
+ z = sin$1(phi),
+ w = atan2(sqrt$1((w = y0$4 * z - z0 * y) * w + (w = z0 * x - x0$4 * z) * w + (w = x0$4 * y - y0$4 * x) * w), x0$4 * x + y0$4 * y + z0 * z);
W1 += w;
X1$1 += w * (x0$4 + (x0$4 = x));
Y1$1 += w * (y0$4 + (y0$4 = y));
Z1$1 += w * (z0 + (z0 = z));
centroidPointCartesian(x0$4, y0$4, z0);
}
-
function centroidLineEnd$1() {
centroidStream$1.point = centroidPoint$1;
- } // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
- // J. Applied Mechanics 42, 239 (1975).
+ }
-
+ // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
+ // J. Applied Mechanics 42, 239 (1975).
function centroidRingStart$1() {
centroidStream$1.point = centroidRingPointFirst;
}
-
function centroidRingEnd$1() {
centroidRingPoint(lambda00, phi00);
centroidStream$1.point = centroidPoint$1;
}
-
function centroidRingPointFirst(lambda, phi) {
lambda00 = lambda, phi00 = phi;
lambda *= radians, phi *= radians;
centroidStream$1.point = centroidRingPoint;
var cosPhi = cos$1(phi);
x0$4 = cosPhi * cos$1(lambda);
y0$4 = cosPhi * sin$1(lambda);
z0 = sin$1(phi);
centroidPointCartesian(x0$4, y0$4, z0);
}
-
function centroidRingPoint(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi),
- x = cosPhi * cos$1(lambda),
- y = cosPhi * sin$1(lambda),
- z = sin$1(phi),
- cx = y0$4 * z - z0 * y,
- cy = z0 * x - x0$4 * z,
- cz = x0$4 * y - y0$4 * x,
- m = hypot(cx, cy, cz),
- w = asin$1(m),
- // line weight = angle
- v = m && -w / m; // area weight multiplier
-
+ x = cosPhi * cos$1(lambda),
+ y = cosPhi * sin$1(lambda),
+ z = sin$1(phi),
+ cx = y0$4 * z - z0 * y,
+ cy = z0 * x - x0$4 * z,
+ cz = x0$4 * y - y0$4 * x,
+ m = hypot(cx, cy, cz),
+ w = asin$1(m),
+ // line weight = angle
+ v = m && -w / m; // area weight multiplier
X2$1.add(v * cx);
Y2$1.add(v * cy);
Z2$1.add(v * cz);
W1 += w;
X1$1 += w * (x0$4 + (x0$4 = x));
Y1$1 += w * (y0$4 + (y0$4 = y));
Z1$1 += w * (z0 + (z0 = z));
centroidPointCartesian(x0$4, y0$4, z0);
}
-
function geoCentroid$1 (object) {
W0 = W1 = X0$1 = Y0$1 = Z0$1 = X1$1 = Y1$1 = Z1$1 = 0;
X2$1 = new Adder();
Y2$1 = new Adder();
Z2$1 = new Adder();
geoStream(object, centroidStream$1);
var x = +X2$1,
- y = +Y2$1,
- z = +Z2$1,
- m = hypot(x, y, z); // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
+ y = +Y2$1,
+ z = +Z2$1,
+ m = hypot(x, y, z);
+ // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
if (m < epsilon2) {
- x = X1$1, y = Y1$1, z = Z1$1; // If the feature has zero length, fall back to arithmetic mean of point vectors.
-
+ x = X1$1, y = Y1$1, z = Z1$1;
+ // If the feature has zero length, fall back to arithmetic mean of point vectors.
if (W1 < epsilon$3) x = X0$1, y = Y0$1, z = Z0$1;
- m = hypot(x, y, z); // If the feature still has an undefined ccentroid, then return.
-
+ m = hypot(x, y, z);
+ // If the feature still has an undefined ccentroid, then return.
if (m < epsilon2) return [NaN, NaN];
}
-
return [atan2(y, x) * degrees, asin$1(z / m) * degrees];
}
function compose (a, b) {
function compose(x, y) {
return x = a(x, y), b(x[0], x[1]);
}
-
if (a.invert && b.invert) compose.invert = function (x, y) {
return x = b.invert(x, y), x && a.invert(x[0], x[1]);
};
return compose;
}
function rotationIdentity(lambda, phi) {
- return [abs$1(lambda) > pi$1 ? lambda + Math.round(-lambda / tau$1) * tau$1 : lambda, phi];
+ if (abs$1(lambda) > pi$1) lambda -= Math.round(lambda / tau$1) * tau$1;
+ return [lambda, phi];
}
-
rotationIdentity.invert = rotationIdentity;
function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
return (deltaLambda %= tau$1) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
}
-
function forwardRotationLambda(deltaLambda) {
return function (lambda, phi) {
- return lambda += deltaLambda, [lambda > pi$1 ? lambda - tau$1 : lambda < -pi$1 ? lambda + tau$1 : lambda, phi];
+ lambda += deltaLambda;
+ if (abs$1(lambda) > pi$1) lambda -= Math.round(lambda / tau$1) * tau$1;
+ return [lambda, phi];
};
}
-
function rotationLambda(deltaLambda) {
var rotation = forwardRotationLambda(deltaLambda);
rotation.invert = forwardRotationLambda(-deltaLambda);
return rotation;
}
-
function rotationPhiGamma(deltaPhi, deltaGamma) {
var cosDeltaPhi = cos$1(deltaPhi),
- sinDeltaPhi = sin$1(deltaPhi),
- cosDeltaGamma = cos$1(deltaGamma),
- sinDeltaGamma = sin$1(deltaGamma);
-
+ sinDeltaPhi = sin$1(deltaPhi),
+ cosDeltaGamma = cos$1(deltaGamma),
+ sinDeltaGamma = sin$1(deltaGamma);
function rotation(lambda, phi) {
var cosPhi = cos$1(phi),
- x = cos$1(lambda) * cosPhi,
- y = sin$1(lambda) * cosPhi,
- z = sin$1(phi),
- k = z * cosDeltaPhi + x * sinDeltaPhi;
+ x = cos$1(lambda) * cosPhi,
+ y = sin$1(lambda) * cosPhi,
+ z = sin$1(phi),
+ k = z * cosDeltaPhi + x * sinDeltaPhi;
return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin$1(k * cosDeltaGamma + y * sinDeltaGamma)];
}
-
rotation.invert = function (lambda, phi) {
var cosPhi = cos$1(phi),
- x = cos$1(lambda) * cosPhi,
- y = sin$1(lambda) * cosPhi,
- z = sin$1(phi),
- k = z * cosDeltaGamma - y * sinDeltaGamma;
+ x = cos$1(lambda) * cosPhi,
+ y = sin$1(lambda) * cosPhi,
+ z = sin$1(phi),
+ k = z * cosDeltaGamma - y * sinDeltaGamma;
return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin$1(k * cosDeltaPhi - x * sinDeltaPhi)];
};
-
return rotation;
}
-
function rotation (rotate) {
rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
-
function forward(coordinates) {
coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
}
-
forward.invert = function (coordinates) {
coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
};
-
return forward;
}
+ // Generates a circle centered at [0°, 0°], with a given radius and precision.
function circleStream(stream, radius, delta, direction, t0, t1) {
if (!delta) return;
var cosRadius = cos$1(radius),
- sinRadius = sin$1(radius),
- step = direction * delta;
-
+ sinRadius = sin$1(radius),
+ step = direction * delta;
if (t0 == null) {
t0 = radius + direction * tau$1;
t1 = radius - step / 2;
} else {
t0 = circleRadius(cosRadius, t0);
t1 = circleRadius(cosRadius, t1);
if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$1;
}
-
for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]);
stream.point(point[0], point[1]);
}
- } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
+ }
+ // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
function circleRadius(cosRadius, point) {
point = cartesian(point), point[0] -= cosRadius;
cartesianNormalizeInPlace(point);
var radius = acos(-point[1]);
return ((-point[2] < 0 ? -radius : radius) + tau$1 - epsilon$3) % tau$1;
}
function clipBuffer () {
var lines = [],
- line;
+ line;
return {
point: function (x, y, m) {
line.push([x, y, m]);
},
lineStart: function () {
@@ -24877,199 +22546,174 @@
function Intersection(point, points, other, entry) {
this.x = point;
this.z = points;
this.o = other; // another intersection
-
this.e = entry; // is an entry?
-
this.v = false; // visited
-
this.n = this.p = null; // next & previous
- } // A generalized polygon clipping algorithm: given a polygon that has been cut
+ }
+
+ // A generalized polygon clipping algorithm: given a polygon that has been cut
// into its visible line segments, and rejoins the segments by interpolating
// along the clip edge.
-
-
function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
var subject = [],
- clip = [],
- i,
- n;
+ clip = [],
+ i,
+ n;
segments.forEach(function (segment) {
if ((n = segment.length - 1) <= 0) return;
var n,
- p0 = segment[0],
- p1 = segment[n],
- x;
-
+ p0 = segment[0],
+ p1 = segment[n],
+ x;
if (pointEqual(p0, p1)) {
if (!p0[2] && !p1[2]) {
stream.lineStart();
-
for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
-
stream.lineEnd();
return;
- } // handle degenerate cases by moving the point
-
-
+ }
+ // handle degenerate cases by moving the point
p1[0] += 2 * epsilon$3;
}
-
subject.push(x = new Intersection(p0, segment, null, true));
clip.push(x.o = new Intersection(p0, null, x, false));
subject.push(x = new Intersection(p1, segment, null, false));
clip.push(x.o = new Intersection(p1, null, x, true));
});
if (!subject.length) return;
clip.sort(compareIntersection);
link(subject);
link(clip);
-
for (i = 0, n = clip.length; i < n; ++i) {
clip[i].e = startInside = !startInside;
}
-
var start = subject[0],
- points,
- point;
-
+ points,
+ point;
while (1) {
// Find first unvisited intersection.
var current = start,
- isSubject = true;
-
+ isSubject = true;
while (current.v) if ((current = current.n) === start) return;
-
points = current.z;
stream.lineStart();
-
do {
current.v = current.o.v = true;
-
if (current.e) {
if (isSubject) {
for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
} else {
interpolate(current.x, current.n.x, 1, stream);
}
-
current = current.n;
} else {
if (isSubject) {
points = current.p.z;
-
for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
} else {
interpolate(current.x, current.p.x, -1, stream);
}
-
current = current.p;
}
-
current = current.o;
points = current.z;
isSubject = !isSubject;
} while (!current.v);
-
stream.lineEnd();
}
}
-
function link(array) {
if (!(n = array.length)) return;
var n,
- i = 0,
- a = array[0],
- b;
-
+ i = 0,
+ a = array[0],
+ b;
while (++i < n) {
a.n = b = array[i];
b.p = a;
a = b;
}
-
a.n = b = array[0];
b.p = a;
}
function longitude(point) {
return abs$1(point[0]) <= pi$1 ? point[0] : sign(point[0]) * ((abs$1(point[0]) + pi$1) % tau$1 - pi$1);
}
-
function polygonContains (polygon, point) {
var lambda = longitude(point),
- phi = point[1],
- sinPhi = sin$1(phi),
- normal = [sin$1(lambda), -cos$1(lambda), 0],
- angle = 0,
- winding = 0;
+ phi = point[1],
+ sinPhi = sin$1(phi),
+ normal = [sin$1(lambda), -cos$1(lambda), 0],
+ angle = 0,
+ winding = 0;
var sum = new Adder();
if (sinPhi === 1) phi = halfPi$1 + epsilon$3;else if (sinPhi === -1) phi = -halfPi$1 - epsilon$3;
-
for (var i = 0, n = polygon.length; i < n; ++i) {
if (!(m = (ring = polygon[i]).length)) continue;
var ring,
- m,
- point0 = ring[m - 1],
- lambda0 = longitude(point0),
- phi0 = point0[1] / 2 + quarterPi,
- sinPhi0 = sin$1(phi0),
- cosPhi0 = cos$1(phi0);
-
+ m,
+ point0 = ring[m - 1],
+ lambda0 = longitude(point0),
+ phi0 = point0[1] / 2 + quarterPi,
+ sinPhi0 = sin$1(phi0),
+ cosPhi0 = cos$1(phi0);
for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
var point1 = ring[j],
- lambda1 = longitude(point1),
- phi1 = point1[1] / 2 + quarterPi,
- sinPhi1 = sin$1(phi1),
- cosPhi1 = cos$1(phi1),
- delta = lambda1 - lambda0,
- sign = delta >= 0 ? 1 : -1,
- absDelta = sign * delta,
- antimeridian = absDelta > pi$1,
- k = sinPhi0 * sinPhi1;
+ lambda1 = longitude(point1),
+ phi1 = point1[1] / 2 + quarterPi,
+ sinPhi1 = sin$1(phi1),
+ cosPhi1 = cos$1(phi1),
+ delta = lambda1 - lambda0,
+ sign = delta >= 0 ? 1 : -1,
+ absDelta = sign * delta,
+ antimeridian = absDelta > pi$1,
+ k = sinPhi0 * sinPhi1;
sum.add(atan2(k * sign * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta)));
- angle += antimeridian ? delta + sign * tau$1 : delta; // Are the longitudes either side of the point’s meridian (lambda),
- // and are the latitudes smaller than the parallel (phi)?
+ angle += antimeridian ? delta + sign * tau$1 : delta;
+ // Are the longitudes either side of the point’s meridian (lambda),
+ // and are the latitudes smaller than the parallel (phi)?
if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
var arc = cartesianCross(cartesian(point0), cartesian(point1));
cartesianNormalizeInPlace(arc);
var intersection = cartesianCross(normal, arc);
cartesianNormalizeInPlace(intersection);
var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin$1(intersection[2]);
-
if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
winding += antimeridian ^ delta >= 0 ? 1 : -1;
}
}
}
- } // First, determine whether the South pole is inside or outside:
+ }
+
+ // First, determine whether the South pole is inside or outside:
//
// It is inside if:
// * the polygon winds around it in a clockwise direction.
// * the polygon does not (cumulatively) wind around it, but has a negative
// (counter-clockwise) area.
//
// Second, count the (signed) number of times a segment crosses a lambda
// from the point to the South pole. If it is zero, then the point is the
// same side as the South pole.
-
return (angle < -epsilon$3 || angle < epsilon$3 && sum < -epsilon2) ^ winding & 1;
}
function clip$1 (pointVisible, clipLine, interpolate, start) {
return function (sink) {
var line = clipLine(sink),
- ringBuffer = clipBuffer(),
- ringSink = clipLine(ringBuffer),
- polygonStarted = false,
- polygon,
- segments,
- ring;
+ ringBuffer = clipBuffer(),
+ ringSink = clipLine(ringBuffer),
+ polygonStarted = false,
+ polygon,
+ segments,
+ ring;
var clip = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: function () {
@@ -25083,21 +22727,19 @@
clip.point = point;
clip.lineStart = lineStart;
clip.lineEnd = lineEnd;
segments = merge$2(segments);
var startInside = polygonContains(polygon, start);
-
if (segments.length) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
} else if (startInside) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
sink.lineStart();
interpolate(null, null, 1, sink);
sink.lineEnd();
}
-
if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
segments = polygon = null;
},
sphere: function () {
sink.polygonStart();
@@ -25105,110 +22747,98 @@
interpolate(null, null, 1, sink);
sink.lineEnd();
sink.polygonEnd();
}
};
-
function point(lambda, phi) {
if (pointVisible(lambda, phi)) sink.point(lambda, phi);
}
-
function pointLine(lambda, phi) {
line.point(lambda, phi);
}
-
function lineStart() {
clip.point = pointLine;
line.lineStart();
}
-
function lineEnd() {
clip.point = point;
line.lineEnd();
}
-
function pointRing(lambda, phi) {
ring.push([lambda, phi]);
ringSink.point(lambda, phi);
}
-
function ringStart() {
ringSink.lineStart();
ring = [];
}
-
function ringEnd() {
pointRing(ring[0][0], ring[0][1]);
ringSink.lineEnd();
var clean = ringSink.clean(),
- ringSegments = ringBuffer.result(),
- i,
- n = ringSegments.length,
- m,
- segment,
- point;
+ ringSegments = ringBuffer.result(),
+ i,
+ n = ringSegments.length,
+ m,
+ segment,
+ point;
ring.pop();
polygon.push(ring);
ring = null;
- if (!n) return; // No intersections.
+ if (!n) return;
+ // No intersections.
if (clean & 1) {
segment = ringSegments[0];
-
if ((m = segment.length - 1) > 0) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
sink.lineStart();
-
for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
-
sink.lineEnd();
}
-
return;
- } // Rejoin connected segments.
- // TODO reuse ringBuffer.rejoin()?
+ }
-
+ // Rejoin connected segments.
+ // TODO reuse ringBuffer.rejoin()?
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
segments.push(ringSegments.filter(validSegment));
}
-
return clip;
};
}
-
function validSegment(segment) {
return segment.length > 1;
- } // Intersections are sorted along the clip edge. For both antimeridian cutting
- // and circle clipping, the same comparison is used.
+ }
-
+ // Intersections are sorted along the clip edge. For both antimeridian cutting
+ // and circle clipping, the same comparison is used.
function compareIntersection(a, b) {
return ((a = a.x)[0] < 0 ? a[1] - halfPi$1 - epsilon$3 : halfPi$1 - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi$1 - epsilon$3 : halfPi$1 - b[1]);
}
var clipAntimeridian = clip$1(function () {
return true;
- }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi$1, -halfPi$1]); // Takes a line and cuts into visible segments. Return values: 0 - there were
+ }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi$1, -halfPi$1]);
+
+ // Takes a line and cuts into visible segments. Return values: 0 - there were
// intersections or the line was empty; 1 - no intersections; 2 - there were
// intersections, and the first and last segments should be rejoined.
-
function clipAntimeridianLine(stream) {
var lambda0 = NaN,
- phi0 = NaN,
- sign0 = NaN,
- clean; // no intersections
+ phi0 = NaN,
+ sign0 = NaN,
+ clean; // no intersections
return {
lineStart: function () {
stream.lineStart();
clean = 1;
},
point: function (lambda1, phi1) {
var sign1 = lambda1 > 0 ? pi$1 : -pi$1,
- delta = abs$1(lambda1 - lambda0);
-
+ delta = abs$1(lambda1 - lambda0);
if (abs$1(delta - pi$1) < epsilon$3) {
// line crosses a pole
stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$1 : -halfPi$1);
stream.point(sign0, phi0);
stream.lineEnd();
@@ -25217,20 +22847,18 @@
stream.point(lambda1, phi0);
clean = 0;
} else if (sign0 !== sign1 && delta >= pi$1) {
// line crosses antimeridian
if (abs$1(lambda0 - sign0) < epsilon$3) lambda0 -= sign0 * epsilon$3; // handle degeneracies
-
if (abs$1(lambda1 - sign1) < epsilon$3) lambda1 -= sign1 * epsilon$3;
phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
stream.point(sign0, phi0);
stream.lineEnd();
stream.lineStart();
stream.point(sign1, phi0);
clean = 0;
}
-
stream.point(lambda0 = lambda1, phi0 = phi1);
sign0 = sign1;
},
lineEnd: function () {
stream.lineEnd();
@@ -25239,21 +22867,18 @@
clean: function () {
return 2 - clean; // if intersections, rejoin first and last segments
}
};
}
-
function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
var cosPhi0,
- cosPhi1,
- sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
+ cosPhi1,
+ sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
return abs$1(sinLambda0Lambda1) > epsilon$3 ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1) - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2;
}
-
function clipAntimeridianInterpolate(from, to, direction, stream) {
var phi;
-
if (from == null) {
phi = direction * halfPi$1;
stream.point(-pi$1, phi);
stream.point(0, phi);
stream.point(pi$1, phi);
@@ -25274,53 +22899,52 @@
}
}
function clipCircle (radius) {
var cr = cos$1(radius),
- delta = 6 * radians,
- smallRadius = cr > 0,
- notHemisphere = abs$1(cr) > epsilon$3; // TODO optimise for this common case
+ delta = 6 * radians,
+ smallRadius = cr > 0,
+ notHemisphere = abs$1(cr) > epsilon$3; // TODO optimise for this common case
function interpolate(from, to, direction, stream) {
circleStream(stream, radius, delta, direction, from, to);
}
-
function visible(lambda, phi) {
return cos$1(lambda) * cos$1(phi) > cr;
- } // Takes a line and cuts into visible segments. Return values used for polygon
+ }
+
+ // Takes a line and cuts into visible segments. Return values used for polygon
// clipping: 0 - there were intersections or the line was empty; 1 - no
// intersections 2 - there were intersections, and the first and last segments
// should be rejoined.
-
-
function clipLine(stream) {
- var point0, // previous point
- c0, // code for previous point
- v0, // visibility of previous point
- v00, // visibility of first point
- clean; // no intersections
-
+ var point0,
+ // previous point
+ c0,
+ // code for previous point
+ v0,
+ // visibility of previous point
+ v00,
+ // visibility of first point
+ clean; // no intersections
return {
lineStart: function () {
v00 = v0 = false;
clean = 1;
},
point: function (lambda, phi) {
var point1 = [lambda, phi],
- point2,
- v = visible(lambda, phi),
- c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi$1 : -pi$1), phi) : 0;
+ point2,
+ v = visible(lambda, phi),
+ c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi$1 : -pi$1), phi) : 0;
if (!point0 && (v00 = v0 = v)) stream.lineStart();
-
if (v !== v0) {
point2 = intersect(point0, point1);
if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
}
-
if (v !== v0) {
clean = 0;
-
if (v) {
// outside going in
stream.lineStart();
point2 = intersect(point1, point0);
stream.point(point2[0], point2[1]);
@@ -25328,19 +22952,17 @@
// inside going out
point2 = intersect(point0, point1);
stream.point(point2[0], point2[1], 2);
stream.lineEnd();
}
-
point0 = point2;
} else if (notHemisphere && point0 && smallRadius ^ v) {
- var t; // If the codes for two points are different, or are both zero,
+ var t;
+ // If the codes for two points are different, or are both zero,
// and there this segment intersects with the small circle.
-
if (!(c & c0) && (t = intersect(point1, point0, true))) {
clean = 0;
-
if (smallRadius) {
stream.lineStart();
stream.point(t[0][0], t[0][1]);
stream.point(t[1][0], t[1][1]);
stream.lineEnd();
@@ -25350,15 +22972,13 @@
stream.lineStart();
stream.point(t[0][0], t[0][1], 3);
}
}
}
-
if (v && (!point0 || !pointEqual(point0, point1))) {
stream.point(point1[0], point1[1]);
}
-
point0 = point1, v0 = v, c0 = c;
},
lineEnd: function () {
if (v0) stream.lineEnd();
point0 = null;
@@ -25367,311 +22987,281 @@
// and last points were visible.
clean: function () {
return clean | (v00 && v0) << 1;
}
};
- } // Intersects the great circle between a and b with the clip circle.
+ }
-
+ // Intersects the great circle between a and b with the clip circle.
function intersect(a, b, two) {
var pa = cartesian(a),
- pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
- // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
+ pb = cartesian(b);
+ // We have two planes, n1.p = d1 and n2.p = d2.
+ // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
var n1 = [1, 0, 0],
- // normal
- n2 = cartesianCross(pa, pb),
- n2n2 = cartesianDot(n2, n2),
- n1n2 = n2[0],
- // cartesianDot(n1, n2),
- determinant = n2n2 - n1n2 * n1n2; // Two polar points.
+ // normal
+ n2 = cartesianCross(pa, pb),
+ n2n2 = cartesianDot(n2, n2),
+ n1n2 = n2[0],
+ // cartesianDot(n1, n2),
+ determinant = n2n2 - n1n2 * n1n2;
+ // Two polar points.
if (!determinant) return !two && a;
var c1 = cr * n2n2 / determinant,
- c2 = -cr * n1n2 / determinant,
- n1xn2 = cartesianCross(n1, n2),
- A = cartesianScale(n1, c1),
- B = cartesianScale(n2, c2);
- cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
+ c2 = -cr * n1n2 / determinant,
+ n1xn2 = cartesianCross(n1, n2),
+ A = cartesianScale(n1, c1),
+ B = cartesianScale(n2, c2);
+ cartesianAddInPlace(A, B);
+ // Solve |p(t)|^2 = 1.
var u = n1xn2,
- w = cartesianDot(A, u),
- uu = cartesianDot(u, u),
- t2 = w * w - uu * (cartesianDot(A, A) - 1);
+ w = cartesianDot(A, u),
+ uu = cartesianDot(u, u),
+ t2 = w * w - uu * (cartesianDot(A, A) - 1);
if (t2 < 0) return;
var t = sqrt$1(t2),
- q = cartesianScale(u, (-w - t) / uu);
+ q = cartesianScale(u, (-w - t) / uu);
cartesianAddInPlace(q, A);
q = spherical(q);
- if (!two) return q; // Two intersection points.
+ if (!two) return q;
+ // Two intersection points.
var lambda0 = a[0],
- lambda1 = b[0],
- phi0 = a[1],
- phi1 = b[1],
- z;
+ lambda1 = b[0],
+ phi0 = a[1],
+ phi1 = b[1],
+ z;
if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
var delta = lambda1 - lambda0,
- polar = abs$1(delta - pi$1) < epsilon$3,
- meridian = polar || delta < epsilon$3;
- if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
+ polar = abs$1(delta - pi$1) < epsilon$3,
+ meridian = polar || delta < epsilon$3;
+ if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
+ // Check that the first point is between a and b.
if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$1(q[0] - lambda0) < epsilon$3 ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi$1 ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
var q1 = cartesianScale(u, (-w + t) / uu);
cartesianAddInPlace(q1, A);
return [q, spherical(q1)];
}
- } // Generates a 4-bit vector representing the location of a point relative to
- // the small circle's bounding box.
+ }
-
+ // Generates a 4-bit vector representing the location of a point relative to
+ // the small circle's bounding box.
function code(lambda, phi) {
var r = smallRadius ? radius : pi$1 - radius,
- code = 0;
+ code = 0;
if (lambda < -r) code |= 1; // left
else if (lambda > r) code |= 2; // right
-
if (phi < -r) code |= 4; // below
else if (phi > r) code |= 8; // above
-
return code;
}
-
return clip$1(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$1, radius - pi$1]);
}
function clipLine (a, b, x0, y0, x1, y1) {
var ax = a[0],
- ay = a[1],
- bx = b[0],
- by = b[1],
- t0 = 0,
- t1 = 1,
- dx = bx - ax,
- dy = by - ay,
- r;
+ ay = a[1],
+ bx = b[0],
+ by = b[1],
+ t0 = 0,
+ t1 = 1,
+ dx = bx - ax,
+ dy = by - ay,
+ r;
r = x0 - ax;
if (!dx && r > 0) return;
r /= dx;
-
if (dx < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dx > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
-
r = x1 - ax;
if (!dx && r < 0) return;
r /= dx;
-
if (dx < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dx > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
-
r = y0 - ay;
if (!dy && r > 0) return;
r /= dy;
-
if (dy < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dy > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
-
r = y1 - ay;
if (!dy && r < 0) return;
r /= dy;
-
if (dy < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dy > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
-
if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
return true;
}
var clipMax = 1e9,
- clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
+ clipMin = -clipMax;
+
+ // TODO Use d3-polygon’s polygonContains here for the ring check?
// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
function clipRectangle(x0, y0, x1, y1) {
function visible(x, y) {
return x0 <= x && x <= x1 && y0 <= y && y <= y1;
}
-
function interpolate(from, to, direction, stream) {
var a = 0,
- a1 = 0;
-
+ a1 = 0;
if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); while ((a = (a + direction + 4) % 4) !== a1);
} else {
stream.point(to[0], to[1]);
}
}
-
function corner(p, direction) {
return abs$1(p[0] - x0) < epsilon$3 ? direction > 0 ? 0 : 3 : abs$1(p[0] - x1) < epsilon$3 ? direction > 0 ? 2 : 1 : abs$1(p[1] - y0) < epsilon$3 ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
}
-
function compareIntersection(a, b) {
return comparePoint(a.x, b.x);
}
-
function comparePoint(a, b) {
var ca = corner(a, 1),
- cb = corner(b, 1);
+ cb = corner(b, 1);
return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
}
-
return function (stream) {
var activeStream = stream,
- bufferStream = clipBuffer(),
- segments,
- polygon,
- ring,
- x__,
- y__,
- v__,
- // first point
- x_,
- y_,
- v_,
- // previous point
- first,
- clean;
+ bufferStream = clipBuffer(),
+ segments,
+ polygon,
+ ring,
+ x__,
+ y__,
+ v__,
+ // first point
+ x_,
+ y_,
+ v_,
+ // previous point
+ first,
+ clean;
var clipStream = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: polygonStart,
polygonEnd: polygonEnd
};
-
function point(x, y) {
if (visible(x, y)) activeStream.point(x, y);
}
-
function polygonInside() {
var winding = 0;
-
for (var i = 0, n = polygon.length; i < n; ++i) {
for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
-
if (a1 <= y1) {
if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
} else {
if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
}
}
}
-
return winding;
- } // Buffer geometry within a polygon and then clip it en masse.
+ }
-
+ // Buffer geometry within a polygon and then clip it en masse.
function polygonStart() {
activeStream = bufferStream, segments = [], polygon = [], clean = true;
}
-
function polygonEnd() {
var startInside = polygonInside(),
- cleanInside = clean && startInside,
- visible = (segments = merge$2(segments)).length;
-
+ cleanInside = clean && startInside,
+ visible = (segments = merge$2(segments)).length;
if (cleanInside || visible) {
stream.polygonStart();
-
if (cleanInside) {
stream.lineStart();
interpolate(null, null, 1, stream);
stream.lineEnd();
}
-
if (visible) {
clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
}
-
stream.polygonEnd();
}
-
activeStream = stream, segments = polygon = ring = null;
}
-
function lineStart() {
clipStream.point = linePoint;
if (polygon) polygon.push(ring = []);
first = true;
v_ = false;
x_ = y_ = NaN;
- } // TODO rather than special-case polygons, simply handle them separately.
+ }
+
+ // TODO rather than special-case polygons, simply handle them separately.
// Ideally, coincident intersection points should be jittered to avoid
// clipping issues.
-
-
function lineEnd() {
if (segments) {
linePoint(x__, y__);
if (v__ && v_) bufferStream.rejoin();
segments.push(bufferStream.result());
}
-
clipStream.point = point;
if (v_) activeStream.lineEnd();
}
-
function linePoint(x, y) {
var v = visible(x, y);
if (polygon) ring.push([x, y]);
-
if (first) {
x__ = x, y__ = y, v__ = v;
first = false;
-
if (v) {
activeStream.lineStart();
activeStream.point(x, y);
}
} else {
if (v && v_) activeStream.point(x, y);else {
var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
- b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
-
+ b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
if (clipLine(a, b, x0, y0, x1, y1)) {
if (!v_) {
activeStream.lineStart();
activeStream.point(a[0], a[1]);
}
-
activeStream.point(b[0], b[1]);
if (!v) activeStream.lineEnd();
clean = false;
} else if (v) {
activeStream.lineStart();
activeStream.point(x, y);
clean = false;
}
}
}
-
x_ = x, y_ = y, v_ = v;
}
-
return clipStream;
};
}
function graticuleX(y0, y1, dy) {
@@ -25680,131 +23270,117 @@
return y.map(function (y) {
return [x, y];
});
};
}
-
function graticuleY(x0, x1, dx) {
var x = range$3(x0, x1 - epsilon$3, dx).concat(x1);
return function (y) {
return x.map(function (x) {
return [x, y];
});
};
}
-
function graticule() {
var x1,
- x0,
- X1,
- X0,
- y1,
- y0,
- Y1,
- Y0,
- dx = 10,
- dy = dx,
- DX = 90,
- DY = 360,
- x,
- y,
- X,
- Y,
- precision = 2.5;
-
+ x0,
+ X1,
+ X0,
+ y1,
+ y0,
+ Y1,
+ Y0,
+ dx = 10,
+ dy = dx,
+ DX = 90,
+ DY = 360,
+ x,
+ y,
+ X,
+ Y,
+ precision = 2.5;
function graticule() {
return {
type: "MultiLineString",
coordinates: lines()
};
}
-
function lines() {
return range$3(ceil(X0 / DX) * DX, X1, DX).map(X).concat(range$3(ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(range$3(ceil(x0 / dx) * dx, x1, dx).filter(function (x) {
return abs$1(x % DX) > epsilon$3;
}).map(x)).concat(range$3(ceil(y0 / dy) * dy, y1, dy).filter(function (y) {
return abs$1(y % DY) > epsilon$3;
}).map(y));
}
-
graticule.lines = function () {
return lines().map(function (coordinates) {
return {
type: "LineString",
coordinates: coordinates
};
});
};
-
graticule.outline = function () {
return {
type: "Polygon",
coordinates: [X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1))]
};
};
-
graticule.extent = function (_) {
if (!arguments.length) return graticule.extentMinor();
return graticule.extentMajor(_).extentMinor(_);
};
-
graticule.extentMajor = function (_) {
if (!arguments.length) return [[X0, Y0], [X1, Y1]];
X0 = +_[0][0], X1 = +_[1][0];
Y0 = +_[0][1], Y1 = +_[1][1];
if (X0 > X1) _ = X0, X0 = X1, X1 = _;
if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
return graticule.precision(precision);
};
-
graticule.extentMinor = function (_) {
if (!arguments.length) return [[x0, y0], [x1, y1]];
x0 = +_[0][0], x1 = +_[1][0];
y0 = +_[0][1], y1 = +_[1][1];
if (x0 > x1) _ = x0, x0 = x1, x1 = _;
if (y0 > y1) _ = y0, y0 = y1, y1 = _;
return graticule.precision(precision);
};
-
graticule.step = function (_) {
if (!arguments.length) return graticule.stepMinor();
return graticule.stepMajor(_).stepMinor(_);
};
-
graticule.stepMajor = function (_) {
if (!arguments.length) return [DX, DY];
DX = +_[0], DY = +_[1];
return graticule;
};
-
graticule.stepMinor = function (_) {
if (!arguments.length) return [dx, dy];
dx = +_[0], dy = +_[1];
return graticule;
};
-
graticule.precision = function (_) {
if (!arguments.length) return precision;
precision = +_;
x = graticuleX(y0, y1, 90);
y = graticuleY(x0, x1, precision);
X = graticuleX(Y0, Y1, 90);
Y = graticuleY(X0, X1, precision);
return graticule;
};
-
return graticule.extentMajor([[-180, -90 + epsilon$3], [180, 90 - epsilon$3]]).extentMinor([[-180, -80 - epsilon$3], [180, 80 + epsilon$3]]);
}
var identity = (x => x);
var areaSum = new Adder(),
- areaRingSum = new Adder(),
- x00$2,
- y00$2,
- x0$3,
- y0$3;
+ areaRingSum = new Adder(),
+ x00$2,
+ y00$2,
+ x0$3,
+ y0$3;
var areaStream = {
point: noop$2,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: function () {
@@ -25820,35 +23396,29 @@
var area = areaSum / 2;
areaSum = new Adder();
return area;
}
};
-
function areaRingStart() {
areaStream.point = areaPointFirst;
}
-
function areaPointFirst(x, y) {
areaStream.point = areaPoint;
x00$2 = x0$3 = x, y00$2 = y0$3 = y;
}
-
function areaPoint(x, y) {
areaRingSum.add(y0$3 * x - x0$3 * y);
x0$3 = x, y0$3 = y;
}
-
function areaRingEnd() {
areaPoint(x00$2, y00$2);
}
- var pathArea = areaStream;
-
var x0$2 = Infinity,
- y0$2 = x0$2,
- x1 = -x0$2,
- y1 = x1;
+ y0$2 = x0$2,
+ x1 = -x0$2,
+ y1 = x1;
var boundsStream = {
point: boundsPoint,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: noop$2,
@@ -25857,33 +23427,32 @@
var bounds = [[x0$2, y0$2], [x1, y1]];
x1 = y1 = -(y0$2 = x0$2 = Infinity);
return bounds;
}
};
-
function boundsPoint(x, y) {
if (x < x0$2) x0$2 = x;
if (x > x1) x1 = x;
if (y < y0$2) y0$2 = y;
if (y > y1) y1 = y;
}
- var boundsStream$1 = boundsStream;
+ // TODO Enforce positive area for exterior, negative area for interior?
var X0 = 0,
- Y0 = 0,
- Z0 = 0,
- X1 = 0,
- Y1 = 0,
- Z1 = 0,
- X2 = 0,
- Y2 = 0,
- Z2 = 0,
- x00$1,
- y00$1,
- x0$1,
- y0$1;
+ Y0 = 0,
+ Z0 = 0,
+ X1 = 0,
+ Y1 = 0,
+ Z1 = 0,
+ X2 = 0,
+ Y2 = 0,
+ Z2 = 0,
+ x00$1,
+ y00$1,
+ x0$1,
+ y0$1;
var centroidStream = {
point: centroidPoint,
lineStart: centroidLineStart,
lineEnd: centroidLineEnd,
polygonStart: function () {
@@ -25899,69 +23468,58 @@
var centroid = Z2 ? [X2 / Z2, Y2 / Z2] : Z1 ? [X1 / Z1, Y1 / Z1] : Z0 ? [X0 / Z0, Y0 / Z0] : [NaN, NaN];
X0 = Y0 = Z0 = X1 = Y1 = Z1 = X2 = Y2 = Z2 = 0;
return centroid;
}
};
-
function centroidPoint(x, y) {
X0 += x;
Y0 += y;
++Z0;
}
-
function centroidLineStart() {
centroidStream.point = centroidPointFirstLine;
}
-
function centroidPointFirstLine(x, y) {
centroidStream.point = centroidPointLine;
centroidPoint(x0$1 = x, y0$1 = y);
}
-
function centroidPointLine(x, y) {
var dx = x - x0$1,
- dy = y - y0$1,
- z = sqrt$1(dx * dx + dy * dy);
+ dy = y - y0$1,
+ z = sqrt$1(dx * dx + dy * dy);
X1 += z * (x0$1 + x) / 2;
Y1 += z * (y0$1 + y) / 2;
Z1 += z;
centroidPoint(x0$1 = x, y0$1 = y);
}
-
function centroidLineEnd() {
centroidStream.point = centroidPoint;
}
-
function centroidRingStart() {
centroidStream.point = centroidPointFirstRing;
}
-
function centroidRingEnd() {
centroidPointRing(x00$1, y00$1);
}
-
function centroidPointFirstRing(x, y) {
centroidStream.point = centroidPointRing;
centroidPoint(x00$1 = x0$1 = x, y00$1 = y0$1 = y);
}
-
function centroidPointRing(x, y) {
var dx = x - x0$1,
- dy = y - y0$1,
- z = sqrt$1(dx * dx + dy * dy);
+ dy = y - y0$1,
+ z = sqrt$1(dx * dx + dy * dy);
X1 += z * (x0$1 + x) / 2;
Y1 += z * (y0$1 + y) / 2;
Z1 += z;
z = y0$1 * x - x0$1 * y;
X2 += z * (x0$1 + x);
Y2 += z * (y0$1 + y);
Z2 += z * 3;
centroidPoint(x0$1 = x, y0$1 = y);
}
- var pathCentroid = centroidStream;
-
function PathContext(context) {
this._context = context;
}
PathContext.prototype = {
_radius: 4.5,
@@ -25984,41 +23542,35 @@
point: function (x, y) {
switch (this._point) {
case 0:
{
this._context.moveTo(x, y);
-
this._point = 1;
break;
}
-
case 1:
{
this._context.lineTo(x, y);
-
break;
}
-
default:
{
this._context.moveTo(x + this._radius, y);
-
this._context.arc(x, y, this._radius, 0, tau$1);
-
break;
}
}
},
result: noop$2
};
var lengthSum = new Adder(),
- lengthRing,
- x00,
- y00,
- x0,
- y0;
+ lengthRing,
+ x00,
+ y00,
+ x0,
+ y0;
var lengthStream = {
point: noop$2,
lineStart: function () {
lengthStream.point = lengthPointFirst;
},
@@ -26036,157 +23588,173 @@
var length = +lengthSum;
lengthSum = new Adder();
return length;
}
};
-
function lengthPointFirst(x, y) {
lengthStream.point = lengthPoint;
x00 = x0 = x, y00 = y0 = y;
}
-
function lengthPoint(x, y) {
x0 -= x, y0 -= y;
lengthSum.add(sqrt$1(x0 * x0 + y0 * y0));
x0 = x, y0 = y;
}
- var pathMeasure = lengthStream;
-
- function PathString() {
- this._string = [];
- }
- PathString.prototype = {
- _radius: 4.5,
- _circle: circle(4.5),
- pointRadius: function (_) {
- if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
+ // Simple caching for constant-radius points.
+ let cacheDigits, cacheAppend, cacheRadius, cacheCircle;
+ class PathString {
+ constructor(digits) {
+ this._append = digits == null ? append : appendRound(digits);
+ this._radius = 4.5;
+ this._ = "";
+ }
+ pointRadius(_) {
+ this._radius = +_;
return this;
- },
- polygonStart: function () {
+ }
+ polygonStart() {
this._line = 0;
- },
- polygonEnd: function () {
+ }
+ polygonEnd() {
this._line = NaN;
- },
- lineStart: function () {
+ }
+ lineStart() {
this._point = 0;
- },
- lineEnd: function () {
- if (this._line === 0) this._string.push("Z");
+ }
+ lineEnd() {
+ if (this._line === 0) this._ += "Z";
this._point = NaN;
- },
- point: function (x, y) {
+ }
+ point(x, y) {
switch (this._point) {
case 0:
{
- this._string.push("M", x, ",", y);
-
+ this._append`M${x},${y}`;
this._point = 1;
break;
}
-
case 1:
{
- this._string.push("L", x, ",", y);
-
+ this._append`L${x},${y}`;
break;
}
-
default:
{
- if (this._circle == null) this._circle = circle(this._radius);
-
- this._string.push("M", x, ",", y, this._circle);
-
+ this._append`M${x},${y}`;
+ if (this._radius !== cacheRadius || this._append !== cacheAppend) {
+ const r = this._radius;
+ const s = this._;
+ this._ = ""; // stash the old string so we can cache the circle path fragment
+ this._append`m0,${r}a${r},${r} 0 1,1 0,${-2 * r}a${r},${r} 0 1,1 0,${2 * r}z`;
+ cacheRadius = r;
+ cacheAppend = this._append;
+ cacheCircle = this._;
+ this._ = s;
+ }
+ this._ += cacheCircle;
break;
}
}
- },
- result: function () {
- if (this._string.length) {
- var result = this._string.join("");
-
- this._string = [];
- return result;
- } else {
- return null;
- }
}
- };
-
- function circle(radius) {
- return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
+ result() {
+ const result = this._;
+ this._ = "";
+ return result.length ? result : null;
+ }
}
+ function append(strings) {
+ let i = 1;
+ this._ += strings[0];
+ for (const j = strings.length; i < j; ++i) {
+ this._ += arguments[i] + strings[i];
+ }
+ }
+ function appendRound(digits) {
+ const d = Math.floor(digits);
+ if (!(d >= 0)) throw new RangeError(`invalid digits: ${digits}`);
+ if (d > 15) return append;
+ if (d !== cacheDigits) {
+ const k = 10 ** d;
+ cacheDigits = d;
+ cacheAppend = function append(strings) {
+ let i = 1;
+ this._ += strings[0];
+ for (const j = strings.length; i < j; ++i) {
+ this._ += Math.round(arguments[i] * k) / k + strings[i];
+ }
+ };
+ }
+ return cacheAppend;
+ }
function geoPath (projection, context) {
- var pointRadius = 4.5,
- projectionStream,
- contextStream;
-
+ let digits = 3,
+ pointRadius = 4.5,
+ projectionStream,
+ contextStream;
function path(object) {
if (object) {
if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
geoStream(object, projectionStream(contextStream));
}
-
return contextStream.result();
}
-
path.area = function (object) {
- geoStream(object, projectionStream(pathArea));
- return pathArea.result();
+ geoStream(object, projectionStream(areaStream));
+ return areaStream.result();
};
-
path.measure = function (object) {
- geoStream(object, projectionStream(pathMeasure));
- return pathMeasure.result();
+ geoStream(object, projectionStream(lengthStream));
+ return lengthStream.result();
};
-
path.bounds = function (object) {
- geoStream(object, projectionStream(boundsStream$1));
- return boundsStream$1.result();
+ geoStream(object, projectionStream(boundsStream));
+ return boundsStream.result();
};
-
path.centroid = function (object) {
- geoStream(object, projectionStream(pathCentroid));
- return pathCentroid.result();
+ geoStream(object, projectionStream(centroidStream));
+ return centroidStream.result();
};
-
path.projection = function (_) {
- return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
+ if (!arguments.length) return projection;
+ projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream;
+ return path;
};
-
path.context = function (_) {
if (!arguments.length) return context;
- contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
+ contextStream = _ == null ? (context = null, new PathString(digits)) : new PathContext(context = _);
if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
return path;
};
-
path.pointRadius = function (_) {
if (!arguments.length) return pointRadius;
pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
return path;
};
-
- return path.projection(projection).context(context);
+ path.digits = function (_) {
+ if (!arguments.length) return digits;
+ if (_ == null) digits = null;else {
+ const d = Math.floor(_);
+ if (!(d >= 0)) throw new RangeError(`invalid digits: ${_}`);
+ digits = d;
+ }
+ if (context === null) contextStream = new PathString(digits);
+ return path;
+ };
+ return path.projection(projection).digits(digits).context(context);
}
function transformer(methods) {
return function (stream) {
var s = new TransformStream();
-
for (var key in methods) s[key] = methods[key];
-
s.stream = stream;
return s;
};
}
-
function TransformStream() {}
-
TransformStream.prototype = {
constructor: TransformStream,
point: function (x, y) {
this.stream.point(x, y);
},
@@ -26209,99 +23777,94 @@
function fit$1(projection, fitBounds, object) {
var clip = projection.clipExtent && projection.clipExtent();
projection.scale(150).translate([0, 0]);
if (clip != null) projection.clipExtent(null);
- geoStream(object, projection.stream(boundsStream$1));
- fitBounds(boundsStream$1.result());
+ geoStream(object, projection.stream(boundsStream));
+ fitBounds(boundsStream.result());
if (clip != null) projection.clipExtent(clip);
return projection;
}
-
function fitExtent(projection, extent, object) {
return fit$1(projection, function (b) {
var w = extent[1][0] - extent[0][0],
- h = extent[1][1] - extent[0][1],
- k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
- x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
- y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
+ h = extent[1][1] - extent[0][1],
+ k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
+ x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
+ y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
projection.scale(150 * k).translate([x, y]);
}, object);
}
function fitSize(projection, size, object) {
return fitExtent(projection, [[0, 0], size], object);
}
function fitWidth(projection, width, object) {
return fit$1(projection, function (b) {
var w = +width,
- k = w / (b[1][0] - b[0][0]),
- x = (w - k * (b[1][0] + b[0][0])) / 2,
- y = -k * b[0][1];
+ k = w / (b[1][0] - b[0][0]),
+ x = (w - k * (b[1][0] + b[0][0])) / 2,
+ y = -k * b[0][1];
projection.scale(150 * k).translate([x, y]);
}, object);
}
function fitHeight(projection, height, object) {
return fit$1(projection, function (b) {
var h = +height,
- k = h / (b[1][1] - b[0][1]),
- x = -k * b[0][0],
- y = (h - k * (b[1][1] + b[0][1])) / 2;
+ k = h / (b[1][1] - b[0][1]),
+ x = -k * b[0][0],
+ y = (h - k * (b[1][1] + b[0][1])) / 2;
projection.scale(150 * k).translate([x, y]);
}, object);
}
var maxDepth = 16,
- // maximum depth of subdivision
- cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)
+ // maximum depth of subdivision
+ cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)
function resample (project, delta2) {
return +delta2 ? resample$1(project, delta2) : resampleNone(project);
}
-
function resampleNone(project) {
return transformer({
point: function (x, y) {
x = project(x, y);
this.stream.point(x[0], x[1]);
}
});
}
-
function resample$1(project, delta2) {
function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
var dx = x1 - x0,
- dy = y1 - y0,
- d2 = dx * dx + dy * dy;
-
+ dy = y1 - y0,
+ d2 = dx * dx + dy * dy;
if (d2 > 4 * delta2 && depth--) {
var a = a0 + a1,
- b = b0 + b1,
- c = c0 + c1,
- m = sqrt$1(a * a + b * b + c * c),
- phi2 = asin$1(c /= m),
- lambda2 = abs$1(abs$1(c) - 1) < epsilon$3 || abs$1(lambda0 - lambda1) < epsilon$3 ? (lambda0 + lambda1) / 2 : atan2(b, a),
- p = project(lambda2, phi2),
- x2 = p[0],
- y2 = p[1],
- dx2 = x2 - x0,
- dy2 = y2 - y0,
- dz = dy * dx2 - dx * dy2;
-
+ b = b0 + b1,
+ c = c0 + c1,
+ m = sqrt$1(a * a + b * b + c * c),
+ phi2 = asin$1(c /= m),
+ lambda2 = abs$1(abs$1(c) - 1) < epsilon$3 || abs$1(lambda0 - lambda1) < epsilon$3 ? (lambda0 + lambda1) / 2 : atan2(b, a),
+ p = project(lambda2, phi2),
+ x2 = p[0],
+ y2 = p[1],
+ dx2 = x2 - x0,
+ dy2 = y2 - y0,
+ dz = dy * dx2 - dx * dy2;
if (dz * dz / d2 > delta2 // perpendicular projected distance
|| abs$1((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
|| a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
// angular distance
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
stream.point(x2, y2);
resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
}
}
}
-
return function (stream) {
- var lambda00, x00, y00, a00, b00, c00, // first point
- lambda0, x0, y0, a0, b0, c0; // previous point
+ var lambda00, x00, y00, a00, b00, c00,
+ // first point
+ lambda0, x0, y0, a0, b0, c0; // previous point
var resampleStream = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
@@ -26312,448 +23875,384 @@
polygonEnd: function () {
stream.polygonEnd();
resampleStream.lineStart = lineStart;
}
};
-
function point(x, y) {
x = project(x, y);
stream.point(x[0], x[1]);
}
-
function lineStart() {
x0 = NaN;
resampleStream.point = linePoint;
stream.lineStart();
}
-
function linePoint(lambda, phi) {
var c = cartesian([lambda, phi]),
- p = project(lambda, phi);
+ p = project(lambda, phi);
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
stream.point(x0, y0);
}
-
function lineEnd() {
resampleStream.point = point;
stream.lineEnd();
}
-
function ringStart() {
lineStart();
resampleStream.point = ringPoint;
resampleStream.lineEnd = ringEnd;
}
-
function ringPoint(lambda, phi) {
linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
resampleStream.point = linePoint;
}
-
function ringEnd() {
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
resampleStream.lineEnd = lineEnd;
lineEnd();
}
-
return resampleStream;
};
}
var transformRadians = transformer({
point: function (x, y) {
this.stream.point(x * radians, y * radians);
}
});
-
function transformRotate(rotate) {
return transformer({
point: function (x, y) {
var r = rotate(x, y);
return this.stream.point(r[0], r[1]);
}
});
}
-
function scaleTranslate(k, dx, dy, sx, sy) {
function transform(x, y) {
x *= sx;
y *= sy;
return [dx + k * x, dy - k * y];
}
-
transform.invert = function (x, y) {
return [(x - dx) / k * sx, (dy - y) / k * sy];
};
-
return transform;
}
-
function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
var cosAlpha = cos$1(alpha),
- sinAlpha = sin$1(alpha),
- a = cosAlpha * k,
- b = sinAlpha * k,
- ai = cosAlpha / k,
- bi = sinAlpha / k,
- ci = (sinAlpha * dy - cosAlpha * dx) / k,
- fi = (sinAlpha * dx + cosAlpha * dy) / k;
-
+ sinAlpha = sin$1(alpha),
+ a = cosAlpha * k,
+ b = sinAlpha * k,
+ ai = cosAlpha / k,
+ bi = sinAlpha / k,
+ ci = (sinAlpha * dy - cosAlpha * dx) / k,
+ fi = (sinAlpha * dx + cosAlpha * dy) / k;
function transform(x, y) {
x *= sx;
y *= sy;
return [a * x - b * y + dx, dy - b * x - a * y];
}
-
transform.invert = function (x, y) {
return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
};
-
return transform;
}
-
function projection$1(project) {
return projectionMutator(function () {
return project;
})();
}
function projectionMutator(projectAt) {
var project,
- k = 150,
- // scale
- x = 480,
- y = 250,
- // translate
- lambda = 0,
- phi = 0,
- // center
- deltaLambda = 0,
- deltaPhi = 0,
- deltaGamma = 0,
- rotate,
- // pre-rotate
- alpha = 0,
- // post-rotate angle
- sx = 1,
- // reflectX
- sy = 1,
- // reflectX
- theta = null,
- preclip = clipAntimeridian,
- // pre-clip angle
- x0 = null,
- y0,
- x1,
- y1,
- postclip = identity,
- // post-clip extent
- delta2 = 0.5,
- // precision
- projectResample,
- projectTransform,
- projectRotateTransform,
- cache,
- cacheStream;
-
+ k = 150,
+ // scale
+ x = 480,
+ y = 250,
+ // translate
+ lambda = 0,
+ phi = 0,
+ // center
+ deltaLambda = 0,
+ deltaPhi = 0,
+ deltaGamma = 0,
+ rotate,
+ // pre-rotate
+ alpha = 0,
+ // post-rotate angle
+ sx = 1,
+ // reflectX
+ sy = 1,
+ // reflectX
+ theta = null,
+ preclip = clipAntimeridian,
+ // pre-clip angle
+ x0 = null,
+ y0,
+ x1,
+ y1,
+ postclip = identity,
+ // post-clip extent
+ delta2 = 0.5,
+ // precision
+ projectResample,
+ projectTransform,
+ projectRotateTransform,
+ cache,
+ cacheStream;
function projection(point) {
return projectRotateTransform(point[0] * radians, point[1] * radians);
}
-
function invert(point) {
point = projectRotateTransform.invert(point[0], point[1]);
return point && [point[0] * degrees, point[1] * degrees];
}
-
projection.stream = function (stream) {
return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
};
-
projection.preclip = function (_) {
return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
};
-
projection.postclip = function (_) {
return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
};
-
projection.clipAngle = function (_) {
return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
};
-
projection.clipExtent = function (_) {
return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
};
-
projection.scale = function (_) {
return arguments.length ? (k = +_, recenter()) : k;
};
-
projection.translate = function (_) {
return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
};
-
projection.center = function (_) {
return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
};
-
projection.rotate = function (_) {
return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees, deltaPhi * degrees, deltaGamma * degrees];
};
-
projection.angle = function (_) {
return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
};
-
projection.reflectX = function (_) {
return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
};
-
projection.reflectY = function (_) {
return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
};
-
projection.precision = function (_) {
return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2);
};
-
projection.fitExtent = function (extent, object) {
return fitExtent(projection, extent, object);
};
-
projection.fitSize = function (size, object) {
return fitSize(projection, size, object);
};
-
projection.fitWidth = function (width, object) {
return fitWidth(projection, width, object);
};
-
projection.fitHeight = function (height, object) {
return fitHeight(projection, height, object);
};
-
function recenter() {
var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
- transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
+ transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
projectTransform = compose(project, transform);
projectRotateTransform = compose(rotate, projectTransform);
projectResample = resample(projectTransform, delta2);
return reset();
}
-
function reset() {
cache = cacheStream = null;
return projection;
}
-
return function () {
project = projectAt.apply(this, arguments);
projection.invert = project.invert && invert;
return recenter();
};
}
function conicProjection(projectAt) {
var phi0 = 0,
- phi1 = pi$1 / 3,
- m = projectionMutator(projectAt),
- p = m(phi0, phi1);
-
+ phi1 = pi$1 / 3,
+ m = projectionMutator(projectAt),
+ p = m(phi0, phi1);
p.parallels = function (_) {
return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees, phi1 * degrees];
};
-
return p;
}
function cylindricalEqualAreaRaw(phi0) {
var cosPhi0 = cos$1(phi0);
-
function forward(lambda, phi) {
return [lambda * cosPhi0, sin$1(phi) / cosPhi0];
}
-
forward.invert = function (x, y) {
return [x / cosPhi0, asin$1(y * cosPhi0)];
};
-
return forward;
}
function conicEqualAreaRaw(y0, y1) {
var sy0 = sin$1(y0),
- n = (sy0 + sin$1(y1)) / 2; // Are the parallels symmetrical around the Equator?
+ n = (sy0 + sin$1(y1)) / 2;
+ // Are the parallels symmetrical around the Equator?
if (abs$1(n) < epsilon$3) return cylindricalEqualAreaRaw(y0);
var c = 1 + sy0 * (2 * n - sy0),
- r0 = sqrt$1(c) / n;
-
+ r0 = sqrt$1(c) / n;
function project(x, y) {
var r = sqrt$1(c - 2 * n * sin$1(y)) / n;
return [r * sin$1(x *= n), r0 - r * cos$1(x)];
}
-
project.invert = function (x, y) {
var r0y = r0 - y,
- l = atan2(x, abs$1(r0y)) * sign(r0y);
+ l = atan2(x, abs$1(r0y)) * sign(r0y);
if (r0y * n < 0) l -= pi$1 * sign(x) * sign(r0y);
return [l / n, asin$1((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
};
-
return project;
}
function geoConicEqualArea () {
return conicProjection(conicEqualAreaRaw).scale(155.424).center([0, 33.6442]);
}
function geoAlbers () {
return geoConicEqualArea().parallels([29.5, 45.5]).scale(1070).translate([480, 250]).rotate([96, 0]).center([-0.6, 38.7]);
}
+ // The projections must have mutually exclusive clip regions on the sphere,
// as this will avoid emitting interleaving lines and polygons.
-
function multiplex(streams) {
var n = streams.length;
return {
point: function (x, y) {
var i = -1;
-
while (++i < n) streams[i].point(x, y);
},
sphere: function () {
var i = -1;
-
while (++i < n) streams[i].sphere();
},
lineStart: function () {
var i = -1;
-
while (++i < n) streams[i].lineStart();
},
lineEnd: function () {
var i = -1;
-
while (++i < n) streams[i].lineEnd();
},
polygonStart: function () {
var i = -1;
-
while (++i < n) streams[i].polygonStart();
},
polygonEnd: function () {
var i = -1;
-
while (++i < n) streams[i].polygonEnd();
}
};
- } // A composite projection for the United States, configured by default for
+ }
+
+ // A composite projection for the United States, configured by default for
// 960×500. The projection also works quite well at 960×600 if you change the
// scale to 1285 and adjust the translate accordingly. The set of standard
// parallels for each region comes from USGS, which is published here:
// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
-
-
function geoAlbersUsa () {
var cache,
- cacheStream,
- lower48 = geoAlbers(),
- lower48Point,
- alaska = geoConicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]),
- alaskaPoint,
- // EPSG:3338
- hawaii = geoConicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]),
- hawaiiPoint,
- // ESRI:102007
- point,
- pointStream = {
- point: function (x, y) {
- point = [x, y];
- }
- };
-
+ cacheStream,
+ lower48 = geoAlbers(),
+ lower48Point,
+ alaska = geoConicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]),
+ alaskaPoint,
+ // EPSG:3338
+ hawaii = geoConicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]),
+ hawaiiPoint,
+ // ESRI:102007
+ point,
+ pointStream = {
+ point: function (x, y) {
+ point = [x, y];
+ }
+ };
function albersUsa(coordinates) {
var x = coordinates[0],
- y = coordinates[1];
+ y = coordinates[1];
return point = null, (lower48Point.point(x, y), point) || (alaskaPoint.point(x, y), point) || (hawaiiPoint.point(x, y), point);
}
-
albersUsa.invert = function (coordinates) {
var k = lower48.scale(),
- t = lower48.translate(),
- x = (coordinates[0] - t[0]) / k,
- y = (coordinates[1] - t[1]) / k;
+ t = lower48.translate(),
+ x = (coordinates[0] - t[0]) / k,
+ y = (coordinates[1] - t[1]) / k;
return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii : lower48).invert(coordinates);
};
-
albersUsa.stream = function (stream) {
return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
};
-
albersUsa.precision = function (_) {
if (!arguments.length) return lower48.precision();
lower48.precision(_), alaska.precision(_), hawaii.precision(_);
return reset();
};
-
albersUsa.scale = function (_) {
if (!arguments.length) return lower48.scale();
lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
return albersUsa.translate(lower48.translate());
};
-
albersUsa.translate = function (_) {
if (!arguments.length) return lower48.translate();
var k = lower48.scale(),
- x = +_[0],
- y = +_[1];
+ x = +_[0],
+ y = +_[1];
lower48Point = lower48.translate(_).clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]]).stream(pointStream);
alaskaPoint = alaska.translate([x - 0.307 * k, y + 0.201 * k]).clipExtent([[x - 0.425 * k + epsilon$3, y + 0.120 * k + epsilon$3], [x - 0.214 * k - epsilon$3, y + 0.234 * k - epsilon$3]]).stream(pointStream);
hawaiiPoint = hawaii.translate([x - 0.205 * k, y + 0.212 * k]).clipExtent([[x - 0.214 * k + epsilon$3, y + 0.166 * k + epsilon$3], [x - 0.115 * k - epsilon$3, y + 0.234 * k - epsilon$3]]).stream(pointStream);
return reset();
};
-
albersUsa.fitExtent = function (extent, object) {
return fitExtent(albersUsa, extent, object);
};
-
albersUsa.fitSize = function (size, object) {
return fitSize(albersUsa, size, object);
};
-
albersUsa.fitWidth = function (width, object) {
return fitWidth(albersUsa, width, object);
};
-
albersUsa.fitHeight = function (height, object) {
return fitHeight(albersUsa, height, object);
};
-
function reset() {
cache = cacheStream = null;
return albersUsa;
}
-
return albersUsa.scale(1070);
}
function azimuthalRaw(scale) {
return function (x, y) {
var cx = cos$1(x),
- cy = cos$1(y),
- k = scale(cx * cy);
+ cy = cos$1(y),
+ k = scale(cx * cy);
if (k === Infinity) return [2, 0];
return [k * cy * sin$1(x), k * sin$1(y)];
};
}
function azimuthalInvert(angle) {
return function (x, y) {
var z = sqrt$1(x * x + y * y),
- c = angle(z),
- sc = sin$1(c),
- cc = cos$1(c);
+ c = angle(z),
+ sc = sin$1(c),
+ cc = cos$1(c);
return [atan2(x * sc, z * cc), asin$1(z && y * sc / z)];
};
}
var azimuthalEqualAreaRaw = azimuthalRaw(function (cxcy) {
@@ -26777,83 +24276,71 @@
}
function mercatorRaw(lambda, phi) {
return [lambda, log$1(tan((halfPi$1 + phi) / 2))];
}
-
mercatorRaw.invert = function (x, y) {
return [x, 2 * atan(exp(y)) - halfPi$1];
};
-
function geoMercator () {
return mercatorProjection(mercatorRaw).scale(961 / tau$1);
}
function mercatorProjection(project) {
var m = projection$1(project),
- center = m.center,
- scale = m.scale,
- translate = m.translate,
- clipExtent = m.clipExtent,
- x0 = null,
- y0,
- x1,
- y1; // clip extent
+ center = m.center,
+ scale = m.scale,
+ translate = m.translate,
+ clipExtent = m.clipExtent,
+ x0 = null,
+ y0,
+ x1,
+ y1; // clip extent
m.scale = function (_) {
return arguments.length ? (scale(_), reclip()) : scale();
};
-
m.translate = function (_) {
return arguments.length ? (translate(_), reclip()) : translate();
};
-
m.center = function (_) {
return arguments.length ? (center(_), reclip()) : center();
};
-
m.clipExtent = function (_) {
return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
};
-
function reclip() {
var k = pi$1 * scale(),
- t = m(rotation(m.rotate()).invert([0, 0]));
+ t = m(rotation(m.rotate()).invert([0, 0]));
return clipExtent(x0 == null ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
}
-
return reclip();
}
function tany(y) {
return tan((halfPi$1 + y) / 2);
}
-
function conicConformalRaw(y0, y1) {
var cy0 = cos$1(y0),
- n = y0 === y1 ? sin$1(y0) : log$1(cy0 / cos$1(y1)) / log$1(tany(y1) / tany(y0)),
- f = cy0 * pow$1(tany(y0), n) / n;
+ n = y0 === y1 ? sin$1(y0) : log$1(cy0 / cos$1(y1)) / log$1(tany(y1) / tany(y0)),
+ f = cy0 * pow$1(tany(y0), n) / n;
if (!n) return mercatorRaw;
-
function project(x, y) {
if (f > 0) {
if (y < -halfPi$1 + epsilon$3) y = -halfPi$1 + epsilon$3;
} else {
if (y > halfPi$1 - epsilon$3) y = halfPi$1 - epsilon$3;
}
-
var r = f / pow$1(tany(y), n);
return [r * sin$1(n * x), f - r * cos$1(n * x)];
}
-
project.invert = function (x, y) {
var fy = f - y,
- r = sign(n) * sqrt$1(x * x + fy * fy),
- l = atan2(x, abs$1(fy)) * sign(fy);
+ r = sign(n) * sqrt$1(x * x + fy * fy),
+ l = atan2(x, abs$1(fy)) * sign(fy);
if (fy * n < 0) l -= pi$1 * sign(x) * sign(fy);
return [l / n, 2 * atan(pow$1(f / r, 1 / n)) - halfPi$1];
};
-
return project;
}
function geoConicConformal () {
return conicProjection(conicConformalRaw).scale(109.5).parallels([30, 30]);
}
@@ -26866,207 +24353,176 @@
return projection$1(equirectangularRaw).scale(152.63);
}
function conicEquidistantRaw(y0, y1) {
var cy0 = cos$1(y0),
- n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
- g = cy0 / n + y0;
+ n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
+ g = cy0 / n + y0;
if (abs$1(n) < epsilon$3) return equirectangularRaw;
-
function project(x, y) {
var gy = g - y,
- nx = n * x;
+ nx = n * x;
return [gy * sin$1(nx), g - gy * cos$1(nx)];
}
-
project.invert = function (x, y) {
var gy = g - y,
- l = atan2(x, abs$1(gy)) * sign(gy);
+ l = atan2(x, abs$1(gy)) * sign(gy);
if (gy * n < 0) l -= pi$1 * sign(x) * sign(gy);
return [l / n, g - sign(n) * sqrt$1(x * x + gy * gy)];
};
-
return project;
}
function geoConicEquidistant () {
return conicProjection(conicEquidistantRaw).scale(131.154).center([0, 13.9389]);
}
var A1 = 1.340264,
- A2 = -0.081106,
- A3 = 0.000893,
- A4 = 0.003796,
- M = sqrt$1(3) / 2,
- iterations = 12;
+ A2 = -0.081106,
+ A3 = 0.000893,
+ A4 = 0.003796,
+ M = sqrt$1(3) / 2,
+ iterations = 12;
function equalEarthRaw(lambda, phi) {
var l = asin$1(M * sin$1(phi)),
- l2 = l * l,
- l6 = l2 * l2 * l2;
+ l2 = l * l,
+ l6 = l2 * l2 * l2;
return [lambda * cos$1(l) / (M * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2))), l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2))];
}
-
equalEarthRaw.invert = function (x, y) {
var l = y,
- l2 = l * l,
- l6 = l2 * l2 * l2;
-
+ l2 = l * l,
+ l6 = l2 * l2 * l2;
for (var i = 0, delta, fy, fpy; i < iterations; ++i) {
fy = l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2)) - y;
fpy = A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2);
l -= delta = fy / fpy, l2 = l * l, l6 = l2 * l2 * l2;
if (abs$1(delta) < epsilon2) break;
}
-
return [M * x * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2)) / cos$1(l), asin$1(sin$1(l) / M)];
};
-
function geoEqualEarth () {
return projection$1(equalEarthRaw).scale(177.158);
}
function gnomonicRaw(x, y) {
var cy = cos$1(y),
- k = cos$1(x) * cy;
+ k = cos$1(x) * cy;
return [cy * sin$1(x) / k, sin$1(y) / k];
}
gnomonicRaw.invert = azimuthalInvert(atan);
function geoGnomonic () {
return projection$1(gnomonicRaw).scale(144.049).clipAngle(60);
}
function geoIdentity () {
var k = 1,
- tx = 0,
- ty = 0,
- sx = 1,
- sy = 1,
- // scale, translate and reflect
- alpha = 0,
- ca,
- sa,
- // angle
- x0 = null,
- y0,
- x1,
- y1,
- // clip extent
- kx = 1,
- ky = 1,
- transform = transformer({
- point: function (x, y) {
- var p = projection([x, y]);
- this.stream.point(p[0], p[1]);
- }
- }),
- postclip = identity,
- cache,
- cacheStream;
-
+ tx = 0,
+ ty = 0,
+ sx = 1,
+ sy = 1,
+ // scale, translate and reflect
+ alpha = 0,
+ ca,
+ sa,
+ // angle
+ x0 = null,
+ y0,
+ x1,
+ y1,
+ // clip extent
+ kx = 1,
+ ky = 1,
+ transform = transformer({
+ point: function (x, y) {
+ var p = projection([x, y]);
+ this.stream.point(p[0], p[1]);
+ }
+ }),
+ postclip = identity,
+ cache,
+ cacheStream;
function reset() {
kx = k * sx;
ky = k * sy;
cache = cacheStream = null;
return projection;
}
-
function projection(p) {
var x = p[0] * kx,
- y = p[1] * ky;
-
+ y = p[1] * ky;
if (alpha) {
var t = y * ca - x * sa;
x = x * ca + y * sa;
y = t;
}
-
return [x + tx, y + ty];
}
-
projection.invert = function (p) {
var x = p[0] - tx,
- y = p[1] - ty;
-
+ y = p[1] - ty;
if (alpha) {
var t = y * ca + x * sa;
x = x * ca - y * sa;
y = t;
}
-
return [x / kx, y / ky];
};
-
projection.stream = function (stream) {
return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
};
-
projection.postclip = function (_) {
return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
};
-
projection.clipExtent = function (_) {
return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
};
-
projection.scale = function (_) {
return arguments.length ? (k = +_, reset()) : k;
};
-
projection.translate = function (_) {
return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
};
-
projection.angle = function (_) {
return arguments.length ? (alpha = _ % 360 * radians, sa = sin$1(alpha), ca = cos$1(alpha), reset()) : alpha * degrees;
};
-
projection.reflectX = function (_) {
return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
};
-
projection.reflectY = function (_) {
return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
};
-
projection.fitExtent = function (extent, object) {
return fitExtent(projection, extent, object);
};
-
projection.fitSize = function (size, object) {
return fitSize(projection, size, object);
};
-
projection.fitWidth = function (width, object) {
return fitWidth(projection, width, object);
};
-
projection.fitHeight = function (height, object) {
return fitHeight(projection, height, object);
};
-
return projection;
}
function naturalEarth1Raw(lambda, phi) {
var phi2 = phi * phi,
- phi4 = phi2 * phi2;
+ phi4 = phi2 * phi2;
return [lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))), phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))];
}
-
naturalEarth1Raw.invert = function (x, y) {
var phi = y,
- i = 25,
- delta;
-
+ i = 25,
+ delta;
do {
var phi2 = phi * phi,
- phi4 = phi2 * phi2;
+ phi4 = phi2 * phi2;
phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) / (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));
} while (abs$1(delta) > epsilon$3 && --i > 0);
-
return [x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))), phi];
};
-
function geoNaturalEarth1 () {
return projection$1(naturalEarth1Raw).scale(175.295);
}
function orthographicRaw(x, y) {
@@ -27077,11 +24533,11 @@
return projection$1(orthographicRaw).scale(249.5).clipAngle(90 + epsilon$3);
}
function stereographicRaw(x, y) {
var cy = cos$1(y),
- k = 1 + cos$1(x) * cy;
+ k = 1 + cos$1(x) * cy;
return [cy * sin$1(x) / k, sin$1(y) / k];
}
stereographicRaw.invert = azimuthalInvert(function (z) {
return 2 * atan(z);
});
@@ -27090,28 +24546,23 @@
}
function transverseMercatorRaw(lambda, phi) {
return [log$1(tan((halfPi$1 + phi) / 2)), -lambda];
}
-
transverseMercatorRaw.invert = function (x, y) {
return [-y, 2 * atan(exp(x)) - halfPi$1];
};
-
function geoTransverseMercator () {
var m = mercatorProjection(transverseMercatorRaw),
- center = m.center,
- rotate = m.rotate;
-
+ center = m.center,
+ rotate = m.rotate;
m.center = function (_) {
return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
};
-
m.rotate = function (_) {
return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
};
-
return rotate([0, 0, 90]).scale(159.155);
}
var abs = Math.abs;
var cos = Math.cos;
@@ -27127,79 +24578,70 @@
return x > 0 ? Math.sqrt(x) : 0;
}
function mollweideBromleyTheta(cp, phi) {
var cpsinPhi = cp * sin(phi),
- i = 30,
- delta;
-
+ i = 30,
+ delta;
do phi -= delta = (phi + sin(phi) - cpsinPhi) / (1 + cos(phi)); while (abs(delta) > epsilon$2 && --i > 0);
-
return phi / 2;
}
function mollweideBromleyRaw(cx, cy, cp) {
function forward(lambda, phi) {
return [cx * lambda * cos(phi = mollweideBromleyTheta(cp, phi)), cy * sin(phi)];
}
-
forward.invert = function (x, y) {
return y = asin(y / cy), [x / (cx * cos(y)), asin((2 * y + sin(2 * y)) / cp)];
};
-
return forward;
}
var mollweideRaw = mollweideBromleyRaw(sqrt2 / halfPi, sqrt2, pi);
function geoMollweide () {
return projection$1(mollweideRaw).scale(169.529);
}
const defaultPath = geoPath();
- const projectionProperties = [// standard properties in d3-geo
- 'clipAngle', 'clipExtent', 'scale', 'translate', 'center', 'rotate', 'parallels', 'precision', 'reflectX', 'reflectY', // extended properties in d3-geo-projections
+ const projectionProperties = [
+ // standard properties in d3-geo
+ 'clipAngle', 'clipExtent', 'scale', 'translate', 'center', 'rotate', 'parallels', 'precision', 'reflectX', 'reflectY',
+ // extended properties in d3-geo-projections
'coefficient', 'distance', 'fraction', 'lobes', 'parallel', 'radius', 'ratio', 'spacing', 'tilt'];
+
/**
* Augment projections with their type and a copy method.
*/
-
function create$1(type, constructor) {
return function projection() {
const p = constructor();
p.type = type;
p.path = geoPath().projection(p);
-
p.copy = p.copy || function () {
const c = projection();
projectionProperties.forEach(prop => {
if (p[prop]) c[prop](p[prop]());
});
c.path.pointRadius(p.path.pointRadius());
return c;
};
-
- return p;
+ return registerScale(p);
};
}
-
function projection(type, proj) {
if (!type || typeof type !== 'string') {
throw new Error('Projection type must be a name string.');
}
-
type = type.toLowerCase();
-
if (arguments.length > 1) {
projections[type] = create$1(type, proj);
return this;
} else {
return projections[type] || null;
}
}
-
function getProjectionPath(proj) {
return proj && proj.path || defaultPath;
}
-
const projections = {
// base d3-geo projection types
albers: geoAlbers,
albersusa: geoAlbersUsa,
azimuthalequalarea: geoAzimuthalEqualArea,
@@ -27216,33 +24658,31 @@
naturalEarth1: geoNaturalEarth1,
orthographic: geoOrthographic,
stereographic: geoStereographic,
transversemercator: geoTransverseMercator
};
-
for (const key in projections) {
projection(key, projections[key]);
}
function noop$1() {}
+ const cases = [[], [[[1.0, 1.5], [0.5, 1.0]]], [[[1.5, 1.0], [1.0, 1.5]]], [[[1.5, 1.0], [0.5, 1.0]]], [[[1.0, 0.5], [1.5, 1.0]]], [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]], [[[1.0, 0.5], [1.0, 1.5]]], [[[1.0, 0.5], [0.5, 1.0]]], [[[0.5, 1.0], [1.0, 0.5]]], [[[1.0, 1.5], [1.0, 0.5]]], [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]], [[[1.5, 1.0], [1.0, 0.5]]], [[[0.5, 1.0], [1.5, 1.0]]], [[[1.0, 1.5], [1.5, 1.0]]], [[[0.5, 1.0], [1.0, 1.5]]], []];
- const cases = [[], [[[1.0, 1.5], [0.5, 1.0]]], [[[1.5, 1.0], [1.0, 1.5]]], [[[1.5, 1.0], [0.5, 1.0]]], [[[1.0, 0.5], [1.5, 1.0]]], [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]], [[[1.0, 0.5], [1.0, 1.5]]], [[[1.0, 0.5], [0.5, 1.0]]], [[[0.5, 1.0], [1.0, 0.5]]], [[[1.0, 1.5], [1.0, 0.5]]], [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]], [[[1.5, 1.0], [1.0, 0.5]]], [[[0.5, 1.0], [1.5, 1.0]]], [[[1.0, 1.5], [1.5, 1.0]]], [[[0.5, 1.0], [1.0, 1.5]]], []]; // Implementation adapted from d3/d3-contour. Thanks!
-
+ // Implementation adapted from d3/d3-contour. Thanks!
function contours() {
var dx = 1,
- dy = 1,
- smooth = smoothLinear;
-
+ dy = 1,
+ smooth = smoothLinear;
function contours(values, tz) {
return tz.map(value => contour(values, value));
- } // Accumulate, smooth contour rings, assign holes to exterior rings.
- // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
+ }
-
+ // Accumulate, smooth contour rings, assign holes to exterior rings.
+ // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
function contour(values, value) {
var polygons = [],
- holes = [];
+ holes = [];
isorings(values, value, ring => {
smooth(ring, values, value);
if (area(ring) > 0) polygons.push([ring]);else holes.push(ring);
});
holes.forEach(hole => {
@@ -27256,75 +24696,68 @@
return {
type: 'MultiPolygon',
value: value,
coordinates: polygons
};
- } // Marching squares with isolines stitched into rings.
- // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
+ }
-
+ // Marching squares with isolines stitched into rings.
+ // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
function isorings(values, value, callback) {
- var fragmentByStart = new Array(),
- fragmentByEnd = new Array(),
- x,
- y,
- t0,
- t1,
- t2,
- t3; // Special case for the first row (y = -1, t2 = t3 = 0).
+ var fragmentByStart = [],
+ fragmentByEnd = [],
+ x,
+ y,
+ t0,
+ t1,
+ t2,
+ t3;
+ // Special case for the first row (y = -1, t2 = t3 = 0).
x = y = -1;
t1 = values[0] >= value;
cases[t1 << 1].forEach(stitch);
-
while (++x < dx - 1) {
t0 = t1, t1 = values[x + 1] >= value;
cases[t0 | t1 << 1].forEach(stitch);
}
+ cases[t1 << 0].forEach(stitch);
- cases[t1 << 0].forEach(stitch); // General case for the intermediate rows.
-
+ // General case for the intermediate rows.
while (++y < dy - 1) {
x = -1;
t1 = values[y * dx + dx] >= value;
t2 = values[y * dx] >= value;
cases[t1 << 1 | t2 << 2].forEach(stitch);
-
while (++x < dx - 1) {
t0 = t1, t1 = values[y * dx + dx + x + 1] >= value;
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);
}
-
cases[t1 | t2 << 3].forEach(stitch);
- } // Special case for the last row (y = dy - 1, t0 = t1 = 0).
+ }
-
+ // Special case for the last row (y = dy - 1, t0 = t1 = 0).
x = -1;
t2 = values[y * dx] >= value;
cases[t2 << 2].forEach(stitch);
-
while (++x < dx - 1) {
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t2 << 2 | t3 << 3].forEach(stitch);
}
-
cases[t2 << 3].forEach(stitch);
-
function stitch(line) {
var start = [line[0][0] + x, line[0][1] + y],
- end = [line[1][0] + x, line[1][1] + y],
- startIndex = index(start),
- endIndex = index(end),
- f,
- g;
-
+ end = [line[1][0] + x, line[1][1] + y],
+ startIndex = index(start),
+ endIndex = index(end),
+ f,
+ g;
if (f = fragmentByEnd[startIndex]) {
if (g = fragmentByStart[endIndex]) {
delete fragmentByEnd[f.end];
delete fragmentByStart[g.start];
-
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[f.start] = fragmentByEnd[g.end] = {
@@ -27340,11 +24773,10 @@
}
} else if (f = fragmentByStart[endIndex]) {
if (g = fragmentByEnd[startIndex]) {
delete fragmentByStart[f.start];
delete fragmentByEnd[g.end];
-
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[g.start] = fragmentByEnd[f.end] = {
@@ -27365,117 +24797,95 @@
ring: [start, end]
};
}
}
}
-
function index(point) {
return point[0] * 2 + point[1] * (dx + 1) * 4;
}
-
function smoothLinear(ring, values, value) {
ring.forEach(point => {
var x = point[0],
- y = point[1],
- xt = x | 0,
- yt = y | 0,
- v0,
- v1 = values[yt * dx + xt];
-
+ y = point[1],
+ xt = x | 0,
+ yt = y | 0,
+ v0,
+ v1 = values[yt * dx + xt];
if (x > 0 && x < dx && xt === x) {
v0 = values[yt * dx + xt - 1];
point[0] = x + (value - v0) / (v1 - v0) - 0.5;
}
-
if (y > 0 && y < dy && yt === y) {
v0 = values[(yt - 1) * dx + xt];
point[1] = y + (value - v0) / (v1 - v0) - 0.5;
}
});
}
-
contours.contour = contour;
-
contours.size = function (_) {
if (!arguments.length) return [dx, dy];
-
var _0 = Math.floor(_[0]),
- _1 = Math.floor(_[1]);
-
+ _1 = Math.floor(_[1]);
if (!(_0 >= 0 && _1 >= 0)) error('invalid size');
return dx = _0, dy = _1, contours;
};
-
contours.smooth = function (_) {
return arguments.length ? (smooth = _ ? smoothLinear : noop$1, contours) : smooth === smoothLinear;
};
-
return contours;
}
-
function area(ring) {
var i = 0,
- n = ring.length,
- area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
-
+ n = ring.length,
+ area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
-
return area;
}
-
function contains(ring, hole) {
var i = -1,
- n = hole.length,
- c;
-
+ n = hole.length,
+ c;
while (++i < n) if (c = ringContains(ring, hole[i])) return c;
-
return 0;
}
-
function ringContains(ring, point) {
var x = point[0],
- y = point[1],
- contains = -1;
-
+ y = point[1],
+ contains = -1;
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
var pi = ring[i],
- xi = pi[0],
- yi = pi[1],
- pj = ring[j],
- xj = pj[0],
- yj = pj[1];
+ xi = pi[0],
+ yi = pi[1],
+ pj = ring[j],
+ xj = pj[0],
+ yj = pj[1];
if (segmentContains(pi, pj, point)) return 0;
if (yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) contains = -contains;
}
-
return contains;
}
-
function segmentContains(a, b, c) {
var i;
return collinear$1(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);
}
-
function collinear$1(a, b, c) {
return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);
}
-
function within(p, q, r) {
return p <= q && q <= r || r <= q && q <= p;
}
-
function quantize(k, nice, zero) {
return function (values) {
var ex = extent(values),
- start = zero ? Math.min(ex[0], 0) : ex[0],
- stop = ex[1],
- span = stop - start,
- step = nice ? tickStep(start, stop, k) : span / (k + 1);
+ start = zero ? Math.min(ex[0], 0) : ex[0],
+ stop = ex[1],
+ span = stop - start,
+ step = nice ? tickStep(start, stop, k) : span / (k + 1);
return range$3(start + step, stop, step);
};
}
+
/**
* Generate isocontours (level sets) based on input raster grid data.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} [params.field] - The field with raster grid
@@ -27500,16 +24910,13 @@
* scale the output isocontour coordinates. This parameter can be useful
* to scale the contours to match a desired output resolution.
* @param {string} [params.as='contour'] - The output field in which to store
* the generated isocontour data (default 'contour').
*/
-
-
function Isocontour(params) {
Transform.call(this, null, params);
}
-
Isocontour.Definition = {
'type': 'Isocontour',
'metadata': {
'generates': true
},
@@ -27559,127 +24966,115 @@
inherits(Isocontour, Transform, {
transform(_, pulse) {
if (this.value && !pulse.changed() && !_.modified()) {
return pulse.StopPropagation;
}
-
var out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- source = pulse.materialize(pulse.SOURCE).source,
- field = _.field || identity$6,
- contour = contours().smooth(_.smooth !== false),
- tz = _.thresholds || levels(source, field, _),
- as = _.as === null ? null : _.as || 'contour',
- values = [];
+ source = pulse.materialize(pulse.SOURCE).source,
+ field = _.field || identity$6,
+ contour = contours().smooth(_.smooth !== false),
+ tz = _.thresholds || levels(source, field, _),
+ as = _.as === null ? null : _.as || 'contour',
+ values = [];
source.forEach(t => {
- const grid = field(t); // generate contour paths in GeoJSON format
+ const grid = field(t);
- const paths = contour.size([grid.width, grid.height])(grid.values, isArray(tz) ? tz : tz(grid.values)); // adjust contour path coordinates as needed
+ // generate contour paths in GeoJSON format
+ const paths = contour.size([grid.width, grid.height])(grid.values, isArray(tz) ? tz : tz(grid.values));
- transformPaths(paths, grid, t, _); // ingest; copy source data properties to output
+ // adjust contour path coordinates as needed
+ transformPaths(paths, grid, t, _);
+ // ingest; copy source data properties to output
paths.forEach(p => {
values.push(rederive(t, ingest$1(as != null ? {
[as]: p
} : p)));
});
});
if (this.value) out.rem = this.value;
this.value = out.source = out.add = values;
return out;
}
-
});
-
function levels(values, f, _) {
const q = quantize(_.levels || 10, _.nice, _.zero !== false);
return _.resolve !== 'shared' ? q : q(values.map(t => max$2(f(t).values)));
}
-
function transformPaths(paths, grid, datum, _) {
let s = _.scale || grid.scale,
- t = _.translate || grid.translate;
+ t = _.translate || grid.translate;
if (isFunction(s)) s = s(datum, _);
if (isFunction(t)) t = t(datum, _);
if ((s === 1 || s == null) && !t) return;
const sx = (isNumber$1(s) ? s : s[0]) || 1,
- sy = (isNumber$1(s) ? s : s[1]) || 1,
- tx = t && t[0] || 0,
- ty = t && t[1] || 0;
+ sy = (isNumber$1(s) ? s : s[1]) || 1,
+ tx = t && t[0] || 0,
+ ty = t && t[1] || 0;
paths.forEach(transform$1(grid, sx, sy, tx, ty));
}
-
function transform$1(grid, sx, sy, tx, ty) {
const x1 = grid.x1 || 0,
- y1 = grid.y1 || 0,
- flip = sx * sy < 0;
-
+ y1 = grid.y1 || 0,
+ flip = sx * sy < 0;
function transformPolygon(coordinates) {
coordinates.forEach(transformRing);
}
-
function transformRing(coordinates) {
if (flip) coordinates.reverse(); // maintain winding order
-
coordinates.forEach(transformPoint);
}
-
function transformPoint(coordinates) {
coordinates[0] = (coordinates[0] - x1) * sx + tx;
coordinates[1] = (coordinates[1] - y1) * sy + ty;
}
-
return function (geometry) {
geometry.coordinates.forEach(transformPolygon);
return geometry;
};
}
-
function radius(bw, data, f) {
const v = bw >= 0 ? bw : estimateBandwidth(data, f);
return Math.round((Math.sqrt(4 * v * v + 1) - 1) / 2);
}
-
function number$2(_) {
- return isFunction(_) ? _ : constant$4(+_);
- } // Implementation adapted from d3/d3-contour. Thanks!
+ return isFunction(_) ? _ : constant$5(+_);
+ }
-
+ // Implementation adapted from d3/d3-contour. Thanks!
function density2D() {
var x = d => d[0],
- y = d => d[1],
- weight = one$2,
- bandwidth = [-1, -1],
- dx = 960,
- dy = 500,
- k = 2; // log2(cellSize)
+ y = d => d[1],
+ weight = one$2,
+ bandwidth = [-1, -1],
+ dx = 960,
+ dy = 500,
+ k = 2; // log2(cellSize)
-
function density(data, counts) {
const rx = radius(bandwidth[0], data, x) >> k,
- // blur x-radius
- ry = radius(bandwidth[1], data, y) >> k,
- // blur y-radius
- ox = rx ? rx + 2 : 0,
- // x-offset padding for blur
- oy = ry ? ry + 2 : 0,
- // y-offset padding for blur
- n = 2 * ox + (dx >> k),
- // grid width
- m = 2 * oy + (dy >> k),
- // grid height
- values0 = new Float32Array(n * m),
- values1 = new Float32Array(n * m);
+ // blur x-radius
+ ry = radius(bandwidth[1], data, y) >> k,
+ // blur y-radius
+ ox = rx ? rx + 2 : 0,
+ // x-offset padding for blur
+ oy = ry ? ry + 2 : 0,
+ // y-offset padding for blur
+ n = 2 * ox + (dx >> k),
+ // grid width
+ m = 2 * oy + (dy >> k),
+ // grid height
+ values0 = new Float32Array(n * m),
+ values1 = new Float32Array(n * m);
let values = values0;
data.forEach(d => {
const xi = ox + (+x(d) >> k),
- yi = oy + (+y(d) >> k);
-
+ yi = oy + (+y(d) >> k);
if (xi >= 0 && xi < n && yi >= 0 && yi < m) {
values0[xi + yi * n] += +weight(d);
}
});
-
if (rx > 0 && ry > 0) {
blurX(n, m, values0, values1, rx);
blurY(n, m, values1, values0, ry);
blurX(n, m, values0, values1, rx);
blurY(n, m, values1, values0, ry);
@@ -27693,18 +25088,16 @@
} else if (ry > 0) {
blurY(n, m, values0, values1, ry);
blurY(n, m, values1, values0, ry);
blurY(n, m, values0, values1, ry);
values = values1;
- } // scale density estimates
- // density in points per square pixel or probability density
+ }
-
+ // scale density estimates
+ // density in points per square pixel or probability density
const s = counts ? Math.pow(2, -2 * k) : 1 / sum$1(values);
-
for (let i = 0, sz = n * m; i < sz; ++i) values[i] *= s;
-
return {
values: values,
scale: 1 << k,
width: n,
height: m,
@@ -27712,90 +25105,74 @@
y1: oy,
x2: ox + (dx >> k),
y2: oy + (dy >> k)
};
}
-
density.x = function (_) {
return arguments.length ? (x = number$2(_), density) : x;
};
-
density.y = function (_) {
return arguments.length ? (y = number$2(_), density) : y;
};
-
density.weight = function (_) {
return arguments.length ? (weight = number$2(_), density) : weight;
};
-
density.size = function (_) {
if (!arguments.length) return [dx, dy];
-
var _0 = +_[0],
- _1 = +_[1];
-
+ _1 = +_[1];
if (!(_0 >= 0 && _1 >= 0)) error('invalid size');
return dx = _0, dy = _1, density;
};
-
density.cellSize = function (_) {
if (!arguments.length) return 1 << k;
if (!((_ = +_) >= 1)) error('invalid cell size');
k = Math.floor(Math.log(_) / Math.LN2);
return density;
};
-
density.bandwidth = function (_) {
if (!arguments.length) return bandwidth;
_ = array$5(_);
if (_.length === 1) _ = [+_[0], +_[0]];
if (_.length !== 2) error('invalid bandwidth');
return bandwidth = _, density;
};
-
return density;
}
-
function blurX(n, m, source, target, r) {
const w = (r << 1) + 1;
-
for (let j = 0; j < m; ++j) {
for (let i = 0, sr = 0; i < n + r; ++i) {
if (i < n) {
sr += source[i + j * n];
}
-
if (i >= r) {
if (i >= w) {
sr -= source[i - w + j * n];
}
-
target[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w);
}
}
}
}
-
function blurY(n, m, source, target, r) {
const w = (r << 1) + 1;
-
for (let i = 0; i < n; ++i) {
for (let j = 0, sr = 0; j < m + r; ++j) {
if (j < m) {
sr += source[i + j * n];
}
-
if (j >= r) {
if (j >= w) {
sr -= source[i + (j - w) * n];
}
-
target[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w);
}
}
}
}
+
/**
* Perform 2D kernel-density estimation of point data.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<number>} params.size - The [width, height] extent (in
@@ -27819,16 +25196,13 @@
* output values should be probability estimates (false, default) or
* smoothed counts (true).
* @param {string} [params.as='grid'] - The output field in which to store
* the generated raster grid (default 'grid').
*/
-
-
function KDE2D(params) {
Transform.call(this, null, params);
}
-
KDE2D.Definition = {
'type': 'KDE2D',
'metadata': {
'generates': true
},
@@ -27870,75 +25244,67 @@
'type': 'string',
'default': 'grid'
}]
};
const PARAMS = ['x', 'y', 'weight', 'size', 'cellSize', 'bandwidth'];
-
function params(obj, _) {
PARAMS.forEach(param => _[param] != null ? obj[param](_[param]) : 0);
return obj;
}
-
inherits(KDE2D, Transform, {
transform(_, pulse) {
if (this.value && !pulse.changed() && !_.modified()) return pulse.StopPropagation;
var out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- source = pulse.materialize(pulse.SOURCE).source,
- groups = partition$2(source, _.groupby),
- names = (_.groupby || []).map(accessorName),
- kde = params(density2D(), _),
- as = _.as || 'grid',
- values = [];
-
+ source = pulse.materialize(pulse.SOURCE).source,
+ groups = partition$2(source, _.groupby),
+ names = (_.groupby || []).map(accessorName),
+ kde = params(density2D(), _),
+ as = _.as || 'grid',
+ values = [];
function set(t, vals) {
for (let i = 0; i < names.length; ++i) t[names[i]] = vals[i];
-
return t;
- } // generate density raster grids
+ }
-
+ // generate density raster grids
values = groups.map(g => ingest$1(set({
[as]: kde(g, _.counts)
}, g.dims)));
if (this.value) out.rem = this.value;
this.value = out.source = out.add = values;
return out;
}
-
});
-
function partition$2(data, groupby) {
var groups = [],
- get = f => f(t),
- map,
- i,
- n,
- t,
- k,
- g; // partition data points into groups
+ get = f => f(t),
+ map,
+ i,
+ n,
+ t,
+ k,
+ g;
-
+ // partition data points into groups
if (groupby == null) {
groups.push(data);
} else {
for (map = {}, i = 0, n = data.length; i < n; ++i) {
t = data[i];
k = groupby.map(get);
g = map[k];
-
if (!g) {
map[k] = g = [];
g.dims = k;
groups.push(g);
}
-
g.push(t);
}
}
-
return groups;
}
+
/**
* Generate contours based on kernel-density estimation of point data.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<number>} params.size - The dimensions [width, height] over which to compute contours.
@@ -27962,16 +25328,13 @@
* thresholds to deviate from the specified count.
* @param {boolean} [params.smooth] - Boolean flag indicating if the contour
* polygons should be smoothed using linear interpolation. The default is
* true. The parameter is ignored when using density estimation.
*/
-
-
function Contour(params) {
Transform.call(this, null, params);
}
-
Contour.Definition = {
'type': 'Contour',
'metadata': {
'generates': true
},
@@ -28020,39 +25383,36 @@
inherits(Contour, Transform, {
transform(_, pulse) {
if (this.value && !pulse.changed() && !_.modified()) {
return pulse.StopPropagation;
}
-
var out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS),
- contour = contours().smooth(_.smooth !== false),
- values = _.values,
- thresh = _.thresholds || quantize(_.count || 10, _.nice, !!values),
- size = _.size,
- grid,
- post;
-
+ contour = contours().smooth(_.smooth !== false),
+ values = _.values,
+ thresh = _.thresholds || quantize(_.count || 10, _.nice, !!values),
+ size = _.size,
+ grid,
+ post;
if (!values) {
values = pulse.materialize(pulse.SOURCE).source;
grid = params(density2D(), _)(values, true);
post = transform$1(grid, grid.scale || 1, grid.scale || 1, 0, 0);
size = [grid.width, grid.height];
values = grid.values;
}
-
thresh = isArray(thresh) ? thresh : thresh(values);
values = contour.size(size)(values, thresh);
if (post) values.forEach(post);
if (this.value) out.rem = this.value;
this.value = out.source = out.add = (values || []).map(ingest$1);
return out;
}
-
});
const Feature = 'Feature';
const FeatureCollection = 'FeatureCollection';
const MultiPoint = 'MultiPoint';
+
/**
* Consolidate an array of [longitude, latitude] points or GeoJSON features
* into a combined GeoJSON object. This transform is particularly useful for
* combining geo data for a Projection's fit argument. The resulting GeoJSON
* data is available as this transform's value. Input pulses are unchanged.
@@ -28061,15 +25421,13 @@
* @param {Array<function(object): *>} [params.fields] - A two-element array
* of field accessors for the longitude and latitude values.
* @param {function(object): *} params.geojson - A field accessor for
* retrieving GeoJSON feature data.
*/
-
function GeoJSON(params) {
Transform.call(this, null, params);
}
-
GeoJSON.Definition = {
'type': 'GeoJSON',
'metadata': {},
'params': [{
'name': 'fields',
@@ -28082,34 +25440,30 @@
}]
};
inherits(GeoJSON, Transform, {
transform(_, pulse) {
var features = this._features,
- points = this._points,
- fields = _.fields,
- lon = fields && fields[0],
- lat = fields && fields[1],
- geojson = _.geojson || !fields && identity$6,
- flag = pulse.ADD,
- mod;
+ points = this._points,
+ fields = _.fields,
+ lon = fields && fields[0],
+ lat = fields && fields[1],
+ geojson = _.geojson || !fields && identity$6,
+ flag = pulse.ADD,
+ mod;
mod = _.modified() || pulse.changed(pulse.REM) || pulse.modified(accessorFields(geojson)) || lon && pulse.modified(accessorFields(lon)) || lat && pulse.modified(accessorFields(lat));
-
if (!this.value || mod) {
flag = pulse.SOURCE;
this._features = features = [];
this._points = points = [];
}
-
if (geojson) {
pulse.visit(flag, t => features.push(geojson(t)));
}
-
if (lon && lat) {
pulse.visit(flag, t => {
var x = lon(t),
- y = lat(t);
-
+ y = lat(t);
if (x != null && y != null && (x = +x) === x && (y = +y) === y) {
points.push([x, y]);
}
});
features = features.concat({
@@ -28118,18 +25472,17 @@
type: MultiPoint,
coordinates: points
}
});
}
-
this.value = {
type: FeatureCollection,
features: features
};
}
-
});
+
/**
* Map GeoJSON data to an SVG path string.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(number, number): *} params.projection - The cartographic
@@ -28137,15 +25490,13 @@
* @param {function(object): *} [params.field] - The field with GeoJSON data,
* or null if the tuple itself is a GeoJSON feature.
* @param {string} [params.as='path'] - The output field in which to store
* the generated path data (default 'path').
*/
-
function GeoPath(params) {
Transform.call(this, null, params);
}
-
GeoPath.Definition = {
'type': 'GeoPath',
'metadata': {
'modifies': true
},
@@ -28166,41 +25517,36 @@
}]
};
inherits(GeoPath, Transform, {
transform(_, pulse) {
var out = pulse.fork(pulse.ALL),
- path = this.value,
- field = _.field || identity$6,
- as = _.as || 'path',
- flag = out.SOURCE;
-
+ path = this.value,
+ field = _.field || identity$6,
+ as = _.as || 'path',
+ flag = out.SOURCE;
if (!path || _.modified()) {
// parameters updated, reset and reflow
this.value = path = getProjectionPath(_.projection);
out.materialize().reflow();
} else {
flag = field === identity$6 || pulse.modified(field.fields) ? out.ADD_MOD : out.ADD;
}
-
const prev = initPath(path, _.pointRadius);
out.visit(flag, t => t[as] = path(field(t)));
path.pointRadius(prev);
return out.modifies(as);
}
-
});
-
function initPath(path, pointRadius) {
const prev = path.pointRadius();
path.context(null);
-
if (pointRadius != null) {
path.pointRadius(pointRadius);
}
-
return prev;
}
+
/**
* Geo-code a longitude/latitude point to an x/y coordinate.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(number, number): *} params.projection - The cartographic
@@ -28208,16 +25554,13 @@
* @param {Array<function(object): *>} params.fields - A two-element array of
* field accessors for the longitude and latitude values.
* @param {Array<string>} [params.as] - A two-element array of field names
* under which to store the result. Defaults to ['x','y'].
*/
-
-
function GeoPoint(params) {
Transform.call(this, null, params);
}
-
GeoPoint.Definition = {
'type': 'GeoPoint',
'metadata': {
'modifies': true
},
@@ -28240,41 +25583,37 @@
}]
};
inherits(GeoPoint, Transform, {
transform(_, pulse) {
var proj = _.projection,
- lon = _.fields[0],
- lat = _.fields[1],
- as = _.as || ['x', 'y'],
- x = as[0],
- y = as[1],
- mod;
-
+ lon = _.fields[0],
+ lat = _.fields[1],
+ as = _.as || ['x', 'y'],
+ x = as[0],
+ y = as[1],
+ mod;
function set(t) {
const xy = proj([lon(t), lat(t)]);
-
if (xy) {
t[x] = xy[0];
t[y] = xy[1];
} else {
t[x] = undefined;
t[y] = undefined;
}
}
-
if (_.modified()) {
// parameters updated, reflow
pulse = pulse.materialize().reflow(true).visit(pulse.SOURCE, set);
} else {
mod = pulse.modified(lon.fields) || pulse.modified(lat.fields);
pulse.visit(mod ? pulse.ADD_MOD : pulse.ADD, set);
}
-
return pulse.modifies(as);
}
-
});
+
/**
* Annotate items with a geopath shape generator.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(number, number): *} params.projection - The cartographic
@@ -28282,15 +25621,13 @@
* @param {function(object): *} [params.field] - The field with GeoJSON data,
* or null if the tuple itself is a GeoJSON feature.
* @param {string} [params.as='shape'] - The output field in which to store
* the generated path data (default 'shape').
*/
-
function GeoShape(params) {
Transform.call(this, null, params);
}
-
GeoShape.Definition = {
'type': 'GeoShape',
'metadata': {
'modifies': true,
'nomod': true
@@ -28313,53 +25650,45 @@
}]
};
inherits(GeoShape, Transform, {
transform(_, pulse) {
var out = pulse.fork(pulse.ALL),
- shape = this.value,
- as = _.as || 'shape',
- flag = out.ADD;
-
+ shape = this.value,
+ as = _.as || 'shape',
+ flag = out.ADD;
if (!shape || _.modified()) {
// parameters updated, reset and reflow
this.value = shape = shapeGenerator(getProjectionPath(_.projection), _.field || field$1('datum'), _.pointRadius);
out.materialize().reflow();
flag = out.SOURCE;
}
-
out.visit(flag, t => t[as] = shape);
return out.modifies(as);
}
-
});
-
function shapeGenerator(path, field, pointRadius) {
const shape = pointRadius == null ? _ => path(field(_)) : _ => {
var prev = path.pointRadius(),
- value = path.pointRadius(pointRadius)(field(_));
+ value = path.pointRadius(pointRadius)(field(_));
path.pointRadius(prev);
return value;
};
-
shape.context = _ => {
path.context(_);
return shape;
};
-
return shape;
}
+
/**
* GeoJSON feature generator for creating graticules.
* @constructor
*/
-
-
function Graticule(params) {
Transform.call(this, [], params);
this.generator = graticule();
}
-
Graticule.Definition = {
'type': 'Graticule',
'metadata': {
'changes': true,
'generates': true
@@ -28418,34 +25747,30 @@
}]
};
inherits(Graticule, Transform, {
transform(_, pulse) {
var src = this.value,
- gen = this.generator,
- t;
-
+ gen = this.generator,
+ t;
if (!src.length || _.modified()) {
for (const prop in _) {
if (isFunction(gen[prop])) {
gen[prop](_[prop]);
}
}
}
-
t = gen();
-
if (src.length) {
pulse.mod.push(replace$1(src[0], t));
} else {
pulse.add.push(ingest$1(t));
}
-
src[0] = t;
return pulse;
}
-
});
+
/**
* Render a heatmap image for input raster grid data.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} [params.field] - The field with raster grid
@@ -28461,15 +25786,13 @@
* calculation will be performed separately for each grid. If 'shared',
* a single global maximum will be used for all input grids.
* @param {string} [params.as='image'] - The output field in which to store
* the generated bitmap canvas images (default 'image').
*/
-
function Heatmap(params) {
Transform.call(this, null, params);
}
-
Heatmap.Definition = {
'type': 'heatmap',
'metadata': {
'modifies': true
},
@@ -28498,169 +25821,150 @@
inherits(Heatmap, Transform, {
transform(_, pulse) {
if (!pulse.changed() && !_.modified()) {
return pulse.StopPropagation;
}
-
var source = pulse.materialize(pulse.SOURCE).source,
- shared = _.resolve === 'shared',
- field = _.field || identity$6,
- opacity = opacity_(_.opacity, _),
- color = color_(_.color, _),
- as = _.as || 'image',
- obj = {
- $x: 0,
- $y: 0,
- $value: 0,
- $max: shared ? max$2(source.map(t => max$2(field(t).values))) : 0
- };
+ shared = _.resolve === 'shared',
+ field = _.field || identity$6,
+ opacity = opacity_(_.opacity, _),
+ color = color_(_.color, _),
+ as = _.as || 'image',
+ obj = {
+ $x: 0,
+ $y: 0,
+ $value: 0,
+ $max: shared ? max$2(source.map(t => max$2(field(t).values))) : 0
+ };
source.forEach(t => {
- const v = field(t); // build proxy data object
+ const v = field(t);
- const o = extend$1({}, t, obj); // set maximum value if not globally shared
+ // build proxy data object
+ const o = extend$1({}, t, obj);
+ // set maximum value if not globally shared
+ if (!shared) o.$max = max$2(v.values || []);
- if (!shared) o.$max = max$2(v.values || []); // generate canvas image
+ // generate canvas image
// optimize color/opacity if not pixel-dependent
-
- t[as] = toCanvas(v, o, color.dep ? color : constant$4(color(o)), opacity.dep ? opacity : constant$4(opacity(o)));
+ t[as] = toCanvas(v, o, color.dep ? color : constant$5(color(o)), opacity.dep ? opacity : constant$5(opacity(o)));
});
return pulse.reflow(true).modifies(as);
}
+ });
- }); // get image color function
-
+ // get image color function
function color_(color, _) {
let f;
-
if (isFunction(color)) {
f = obj => rgb$1(color(obj, _));
-
f.dep = dependency(color);
} else {
// default to mid-grey
- f = constant$4(rgb$1(color || '#888'));
+ f = constant$5(rgb$1(color || '#888'));
}
-
return f;
- } // get image opacity function
+ }
-
+ // get image opacity function
function opacity_(opacity, _) {
let f;
-
if (isFunction(opacity)) {
f = obj => opacity(obj, _);
-
f.dep = dependency(opacity);
} else if (opacity) {
- f = constant$4(opacity);
+ f = constant$5(opacity);
} else {
// default to [0, max] opacity gradient
f = obj => obj.$value / obj.$max || 0;
-
f.dep = true;
}
-
return f;
- } // check if function depends on individual pixel data
+ }
-
+ // check if function depends on individual pixel data
function dependency(f) {
if (!isFunction(f)) return false;
const set = toSet(accessorFields(f));
return set.$x || set.$y || set.$value || set.$max;
- } // render raster grid to canvas
+ }
-
+ // render raster grid to canvas
function toCanvas(grid, obj, color, opacity) {
const n = grid.width,
- m = grid.height,
- x1 = grid.x1 || 0,
- y1 = grid.y1 || 0,
- x2 = grid.x2 || n,
- y2 = grid.y2 || m,
- val = grid.values,
- value = val ? i => val[i] : zero$2,
- can = domCanvas(x2 - x1, y2 - y1),
- ctx = can.getContext('2d'),
- img = ctx.getImageData(0, 0, x2 - x1, y2 - y1),
- pix = img.data;
-
+ m = grid.height,
+ x1 = grid.x1 || 0,
+ y1 = grid.y1 || 0,
+ x2 = grid.x2 || n,
+ y2 = grid.y2 || m,
+ val = grid.values,
+ value = val ? i => val[i] : zero$3,
+ can = domCanvas(x2 - x1, y2 - y1),
+ ctx = can.getContext('2d'),
+ img = ctx.getImageData(0, 0, x2 - x1, y2 - y1),
+ pix = img.data;
for (let j = y1, k = 0; j < y2; ++j) {
obj.$y = j - y1;
-
for (let i = x1, r = j * n; i < x2; ++i, k += 4) {
obj.$x = i - x1;
obj.$value = value(i + r);
const v = color(obj);
pix[k + 0] = v.r;
pix[k + 1] = v.g;
pix[k + 2] = v.b;
pix[k + 3] = ~~(255 * opacity(obj));
}
}
-
ctx.putImageData(img, 0, 0);
return can;
}
+
/**
* Maintains a cartographic projection.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
-
function Projection$1(params) {
Transform.call(this, null, params);
this.modified(true); // always treat as modified
}
-
inherits(Projection$1, Transform, {
transform(_, pulse) {
let proj = this.value;
-
if (!proj || _.modified('type')) {
this.value = proj = create(_.type);
projectionProperties.forEach(prop => {
if (_[prop] != null) set$1(proj, prop, _[prop]);
});
} else {
projectionProperties.forEach(prop => {
if (_.modified(prop)) set$1(proj, prop, _[prop]);
});
}
-
if (_.pointRadius != null) proj.path.pointRadius(_.pointRadius);
if (_.fit) fit(proj, _);
return pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
}
-
});
-
function fit(proj, _) {
const data = collectGeoJSON(_.fit);
_.extent ? proj.fitExtent(_.extent, data) : _.size ? proj.fitSize(_.size, data) : 0;
}
-
function create(type) {
const constructor = projection((type || 'mercator').toLowerCase());
if (!constructor) error('Unrecognized projection type: ' + type);
return constructor();
}
-
function set$1(proj, key, value) {
if (isFunction(proj[key])) proj[key](value);
}
-
function collectGeoJSON(data) {
data = array$5(data);
return data.length === 1 ? data[0] : {
type: FeatureCollection,
features: data.reduce((a, f) => a.concat(featurize(f)), [])
};
}
-
function featurize(f) {
return f.type === FeatureCollection ? f.features : array$5(f).filter(d => d != null).map(d => d.type === Feature ? d : {
type: Feature,
geometry: d
});
@@ -28680,179 +25984,169 @@
projection: Projection$1
});
function forceCenter (x, y) {
var nodes,
- strength = 1;
+ strength = 1;
if (x == null) x = 0;
if (y == null) y = 0;
-
function force() {
var i,
- n = nodes.length,
- node,
- sx = 0,
- sy = 0;
-
+ n = nodes.length,
+ node,
+ sx = 0,
+ sy = 0;
for (i = 0; i < n; ++i) {
node = nodes[i], sx += node.x, sy += node.y;
}
-
for (sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, i = 0; i < n; ++i) {
node = nodes[i], node.x -= sx, node.y -= sy;
}
}
-
force.initialize = function (_) {
nodes = _;
};
-
force.x = function (_) {
return arguments.length ? (x = +_, force) : x;
};
-
force.y = function (_) {
return arguments.length ? (y = +_, force) : y;
};
-
force.strength = function (_) {
return arguments.length ? (strength = +_, force) : strength;
};
-
return force;
}
function tree_add (d) {
const x = +this._x.call(null, d),
- y = +this._y.call(null, d);
+ y = +this._y.call(null, d);
return add(this.cover(x, y), x, y, d);
}
-
function add(tree, x, y, d) {
if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
var parent,
- node = tree._root,
- leaf = {
- data: d
- },
- x0 = tree._x0,
- y0 = tree._y0,
- x1 = tree._x1,
- y1 = tree._y1,
- xm,
- ym,
- xp,
- yp,
- right,
- bottom,
- i,
- j; // If the tree is empty, initialize the root as a leaf.
+ node = tree._root,
+ leaf = {
+ data: d
+ },
+ x0 = tree._x0,
+ y0 = tree._y0,
+ x1 = tree._x1,
+ y1 = tree._y1,
+ xm,
+ ym,
+ xp,
+ yp,
+ right,
+ bottom,
+ i,
+ j;
- if (!node) return tree._root = leaf, tree; // Find the existing leaf for the new point, or add it.
+ // If the tree is empty, initialize the root as a leaf.
+ if (!node) return tree._root = leaf, tree;
+ // Find the existing leaf for the new point, or add it.
while (node.length) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm;else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym;else y1 = ym;
if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
- } // Is the new point is exactly coincident with the existing point?
+ }
-
+ // Is the new point is exactly coincident with the existing point?
xp = +tree._x.call(null, node.data);
yp = +tree._y.call(null, node.data);
- if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; // Otherwise, split the leaf node until the old and new point are separated.
+ if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
+ // Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm;else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym;else y1 = ym;
} while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | xp >= xm));
-
return parent[j] = node, parent[i] = leaf, tree;
}
-
function addAll(data) {
var d,
- i,
- n = data.length,
- x,
- y,
- xz = new Array(n),
- yz = new Array(n),
- x0 = Infinity,
- y0 = Infinity,
- x1 = -Infinity,
- y1 = -Infinity; // Compute the points and their extent.
+ i,
+ n = data.length,
+ x,
+ y,
+ xz = new Array(n),
+ yz = new Array(n),
+ x0 = Infinity,
+ y0 = Infinity,
+ x1 = -Infinity,
+ y1 = -Infinity;
+ // Compute the points and their extent.
for (i = 0; i < n; ++i) {
if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
xz[i] = x;
yz[i] = y;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
if (y < y0) y0 = y;
if (y > y1) y1 = y;
- } // If there were no (valid) points, abort.
+ }
+ // If there were no (valid) points, abort.
+ if (x0 > x1 || y0 > y1) return this;
- if (x0 > x1 || y0 > y1) return this; // Expand the tree to cover the new points.
+ // Expand the tree to cover the new points.
+ this.cover(x0, y0).cover(x1, y1);
- this.cover(x0, y0).cover(x1, y1); // Add the new points.
-
+ // Add the new points.
for (i = 0; i < n; ++i) {
add(this, xz[i], yz[i], data[i]);
}
-
return this;
}
function tree_cover (x, y) {
if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
var x0 = this._x0,
- y0 = this._y0,
- x1 = this._x1,
- y1 = this._y1; // If the quadtree has no extent, initialize them.
+ y0 = this._y0,
+ x1 = this._x1,
+ y1 = this._y1;
+
+ // If the quadtree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing quadrant boundaries don’t change due to floating point error!
-
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
y1 = (y0 = Math.floor(y)) + 1;
- } // Otherwise, double repeatedly to cover.
+ }
+
+ // Otherwise, double repeatedly to cover.
else {
var z = x1 - x0 || 1,
- node = this._root,
- parent,
- i;
-
+ node = this._root,
+ parent,
+ i;
while (x0 > x || x >= x1 || y0 > y || y >= y1) {
i = (y < y0) << 1 | x < x0;
parent = new Array(4), parent[i] = node, node = parent, z *= 2;
-
switch (i) {
case 0:
x1 = x0 + z, y1 = y0 + z;
break;
-
case 1:
x0 = x1 - z, y1 = y0 + z;
break;
-
case 2:
x1 = x0 + z, y0 = y1 - z;
break;
-
case 3:
x0 = x1 - z, y0 = y1 - z;
break;
}
}
-
if (this._root && this._root.length) this._root = node;
}
-
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
return this;
@@ -28878,112 +26172,117 @@
this.y1 = y1;
}
function tree_find (x, y, radius) {
var data,
- x0 = this._x0,
- y0 = this._y0,
- x1,
- y1,
- x2,
- y2,
- x3 = this._x1,
- y3 = this._y1,
- quads = [],
- node = this._root,
- q,
- i;
+ x0 = this._x0,
+ y0 = this._y0,
+ x1,
+ y1,
+ x2,
+ y2,
+ x3 = this._x1,
+ y3 = this._y1,
+ quads = [],
+ node = this._root,
+ q,
+ i;
if (node) quads.push(new Quad(node, x0, y0, x3, y3));
if (radius == null) radius = Infinity;else {
x0 = x - radius, y0 = y - radius;
x3 = x + radius, y3 = y + radius;
radius *= radius;
}
-
while (q = quads.pop()) {
// Stop searching if this quadrant can’t contain a closer node.
- if (!(node = q.node) || (x1 = q.x0) > x3 || (y1 = q.y0) > y3 || (x2 = q.x1) < x0 || (y2 = q.y1) < y0) continue; // Bisect the current quadrant.
+ if (!(node = q.node) || (x1 = q.x0) > x3 || (y1 = q.y0) > y3 || (x2 = q.x1) < x0 || (y2 = q.y1) < y0) continue;
+ // Bisect the current quadrant.
if (node.length) {
var xm = (x1 + x2) / 2,
- ym = (y1 + y2) / 2;
- quads.push(new Quad(node[3], xm, ym, x2, y2), new Quad(node[2], x1, ym, xm, y2), new Quad(node[1], xm, y1, x2, ym), new Quad(node[0], x1, y1, xm, ym)); // Visit the closest quadrant first.
+ ym = (y1 + y2) / 2;
+ quads.push(new Quad(node[3], xm, ym, x2, y2), new Quad(node[2], x1, ym, xm, y2), new Quad(node[1], xm, y1, x2, ym), new Quad(node[0], x1, y1, xm, ym));
+ // Visit the closest quadrant first.
if (i = (y >= ym) << 1 | x >= xm) {
q = quads[quads.length - 1];
quads[quads.length - 1] = quads[quads.length - 1 - i];
quads[quads.length - 1 - i] = q;
}
- } // Visit this point. (Visiting coincident points isn’t necessary!)
+ }
+
+ // Visit this point. (Visiting coincident points isn’t necessary!)
else {
var dx = x - +this._x.call(null, node.data),
- dy = y - +this._y.call(null, node.data),
- d2 = dx * dx + dy * dy;
-
+ dy = y - +this._y.call(null, node.data),
+ d2 = dx * dx + dy * dy;
if (d2 < radius) {
var d = Math.sqrt(radius = d2);
x0 = x - d, y0 = y - d;
x3 = x + d, y3 = y + d;
data = node.data;
}
}
}
-
return data;
}
function tree_remove (d) {
if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
var parent,
- node = this._root,
- retainer,
- previous,
- next,
- x0 = this._x0,
- y0 = this._y0,
- x1 = this._x1,
- y1 = this._y1,
- x,
- y,
- xm,
- ym,
- right,
- bottom,
- i,
- j; // If the tree is empty, initialize the root as a leaf.
+ node = this._root,
+ retainer,
+ previous,
+ next,
+ x0 = this._x0,
+ y0 = this._y0,
+ x1 = this._x1,
+ y1 = this._y1,
+ x,
+ y,
+ xm,
+ ym,
+ right,
+ bottom,
+ i,
+ j;
- if (!node) return this; // Find the leaf node for the point.
- // While descending, also retain the deepest parent with a non-removed sibling.
+ // If the tree is empty, initialize the root as a leaf.
+ if (!node) return this;
+ // Find the leaf node for the point.
+ // While descending, also retain the deepest parent with a non-removed sibling.
if (node.length) while (true) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm;else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym;else y1 = ym;
if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
if (!node.length) break;
if (parent[i + 1 & 3] || parent[i + 2 & 3] || parent[i + 3 & 3]) retainer = parent, j = i;
- } // Find the point to remove.
+ }
+ // Find the point to remove.
while (node.data !== d) if (!(previous = node, node = node.next)) return this;
+ if (next = node.next) delete node.next;
- if (next = node.next) delete node.next; // If there are multiple coincident points, remove just the point.
+ // If there are multiple coincident points, remove just the point.
+ if (previous) return next ? previous.next = next : delete previous.next, this;
- if (previous) return next ? previous.next = next : delete previous.next, this; // If this is the root point, remove it.
+ // If this is the root point, remove it.
+ if (!parent) return this._root = next, this;
- if (!parent) return this._root = next, this; // Remove this leaf.
+ // Remove this leaf.
+ next ? parent[i] = next : delete parent[i];
- next ? parent[i] = next : delete parent[i]; // If the parent now contains exactly one leaf, collapse superfluous parents.
-
+ // If the parent now contains exactly one leaf, collapse superfluous parents.
if ((node = parent[0] || parent[1] || parent[2] || parent[3]) && node === (parent[3] || parent[2] || parent[1] || parent[0]) && !node.length) {
if (retainer) retainer[j] = node;else this._root = node;
}
-
return this;
}
function removeAll(data) {
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
-
return this;
}
function tree_root () {
return this._root;
@@ -28997,63 +26296,56 @@
return size;
}
function tree_visit (callback) {
var quads = [],
- q,
- node = this._root,
- child,
- x0,
- y0,
- x1,
- y1;
+ q,
+ node = this._root,
+ child,
+ x0,
+ y0,
+ x1,
+ y1;
if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
-
while (q = quads.pop()) {
if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
var xm = (x0 + x1) / 2,
- ym = (y0 + y1) / 2;
+ ym = (y0 + y1) / 2;
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
}
}
-
return this;
}
function tree_visitAfter (callback) {
var quads = [],
- next = [],
- q;
+ next = [],
+ q;
if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
-
while (q = quads.pop()) {
var node = q.node;
-
if (node.length) {
var child,
- x0 = q.x0,
- y0 = q.y0,
- x1 = q.x1,
- y1 = q.y1,
- xm = (x0 + x1) / 2,
- ym = (y0 + y1) / 2;
+ x0 = q.x0,
+ y0 = q.y0,
+ x1 = q.x1,
+ y1 = q.y1,
+ xm = (x0 + x1) / 2,
+ ym = (y0 + y1) / 2;
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
}
-
next.push(q);
}
-
while (q = next.pop()) {
callback(q.node, q.x0, q.y0, q.x1, q.y1);
}
-
return this;
}
function defaultX(d) {
return d[0];
@@ -29071,62 +26363,53 @@
function quadtree(nodes, x, y) {
var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN);
return nodes == null ? tree : tree.addAll(nodes);
}
-
function Quadtree(x, y, x0, y0, x1, y1) {
this._x = x;
this._y = y;
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
this._root = undefined;
}
-
function leaf_copy(leaf) {
var copy = {
- data: leaf.data
- },
- next = copy;
-
+ data: leaf.data
+ },
+ next = copy;
while (leaf = leaf.next) next = next.next = {
data: leaf.data
};
-
return copy;
}
-
var treeProto = quadtree.prototype = Quadtree.prototype;
-
treeProto.copy = function () {
var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
- node = this._root,
- nodes,
- child;
+ node = this._root,
+ nodes,
+ child;
if (!node) return copy;
if (!node.length) return copy._root = leaf_copy(node), copy;
nodes = [{
source: node,
target: copy._root = new Array(4)
}];
-
while (node = nodes.pop()) {
for (var i = 0; i < 4; ++i) {
if (child = node.source[i]) {
if (child.length) nodes.push({
source: child,
target: node.target[i] = new Array(4)
});else node.target[i] = leaf_copy(child);
}
}
}
-
return copy;
};
-
treeProto.add = tree_add;
treeProto.addAll = addAll;
treeProto.cover = tree_cover;
treeProto.data = tree_data;
treeProto.extent = tree_extent;
@@ -29151,142 +26434,119 @@
}
function x$1(d) {
return d.x + d.vx;
}
-
function y$1(d) {
return d.y + d.vy;
}
-
function forceCollide (radius) {
var nodes,
- radii,
- random,
- strength = 1,
- iterations = 1;
+ radii,
+ random,
+ strength = 1,
+ iterations = 1;
if (typeof radius !== "function") radius = constant$1(radius == null ? 1 : +radius);
-
function force() {
var i,
- n = nodes.length,
- tree,
- node,
- xi,
- yi,
- ri,
- ri2;
-
+ n = nodes.length,
+ tree,
+ node,
+ xi,
+ yi,
+ ri,
+ ri2;
for (var k = 0; k < iterations; ++k) {
tree = quadtree(nodes, x$1, y$1).visitAfter(prepare);
-
for (i = 0; i < n; ++i) {
node = nodes[i];
ri = radii[node.index], ri2 = ri * ri;
xi = node.x + node.vx;
yi = node.y + node.vy;
tree.visit(apply);
}
}
-
function apply(quad, x0, y0, x1, y1) {
var data = quad.data,
- rj = quad.r,
- r = ri + rj;
-
+ rj = quad.r,
+ r = ri + rj;
if (data) {
if (data.index > node.index) {
var x = xi - data.x - data.vx,
- y = yi - data.y - data.vy,
- l = x * x + y * y;
-
+ y = yi - data.y - data.vy,
+ l = x * x + y * y;
if (l < r * r) {
if (x === 0) x = jiggle(random), l += x * x;
if (y === 0) y = jiggle(random), l += y * y;
l = (r - (l = Math.sqrt(l))) / l * strength;
node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
node.vy += (y *= l) * r;
data.vx -= x * (r = 1 - r);
data.vy -= y * r;
}
}
-
return;
}
-
return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
}
}
-
function prepare(quad) {
if (quad.data) return quad.r = radii[quad.data.index];
-
for (var i = quad.r = 0; i < 4; ++i) {
if (quad[i] && quad[i].r > quad.r) {
quad.r = quad[i].r;
}
}
}
-
function initialize() {
if (!nodes) return;
var i,
- n = nodes.length,
- node;
+ n = nodes.length,
+ node;
radii = new Array(n);
-
for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
}
-
force.initialize = function (_nodes, _random) {
nodes = _nodes;
random = _random;
initialize();
};
-
force.iterations = function (_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
-
force.strength = function (_) {
return arguments.length ? (strength = +_, force) : strength;
};
-
force.radius = function (_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : radius;
};
-
return force;
}
function index$1(d) {
return d.index;
}
-
function find$1(nodeById, nodeId) {
var node = nodeById.get(nodeId);
if (!node) throw new Error("node not found: " + nodeId);
return node;
}
-
function forceLink (links) {
var id = index$1,
- strength = defaultStrength,
- strengths,
- distance = constant$1(30),
- distances,
- nodes,
- count,
- bias,
- random,
- iterations = 1;
+ strength = defaultStrength,
+ strengths,
+ distance = constant$1(30),
+ distances,
+ nodes,
+ count,
+ bias,
+ random,
+ iterations = 1;
if (links == null) links = [];
-
function defaultStrength(link) {
return 1 / Math.min(count[link.source.index], count[link.target.index]);
}
-
function force(alpha) {
for (var k = 0, n = links.length; k < iterations; ++k) {
for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {
link = links[i], source = link.source, target = link.target;
x = target.x + target.vx - source.x - source.vx || jiggle(random);
@@ -29299,218 +26559,185 @@
source.vx += x * (b = 1 - b);
source.vy += y * b;
}
}
}
-
function initialize() {
if (!nodes) return;
var i,
- n = nodes.length,
- m = links.length,
- nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])),
- link;
-
+ n = nodes.length,
+ m = links.length,
+ nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])),
+ link;
for (i = 0, count = new Array(n); i < m; ++i) {
link = links[i], link.index = i;
if (typeof link.source !== "object") link.source = find$1(nodeById, link.source);
if (typeof link.target !== "object") link.target = find$1(nodeById, link.target);
count[link.source.index] = (count[link.source.index] || 0) + 1;
count[link.target.index] = (count[link.target.index] || 0) + 1;
}
-
for (i = 0, bias = new Array(m); i < m; ++i) {
link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
}
-
strengths = new Array(m), initializeStrength();
distances = new Array(m), initializeDistance();
}
-
function initializeStrength() {
if (!nodes) return;
-
for (var i = 0, n = links.length; i < n; ++i) {
strengths[i] = +strength(links[i], i, links);
}
}
-
function initializeDistance() {
if (!nodes) return;
-
for (var i = 0, n = links.length; i < n; ++i) {
distances[i] = +distance(links[i], i, links);
}
}
-
force.initialize = function (_nodes, _random) {
nodes = _nodes;
random = _random;
initialize();
};
-
force.links = function (_) {
return arguments.length ? (links = _, initialize(), force) : links;
};
-
force.id = function (_) {
return arguments.length ? (id = _, force) : id;
};
-
force.iterations = function (_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
-
force.strength = function (_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$1(+_), initializeStrength(), force) : strength;
};
-
force.distance = function (_) {
return arguments.length ? (distance = typeof _ === "function" ? _ : constant$1(+_), initializeDistance(), force) : distance;
};
-
return force;
}
var noop = {
value: () => {}
};
-
function dispatch() {
for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
_[t] = [];
}
-
return new Dispatch(_);
}
-
function Dispatch(_) {
this._ = _;
}
-
function parseTypenames(typenames, types) {
return typenames.trim().split(/^|\s+/).map(function (t) {
var name = "",
- i = t.indexOf(".");
+ i = t.indexOf(".");
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
return {
type: t,
name: name
};
});
}
-
Dispatch.prototype = dispatch.prototype = {
constructor: Dispatch,
on: function (typename, callback) {
var _ = this._,
- T = parseTypenames(typename + "", _),
- t,
- i = -1,
- n = T.length; // If no callback was specified, return the callback of the given type and name.
+ T = parseTypenames(typename + "", _),
+ t,
+ i = -1,
+ n = T.length;
+ // If no callback was specified, return the callback of the given type and name.
if (arguments.length < 2) {
while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
-
return;
- } // If a type was specified, set the callback for the given type and name.
- // Otherwise, if a null callback was specified, remove callbacks of the given name.
+ }
-
+ // If a type was specified, set the callback for the given type and name.
+ // Otherwise, if a null callback was specified, remove callbacks of the given name.
if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
-
while (++i < n) {
if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
}
-
return this;
},
copy: function () {
var copy = {},
- _ = this._;
-
+ _ = this._;
for (var t in _) copy[t] = _[t].slice();
-
return new Dispatch(copy);
},
call: function (type, that) {
if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
-
for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
},
apply: function (type, that, args) {
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
-
for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
}
};
-
function get(type, name) {
for (var i = 0, n = type.length, c; i < n; ++i) {
if ((c = type[i]).name === name) {
return c.value;
}
}
}
-
function set(type, name, callback) {
for (var i = 0, n = type.length; i < n; ++i) {
if (type[i].name === name) {
type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
break;
}
}
-
if (callback != null) type.push({
name: name,
value: callback
});
return type;
}
var frame = 0,
- // is an animation frame pending?
- timeout = 0,
- // is a timeout pending?
- interval$1 = 0,
- // are any timers active?
- pokeDelay = 1000,
- // how frequently we check for clock skew
- taskHead,
- taskTail,
- clockLast = 0,
- clockNow = 0,
- clockSkew = 0,
- clock = typeof performance === "object" && performance.now ? performance : Date,
- setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
- setTimeout(f, 17);
- };
+ // is an animation frame pending?
+ timeout = 0,
+ // is a timeout pending?
+ interval$1 = 0,
+ // are any timers active?
+ pokeDelay = 1000,
+ // how frequently we check for clock skew
+ taskHead,
+ taskTail,
+ clockLast = 0,
+ clockNow = 0,
+ clockSkew = 0,
+ clock = typeof performance === "object" && performance.now ? performance : Date,
+ setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
+ setTimeout(f, 17);
+ };
function now() {
return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
}
-
function clearNow() {
clockNow = 0;
}
-
function Timer$1() {
this._call = this._time = this._next = null;
}
Timer$1.prototype = timer$1.prototype = {
constructor: Timer$1,
restart: function (callback, delay, time) {
if (typeof callback !== "function") throw new TypeError("callback is not a function");
time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
-
if (!this._next && taskTail !== this) {
if (taskTail) taskTail._next = this;else taskHead = this;
taskTail = this;
}
-
this._call = callback;
this._time = time;
sleep();
},
stop: function () {
@@ -29526,69 +26753,56 @@
t.restart(callback, delay, time);
return t;
}
function timerFlush() {
now(); // Get the current time, if not already set.
-
++frame; // Pretend we’ve set an alarm, if we haven’t already.
-
var t = taskHead,
- e;
-
+ e;
while (t) {
if ((e = clockNow - t._time) >= 0) t._call.call(undefined, e);
t = t._next;
}
-
--frame;
}
-
function wake() {
clockNow = (clockLast = clock.now()) + clockSkew;
frame = timeout = 0;
-
try {
timerFlush();
} finally {
frame = 0;
nap();
clockNow = 0;
}
}
-
function poke() {
var now = clock.now(),
- delay = now - clockLast;
+ delay = now - clockLast;
if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
}
-
function nap() {
var t0,
- t1 = taskHead,
- t2,
- time = Infinity;
-
+ t1 = taskHead,
+ t2,
+ time = Infinity;
while (t1) {
if (t1._call) {
if (time > t1._time) time = t1._time;
t0 = t1, t1 = t1._next;
} else {
t2 = t1._next, t1._next = null;
t1 = t0 ? t0._next = t2 : taskHead = t2;
}
}
-
taskTail = t0;
sleep(time);
}
-
function sleep(time) {
if (frame) return; // Soonest alarm already set, or will be.
-
if (timeout) timeout = clearTimeout(timeout);
var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
-
if (delay > 24) {
if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
if (interval$1) interval$1 = clearInterval(interval$1);
} else {
if (!interval$1) clockLast = clock.now(), interval$1 = setInterval(poke, pokeDelay);
@@ -29596,117 +26810,101 @@
}
}
function interval (callback, delay, time) {
var t = new Timer$1(),
- total = delay;
+ total = delay;
if (delay == null) return t.restart(callback, delay, time), t;
t._restart = t.restart;
-
t.restart = function (callback, delay, time) {
delay = +delay, time = time == null ? now() : +time;
-
t._restart(function tick(elapsed) {
elapsed += total;
-
t._restart(tick, total += delay, time);
-
callback(elapsed);
}, delay, time);
};
-
t.restart(callback, delay, time);
return t;
}
// https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
- const a = 1664525;
- const c = 1013904223;
- const m = 4294967296; // 2^32
+ const a$1 = 1664525;
+ const c$1 = 1013904223;
+ const m$1 = 4294967296; // 2^32
- function lcg () {
+ function lcg$1 () {
let s = 1;
- return () => (s = (a * s + c) % m) / m;
+ return () => (s = (a$1 * s + c$1) % m$1) / m$1;
}
function x(d) {
return d.x;
}
function y(d) {
return d.y;
}
var initialRadius = 10,
- initialAngle = Math.PI * (3 - Math.sqrt(5));
+ initialAngle = Math.PI * (3 - Math.sqrt(5));
function forceSimulation (nodes) {
var simulation,
- alpha = 1,
- alphaMin = 0.001,
- alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
- alphaTarget = 0,
- velocityDecay = 0.6,
- forces = new Map(),
- stepper = timer$1(step),
- event = dispatch("tick", "end"),
- random = lcg();
+ alpha = 1,
+ alphaMin = 0.001,
+ alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
+ alphaTarget = 0,
+ velocityDecay = 0.6,
+ forces = new Map(),
+ stepper = timer$1(step),
+ event = dispatch("tick", "end"),
+ random = lcg$1();
if (nodes == null) nodes = [];
-
function step() {
tick();
event.call("tick", simulation);
-
if (alpha < alphaMin) {
stepper.stop();
event.call("end", simulation);
}
}
-
function tick(iterations) {
var i,
- n = nodes.length,
- node;
+ n = nodes.length,
+ node;
if (iterations === undefined) iterations = 1;
-
for (var k = 0; k < iterations; ++k) {
alpha += (alphaTarget - alpha) * alphaDecay;
forces.forEach(function (force) {
force(alpha);
});
-
for (i = 0; i < n; ++i) {
node = nodes[i];
if (node.fx == null) node.x += node.vx *= velocityDecay;else node.x = node.fx, node.vx = 0;
if (node.fy == null) node.y += node.vy *= velocityDecay;else node.y = node.fy, node.vy = 0;
}
}
-
return simulation;
}
-
function initializeNodes() {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.index = i;
if (node.fx != null) node.x = node.fx;
if (node.fy != null) node.y = node.fy;
-
if (isNaN(node.x) || isNaN(node.y)) {
var radius = initialRadius * Math.sqrt(0.5 + i),
- angle = i * initialAngle;
+ angle = i * initialAngle;
node.x = radius * Math.cos(angle);
node.y = radius * Math.sin(angle);
}
-
if (isNaN(node.vx) || isNaN(node.vy)) {
node.vx = node.vy = 0;
}
}
}
-
function initializeForce(force) {
if (force.initialize) force.initialize(nodes, random);
return force;
}
-
initializeNodes();
return simulation = {
tick: tick,
restart: function () {
return stepper.restart(step), simulation;
@@ -29738,232 +26936,205 @@
force: function (name, _) {
return arguments.length > 1 ? (_ == null ? forces.delete(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name);
},
find: function (x, y, radius) {
var i = 0,
- n = nodes.length,
- dx,
- dy,
- d2,
- node,
- closest;
+ n = nodes.length,
+ dx,
+ dy,
+ d2,
+ node,
+ closest;
if (radius == null) radius = Infinity;else radius *= radius;
-
for (i = 0; i < n; ++i) {
node = nodes[i];
dx = x - node.x;
dy = y - node.y;
d2 = dx * dx + dy * dy;
if (d2 < radius) closest = node, radius = d2;
}
-
return closest;
},
on: function (name, _) {
return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
}
};
}
function forceManyBody () {
var nodes,
- node,
- random,
- alpha,
- strength = constant$1(-30),
- strengths,
- distanceMin2 = 1,
- distanceMax2 = Infinity,
- theta2 = 0.81;
-
+ node,
+ random,
+ alpha,
+ strength = constant$1(-30),
+ strengths,
+ distanceMin2 = 1,
+ distanceMax2 = Infinity,
+ theta2 = 0.81;
function force(_) {
var i,
- n = nodes.length,
- tree = quadtree(nodes, x, y).visitAfter(accumulate);
-
+ n = nodes.length,
+ tree = quadtree(nodes, x, y).visitAfter(accumulate);
for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
}
-
function initialize() {
if (!nodes) return;
var i,
- n = nodes.length,
- node;
+ n = nodes.length,
+ node;
strengths = new Array(n);
-
for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
}
-
function accumulate(quad) {
var strength = 0,
- q,
- c,
- weight = 0,
- x,
- y,
- i; // For internal nodes, accumulate forces from child quadrants.
+ q,
+ c,
+ weight = 0,
+ x,
+ y,
+ i;
+ // For internal nodes, accumulate forces from child quadrants.
if (quad.length) {
for (x = y = i = 0; i < 4; ++i) {
if ((q = quad[i]) && (c = Math.abs(q.value))) {
strength += q.value, weight += c, x += c * q.x, y += c * q.y;
}
}
-
quad.x = x / weight;
quad.y = y / weight;
- } // For leaf nodes, accumulate forces from coincident quadrants.
+ }
+
+ // For leaf nodes, accumulate forces from coincident quadrants.
else {
q = quad;
q.x = q.data.x;
q.y = q.data.y;
-
do strength += strengths[q.data.index]; while (q = q.next);
}
-
quad.value = strength;
}
-
function apply(quad, x1, _, x2) {
if (!quad.value) return true;
var x = quad.x - node.x,
- y = quad.y - node.y,
- w = x2 - x1,
- l = x * x + y * y; // Apply the Barnes-Hut approximation if possible.
- // Limit forces for very close nodes; randomize direction if coincident.
+ y = quad.y - node.y,
+ w = x2 - x1,
+ l = x * x + y * y;
+ // Apply the Barnes-Hut approximation if possible.
+ // Limit forces for very close nodes; randomize direction if coincident.
if (w * w / theta2 < l) {
if (l < distanceMax2) {
if (x === 0) x = jiggle(random), l += x * x;
if (y === 0) y = jiggle(random), l += y * y;
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
node.vx += x * quad.value * alpha / l;
node.vy += y * quad.value * alpha / l;
}
-
return true;
- } // Otherwise, process points directly.
- else if (quad.length || l >= distanceMax2) return; // Limit forces for very close nodes; randomize direction if coincident.
+ }
+ // Otherwise, process points directly.
+ else if (quad.length || l >= distanceMax2) return;
+ // Limit forces for very close nodes; randomize direction if coincident.
if (quad.data !== node || quad.next) {
if (x === 0) x = jiggle(random), l += x * x;
if (y === 0) y = jiggle(random), l += y * y;
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
}
-
do if (quad.data !== node) {
w = strengths[quad.data.index] * alpha / l;
node.vx += x * w;
node.vy += y * w;
} while (quad = quad.next);
}
-
force.initialize = function (_nodes, _random) {
nodes = _nodes;
random = _random;
initialize();
};
-
force.strength = function (_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : strength;
};
-
force.distanceMin = function (_) {
return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
};
-
force.distanceMax = function (_) {
return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
};
-
force.theta = function (_) {
return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
};
-
return force;
}
function forceX (x) {
var strength = constant$1(0.1),
- nodes,
- strengths,
- xz;
+ nodes,
+ strengths,
+ xz;
if (typeof x !== "function") x = constant$1(x == null ? 0 : +x);
-
function force(alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
}
}
-
function initialize() {
if (!nodes) return;
var i,
- n = nodes.length;
+ n = nodes.length;
strengths = new Array(n);
xz = new Array(n);
-
for (i = 0; i < n; ++i) {
strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
}
}
-
force.initialize = function (_) {
nodes = _;
initialize();
};
-
force.strength = function (_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : strength;
};
-
force.x = function (_) {
return arguments.length ? (x = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : x;
};
-
return force;
}
function forceY (y) {
var strength = constant$1(0.1),
- nodes,
- strengths,
- yz;
+ nodes,
+ strengths,
+ yz;
if (typeof y !== "function") y = constant$1(y == null ? 0 : +y);
-
function force(alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
}
}
-
function initialize() {
if (!nodes) return;
var i,
- n = nodes.length;
+ n = nodes.length;
strengths = new Array(n);
yz = new Array(n);
-
for (i = 0; i < n; ++i) {
strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
}
}
-
force.initialize = function (_) {
nodes = _;
initialize();
};
-
force.strength = function (_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : strength;
};
-
force.y = function (_) {
return arguments.length ? (y = typeof _ === "function" ? _ : constant$1(+_), initialize(), force) : y;
};
-
return force;
}
const ForceMap = {
center: forceCenter,
@@ -29972,24 +27143,23 @@
link: forceLink,
x: forceX,
y: forceY
};
const Forces = 'forces',
- ForceParams = ['alpha', 'alphaMin', 'alphaTarget', 'velocityDecay', 'forces'],
- ForceConfig = ['static', 'iterations'],
- ForceOutput = ['x', 'y', 'vx', 'vy'];
+ ForceParams = ['alpha', 'alphaMin', 'alphaTarget', 'velocityDecay', 'forces'],
+ ForceConfig = ['static', 'iterations'],
+ ForceOutput = ['x', 'y', 'vx', 'vy'];
+
/**
* Force simulation layout.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<object>} params.forces - The forces to apply.
*/
-
function Force(params) {
Transform.call(this, null, params);
}
-
Force.Definition = {
'type': 'Force',
'metadata': {
'modifies': true
},
@@ -30060,11 +27230,12 @@
'force': 'nbody'
},
'params': [{
'name': 'strength',
'type': 'number',
- 'default': -30
+ 'default': -30,
+ 'expr': true
}, {
'name': 'theta',
'type': 'number',
'default': 0.9
}, {
@@ -30133,146 +27304,118 @@
}]
};
inherits(Force, Transform, {
transform(_, pulse) {
var sim = this.value,
- change = pulse.changed(pulse.ADD_REM),
- params = _.modified(ForceParams),
- iters = _.iterations || 300; // configure simulation
+ change = pulse.changed(pulse.ADD_REM),
+ params = _.modified(ForceParams),
+ iters = _.iterations || 300;
-
+ // configure simulation
if (!sim) {
this.value = sim = simulation(pulse.source, _);
sim.on('tick', rerun(pulse.dataflow, this));
-
if (!_.static) {
change = true;
sim.tick(); // ensure we run on init
}
-
pulse.modifies('index');
} else {
if (change) {
pulse.modifies('index');
sim.nodes(pulse.source);
}
-
if (params || pulse.changed(pulse.MOD)) {
setup(sim, _, 0, pulse);
}
- } // run simulation
+ }
-
+ // run simulation
if (params || change || _.modified(ForceConfig) || pulse.changed() && _.restart) {
sim.alpha(Math.max(sim.alpha(), _.alpha || 1)).alphaDecay(1 - Math.pow(sim.alphaMin(), 1 / iters));
-
if (_.static) {
for (sim.stop(); --iters >= 0;) sim.tick();
} else {
if (sim.stopped()) sim.restart();
if (!change) return pulse.StopPropagation; // defer to sim ticks
}
}
-
return this.finish(_, pulse);
},
-
finish(_, pulse) {
- const dataflow = pulse.dataflow; // inspect dependencies, touch link source data
+ const dataflow = pulse.dataflow;
+ // inspect dependencies, touch link source data
for (let args = this._argops, j = 0, m = args.length, arg; j < m; ++j) {
arg = args[j];
-
if (arg.name !== Forces || arg.op._argval.force !== 'link') {
continue;
}
-
for (var ops = arg.op._argops, i = 0, n = ops.length, op; i < n; ++i) {
if (ops[i].name === 'links' && (op = ops[i].op.source)) {
dataflow.pulse(op, dataflow.changeset().reflow());
break;
}
}
- } // reflow all nodes
+ }
-
+ // reflow all nodes
return pulse.reflow(_.modified()).modifies(ForceOutput);
}
-
});
-
function rerun(df, op) {
return () => df.touch(op).run();
}
-
function simulation(nodes, _) {
const sim = forceSimulation(nodes),
- stop = sim.stop,
- restart = sim.restart;
+ stop = sim.stop,
+ restart = sim.restart;
let stopped = false;
-
sim.stopped = () => stopped;
-
sim.restart = () => (stopped = false, restart());
-
sim.stop = () => (stopped = true, stop());
-
return setup(sim, _, true).on('end', () => stopped = true);
}
-
function setup(sim, _, init, pulse) {
var f = array$5(_.forces),
- i,
- n,
- p,
- name;
-
+ i,
+ n,
+ p,
+ name;
for (i = 0, n = ForceParams.length; i < n; ++i) {
p = ForceParams[i];
if (p !== Forces && _.modified(p)) sim[p](_[p]);
}
-
for (i = 0, n = f.length; i < n; ++i) {
name = Forces + i;
p = init || _.modified(Forces, i) ? getForce(f[i]) : pulse && modified(f[i], pulse) ? sim.force(name) : null;
if (p) sim.force(name, p);
}
-
for (n = sim.numForces || 0; i < n; ++i) {
sim.force(Forces + i, null); // remove
}
-
sim.numForces = f.length;
return sim;
}
-
function modified(f, pulse) {
var k, v;
-
for (k in f) {
if (isFunction(v = f[k]) && pulse.modified(accessorFields(v))) return 1;
}
-
return 0;
}
-
function getForce(_) {
var f, p;
-
if (!has$1(ForceMap, _.force)) {
error('Unrecognized force: ' + _.force);
}
-
f = ForceMap[_.force]();
-
for (p in _) {
if (isFunction(f[p])) setForceParam(f[p], _[p], _);
}
-
return f;
}
-
function setForceParam(f, v, _) {
f(isFunction(v) ? d => v(d, _) : v);
}
var force = /*#__PURE__*/Object.freeze({
@@ -30281,180 +27424,152 @@
});
function defaultSeparation$2(a, b) {
return a.parent === b.parent ? 1 : 2;
}
-
function meanX(children) {
return children.reduce(meanXReduce, 0) / children.length;
}
-
function meanXReduce(x, c) {
return x + c.x;
}
-
function maxY(children) {
return 1 + children.reduce(maxYReduce, 0);
}
-
function maxYReduce(y, c) {
return Math.max(y, c.y);
}
-
function leafLeft(node) {
var children;
-
while (children = node.children) node = children[0];
-
return node;
}
-
function leafRight(node) {
var children;
-
while (children = node.children) node = children[children.length - 1];
-
return node;
}
-
function cluster () {
var separation = defaultSeparation$2,
- dx = 1,
- dy = 1,
- nodeSize = false;
-
+ dx = 1,
+ dy = 1,
+ nodeSize = false;
function cluster(root) {
var previousNode,
- x = 0; // First walk, computing the initial x & y values.
+ x = 0;
+ // First walk, computing the initial x & y values.
root.eachAfter(function (node) {
var children = node.children;
-
if (children) {
node.x = meanX(children);
node.y = maxY(children);
} else {
node.x = previousNode ? x += separation(node, previousNode) : 0;
node.y = 0;
previousNode = node;
}
});
var left = leafLeft(root),
- right = leafRight(root),
- x0 = left.x - separation(left, right) / 2,
- x1 = right.x + separation(right, left) / 2; // Second walk, normalizing x & y to the desired size.
+ right = leafRight(root),
+ x0 = left.x - separation(left, right) / 2,
+ x1 = right.x + separation(right, left) / 2;
+ // Second walk, normalizing x & y to the desired size.
return root.eachAfter(nodeSize ? function (node) {
node.x = (node.x - root.x) * dx;
node.y = (root.y - node.y) * dy;
} : function (node) {
node.x = (node.x - x0) / (x1 - x0) * dx;
node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;
});
}
-
cluster.separation = function (x) {
return arguments.length ? (separation = x, cluster) : separation;
};
-
cluster.size = function (x) {
return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : nodeSize ? null : [dx, dy];
};
-
cluster.nodeSize = function (x) {
return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : nodeSize ? [dx, dy] : null;
};
-
return cluster;
}
function count(node) {
var sum = 0,
- children = node.children,
- i = children && children.length;
+ children = node.children,
+ i = children && children.length;
if (!i) sum = 1;else while (--i >= 0) sum += children[i].value;
node.value = sum;
}
-
function node_count () {
return this.eachAfter(count);
}
function node_each (callback, that) {
let index = -1;
-
for (const node of this) {
callback.call(that, node, ++index, this);
}
-
return this;
}
function node_eachBefore (callback, that) {
var node = this,
- nodes = [node],
- children,
- i,
- index = -1;
-
+ nodes = [node],
+ children,
+ i,
+ index = -1;
while (node = nodes.pop()) {
callback.call(that, node, ++index, this);
-
if (children = node.children) {
for (i = children.length - 1; i >= 0; --i) {
nodes.push(children[i]);
}
}
}
-
return this;
}
function node_eachAfter (callback, that) {
var node = this,
- nodes = [node],
- next = [],
- children,
- i,
- n,
- index = -1;
-
+ nodes = [node],
+ next = [],
+ children,
+ i,
+ n,
+ index = -1;
while (node = nodes.pop()) {
next.push(node);
-
if (children = node.children) {
for (i = 0, n = children.length; i < n; ++i) {
nodes.push(children[i]);
}
}
}
-
while (node = next.pop()) {
callback.call(that, node, ++index, this);
}
-
return this;
}
function node_find (callback, that) {
let index = -1;
-
for (const node of this) {
if (callback.call(that, node, ++index, this)) {
return node;
}
}
}
function node_sum (value) {
return this.eachAfter(function (node) {
var sum = +value(node.data) || 0,
- children = node.children,
- i = children && children.length;
-
+ children = node.children,
+ i = children && children.length;
while (--i >= 0) sum += children[i].value;
-
node.value = sum;
});
}
function node_sort (compare) {
@@ -30465,53 +27580,44 @@
});
}
function node_path (end) {
var start = this,
- ancestor = leastCommonAncestor(start, end),
- nodes = [start];
-
+ ancestor = leastCommonAncestor(start, end),
+ nodes = [start];
while (start !== ancestor) {
start = start.parent;
nodes.push(start);
}
-
var k = nodes.length;
-
while (end !== ancestor) {
nodes.splice(k, 0, end);
end = end.parent;
}
-
return nodes;
}
-
function leastCommonAncestor(a, b) {
if (a === b) return a;
var aNodes = a.ancestors(),
- bNodes = b.ancestors(),
- c = null;
+ bNodes = b.ancestors(),
+ c = null;
a = aNodes.pop();
b = bNodes.pop();
-
while (a === b) {
c = a;
a = aNodes.pop();
b = bNodes.pop();
}
-
return c;
}
function node_ancestors () {
var node = this,
- nodes = [node];
-
+ nodes = [node];
while (node = node.parent) {
nodes.push(node);
}
-
return nodes;
}
function node_descendants () {
return Array.from(this);
@@ -30527,11 +27633,11 @@
return leaves;
}
function node_links () {
var root = this,
- links = [];
+ links = [];
root.each(function (node) {
if (node !== root) {
// Don’t include the root’s parent, if any.
links.push({
source: node.parent,
@@ -30542,22 +27648,19 @@
return links;
}
function* node_iterator () {
var node = this,
- current,
- next = [node],
- children,
- i,
- n;
-
+ current,
+ next = [node],
+ children,
+ i,
+ n;
do {
current = next.reverse(), next = [];
-
while (node = current.pop()) {
yield node;
-
if (children = node.children) {
for (i = 0, n = children.length; i < n; ++i) {
next.push(children[i]);
}
}
@@ -30570,54 +27673,44 @@
data = [undefined, data];
if (children === undefined) children = mapChildren;
} else if (children === undefined) {
children = objectChildren;
}
-
var root = new Node$1(data),
- node,
- nodes = [root],
- child,
- childs,
- i,
- n;
-
+ node,
+ nodes = [root],
+ child,
+ childs,
+ i,
+ n;
while (node = nodes.pop()) {
if ((childs = children(node.data)) && (n = (childs = Array.from(childs)).length)) {
node.children = childs;
-
for (i = n - 1; i >= 0; --i) {
nodes.push(child = childs[i] = new Node$1(childs[i]));
child.parent = node;
child.depth = node.depth + 1;
}
}
}
-
return root.eachBefore(computeHeight);
}
-
function node_copy() {
return hierarchy(this).eachBefore(copyData);
}
-
function objectChildren(d) {
return d.children;
}
-
function mapChildren(d) {
return Array.isArray(d) ? d[1] : null;
}
-
function copyData(node) {
if (node.data.value !== undefined) node.value = node.data.value;
node.data = node.data.data;
}
-
function computeHeight(node) {
var height = 0;
-
do node.height = height; while ((node = node.parent) && node.height < ++height);
}
function Node$1(data) {
this.data = data;
this.depth = this.height = 0;
@@ -30639,178 +27732,189 @@
links: node_links,
copy: node_copy,
[Symbol.iterator]: node_iterator
};
+ function optional(f) {
+ return f == null ? null : required(f);
+ }
+ function required(f) {
+ if (typeof f !== "function") throw new Error();
+ return f;
+ }
+
+ function constantZero() {
+ return 0;
+ }
+ function constant (x) {
+ return function () {
+ return x;
+ };
+ }
+
+ // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
+ const a = 1664525;
+ const c = 1013904223;
+ const m = 4294967296; // 2^32
+
+ function lcg () {
+ let s = 1;
+ return () => (s = (a * s + c) % m) / m;
+ }
+
function array$2 (x) {
return typeof x === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
: Array.from(x); // Map, Set, iterable, string, or anything else
}
- function shuffle(array) {
- var m = array.length,
- t,
- i;
-
+ function shuffle(array, random) {
+ let m = array.length,
+ t,
+ i;
while (m) {
- i = Math.random() * m-- | 0;
+ i = random() * m-- | 0;
t = array[m];
array[m] = array[i];
array[i] = t;
}
-
return array;
}
- function enclose (circles) {
+ function packEncloseRandom(circles, random) {
var i = 0,
- n = (circles = shuffle(Array.from(circles))).length,
- B = [],
- p,
- e;
-
+ n = (circles = shuffle(Array.from(circles), random)).length,
+ B = [],
+ p,
+ e;
while (i < n) {
p = circles[i];
if (e && enclosesWeak(e, p)) ++i;else e = encloseBasis(B = extendBasis(B, p)), i = 0;
}
-
return e;
}
-
function extendBasis(B, p) {
var i, j;
- if (enclosesWeakAll(p, B)) return [p]; // If we get here then B must have at least one element.
+ if (enclosesWeakAll(p, B)) return [p];
+ // If we get here then B must have at least one element.
for (i = 0; i < B.length; ++i) {
if (enclosesNot(p, B[i]) && enclosesWeakAll(encloseBasis2(B[i], p), B)) {
return [B[i], p];
}
- } // If we get here then B must have at least two elements.
+ }
-
+ // If we get here then B must have at least two elements.
for (i = 0; i < B.length - 1; ++i) {
for (j = i + 1; j < B.length; ++j) {
if (enclosesNot(encloseBasis2(B[i], B[j]), p) && enclosesNot(encloseBasis2(B[i], p), B[j]) && enclosesNot(encloseBasis2(B[j], p), B[i]) && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {
return [B[i], B[j], p];
}
}
- } // If we get here then something is very wrong.
+ }
-
+ // If we get here then something is very wrong.
throw new Error();
}
-
function enclosesNot(a, b) {
var dr = a.r - b.r,
- dx = b.x - a.x,
- dy = b.y - a.y;
+ dx = b.x - a.x,
+ dy = b.y - a.y;
return dr < 0 || dr * dr < dx * dx + dy * dy;
}
-
function enclosesWeak(a, b) {
var dr = a.r - b.r + Math.max(a.r, b.r, 1) * 1e-9,
- dx = b.x - a.x,
- dy = b.y - a.y;
+ dx = b.x - a.x,
+ dy = b.y - a.y;
return dr > 0 && dr * dr > dx * dx + dy * dy;
}
-
function enclosesWeakAll(a, B) {
for (var i = 0; i < B.length; ++i) {
if (!enclosesWeak(a, B[i])) {
return false;
}
}
-
return true;
}
-
function encloseBasis(B) {
switch (B.length) {
case 1:
return encloseBasis1(B[0]);
-
case 2:
return encloseBasis2(B[0], B[1]);
-
case 3:
return encloseBasis3(B[0], B[1], B[2]);
}
}
-
function encloseBasis1(a) {
return {
x: a.x,
y: a.y,
r: a.r
};
}
-
function encloseBasis2(a, b) {
var x1 = a.x,
- y1 = a.y,
- r1 = a.r,
- x2 = b.x,
- y2 = b.y,
- r2 = b.r,
- x21 = x2 - x1,
- y21 = y2 - y1,
- r21 = r2 - r1,
- l = Math.sqrt(x21 * x21 + y21 * y21);
+ y1 = a.y,
+ r1 = a.r,
+ x2 = b.x,
+ y2 = b.y,
+ r2 = b.r,
+ x21 = x2 - x1,
+ y21 = y2 - y1,
+ r21 = r2 - r1,
+ l = Math.sqrt(x21 * x21 + y21 * y21);
return {
x: (x1 + x2 + x21 / l * r21) / 2,
y: (y1 + y2 + y21 / l * r21) / 2,
r: (l + r1 + r2) / 2
};
}
-
function encloseBasis3(a, b, c) {
var x1 = a.x,
- y1 = a.y,
- r1 = a.r,
- x2 = b.x,
- y2 = b.y,
- r2 = b.r,
- x3 = c.x,
- y3 = c.y,
- r3 = c.r,
- a2 = x1 - x2,
- a3 = x1 - x3,
- b2 = y1 - y2,
- b3 = y1 - y3,
- c2 = r2 - r1,
- c3 = r3 - r1,
- d1 = x1 * x1 + y1 * y1 - r1 * r1,
- d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,
- d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,
- ab = a3 * b2 - a2 * b3,
- xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,
- xb = (b3 * c2 - b2 * c3) / ab,
- ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,
- yb = (a2 * c3 - a3 * c2) / ab,
- A = xb * xb + yb * yb - 1,
- B = 2 * (r1 + xa * xb + ya * yb),
- C = xa * xa + ya * ya - r1 * r1,
- r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);
+ y1 = a.y,
+ r1 = a.r,
+ x2 = b.x,
+ y2 = b.y,
+ r2 = b.r,
+ x3 = c.x,
+ y3 = c.y,
+ r3 = c.r,
+ a2 = x1 - x2,
+ a3 = x1 - x3,
+ b2 = y1 - y2,
+ b3 = y1 - y3,
+ c2 = r2 - r1,
+ c3 = r3 - r1,
+ d1 = x1 * x1 + y1 * y1 - r1 * r1,
+ d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,
+ d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,
+ ab = a3 * b2 - a2 * b3,
+ xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,
+ xb = (b3 * c2 - b2 * c3) / ab,
+ ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,
+ yb = (a2 * c3 - a3 * c2) / ab,
+ A = xb * xb + yb * yb - 1,
+ B = 2 * (r1 + xa * xb + ya * yb),
+ C = xa * xa + ya * ya - r1 * r1,
+ r = -(Math.abs(A) > 1e-6 ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);
return {
x: x1 + xa + xb * r,
y: y1 + ya + yb * r,
r: r
};
}
function place(b, a, c) {
var dx = b.x - a.x,
- x,
- a2,
- dy = b.y - a.y,
- y,
- b2,
- d2 = dx * dx + dy * dy;
-
+ x,
+ a2,
+ dy = b.y - a.y,
+ y,
+ b2,
+ d2 = dx * dx + dy * dy;
if (d2) {
a2 = a.r + c.r, a2 *= a2;
b2 = b.r + c.r, b2 *= b2;
-
if (a2 > b2) {
x = (d2 + b2 - a2) / (2 * d2);
y = Math.sqrt(Math.max(0, b2 / d2 - x * x));
c.x = b.x - x * dx - y * dy;
c.y = b.y - x * dy + y * dx;
@@ -30823,184 +27927,152 @@
} else {
c.x = a.x + c.r;
c.y = a.y;
}
}
-
function intersects(a, b) {
var dr = a.r + b.r - 1e-6,
- dx = b.x - a.x,
- dy = b.y - a.y;
+ dx = b.x - a.x,
+ dy = b.y - a.y;
return dr > 0 && dr * dr > dx * dx + dy * dy;
}
-
function score(node) {
var a = node._,
- b = node.next._,
- ab = a.r + b.r,
- dx = (a.x * b.r + b.x * a.r) / ab,
- dy = (a.y * b.r + b.y * a.r) / ab;
+ b = node.next._,
+ ab = a.r + b.r,
+ dx = (a.x * b.r + b.x * a.r) / ab,
+ dy = (a.y * b.r + b.y * a.r) / ab;
return dx * dx + dy * dy;
}
-
function Node(circle) {
this._ = circle;
this.next = null;
this.previous = null;
}
-
- function packEnclose(circles) {
+ function packSiblingsRandom(circles, random) {
if (!(n = (circles = array$2(circles)).length)) return 0;
- var a, b, c, n, aa, ca, i, j, k, sj, sk; // Place the first circle.
+ var a, b, c, n, aa, ca, i, j, k, sj, sk;
+ // Place the first circle.
a = circles[0], a.x = 0, a.y = 0;
- if (!(n > 1)) return a.r; // Place the second circle.
+ if (!(n > 1)) return a.r;
+ // Place the second circle.
b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
- if (!(n > 2)) return a.r + b.r; // Place the third circle.
+ if (!(n > 2)) return a.r + b.r;
- place(b, a, c = circles[2]); // Initialize the front-chain using the first three circles a, b and c.
+ // Place the third circle.
+ place(b, a, c = circles[2]);
+ // Initialize the front-chain using the first three circles a, b and c.
a = new Node(a), b = new Node(b), c = new Node(c);
a.next = c.previous = b;
b.next = a.previous = c;
- c.next = b.previous = a; // Attempt to place each remaining circle…
+ c.next = b.previous = a;
+ // Attempt to place each remaining circle…
pack: for (i = 3; i < n; ++i) {
- place(a._, b._, c = circles[i]), c = new Node(c); // Find the closest intersecting circle on the front-chain, if any.
+ place(a._, b._, c = circles[i]), c = new Node(c);
+
+ // Find the closest intersecting circle on the front-chain, if any.
// “Closeness” is determined by linear distance along the front-chain.
// “Ahead” or “behind” is likewise determined by linear distance.
-
j = b.next, k = a.previous, sj = b._.r, sk = a._.r;
-
do {
if (sj <= sk) {
if (intersects(j._, c._)) {
b = j, a.next = b, b.previous = a, --i;
continue pack;
}
-
sj += j._.r, j = j.next;
} else {
if (intersects(k._, c._)) {
a = k, a.next = b, b.previous = a, --i;
continue pack;
}
-
sk += k._.r, k = k.previous;
}
- } while (j !== k.next); // Success! Insert the new circle c between a and b.
+ } while (j !== k.next);
+ // Success! Insert the new circle c between a and b.
+ c.previous = a, c.next = b, a.next = b.previous = b = c;
- c.previous = a, c.next = b, a.next = b.previous = b = c; // Compute the new closest circle pair to the centroid.
-
+ // Compute the new closest circle pair to the centroid.
aa = score(a);
-
while ((c = c.next) !== b) {
if ((ca = score(c)) < aa) {
a = c, aa = ca;
}
}
-
b = a.next;
- } // Compute the enclosing circle of the front chain.
+ }
-
+ // Compute the enclosing circle of the front chain.
a = [b._], c = b;
-
while ((c = c.next) !== b) a.push(c._);
+ c = packEncloseRandom(a, random);
- c = enclose(a); // Translate the circles to put the enclosing circle around the origin.
-
+ // Translate the circles to put the enclosing circle around the origin.
for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
-
return c.r;
}
- function optional(f) {
- return f == null ? null : required(f);
- }
- function required(f) {
- if (typeof f !== "function") throw new Error();
- return f;
- }
-
- function constantZero() {
- return 0;
- }
- function constant (x) {
- return function () {
- return x;
- };
- }
-
function defaultRadius(d) {
return Math.sqrt(d.value);
}
-
function pack () {
var radius = null,
- dx = 1,
- dy = 1,
- padding = constantZero;
-
+ dx = 1,
+ dy = 1,
+ padding = constantZero;
function pack(root) {
+ const random = lcg();
root.x = dx / 2, root.y = dy / 2;
-
if (radius) {
- root.eachBefore(radiusLeaf(radius)).eachAfter(packChildren(padding, 0.5)).eachBefore(translateChild(1));
+ root.eachBefore(radiusLeaf(radius)).eachAfter(packChildrenRandom(padding, 0.5, random)).eachBefore(translateChild(1));
} else {
- root.eachBefore(radiusLeaf(defaultRadius)).eachAfter(packChildren(constantZero, 1)).eachAfter(packChildren(padding, root.r / Math.min(dx, dy))).eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
+ root.eachBefore(radiusLeaf(defaultRadius)).eachAfter(packChildrenRandom(constantZero, 1, random)).eachAfter(packChildrenRandom(padding, root.r / Math.min(dx, dy), random)).eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
}
-
return root;
}
-
pack.radius = function (x) {
return arguments.length ? (radius = optional(x), pack) : radius;
};
-
pack.size = function (x) {
return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];
};
-
pack.padding = function (x) {
return arguments.length ? (padding = typeof x === "function" ? x : constant(+x), pack) : padding;
};
-
return pack;
}
-
function radiusLeaf(radius) {
return function (node) {
if (!node.children) {
node.r = Math.max(0, +radius(node) || 0);
}
};
}
-
- function packChildren(padding, k) {
+ function packChildrenRandom(padding, k, random) {
return function (node) {
if (children = node.children) {
var children,
- i,
- n = children.length,
- r = padding(node) * k || 0,
- e;
+ i,
+ n = children.length,
+ r = padding(node) * k || 0,
+ e;
if (r) for (i = 0; i < n; ++i) children[i].r += r;
- e = packEnclose(children);
+ e = packSiblingsRandom(children, random);
if (r) for (i = 0; i < n; ++i) children[i].r -= r;
node.r = e + r;
}
};
}
-
function translateChild(k) {
return function (node) {
var parent = node.parent;
node.r *= k;
-
if (parent) {
node.x = parent.x + k * node.x;
node.y = parent.y + k * node.y;
}
};
@@ -31013,139 +28085,118 @@
node.y1 = Math.round(node.y1);
}
function treemapDice (parent, x0, y0, x1, y1) {
var nodes = parent.children,
- node,
- i = -1,
- n = nodes.length,
- k = parent.value && (x1 - x0) / parent.value;
-
+ node,
+ i = -1,
+ n = nodes.length,
+ k = parent.value && (x1 - x0) / parent.value;
while (++i < n) {
node = nodes[i], node.y0 = y0, node.y1 = y1;
node.x0 = x0, node.x1 = x0 += node.value * k;
}
}
function partition$1 () {
var dx = 1,
- dy = 1,
- padding = 0,
- round = false;
-
+ dy = 1,
+ padding = 0,
+ round = false;
function partition(root) {
var n = root.height + 1;
root.x0 = root.y0 = padding;
root.x1 = dx;
root.y1 = dy / n;
root.eachBefore(positionNode(dy, n));
if (round) root.eachBefore(roundNode);
return root;
}
-
function positionNode(dy, n) {
return function (node) {
if (node.children) {
treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);
}
-
var x0 = node.x0,
- y0 = node.y0,
- x1 = node.x1 - padding,
- y1 = node.y1 - padding;
+ y0 = node.y0,
+ x1 = node.x1 - padding,
+ y1 = node.y1 - padding;
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
node.x0 = x0;
node.y0 = y0;
node.x1 = x1;
node.y1 = y1;
};
}
-
partition.round = function (x) {
return arguments.length ? (round = !!x, partition) : round;
};
-
partition.size = function (x) {
return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];
};
-
partition.padding = function (x) {
return arguments.length ? (padding = +x, partition) : padding;
};
-
return partition;
}
var preroot = {
- depth: -1
- },
- ambiguous = {},
- imputed = {};
-
+ depth: -1
+ },
+ ambiguous = {},
+ imputed = {};
function defaultId(d) {
return d.id;
}
-
function defaultParentId(d) {
return d.parentId;
}
-
function stratify () {
var id = defaultId,
- parentId = defaultParentId,
- path;
-
+ parentId = defaultParentId,
+ path;
function stratify(data) {
var nodes = Array.from(data),
- currentId = id,
- currentParentId = parentId,
- n,
- d,
- i,
- root,
- parent,
- node,
- nodeId,
- nodeKey,
- nodeByKey = new Map();
-
+ currentId = id,
+ currentParentId = parentId,
+ n,
+ d,
+ i,
+ root,
+ parent,
+ node,
+ nodeId,
+ nodeKey,
+ nodeByKey = new Map();
if (path != null) {
const I = nodes.map((d, i) => normalize(path(d, i, data)));
const P = I.map(parentof);
const S = new Set(I).add("");
-
for (const i of P) {
if (!S.has(i)) {
S.add(i);
I.push(i);
P.push(parentof(i));
nodes.push(imputed);
}
}
-
currentId = (_, i) => I[i];
-
currentParentId = (_, i) => P[i];
}
-
for (i = 0, n = nodes.length; i < n; ++i) {
d = nodes[i], node = nodes[i] = new Node$1(d);
-
if ((nodeId = currentId(d, i, data)) != null && (nodeId += "")) {
nodeKey = node.id = nodeId;
nodeByKey.set(nodeKey, nodeByKey.has(nodeKey) ? ambiguous : node);
}
-
if ((nodeId = currentParentId(d, i, data)) != null && (nodeId += "")) {
node.parent = nodeId;
}
}
-
for (i = 0; i < n; ++i) {
node = nodes[i];
-
if (nodeId = node.parent) {
parent = nodeByKey.get(nodeId);
if (!parent) throw new Error("missing: " + nodeId);
if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
if (parent.children) parent.children.push(node);else parent.children = [node];
@@ -31153,452 +28204,409 @@
} else {
if (root) throw new Error("multiple roots");
root = node;
}
}
+ if (!root) throw new Error("no root");
- if (!root) throw new Error("no root"); // When imputing internal nodes, only introduce roots if needed.
+ // When imputing internal nodes, only introduce roots if needed.
// Then replace the imputed marker data with null.
-
if (path != null) {
while (root.data === imputed && root.children.length === 1) {
root = root.children[0], --n;
}
-
for (let i = nodes.length - 1; i >= 0; --i) {
node = nodes[i];
if (node.data !== imputed) break;
node.data = null;
}
}
-
root.parent = preroot;
root.eachBefore(function (node) {
node.depth = node.parent.depth + 1;
--n;
}).eachBefore(computeHeight);
root.parent = null;
if (n > 0) throw new Error("cycle");
return root;
}
-
stratify.id = function (x) {
return arguments.length ? (id = optional(x), stratify) : id;
};
-
stratify.parentId = function (x) {
return arguments.length ? (parentId = optional(x), stratify) : parentId;
};
-
stratify.path = function (x) {
return arguments.length ? (path = optional(x), stratify) : path;
};
-
return stratify;
- } // To normalize a path, we coerce to a string, strip the trailing slash if any
+ }
+
+ // To normalize a path, we coerce to a string, strip the trailing slash if any
// (as long as the trailing slash is not immediately preceded by another slash),
// and add leading slash if missing.
-
function normalize(path) {
- path = "".concat(path);
+ path = `${path}`;
let i = path.length;
if (slash(path, i - 1) && !slash(path, i - 2)) path = path.slice(0, -1);
- return path[0] === "/" ? path : "/".concat(path);
- } // Walk backwards to find the first slash that is not the leading slash, e.g.:
+ return path[0] === "/" ? path : `/${path}`;
+ }
+
+ // Walk backwards to find the first slash that is not the leading slash, e.g.:
// "/foo/bar" ⇥ "/foo", "/foo" ⇥ "/", "/" ↦ "". (The root is special-cased
// because the id of the root must be a truthy value.)
-
-
function parentof(path) {
let i = path.length;
if (i < 2) return "";
-
while (--i > 1) if (slash(path, i)) break;
-
return path.slice(0, i);
- } // Slashes can be escaped; to determine whether a slash is a path delimiter, we
+ }
+
+ // Slashes can be escaped; to determine whether a slash is a path delimiter, we
// count the number of preceding backslashes escaping the forward slash: an odd
// number indicates an escaped forward slash.
-
-
function slash(path, i) {
if (path[i] === "/") {
let k = 0;
-
while (i > 0 && path[--i] === "\\") ++k;
-
if ((k & 1) === 0) return true;
}
-
return false;
}
function defaultSeparation$1(a, b) {
return a.parent === b.parent ? 1 : 2;
- } // function radialSeparation(a, b) {
+ }
+
+ // function radialSeparation(a, b) {
// return (a.parent === b.parent ? 1 : 2) / a.depth;
// }
+
// This function is used to traverse the left contour of a subtree (or
// subforest). It returns the successor of v on this contour. This successor is
// either given by the leftmost child of v or by the thread of v. The function
// returns null if and only if v is on the highest level of its subtree.
-
-
function nextLeft(v) {
var children = v.children;
return children ? children[0] : v.t;
- } // This function works analogously to nextLeft.
+ }
-
+ // This function works analogously to nextLeft.
function nextRight(v) {
var children = v.children;
return children ? children[children.length - 1] : v.t;
- } // Shifts the current subtree rooted at w+. This is done by increasing
- // prelim(w+) and mod(w+) by shift.
+ }
-
+ // Shifts the current subtree rooted at w+. This is done by increasing
+ // prelim(w+) and mod(w+) by shift.
function moveSubtree(wm, wp, shift) {
var change = shift / (wp.i - wm.i);
wp.c -= change;
wp.s += shift;
wm.c += change;
wp.z += shift;
wp.m += shift;
- } // All other shifts, applied to the smaller subtrees between w- and w+, are
+ }
+
+ // All other shifts, applied to the smaller subtrees between w- and w+, are
// performed by this function. To prepare the shifts, we have to adjust
// change(w+), shift(w+), and change(w-).
-
-
function executeShifts(v) {
var shift = 0,
- change = 0,
- children = v.children,
- i = children.length,
- w;
-
+ change = 0,
+ children = v.children,
+ i = children.length,
+ w;
while (--i >= 0) {
w = children[i];
w.z += shift;
w.m += shift;
shift += w.s + (change += w.c);
}
- } // If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
- // returns the specified (default) ancestor.
+ }
-
+ // If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
+ // returns the specified (default) ancestor.
function nextAncestor(vim, v, ancestor) {
return vim.a.parent === v.parent ? vim.a : ancestor;
}
-
function TreeNode(node, i) {
this._ = node;
this.parent = null;
this.children = null;
this.A = null; // default ancestor
-
this.a = this; // ancestor
-
this.z = 0; // prelim
-
this.m = 0; // mod
-
this.c = 0; // change
-
this.s = 0; // shift
-
this.t = null; // thread
-
this.i = i; // number
}
-
TreeNode.prototype = Object.create(Node$1.prototype);
-
function treeRoot(root) {
var tree = new TreeNode(root, 0),
- node,
- nodes = [tree],
- child,
- children,
- i,
- n;
-
+ node,
+ nodes = [tree],
+ child,
+ children,
+ i,
+ n;
while (node = nodes.pop()) {
if (children = node._.children) {
node.children = new Array(n = children.length);
-
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new TreeNode(children[i], i));
child.parent = node;
}
}
}
-
(tree.parent = new TreeNode(null, 0)).children = [tree];
return tree;
- } // Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
+ }
-
+ // Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
function tree$1 () {
var separation = defaultSeparation$1,
- dx = 1,
- dy = 1,
- nodeSize = null;
-
+ dx = 1,
+ dy = 1,
+ nodeSize = null;
function tree(root) {
- var t = treeRoot(root); // Compute the layout using Buchheim et al.’s algorithm.
+ var t = treeRoot(root);
+ // Compute the layout using Buchheim et al.’s algorithm.
t.eachAfter(firstWalk), t.parent.m = -t.z;
- t.eachBefore(secondWalk); // If a fixed node size is specified, scale x and y.
+ t.eachBefore(secondWalk);
- if (nodeSize) root.eachBefore(sizeNode); // If a fixed tree size is specified, scale x and y based on the extent.
+ // If a fixed node size is specified, scale x and y.
+ if (nodeSize) root.eachBefore(sizeNode);
+
+ // If a fixed tree size is specified, scale x and y based on the extent.
// Compute the left-most, right-most, and depth-most nodes for extents.
else {
var left = root,
- right = root,
- bottom = root;
+ right = root,
+ bottom = root;
root.eachBefore(function (node) {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
if (node.depth > bottom.depth) bottom = node;
});
var s = left === right ? 1 : separation(left, right) / 2,
- tx = s - left.x,
- kx = dx / (right.x + s + tx),
- ky = dy / (bottom.depth || 1);
+ tx = s - left.x,
+ kx = dx / (right.x + s + tx),
+ ky = dy / (bottom.depth || 1);
root.eachBefore(function (node) {
node.x = (node.x + tx) * kx;
node.y = node.depth * ky;
});
}
return root;
- } // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
+ }
+
+ // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
// applied recursively to the children of v, as well as the function
// APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
// node v is placed to the midpoint of its outermost children.
-
-
function firstWalk(v) {
var children = v.children,
- siblings = v.parent.children,
- w = v.i ? siblings[v.i - 1] : null;
-
+ siblings = v.parent.children,
+ w = v.i ? siblings[v.i - 1] : null;
if (children) {
executeShifts(v);
var midpoint = (children[0].z + children[children.length - 1].z) / 2;
-
if (w) {
v.z = w.z + separation(v._, w._);
v.m = v.z - midpoint;
} else {
v.z = midpoint;
}
} else if (w) {
v.z = w.z + separation(v._, w._);
}
-
v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
- } // Computes all real x-coordinates by summing up the modifiers recursively.
+ }
-
+ // Computes all real x-coordinates by summing up the modifiers recursively.
function secondWalk(v) {
v._.x = v.z + v.parent.m;
v.m += v.parent.m;
- } // The core of the algorithm. Here, a new subtree is combined with the
+ }
+
+ // The core of the algorithm. Here, a new subtree is combined with the
// previous subtrees. Threads are used to traverse the inside and outside
// contours of the left and right subtree up to the highest common level. The
// vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
// superscript o means outside and i means inside, the subscript - means left
// subtree and + means right subtree. For summing up the modifiers along the
// contour, we use respective variables si+, si-, so-, and so+. Whenever two
// nodes of the inside contours conflict, we compute the left one of the
// greatest uncommon ancestors using the function ANCESTOR and call MOVE
// SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
// Finally, we add a new thread (if necessary).
-
-
function apportion(v, w, ancestor) {
if (w) {
var vip = v,
- vop = v,
- vim = w,
- vom = vip.parent.children[0],
- sip = vip.m,
- sop = vop.m,
- sim = vim.m,
- som = vom.m,
- shift;
-
+ vop = v,
+ vim = w,
+ vom = vip.parent.children[0],
+ sip = vip.m,
+ sop = vop.m,
+ sim = vim.m,
+ som = vom.m,
+ shift;
while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
vom = nextLeft(vom);
vop = nextRight(vop);
vop.a = v;
shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
-
if (shift > 0) {
moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
sip += shift;
sop += shift;
}
-
sim += vim.m;
sip += vip.m;
som += vom.m;
sop += vop.m;
}
-
if (vim && !nextRight(vop)) {
vop.t = vim;
vop.m += sim - sop;
}
-
if (vip && !nextLeft(vom)) {
vom.t = vip;
vom.m += sip - som;
ancestor = v;
}
}
-
return ancestor;
}
-
function sizeNode(node) {
node.x *= dx;
node.y = node.depth * dy;
}
-
tree.separation = function (x) {
return arguments.length ? (separation = x, tree) : separation;
};
-
tree.size = function (x) {
return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : nodeSize ? null : [dx, dy];
};
-
tree.nodeSize = function (x) {
return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : nodeSize ? [dx, dy] : null;
};
-
return tree;
}
function treemapSlice (parent, x0, y0, x1, y1) {
var nodes = parent.children,
- node,
- i = -1,
- n = nodes.length,
- k = parent.value && (y1 - y0) / parent.value;
-
+ node,
+ i = -1,
+ n = nodes.length,
+ k = parent.value && (y1 - y0) / parent.value;
while (++i < n) {
node = nodes[i], node.x0 = x0, node.x1 = x1;
node.y0 = y0, node.y1 = y0 += node.value * k;
}
}
var phi = (1 + Math.sqrt(5)) / 2;
function squarifyRatio(ratio, parent, x0, y0, x1, y1) {
var rows = [],
- nodes = parent.children,
- row,
- nodeValue,
- i0 = 0,
- i1 = 0,
- n = nodes.length,
- dx,
- dy,
- value = parent.value,
- sumValue,
- minValue,
- maxValue,
- newRatio,
- minRatio,
- alpha,
- beta;
-
+ nodes = parent.children,
+ row,
+ nodeValue,
+ i0 = 0,
+ i1 = 0,
+ n = nodes.length,
+ dx,
+ dy,
+ value = parent.value,
+ sumValue,
+ minValue,
+ maxValue,
+ newRatio,
+ minRatio,
+ alpha,
+ beta;
while (i0 < n) {
- dx = x1 - x0, dy = y1 - y0; // Find the next non-empty node.
+ dx = x1 - x0, dy = y1 - y0;
+ // Find the next non-empty node.
do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);
-
minValue = maxValue = sumValue;
alpha = Math.max(dy / dx, dx / dy) / (value * ratio);
beta = sumValue * sumValue * alpha;
- minRatio = Math.max(maxValue / beta, beta / minValue); // Keep adding nodes while the aspect ratio maintains or improves.
+ minRatio = Math.max(maxValue / beta, beta / minValue);
+ // Keep adding nodes while the aspect ratio maintains or improves.
for (; i1 < n; ++i1) {
sumValue += nodeValue = nodes[i1].value;
if (nodeValue < minValue) minValue = nodeValue;
if (nodeValue > maxValue) maxValue = nodeValue;
beta = sumValue * sumValue * alpha;
newRatio = Math.max(maxValue / beta, beta / minValue);
-
if (newRatio > minRatio) {
sumValue -= nodeValue;
break;
}
-
minRatio = newRatio;
- } // Position and record the row orientation.
+ }
-
+ // Position and record the row orientation.
rows.push(row = {
value: sumValue,
dice: dx < dy,
children: nodes.slice(i0, i1)
});
if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);
value -= sumValue, i0 = i1;
}
-
return rows;
}
var treemapSquarify = (function custom(ratio) {
function squarify(parent, x0, y0, x1, y1) {
squarifyRatio(ratio, parent, x0, y0, x1, y1);
}
-
squarify.ratio = function (x) {
return custom((x = +x) > 1 ? x : 1);
};
-
return squarify;
})(phi);
function treemap () {
var tile = treemapSquarify,
- round = false,
- dx = 1,
- dy = 1,
- paddingStack = [0],
- paddingInner = constantZero,
- paddingTop = constantZero,
- paddingRight = constantZero,
- paddingBottom = constantZero,
- paddingLeft = constantZero;
-
+ round = false,
+ dx = 1,
+ dy = 1,
+ paddingStack = [0],
+ paddingInner = constantZero,
+ paddingTop = constantZero,
+ paddingRight = constantZero,
+ paddingBottom = constantZero,
+ paddingLeft = constantZero;
function treemap(root) {
root.x0 = root.y0 = 0;
root.x1 = dx;
root.y1 = dy;
root.eachBefore(positionNode);
paddingStack = [0];
if (round) root.eachBefore(roundNode);
return root;
}
-
function positionNode(node) {
var p = paddingStack[node.depth],
- x0 = node.x0 + p,
- y0 = node.y0 + p,
- x1 = node.x1 - p,
- y1 = node.y1 - p;
+ x0 = node.x0 + p,
+ y0 = node.y0 + p,
+ x1 = node.x1 - p,
+ y1 = node.y1 - p;
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
node.x0 = x0;
node.y0 = y0;
node.x1 = x1;
node.y1 = y1;
-
if (node.children) {
p = paddingStack[node.depth + 1] = paddingInner(node) / 2;
x0 += paddingLeft(node) - p;
y0 += paddingTop(node) - p;
x1 -= paddingRight(node) - p;
@@ -31606,89 +28614,71 @@
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
tile(node, x0, y0, x1, y1);
}
}
-
treemap.round = function (x) {
return arguments.length ? (round = !!x, treemap) : round;
};
-
treemap.size = function (x) {
return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];
};
-
treemap.tile = function (x) {
return arguments.length ? (tile = required(x), treemap) : tile;
};
-
treemap.padding = function (x) {
return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();
};
-
treemap.paddingInner = function (x) {
return arguments.length ? (paddingInner = typeof x === "function" ? x : constant(+x), treemap) : paddingInner;
};
-
treemap.paddingOuter = function (x) {
return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();
};
-
treemap.paddingTop = function (x) {
return arguments.length ? (paddingTop = typeof x === "function" ? x : constant(+x), treemap) : paddingTop;
};
-
treemap.paddingRight = function (x) {
return arguments.length ? (paddingRight = typeof x === "function" ? x : constant(+x), treemap) : paddingRight;
};
-
treemap.paddingBottom = function (x) {
return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant(+x), treemap) : paddingBottom;
};
-
treemap.paddingLeft = function (x) {
return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant(+x), treemap) : paddingLeft;
};
-
return treemap;
}
function treemapBinary (parent, x0, y0, x1, y1) {
var nodes = parent.children,
- i,
- n = nodes.length,
- sum,
- sums = new Array(n + 1);
-
+ i,
+ n = nodes.length,
+ sum,
+ sums = new Array(n + 1);
for (sums[0] = sum = i = 0; i < n; ++i) {
sums[i + 1] = sum += nodes[i].value;
}
-
partition(0, n, parent.value, x0, y0, x1, y1);
-
function partition(i, j, value, x0, y0, x1, y1) {
if (i >= j - 1) {
var node = nodes[i];
node.x0 = x0, node.y0 = y0;
node.x1 = x1, node.y1 = y1;
return;
}
-
var valueOffset = sums[i],
- valueTarget = value / 2 + valueOffset,
- k = i + 1,
- hi = j - 1;
-
+ valueTarget = value / 2 + valueOffset,
+ k = i + 1,
+ hi = j - 1;
while (k < hi) {
var mid = k + hi >>> 1;
if (sums[mid] < valueTarget) k = mid + 1;else hi = mid;
}
-
if (valueTarget - sums[k - 1] < sums[k] - valueTarget && i + 1 < k) --k;
var valueLeft = sums[k] - valueOffset,
- valueRight = value - valueLeft;
-
+ valueRight = value - valueLeft;
if (x1 - x0 > y1 - y0) {
var xk = value ? (x0 * valueRight + x1 * valueLeft) / value : x1;
partition(i, k, valueLeft, x0, y0, xk, y1);
partition(k, j, valueRight, xk, y0, x1, y1);
} else {
@@ -31705,64 +28695,58 @@
var treemapResquarify = (function custom(ratio) {
function resquarify(parent, x0, y0, x1, y1) {
if ((rows = parent._squarify) && rows.ratio === ratio) {
var rows,
- row,
- nodes,
- i,
- j = -1,
- n,
- m = rows.length,
- value = parent.value;
-
+ row,
+ nodes,
+ i,
+ j = -1,
+ n,
+ m = rows.length,
+ value = parent.value;
while (++j < m) {
row = rows[j], nodes = row.children;
-
for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;
-
if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += (y1 - y0) * row.value / value : y1);else treemapSlice(row, x0, y0, value ? x0 += (x1 - x0) * row.value / value : x1, y1);
value -= row.value;
}
} else {
parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1);
rows.ratio = ratio;
}
}
-
resquarify.ratio = function (x) {
return custom((x = +x) > 1 ? x : 1);
};
-
return resquarify;
})(phi);
+ // Build lookup table mapping tuple keys to tree node instances
function lookup$2(tree, key, filter) {
const map = {};
tree.each(node => {
const t = node.data;
if (filter(t)) map[key(t)] = node;
});
tree.lookup = map;
return tree;
}
+
/**
* Nest tuples into a tree structure, grouped by key values.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<function(object): *>} params.keys - The key fields to nest by, in order.
* @param {boolean} [params.generate=false] - A boolean flag indicating if
* non-leaf nodes generated by this transform should be included in the
* output. The default (false) includes only the input data (leaf nodes)
* in the data stream.
*/
-
-
function Nest(params) {
Transform.call(this, null, params);
}
-
Nest.Definition = {
'type': 'Nest',
'metadata': {
'treesource': true,
'changes': true
@@ -31774,189 +28758,159 @@
}, {
'name': 'generate',
'type': 'boolean'
}]
};
-
const children$1 = n => n.values;
-
inherits(Nest, Transform, {
transform(_, pulse) {
if (!pulse.source) {
error('Nest transform requires an upstream data source.');
}
-
var gen = _.generate,
- mod = _.modified(),
- out = pulse.clone(),
- tree = this.value;
-
+ mod = _.modified(),
+ out = pulse.clone(),
+ tree = this.value;
if (!tree || mod || pulse.changed()) {
// collect nodes to remove
if (tree) {
tree.each(node => {
if (node.children && isTuple(node.data)) {
out.rem.push(node.data);
}
});
- } // generate new tree structure
+ }
-
+ // generate new tree structure
this.value = tree = hierarchy({
values: array$5(_.keys).reduce((n, k) => {
n.key(k);
return n;
}, nest()).entries(out.source)
- }, children$1); // collect nodes to add
+ }, children$1);
+ // collect nodes to add
if (gen) {
tree.each(node => {
if (node.children) {
node = ingest$1(node.data);
out.add.push(node);
out.source.push(node);
}
});
- } // build lookup table
+ }
-
+ // build lookup table
lookup$2(tree, tupleid, tupleid);
}
-
out.source.root = tree;
return out;
}
-
});
-
function nest() {
const keys = [],
- nest = {
- entries: array => entries(apply(array, 0), 0),
- key: d => (keys.push(d), nest)
- };
-
+ nest = {
+ entries: array => entries(apply(array, 0), 0),
+ key: d => (keys.push(d), nest)
+ };
function apply(array, depth) {
if (depth >= keys.length) {
return array;
}
-
const n = array.length,
- key = keys[depth++],
- valuesByKey = {},
- result = {};
+ key = keys[depth++],
+ valuesByKey = {},
+ result = {};
let i = -1,
- keyValue,
- value,
- values;
-
+ keyValue,
+ value,
+ values;
while (++i < n) {
keyValue = key(value = array[i]) + '';
-
if (values = valuesByKey[keyValue]) {
values.push(value);
} else {
valuesByKey[keyValue] = [value];
}
}
-
for (keyValue in valuesByKey) {
result[keyValue] = apply(valuesByKey[keyValue], depth);
}
-
return result;
}
-
function entries(map, depth) {
if (++depth > keys.length) return map;
const array = [];
-
for (const key in map) {
array.push({
key,
values: entries(map[key], depth)
});
}
-
return array;
}
-
return nest;
}
+
/**
* Abstract class for tree layout.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
-
function HierarchyLayout(params) {
Transform.call(this, null, params);
}
-
const defaultSeparation = (a, b) => a.parent === b.parent ? 1 : 2;
-
inherits(HierarchyLayout, Transform, {
transform(_, pulse) {
if (!pulse.source || !pulse.source.root) {
error(this.constructor.name + ' transform requires a backing tree data source.');
}
-
const layout = this.layout(_.method),
- fields = this.fields,
- root = pulse.source.root,
- as = _.as || fields;
+ fields = this.fields,
+ root = pulse.source.root,
+ as = _.as || fields;
if (_.field) root.sum(_.field);else root.count();
if (_.sort) root.sort(stableCompare(_.sort, d => d.data));
setParams(layout, this.params, _);
-
if (layout.separation) {
layout.separation(_.separation !== false ? defaultSeparation : one$2);
}
-
try {
this.value = layout(root);
} catch (err) {
error(err);
}
-
root.each(node => setFields(node, fields, as));
return pulse.reflow(_.modified()).modifies(as).modifies('leaf');
}
-
});
-
function setParams(layout, params, _) {
for (let p, i = 0, n = params.length; i < n; ++i) {
p = params[i];
if (p in _) layout[p](_[p]);
}
}
-
function setFields(node, fields, as) {
const t = node.data,
- n = fields.length - 1;
-
+ n = fields.length - 1;
for (let i = 0; i < n; ++i) {
t[as[i]] = node[fields[i]];
}
-
t[as[n]] = node.children ? node.children.length : 0;
}
-
const Output$3 = ['x', 'y', 'r', 'depth', 'children'];
+
/**
* Packed circle tree layout.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to size nodes.
*/
-
function Pack(params) {
HierarchyLayout.call(this, params);
}
-
Pack.Definition = {
'type': 'Pack',
'metadata': {
'tree': true,
'modifies': true
@@ -31992,21 +28946,20 @@
layout: pack,
params: ['radius', 'size', 'padding'],
fields: Output$3
});
const Output$2 = ['x0', 'y0', 'x1', 'y1', 'depth', 'children'];
+
/**
* Partition tree layout.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to size nodes.
*/
-
function Partition(params) {
HierarchyLayout.call(this, params);
}
-
Partition.Definition = {
'type': 'Partition',
'metadata': {
'tree': true,
'modifies': true
@@ -32041,23 +28994,22 @@
inherits(Partition, HierarchyLayout, {
layout: partition$1,
params: ['size', 'round', 'padding'],
fields: Output$2
});
+
/**
* Stratify a collection of tuples into a tree structure based on
* id and parent id fields.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.key - Unique key field for each tuple.
* @param {function(object): *} params.parentKey - Field with key for parent tuple.
*/
-
function Stratify(params) {
Transform.call(this, null, params);
}
-
Stratify.Definition = {
'type': 'Stratify',
'metadata': {
'treesource': true
},
@@ -32074,45 +29026,39 @@
inherits(Stratify, Transform, {
transform(_, pulse) {
if (!pulse.source) {
error('Stratify transform requires an upstream data source.');
}
-
let tree = this.value;
-
const mod = _.modified(),
- out = pulse.fork(pulse.ALL).materialize(pulse.SOURCE),
- run = !tree || mod || pulse.changed(pulse.ADD_REM) || pulse.modified(_.key.fields) || pulse.modified(_.parentKey.fields); // prevent upstream source pollution
+ out = pulse.fork(pulse.ALL).materialize(pulse.SOURCE),
+ run = !tree || mod || pulse.changed(pulse.ADD_REM) || pulse.modified(_.key.fields) || pulse.modified(_.parentKey.fields);
-
+ // prevent upstream source pollution
out.source = out.source.slice();
-
if (run) {
tree = out.source.length ? lookup$2(stratify().id(_.key).parentId(_.parentKey)(out.source), _.key, truthy) : lookup$2(stratify()([{}]), _.key, _.key);
}
-
out.source.root = this.value = tree;
return out;
}
-
});
const Layouts = {
tidy: tree$1,
cluster: cluster
};
const Output$1$1 = ['x', 'y', 'depth', 'children'];
+
/**
* Tree layout. Depending on the method parameter, performs either
* Reingold-Tilford 'tidy' layout or dendrogram 'cluster' layout.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
function Tree(params) {
HierarchyLayout.call(this, params);
}
-
Tree.Definition = {
'type': 'Tree',
'metadata': {
'tree': true,
'modifies': true
@@ -32156,26 +29102,24 @@
*/
layout(method) {
const m = method || 'tidy';
if (has$1(Layouts, m)) return Layouts[m]();else error('Unrecognized Tree layout method: ' + m);
},
-
params: ['size', 'nodeSize'],
fields: Output$1$1
});
+
/**
* Generate tuples representing links between tree nodes.
* The resulting tuples will contain 'source' and 'target' fields,
* which point to parent and child node tuples, respectively.
* @constructor
* @param {object} params - The parameters for this operator.
*/
-
function TreeLinks(params) {
Transform.call(this, [], params);
}
-
TreeLinks.Definition = {
'type': 'TreeLinks',
'metadata': {
'tree': true,
'generates': true,
@@ -32184,68 +29128,66 @@
'params': []
};
inherits(TreeLinks, Transform, {
transform(_, pulse) {
const links = this.value,
- tree = pulse.source && pulse.source.root,
- out = pulse.fork(pulse.NO_SOURCE),
- lut = {};
+ tree = pulse.source && pulse.source.root,
+ out = pulse.fork(pulse.NO_SOURCE),
+ lut = {};
if (!tree) error('TreeLinks transform requires a tree data source.');
-
if (pulse.changed(pulse.ADD_REM)) {
// remove previous links
- out.rem = links; // build lookup table of valid tuples
+ out.rem = links;
- pulse.visit(pulse.SOURCE, t => lut[tupleid(t)] = 1); // generate links for all edges incident on valid tuples
+ // build lookup table of valid tuples
+ pulse.visit(pulse.SOURCE, t => lut[tupleid(t)] = 1);
+ // generate links for all edges incident on valid tuples
tree.each(node => {
const t = node.data,
- p = node.parent && node.parent.data;
-
+ p = node.parent && node.parent.data;
if (p && lut[tupleid(t)] && lut[tupleid(p)]) {
out.add.push(ingest$1({
source: p,
target: t
}));
}
});
this.value = out.add;
} else if (pulse.changed(pulse.MOD)) {
// build lookup table of modified tuples
- pulse.visit(pulse.MOD, t => lut[tupleid(t)] = 1); // gather links incident on modified tuples
+ pulse.visit(pulse.MOD, t => lut[tupleid(t)] = 1);
+ // gather links incident on modified tuples
links.forEach(link => {
if (lut[tupleid(link.source)] || lut[tupleid(link.target)]) {
out.mod.push(link);
}
});
}
-
return out;
}
-
});
const Tiles = {
binary: treemapBinary,
dice: treemapDice,
slice: treemapSlice,
slicedice: treemapSliceDice,
squarify: treemapSquarify,
resquarify: treemapResquarify
};
const Output$4 = ['x0', 'y0', 'x1', 'y1', 'depth', 'children'];
+
/**
* Treemap layout.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.field - The value field to size nodes.
*/
-
function Treemap(params) {
HierarchyLayout.call(this, params);
}
-
Treemap.Definition = {
'type': 'Treemap',
'metadata': {
'tree': true,
'modifies': true
@@ -32315,23 +29257,19 @@
* Treemap layout generator. Adds 'method' and 'ratio' parameters
* to configure the underlying tile method.
*/
layout() {
const x = treemap();
-
x.ratio = _ => {
const t = x.tile();
if (t.ratio) x.tile(t.ratio(_));
};
-
x.method = _ => {
if (has$1(Tiles, _)) x.tile(Tiles[_]);else error('Unrecognized Treemap layout method: ' + _);
};
-
return x;
},
-
params: ['method', 'ratio', 'size', 'round', 'padding', 'paddingInner', 'paddingOuter', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
fields: Output$4
});
var tree = /*#__PURE__*/Object.freeze({
@@ -32343,447 +29281,400 @@
tree: Tree,
treelinks: TreeLinks,
treemap: Treemap
});
+ // bit mask for getting first 2 bytes of alpha value
const ALPHA_MASK = 0xff000000;
-
function baseBitmaps($, data) {
- const bitmap = $.bitmap(); // when there is no base mark but data points are to be avoided
-
+ const bitmap = $.bitmap();
+ // when there is no base mark but data points are to be avoided
(data || []).forEach(d => bitmap.set($(d.boundary[0]), $(d.boundary[3])));
return [bitmap, undefined];
}
-
function markBitmaps($, baseMark, avoidMarks, labelInside, isGroupArea) {
// create canvas
const width = $.width,
- height = $.height,
- border = labelInside || isGroupArea,
- context = domCanvas(width, height).getContext('2d'),
- baseMarkContext = domCanvas(width, height).getContext('2d'),
- strokeContext = border && domCanvas(width, height).getContext('2d'); // render all marks to be avoided into canvas
+ height = $.height,
+ border = labelInside || isGroupArea,
+ context = domCanvas(width, height).getContext('2d'),
+ baseMarkContext = domCanvas(width, height).getContext('2d'),
+ strokeContext = border && domCanvas(width, height).getContext('2d');
+ // render all marks to be avoided into canvas
avoidMarks.forEach(items => draw(context, items, false));
draw(baseMarkContext, baseMark, false);
-
if (border) {
draw(strokeContext, baseMark, true);
- } // get canvas buffer, create bitmaps
+ }
-
+ // get canvas buffer, create bitmaps
const buffer = getBuffer(context, width, height),
- baseMarkBuffer = getBuffer(baseMarkContext, width, height),
- strokeBuffer = border && getBuffer(strokeContext, width, height),
- layer1 = $.bitmap(),
- layer2 = border && $.bitmap(); // populate bitmap layers
+ baseMarkBuffer = getBuffer(baseMarkContext, width, height),
+ strokeBuffer = border && getBuffer(strokeContext, width, height),
+ layer1 = $.bitmap(),
+ layer2 = border && $.bitmap();
+ // populate bitmap layers
let x, y, u, v, index, alpha, strokeAlpha, baseMarkAlpha;
-
for (y = 0; y < height; ++y) {
for (x = 0; x < width; ++x) {
index = y * width + x;
alpha = buffer[index] & ALPHA_MASK;
baseMarkAlpha = baseMarkBuffer[index] & ALPHA_MASK;
strokeAlpha = border && strokeBuffer[index] & ALPHA_MASK;
-
if (alpha || strokeAlpha || baseMarkAlpha) {
u = $(x);
v = $(y);
if (!isGroupArea && (alpha || baseMarkAlpha)) layer1.set(u, v); // update interior bitmap
-
if (border && (alpha || strokeAlpha)) layer2.set(u, v); // update border bitmap
}
}
}
-
return [layer1, layer2];
}
-
function getBuffer(context, width, height) {
return new Uint32Array(context.getImageData(0, 0, width, height).data.buffer);
}
-
function draw(context, items, interior) {
if (!items.length) return;
const type = items[0].mark.marktype;
-
if (type === 'group') {
items.forEach(group => {
group.items.forEach(mark => draw(context, mark.items, interior));
});
} else {
Marks[type].draw(context, {
items: interior ? items.map(prepare) : items
});
}
}
+
/**
* Prepare item before drawing into canvas (setting stroke and opacity)
* @param {object} source item to be prepared
* @returns prepared item
*/
-
-
function prepare(source) {
const item = rederive(source, {});
-
if (item.stroke && item.strokeOpacity !== 0 || item.fill && item.fillOpacity !== 0) {
- return { ...item,
+ return {
+ ...item,
strokeOpacity: 1,
stroke: '#000',
fillOpacity: 0
};
}
-
return item;
}
-
const DIV = 5,
- // bit shift from x, y index to bit vector array index
- MOD = 31,
- // bit mask for index lookup within a bit vector
- SIZE = 32,
- // individual bit vector size
- RIGHT0 = new Uint32Array(SIZE + 1),
- // left-anchored bit vectors, full -> 0
- RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
+ // bit shift from x, y index to bit vector array index
+ MOD = 31,
+ // bit mask for index lookup within a bit vector
+ SIZE = 32,
+ // individual bit vector size
+ RIGHT0 = new Uint32Array(SIZE + 1),
+ // left-anchored bit vectors, full -> 0
+ RIGHT1 = new Uint32Array(SIZE + 1); // right-anchored bit vectors, 0 -> full
RIGHT1[0] = 0;
RIGHT0[0] = ~RIGHT1[0];
-
for (let i = 1; i <= SIZE; ++i) {
RIGHT1[i] = RIGHT1[i - 1] << 1 | 1;
RIGHT0[i] = ~RIGHT1[i];
}
-
function Bitmap(w, h) {
const array = new Uint32Array(~~((w * h + SIZE) / SIZE));
-
function _set(index, mask) {
array[index] |= mask;
}
-
function _clear(index, mask) {
array[index] &= mask;
}
-
return {
array: array,
get: (x, y) => {
const index = y * w + x;
return array[index >>> DIV] & 1 << (index & MOD);
},
set: (x, y) => {
const index = y * w + x;
-
_set(index >>> DIV, 1 << (index & MOD));
},
clear: (x, y) => {
const index = y * w + x;
-
_clear(index >>> DIV, ~(1 << (index & MOD)));
},
getRange: (x, y, x2, y2) => {
let r = y2,
- start,
- end,
- indexStart,
- indexEnd;
-
+ start,
+ end,
+ indexStart,
+ indexEnd;
for (; r >= y; --r) {
start = r * w + x;
end = r * w + x2;
indexStart = start >>> DIV;
indexEnd = end >>> DIV;
-
if (indexStart === indexEnd) {
if (array[indexStart] & RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]) {
return true;
}
} else {
if (array[indexStart] & RIGHT0[start & MOD]) return true;
if (array[indexEnd] & RIGHT1[(end & MOD) + 1]) return true;
-
for (let i = indexStart + 1; i < indexEnd; ++i) {
if (array[i]) return true;
}
}
}
-
return false;
},
setRange: (x, y, x2, y2) => {
let start, end, indexStart, indexEnd, i;
-
for (; y <= y2; ++y) {
start = y * w + x;
end = y * w + x2;
indexStart = start >>> DIV;
indexEnd = end >>> DIV;
-
if (indexStart === indexEnd) {
_set(indexStart, RIGHT0[start & MOD] & RIGHT1[(end & MOD) + 1]);
} else {
_set(indexStart, RIGHT0[start & MOD]);
-
_set(indexEnd, RIGHT1[(end & MOD) + 1]);
-
for (i = indexStart + 1; i < indexEnd; ++i) _set(i, 0xffffffff);
}
}
},
clearRange: (x, y, x2, y2) => {
let start, end, indexStart, indexEnd, i;
-
for (; y <= y2; ++y) {
start = y * w + x;
end = y * w + x2;
indexStart = start >>> DIV;
indexEnd = end >>> DIV;
-
if (indexStart === indexEnd) {
_clear(indexStart, RIGHT1[start & MOD] | RIGHT0[(end & MOD) + 1]);
} else {
_clear(indexStart, RIGHT1[start & MOD]);
-
_clear(indexEnd, RIGHT0[(end & MOD) + 1]);
-
for (i = indexStart + 1; i < indexEnd; ++i) _clear(i, 0);
}
}
},
outOfBounds: (x, y, x2, y2) => x < 0 || y < 0 || y2 >= h || x2 >= w
};
}
-
function scaler(width, height, padding) {
const ratio = Math.max(1, Math.sqrt(width * height / 1e6)),
- w = ~~((width + 2 * padding + ratio) / ratio),
- h = ~~((height + 2 * padding + ratio) / ratio),
- scale = _ => ~~((_ + padding) / ratio);
-
+ w = ~~((width + 2 * padding + ratio) / ratio),
+ h = ~~((height + 2 * padding + ratio) / ratio),
+ scale = _ => ~~((_ + padding) / ratio);
scale.invert = _ => _ * ratio - padding;
-
scale.bitmap = () => Bitmap(w, h);
-
scale.ratio = ratio;
scale.padding = padding;
scale.width = width;
scale.height = height;
return scale;
}
-
function placeAreaLabelNaive($, bitmaps, avoidBaseMark, markIndex) {
const width = $.width,
- height = $.height; // try to place a label within an input area mark
+ height = $.height;
+ // try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
- // area points
- n = items.length,
- // number of points
- textHeight = d.datum.fontSize,
- // label width
- textWidth = textMetrics.width(d.datum, d.datum.text); // label height
+ // area points
+ n = items.length,
+ // number of points
+ textHeight = d.datum.fontSize,
+ // label width
+ textWidth = textMetrics.width(d.datum, d.datum.text); // label height
let maxAreaWidth = 0,
- x1,
- x2,
- y1,
- y2,
- x,
- y,
- areaWidth; // for each area sample point
+ x1,
+ x2,
+ y1,
+ y2,
+ x,
+ y,
+ areaWidth;
+ // for each area sample point
for (let i = 0; i < n; ++i) {
x1 = items[i].x;
y1 = items[i].y;
x2 = items[i].x2 === undefined ? x1 : items[i].x2;
y2 = items[i].y2 === undefined ? y1 : items[i].y2;
x = (x1 + x2) / 2;
y = (y1 + y2) / 2;
areaWidth = Math.abs(x2 - x1 + y2 - y1);
-
if (areaWidth >= maxAreaWidth) {
maxAreaWidth = areaWidth;
d.x = x;
d.y = y;
}
}
-
x = textWidth / 2;
y = textHeight / 2;
x1 = d.x - x;
x2 = d.x + x;
y1 = d.y - y;
y2 = d.y + y;
d.align = 'center';
-
if (x1 < 0 && x2 <= width) {
d.align = 'left';
} else if (0 <= x1 && width < x2) {
d.align = 'right';
}
-
d.baseline = 'middle';
-
if (y1 < 0 && y2 <= height) {
d.baseline = 'top';
} else if (0 <= y1 && height < y2) {
d.baseline = 'bottom';
}
-
return true;
};
}
-
function outOfBounds(x, y, textWidth, textHeight, width, height) {
let r = textWidth / 2;
return x - r < 0 || x + r > width || y - (r = textHeight / 2) < 0 || y + r > height;
}
-
function collision($, x, y, textHeight, textWidth, h, bm0, bm1) {
const w = textWidth * h / (textHeight * 2),
- x1 = $(x - w),
- x2 = $(x + w),
- y1 = $(y - (h = h / 2)),
- y2 = $(y + h);
+ x1 = $(x - w),
+ x2 = $(x + w),
+ y1 = $(y - (h = h / 2)),
+ y2 = $(y + h);
return bm0.outOfBounds(x1, y1, x2, y2) || bm0.getRange(x1, y1, x2, y2) || bm1 && bm1.getRange(x1, y1, x2, y2);
}
-
function placeAreaLabelReducedSearch($, bitmaps, avoidBaseMark, markIndex) {
const width = $.width,
- height = $.height,
- bm0 = bitmaps[0],
- // where labels have been placed
- bm1 = bitmaps[1]; // area outlines
+ height = $.height,
+ bm0 = bitmaps[0],
+ // where labels have been placed
+ bm1 = bitmaps[1]; // area outlines
function tryLabel(_x, _y, maxSize, textWidth, textHeight) {
const x = $.invert(_x),
- y = $.invert(_y);
+ y = $.invert(_y);
let lo = maxSize,
- hi = height,
- mid;
-
+ hi = height,
+ mid;
if (!outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, lo, bm0, bm1) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {
// if the label fits at the current sample point,
// perform binary search to find the largest font size that fits
while (hi - lo >= 1) {
mid = (lo + hi) / 2;
-
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {
hi = mid;
} else {
lo = mid;
}
- } // place label if current lower bound exceeds prior max font size
-
-
+ }
+ // place label if current lower bound exceeds prior max font size
if (lo > maxSize) {
return [x, y, lo, true];
}
}
- } // try to place a label within an input area mark
+ }
-
+ // try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
- // area points
- n = items.length,
- // number of points
- textHeight = d.datum.fontSize,
- // label width
- textWidth = textMetrics.width(d.datum, d.datum.text); // label height
+ // area points
+ n = items.length,
+ // number of points
+ textHeight = d.datum.fontSize,
+ // label width
+ textWidth = textMetrics.width(d.datum, d.datum.text); // label height
let maxSize = avoidBaseMark ? textHeight : 0,
- labelPlaced = false,
- labelPlaced2 = false,
- maxAreaWidth = 0,
- x1,
- x2,
- y1,
- y2,
- x,
- y,
- _x,
- _y,
- _x1,
- _xMid,
- _x2,
- _y1,
- _yMid,
- _y2,
- areaWidth,
- result,
- swapTmp; // for each area sample point
+ labelPlaced = false,
+ labelPlaced2 = false,
+ maxAreaWidth = 0,
+ x1,
+ x2,
+ y1,
+ y2,
+ x,
+ y,
+ _x,
+ _y,
+ _x1,
+ _xMid,
+ _x2,
+ _y1,
+ _yMid,
+ _y2,
+ areaWidth,
+ result,
+ swapTmp;
-
+ // for each area sample point
for (let i = 0; i < n; ++i) {
x1 = items[i].x;
y1 = items[i].y;
x2 = items[i].x2 === undefined ? x1 : items[i].x2;
y2 = items[i].y2 === undefined ? y1 : items[i].y2;
-
if (x1 > x2) {
swapTmp = x1;
x1 = x2;
x2 = swapTmp;
}
-
if (y1 > y2) {
swapTmp = y1;
y1 = y2;
y2 = swapTmp;
}
-
_x1 = $(x1);
_x2 = $(x2);
_xMid = ~~((_x1 + _x2) / 2);
_y1 = $(y1);
_y2 = $(y2);
- _yMid = ~~((_y1 + _y2) / 2); // search along the line from mid point between the 2 border to lower border
+ _yMid = ~~((_y1 + _y2) / 2);
+ // search along the line from mid point between the 2 border to lower border
for (_x = _xMid; _x >= _x1; --_x) {
for (_y = _yMid; _y >= _y1; --_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
-
if (result) {
[d.x, d.y, maxSize, labelPlaced] = result;
}
}
- } // search along the line from mid point between the 2 border to upper border
+ }
-
+ // search along the line from mid point between the 2 border to upper border
for (_x = _xMid; _x <= _x2; ++_x) {
for (_y = _yMid; _y <= _y2; ++_y) {
result = tryLabel(_x, _y, maxSize, textWidth, textHeight);
-
if (result) {
[d.x, d.y, maxSize, labelPlaced] = result;
}
}
- } // place label at slice center if not placed through other means
- // and if we're not avoiding overlap with other areas
+ }
-
+ // place label at slice center if not placed through other means
+ // and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {
// one span is zero, hence we can add
areaWidth = Math.abs(x2 - x1 + y2 - y1);
x = (x1 + x2) / 2;
- y = (y1 + y2) / 2; // place label if it fits and improves the max area width
+ y = (y1 + y2) / 2;
+ // place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {
maxAreaWidth = areaWidth;
d.x = x;
d.y = y;
labelPlaced2 = true;
}
}
- } // record current label placement information, update label bitmap
+ }
-
+ // record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {
x = textWidth / 2;
y = textHeight / 2;
bm0.setRange($(d.x - x), $(d.y - y), $(d.x + x), $(d.y + y));
d.align = 'center';
@@ -32793,121 +29684,123 @@
return false;
}
};
}
+ // pixel direction offsets for flood fill search
const X_DIR = [-1, -1, 1, 1];
const Y_DIR = [-1, 1, -1, 1];
-
function placeAreaLabelFloodFill($, bitmaps, avoidBaseMark, markIndex) {
const width = $.width,
- height = $.height,
- bm0 = bitmaps[0],
- // where labels have been placed
- bm1 = bitmaps[1],
- // area outlines
- bm2 = $.bitmap(); // flood-fill visitations
- // try to place a label within an input area mark
+ height = $.height,
+ bm0 = bitmaps[0],
+ // where labels have been placed
+ bm1 = bitmaps[1],
+ // area outlines
+ bm2 = $.bitmap(); // flood-fill visitations
+ // try to place a label within an input area mark
return function (d) {
const items = d.datum.datum.items[markIndex].items,
- // area points
- n = items.length,
- // number of points
- textHeight = d.datum.fontSize,
- // label width
- textWidth = textMetrics.width(d.datum, d.datum.text),
- // label height
- stack = []; // flood fill stack
+ // area points
+ n = items.length,
+ // number of points
+ textHeight = d.datum.fontSize,
+ // label width
+ textWidth = textMetrics.width(d.datum, d.datum.text),
+ // label height
+ stack = []; // flood fill stack
let maxSize = avoidBaseMark ? textHeight : 0,
- labelPlaced = false,
- labelPlaced2 = false,
- maxAreaWidth = 0,
- x1,
- x2,
- y1,
- y2,
- x,
- y,
- _x,
- _y,
- lo,
- hi,
- mid,
- areaWidth; // for each area sample point
+ labelPlaced = false,
+ labelPlaced2 = false,
+ maxAreaWidth = 0,
+ x1,
+ x2,
+ y1,
+ y2,
+ x,
+ y,
+ _x,
+ _y,
+ lo,
+ hi,
+ mid,
+ areaWidth;
-
+ // for each area sample point
for (let i = 0; i < n; ++i) {
x1 = items[i].x;
y1 = items[i].y;
x2 = items[i].x2 === undefined ? x1 : items[i].x2;
- y2 = items[i].y2 === undefined ? y1 : items[i].y2; // add scaled center point to stack
+ y2 = items[i].y2 === undefined ? y1 : items[i].y2;
- stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]); // perform flood fill, visit points
+ // add scaled center point to stack
+ stack.push([$((x1 + x2) / 2), $((y1 + y2) / 2)]);
+ // perform flood fill, visit points
while (stack.length) {
- [_x, _y] = stack.pop(); // exit if point already marked
+ [_x, _y] = stack.pop();
- if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue; // mark point in flood fill bitmap
- // add search points for all (in bound) directions
+ // exit if point already marked
+ if (bm0.get(_x, _y) || bm1.get(_x, _y) || bm2.get(_x, _y)) continue;
+ // mark point in flood fill bitmap
+ // add search points for all (in bound) directions
bm2.set(_x, _y);
-
for (let j = 0; j < 4; ++j) {
x = _x + X_DIR[j];
y = _y + Y_DIR[j];
if (!bm2.outOfBounds(x, y, x, y)) stack.push([x, y]);
- } // unscale point back to x, y space
+ }
-
+ // unscale point back to x, y space
x = $.invert(_x);
y = $.invert(_y);
lo = maxSize;
hi = height; // TODO: make this bound smaller
if (!outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, lo, bm0, bm1) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {
// if the label fits at the current sample point,
// perform binary search to find the largest font size that fits
while (hi - lo >= 1) {
mid = (lo + hi) / 2;
-
if (collision($, x, y, textHeight, textWidth, mid, bm0, bm1)) {
hi = mid;
} else {
lo = mid;
}
- } // place label if current lower bound exceeds prior max font size
-
-
+ }
+ // place label if current lower bound exceeds prior max font size
if (lo > maxSize) {
d.x = x;
d.y = y;
maxSize = lo;
labelPlaced = true;
}
}
- } // place label at slice center if not placed through other means
- // and if we're not avoiding overlap with other areas
+ }
-
+ // place label at slice center if not placed through other means
+ // and if we're not avoiding overlap with other areas
if (!labelPlaced && !avoidBaseMark) {
// one span is zero, hence we can add
areaWidth = Math.abs(x2 - x1 + y2 - y1);
x = (x1 + x2) / 2;
- y = (y1 + y2) / 2; // place label if it fits and improves the max area width
+ y = (y1 + y2) / 2;
+ // place label if it fits and improves the max area width
if (areaWidth >= maxAreaWidth && !outOfBounds(x, y, textWidth, textHeight, width, height) && !collision($, x, y, textHeight, textWidth, textHeight, bm0, null)) {
maxAreaWidth = areaWidth;
d.x = x;
d.y = y;
labelPlaced2 = true;
}
}
- } // record current label placement information, update label bitmap
+ }
-
+ // record current label placement information, update label bitmap
if (labelPlaced || labelPlaced2) {
x = textWidth / 2;
y = textHeight / 2;
bm0.setRange($(d.x - x), $(d.y - y), $(d.x + x), $(d.y + y));
d.align = 'center';
@@ -32916,48 +29809,44 @@
} else {
return false;
}
};
}
-
const Aligns = ['right', 'center', 'left'],
- Baselines = ['bottom', 'middle', 'top'];
-
+ Baselines = ['bottom', 'middle', 'top'];
function placeMarkLabel($, bitmaps, anchors, offsets) {
const width = $.width,
- height = $.height,
- bm0 = bitmaps[0],
- bm1 = bitmaps[1],
- n = offsets.length;
+ height = $.height,
+ bm0 = bitmaps[0],
+ bm1 = bitmaps[1],
+ n = offsets.length;
return function (d) {
- var _d$textWidth;
-
const boundary = d.boundary,
- textHeight = d.datum.fontSize; // can not be placed if the mark is not visible in the graph bound
+ textHeight = d.datum.fontSize;
+ // can not be placed if the mark is not visible in the graph bound
if (boundary[2] < 0 || boundary[5] < 0 || boundary[0] > width || boundary[3] > height) {
return false;
}
+ let textWidth = d.textWidth ?? 0,
+ dx,
+ dy,
+ isInside,
+ sizeFactor,
+ insideFactor,
+ x1,
+ x2,
+ y1,
+ y2,
+ xc,
+ yc,
+ _x1,
+ _x2,
+ _y1,
+ _y2;
- let textWidth = (_d$textWidth = d.textWidth) !== null && _d$textWidth !== void 0 ? _d$textWidth : 0,
- dx,
- dy,
- isInside,
- sizeFactor,
- insideFactor,
- x1,
- x2,
- y1,
- y2,
- xc,
- yc,
- _x1,
- _x2,
- _y1,
- _y2; // for each anchor and offset
-
-
+ // for each anchor and offset
for (let i = 0; i < n; ++i) {
dx = (anchors[i] & 0x3) - 1;
dy = (anchors[i] >>> 0x2 & 0x3) - 1;
isInside = dx === 0 && dy === 0 || offsets[i] < 0;
sizeFactor = dx && dy ? Math.SQRT1_2 : 1;
@@ -32967,55 +29856,53 @@
y1 = yc - textHeight / 2;
y2 = yc + textHeight / 2;
_x1 = $(x1);
_y1 = $(y1);
_y2 = $(y2);
-
if (!textWidth) {
// to avoid finding width of text label,
if (!test(_x1, _x1, _y1, _y2, bm0, bm1, x1, x1, y1, y2, boundary, isInside)) {
// skip this anchor/offset option if we fail to place a label with 1px width
continue;
} else {
// Otherwise, find the label width
textWidth = textMetrics.width(d.datum, d.datum.text);
}
}
-
xc = x1 + insideFactor * textWidth * dx / 2;
x1 = xc - textWidth / 2;
x2 = xc + textWidth / 2;
_x1 = $(x1);
_x2 = $(x2);
-
if (test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside)) {
// place label if the position is placeable
d.x = !dx ? xc : dx * insideFactor < 0 ? x2 : x1;
d.y = !dy ? yc : dy * insideFactor < 0 ? y2 : y1;
d.align = Aligns[dx * insideFactor + 1];
d.baseline = Baselines[dy * insideFactor + 1];
bm0.setRange(_x1, _y1, _x2, _y2);
return true;
}
}
-
return false;
};
- } // Test if a label with the given dimensions can be added without overlap
+ }
-
+ // Test if a label with the given dimensions can be added without overlap
function test(_x1, _x2, _y1, _y2, bm0, bm1, x1, x2, y1, y2, boundary, isInside) {
return !(bm0.outOfBounds(_x1, _y1, _x2, _y2) || (isInside && bm1 || bm0).getRange(_x1, _y1, _x2, _y2));
}
+ // 8-bit representation of anchors
const TOP = 0x0,
- MIDDLE = 0x4,
- BOTTOM = 0x8,
- LEFT = 0x0,
- CENTER = 0x1,
- RIGHT = 0x2; // Mapping from text anchor to number representation
+ MIDDLE = 0x4,
+ BOTTOM = 0x8,
+ LEFT = 0x0,
+ CENTER = 0x1,
+ RIGHT = 0x2;
+ // Mapping from text anchor to number representation
const anchorCode = {
'top-left': TOP + LEFT,
'top': TOP + CENTER,
'top-right': TOP + RIGHT,
'left': MIDDLE + LEFT,
@@ -33028,26 +29915,26 @@
const placeAreaLabel = {
'naive': placeAreaLabelNaive,
'reduced-search': placeAreaLabelReducedSearch,
'floodfill': placeAreaLabelFloodFill
};
-
function labelLayout(texts, size, compare, offset, anchor, avoidMarks, avoidBaseMark, lineAnchor, markIndex, padding, method) {
// early exit for empty data
if (!texts.length) return texts;
const positions = Math.max(offset.length, anchor.length),
- offsets = getOffsets(offset, positions),
- anchors = getAnchors(anchor, positions),
- marktype = markType(texts[0].datum),
- grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
- isGroupArea = grouptype === 'area',
- boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
- infPadding = padding === null || padding === Infinity,
- isNaiveGroupArea = isGroupArea && method === 'naive';
+ offsets = getOffsets(offset, positions),
+ anchors = getAnchors(anchor, positions),
+ marktype = markType(texts[0].datum),
+ grouptype = marktype === 'group' && texts[0].datum.items[markIndex].marktype,
+ isGroupArea = grouptype === 'area',
+ boundary = markBoundary(marktype, grouptype, lineAnchor, markIndex),
+ infPadding = padding === null || padding === Infinity,
+ isNaiveGroupArea = isGroupArea && method === 'naive';
let maxTextWidth = -1,
- maxTextHeight = -1; // prepare text mark data for placing
+ maxTextHeight = -1;
+ // prepare text mark data for placing
const data = texts.map(d => {
const textWidth = infPadding ? textMetrics.width(d, d.text) : undefined;
maxTextWidth = Math.max(maxTextWidth, textWidth);
maxTextHeight = Math.max(maxTextHeight, d.fontSize);
return {
@@ -33062,77 +29949,66 @@
};
});
padding = padding === null || padding === Infinity ? Math.max(maxTextWidth, maxTextHeight) + Math.max(...offset) : padding;
const $ = scaler(size[0], size[1], padding);
let bitmaps;
-
if (!isNaiveGroupArea) {
// sort labels in priority order, if comparator is provided
if (compare) {
data.sort((a, b) => compare(a.datum, b.datum));
- } // flag indicating if label can be placed inside its base mark
+ }
-
+ // flag indicating if label can be placed inside its base mark
let labelInside = false;
-
for (let i = 0; i < anchors.length && !labelInside; ++i) {
// label inside if anchor is at center
// label inside if offset to be inside the mark bound
labelInside = anchors[i] === 0x5 || offsets[i] < 0;
- } // extract data information from base mark when base mark is to be avoided
+ }
+
+ // extract data information from base mark when base mark is to be avoided
// base mark is implicitly avoided if it is a group area
+ const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum);
-
- const baseMark = (marktype && avoidBaseMark || isGroupArea) && texts.map(d => d.datum); // generate bitmaps for layout calculation
-
+ // generate bitmaps for layout calculation
bitmaps = avoidMarks.length || baseMark ? markBitmaps($, baseMark || [], avoidMarks, labelInside, isGroupArea) : baseBitmaps($, avoidBaseMark && data);
- } // generate label placement function
+ }
+ // generate label placement function
+ const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets);
- const place = isGroupArea ? placeAreaLabel[method]($, bitmaps, avoidBaseMark, markIndex) : placeMarkLabel($, bitmaps, anchors, offsets); // place all labels
-
+ // place all labels
data.forEach(d => d.opacity = +place(d));
return data;
}
-
function getOffsets(_, count) {
const offsets = new Float64Array(count),
- n = _.length;
-
+ n = _.length;
for (let i = 0; i < n; ++i) offsets[i] = _[i] || 0;
-
for (let i = n; i < count; ++i) offsets[i] = offsets[n - 1];
-
return offsets;
}
-
function getAnchors(_, count) {
const anchors = new Int8Array(count),
- n = _.length;
-
+ n = _.length;
for (let i = 0; i < n; ++i) anchors[i] |= anchorCode[_[i]];
-
for (let i = n; i < count; ++i) anchors[i] = anchors[n - 1];
-
return anchors;
}
-
function markType(item) {
return item && item.mark && item.mark.marktype;
}
+
/**
* Factory function for function for getting base mark boundary, depending
* on mark and group type. When mark type is undefined, line or area: boundary
* is the coordinate of each data point. When base mark is grouped line,
* boundary is either at the start or end of the line depending on the
* value of lineAnchor. Otherwise, use bounds of base mark.
*/
-
-
function markBoundary(marktype, grouptype, lineAnchor, markIndex) {
const xy = d => [d.x, d.x, d.x, d.y, d.y, d.y];
-
if (!marktype) {
return xy; // no reactive geometry
} else if (marktype === 'line' || marktype === 'area') {
return d => xy(d.datum);
} else if (grouptype === 'line') {
@@ -33148,13 +30024,13 @@
const b = d.datum.bounds;
return [b.x1, (b.x1 + b.x2) / 2, b.x2, b.y1, (b.y1 + b.y2) / 2, b.y2];
};
}
}
-
const Output$1 = ['x', 'y', 'opacity', 'align', 'baseline'];
const Anchors = ['top-left', 'left', 'bottom-left', 'top', 'bottom', 'top-right', 'right', 'bottom-right'];
+
/**
* Compute text label layout to annotate marks.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<number>} params.size - The size of the layout, provided as a [width, height] array.
@@ -33178,15 +30054,13 @@
* @param {string} [params.method='naive'] - For area make labels only, a method for
* place labels. One of 'naive', 'reduced-search', or 'floodfill'.
* @param {Array<string>} [params.as] - The output fields written by the transform.
* The default is ['x', 'y', 'opacity', 'align', 'baseline'].
*/
-
function Label$1(params) {
Transform.call(this, null, params);
}
-
Label$1.Definition = {
type: 'Label',
metadata: {
modifies: true
},
@@ -33247,21 +30121,18 @@
transform(_, pulse) {
function modp(param) {
const p = _[param];
return isFunction(p) && pulse.modified(p.fields);
}
-
const mod = _.modified();
-
if (!(mod || pulse.changed(pulse.ADD_REM) || modp('sort'))) return;
-
if (!_.size || _.size.length !== 2) {
error('Size parameter should be specified as a [width, height] array.');
}
+ const as = _.as || Output$1;
- const as = _.as || Output$1; // run label layout
-
+ // run label layout
labelLayout(pulse.materialize(pulse.SOURCE).source || [], _.size, _.sort, array$5(_.offset == null ? 1 : _.offset), array$5(_.anchor || Anchors), _.avoidMarks || [], _.avoidBaseMark !== false, _.lineAnchor || 'end', _.markIndex || 0, _.padding === undefined ? 0 : _.padding, _.method || 'naive').forEach(l => {
// write layout results to data stream
const t = l.datum;
t[as[0]] = l.x;
t[as[1]] = l.y;
@@ -33269,66 +30140,60 @@
t[as[3]] = l.align;
t[as[4]] = l.baseline;
});
return pulse.reflow(mod).modifies(as);
}
-
});
var label = /*#__PURE__*/Object.freeze({
__proto__: null,
label: Label$1
});
function partition(data, groupby) {
var groups = [],
- get = function (f) {
- return f(t);
- },
- map,
- i,
- n,
- t,
- k,
- g; // partition data points into stack groups
+ get = function (f) {
+ return f(t);
+ },
+ map,
+ i,
+ n,
+ t,
+ k,
+ g;
-
+ // partition data points into stack groups
if (groupby == null) {
groups.push(data);
} else {
for (map = {}, i = 0, n = data.length; i < n; ++i) {
t = data[i];
k = groupby.map(get);
g = map[k];
-
if (!g) {
map[k] = g = [];
g.dims = k;
groups.push(g);
}
-
g.push(t);
}
}
-
return groups;
}
+
/**
* Compute locally-weighted regression fits for one or more data groups.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.x - An accessor for the predictor data field.
* @param {function(object): *} params.y - An accessor for the predicted data field.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors to groupby.
* @param {number} [params.bandwidth=0.3] - The loess bandwidth.
*/
-
-
function Loess(params) {
Transform.call(this, null, params);
}
-
Loess.Definition = {
'type': 'Loess',
'metadata': {
'generates': true
},
@@ -33355,49 +30220,45 @@
}]
};
inherits(Loess, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
-
if (!this.value || pulse.changed() || _.modified()) {
const source = pulse.materialize(pulse.SOURCE).source,
- groups = partition(source, _.groupby),
- names = (_.groupby || []).map(accessorName),
- m = names.length,
- as = _.as || [accessorName(_.x), accessorName(_.y)],
- values = [];
+ groups = partition(source, _.groupby),
+ names = (_.groupby || []).map(accessorName),
+ m = names.length,
+ as = _.as || [accessorName(_.x), accessorName(_.y)],
+ values = [];
groups.forEach(g => {
loess(g, _.x, _.y, _.bandwidth || 0.3).forEach(p => {
const t = {};
-
for (let i = 0; i < m; ++i) {
t[names[i]] = g.dims[i];
}
-
t[as[0]] = p[0];
t[as[1]] = p[1];
values.push(ingest$1(t));
});
});
if (this.value) out.rem = this.value;
this.value = out.add = out.source = values;
}
-
return out;
}
-
});
const Methods = {
+ constant: constant$4,
linear: linear$2,
log: log$3,
exp: exp$1,
pow: pow$3,
quad: quad,
poly: poly
};
-
const degreesOfFreedom = (method, order) => method === 'poly' ? order : method === 'quad' ? 2 : 1;
+
/**
* Compute regression fits for one or more data groups.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {function(object): *} params.x - An accessor for the predictor data field.
@@ -33405,16 +30266,13 @@
* @param {string} [params.method='linear'] - The regression method to apply.
* @param {Array<function(object): *>} [params.groupby] - An array of accessors to groupby.
* @param {Array<number>} [params.extent] - The domain extent over which to plot the regression line.
* @param {number} [params.order=3] - The polynomial order. Only applies to the 'poly' method.
*/
-
-
function Regression(params) {
Transform.call(this, null, params);
}
-
Regression.Definition = {
'type': 'Regression',
'metadata': {
'generates': true
},
@@ -33455,128 +30313,110 @@
}]
};
inherits(Regression, Transform, {
transform(_, pulse) {
const out = pulse.fork(pulse.NO_SOURCE | pulse.NO_FIELDS);
-
if (!this.value || pulse.changed() || _.modified()) {
const source = pulse.materialize(pulse.SOURCE).source,
- groups = partition(source, _.groupby),
- names = (_.groupby || []).map(accessorName),
- method = _.method || 'linear',
- order = _.order || 3,
- dof = degreesOfFreedom(method, order),
- as = _.as || [accessorName(_.x), accessorName(_.y)],
- fit = Methods[method],
- values = [];
+ groups = partition(source, _.groupby),
+ names = (_.groupby || []).map(accessorName),
+ method = _.method || 'linear',
+ order = _.order == null ? 3 : _.order,
+ dof = degreesOfFreedom(method, order),
+ as = _.as || [accessorName(_.x), accessorName(_.y)],
+ fit = Methods[method],
+ values = [];
let domain = _.extent;
-
if (!has$1(Methods, method)) {
error('Invalid regression method: ' + method);
}
-
if (domain != null) {
if (method === 'log' && domain[0] <= 0) {
pulse.dataflow.warn('Ignoring extent with values <= 0 for log regression.');
domain = null;
}
}
-
groups.forEach(g => {
const n = g.length;
-
if (n <= dof) {
pulse.dataflow.warn('Skipping regression with more parameters than data points.');
return;
}
-
const model = fit(g, _.x, _.y, order);
-
if (_.params) {
// if parameter vectors requested return those
values.push(ingest$1({
keys: g.dims,
coef: model.coef,
rSquared: model.rSquared
}));
return;
}
-
const dom = domain || extent(g, _.x),
- add = p => {
- const t = {};
-
- for (let i = 0; i < names.length; ++i) {
- t[names[i]] = g.dims[i];
- }
-
- t[as[0]] = p[0];
- t[as[1]] = p[1];
- values.push(ingest$1(t));
- };
-
- if (method === 'linear') {
- // for linear regression we only need the end points
+ add = p => {
+ const t = {};
+ for (let i = 0; i < names.length; ++i) {
+ t[names[i]] = g.dims[i];
+ }
+ t[as[0]] = p[0];
+ t[as[1]] = p[1];
+ values.push(ingest$1(t));
+ };
+ if (method === 'linear' || method === 'constant') {
+ // for linear or constant regression we only need the end points
dom.forEach(x => add([x, model.predict(x)]));
} else {
// otherwise return trend line sample points
sampleCurve(model.predict, dom, 25, 200).forEach(add);
}
});
if (this.value) out.rem = this.value;
this.value = out.add = out.source = values;
}
-
return out;
}
-
});
var reg = /*#__PURE__*/Object.freeze({
__proto__: null,
loess: Loess,
regression: Regression
});
const epsilon$1 = 1.1102230246251565e-16;
const splitter = 134217729;
- const resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; // fast_expansion_sum_zeroelim routine from oritinal code
+ const resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
+ // fast_expansion_sum_zeroelim routine from oritinal code
function sum(elen, e, flen, f, h) {
let Q, Qnew, hh, bvirt;
let enow = e[0];
let fnow = f[0];
let eindex = 0;
let findex = 0;
-
if (fnow > enow === fnow > -enow) {
Q = enow;
enow = e[++eindex];
} else {
Q = fnow;
fnow = f[++findex];
}
-
let hindex = 0;
-
if (eindex < elen && findex < flen) {
if (fnow > enow === fnow > -enow) {
Qnew = enow + Q;
hh = Q - (Qnew - enow);
enow = e[++eindex];
} else {
Qnew = fnow + Q;
hh = Q - (Qnew - fnow);
fnow = f[++findex];
}
-
Q = Qnew;
-
if (hh !== 0) {
h[hindex++] = hh;
}
-
while (eindex < elen && findex < flen) {
if (fnow > enow === fnow > -enow) {
Qnew = Q + enow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (enow - bvirt);
@@ -33585,54 +30425,44 @@
Qnew = Q + fnow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (fnow - bvirt);
fnow = f[++findex];
}
-
Q = Qnew;
-
if (hh !== 0) {
h[hindex++] = hh;
}
}
}
-
while (eindex < elen) {
Qnew = Q + enow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (enow - bvirt);
enow = e[++eindex];
Q = Qnew;
-
if (hh !== 0) {
h[hindex++] = hh;
}
}
-
while (findex < flen) {
Qnew = Q + fnow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (fnow - bvirt);
fnow = f[++findex];
Q = Qnew;
-
if (hh !== 0) {
h[hindex++] = hh;
}
}
-
if (Q !== 0 || hindex === 0) {
h[hindex++] = Q;
}
-
return hindex;
}
function estimate(elen, e) {
let Q = e[0];
-
for (let i = 1; i < elen; i++) Q += e[i];
-
return Q;
}
function vec(n) {
return new Float64Array(n);
}
@@ -33643,16 +30473,13 @@
const B = vec(4);
const C1 = vec(8);
const C2 = vec(12);
const D = vec(16);
const u = vec(4);
-
function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
let acxtail, acytail, bcxtail, bcytail;
-
let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
-
const acx = ax - cx;
const bcx = bx - cx;
const acy = ay - cy;
const bcy = by - cy;
s1 = acx * bcy;
@@ -33684,28 +30511,24 @@
bvirt = u3 - _j;
B[2] = _j - (u3 - bvirt) + (_i - bvirt);
B[3] = u3;
let det = estimate(4, B);
let errbound = ccwerrboundB * detsum;
-
if (det >= errbound || -det >= errbound) {
return det;
}
-
bvirt = ax - acx;
acxtail = ax - (acx + bvirt) + (bvirt - cx);
bvirt = bx - bcx;
bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
bvirt = ay - acy;
acytail = ay - (acy + bvirt) + (bvirt - cy);
bvirt = by - bcy;
bcytail = by - (bcy + bvirt) + (bvirt - cy);
-
if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
return det;
}
-
errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
if (det >= errbound || -det >= errbound) return det;
s1 = acxtail * bcy;
c = splitter * acxtail;
@@ -33797,11 +30620,10 @@
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
u[3] = u3;
const Dlen = sum(C2len, C2, 4, u, D);
return D[Dlen - 1];
}
-
function orient2d(ax, ay, bx, by, cx, cy) {
const detleft = (ay - cy) * (bx - cx);
const detright = (ax - cx) * (by - cy);
const det = detleft - detright;
if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) return det;
@@ -33816,141 +30638,127 @@
static from(points) {
let getX = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultGetX;
let getY = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultGetY;
const n = points.length;
const coords = new Float64Array(n * 2);
-
for (let i = 0; i < n; i++) {
const p = points[i];
coords[2 * i] = getX(p);
coords[2 * i + 1] = getY(p);
}
-
return new Delaunator(coords);
}
-
constructor(coords) {
const n = coords.length >> 1;
if (n > 0 && typeof coords[0] !== 'number') throw new Error('Expected coords to contain numbers.');
- this.coords = coords; // arrays that will store the triangulation graph
+ this.coords = coords;
+ // arrays that will store the triangulation graph
const maxTriangles = Math.max(2 * n - 5, 0);
this._triangles = new Uint32Array(maxTriangles * 3);
- this._halfedges = new Int32Array(maxTriangles * 3); // temporary arrays for tracking the edges of the advancing convex hull
+ this._halfedges = new Int32Array(maxTriangles * 3);
+ // temporary arrays for tracking the edges of the advancing convex hull
this._hashSize = Math.ceil(Math.sqrt(n));
this._hullPrev = new Uint32Array(n); // edge to prev edge
-
this._hullNext = new Uint32Array(n); // edge to next edge
-
this._hullTri = new Uint32Array(n); // edge to adjacent triangle
-
this._hullHash = new Int32Array(this._hashSize).fill(-1); // angular edge hash
- // temporary arrays for sorting points
+ // temporary arrays for sorting points
this._ids = new Uint32Array(n);
this._dists = new Float64Array(n);
this.update();
}
-
update() {
const {
coords,
_hullPrev: hullPrev,
_hullNext: hullNext,
_hullTri: hullTri,
_hullHash: hullHash
} = this;
- const n = coords.length >> 1; // populate an array of point indices; calculate input data bbox
+ const n = coords.length >> 1;
+ // populate an array of point indices; calculate input data bbox
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
-
for (let i = 0; i < n; i++) {
const x = coords[2 * i];
const y = coords[2 * i + 1];
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
this._ids[i] = i;
}
-
const cx = (minX + maxX) / 2;
const cy = (minY + maxY) / 2;
let minDist = Infinity;
- let i0, i1, i2; // pick a seed point close to the center
+ let i0, i1, i2;
+ // pick a seed point close to the center
for (let i = 0; i < n; i++) {
const d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]);
-
if (d < minDist) {
i0 = i;
minDist = d;
}
}
-
const i0x = coords[2 * i0];
const i0y = coords[2 * i0 + 1];
- minDist = Infinity; // find the point closest to the seed
+ minDist = Infinity;
+ // find the point closest to the seed
for (let i = 0; i < n; i++) {
if (i === i0) continue;
const d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]);
-
if (d < minDist && d > 0) {
i1 = i;
minDist = d;
}
}
-
let i1x = coords[2 * i1];
let i1y = coords[2 * i1 + 1];
- let minRadius = Infinity; // find the third point which forms the smallest circumcircle with the first two
+ let minRadius = Infinity;
+ // find the third point which forms the smallest circumcircle with the first two
for (let i = 0; i < n; i++) {
if (i === i0 || i === i1) continue;
const r = circumradius(i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]);
-
if (r < minRadius) {
i2 = i;
minRadius = r;
}
}
-
let i2x = coords[2 * i2];
let i2y = coords[2 * i2 + 1];
-
if (minRadius === Infinity) {
// order collinear points by dx (or dy if all x are identical)
// and return the list as a hull
for (let i = 0; i < n; i++) {
this._dists[i] = coords[2 * i] - coords[0] || coords[2 * i + 1] - coords[1];
}
-
quicksort(this._ids, this._dists, 0, n - 1);
const hull = new Uint32Array(n);
let j = 0;
-
for (let i = 0, d0 = -Infinity; i < n; i++) {
const id = this._ids[i];
-
if (this._dists[id] > d0) {
hull[j++] = id;
d0 = this._dists[id];
}
}
-
this.hull = hull.subarray(0, j);
this.triangles = new Uint32Array(0);
this.halfedges = new Uint32Array(0);
return;
- } // swap the order of the seed points for counter-clockwise orientation
+ }
-
+ // swap the order of the seed points for counter-clockwise orientation
if (orient2d(i0x, i0y, i1x, i1y, i2x, i2y) < 0) {
const i = i1;
const x = i1x;
const y = i1y;
i1 = i2;
@@ -33958,22 +30766,21 @@
i1y = i2y;
i2 = i;
i2x = x;
i2y = y;
}
-
const center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
this._cx = center.x;
this._cy = center.y;
-
for (let i = 0; i < n; i++) {
this._dists[i] = dist(coords[2 * i], coords[2 * i + 1], center.x, center.y);
- } // sort the points by distance from the seed triangle circumcenter
+ }
+ // sort the points by distance from the seed triangle circumcenter
+ quicksort(this._ids, this._dists, 0, n - 1);
- quicksort(this._ids, this._dists, 0, n - 1); // set up the seed triangle as the starting hull
-
+ // set up the seed triangle as the starting hull
this._hullStart = i0;
let hullSize = 3;
hullNext[i0] = hullPrev[i2] = i1;
hullNext[i1] = hullPrev[i0] = i2;
hullNext[i2] = hullPrev[i1] = i0;
@@ -33983,117 +30790,107 @@
hullHash.fill(-1);
hullHash[this._hashKey(i0x, i0y)] = i0;
hullHash[this._hashKey(i1x, i1y)] = i1;
hullHash[this._hashKey(i2x, i2y)] = i2;
this.trianglesLen = 0;
-
this._addTriangle(i0, i1, i2, -1, -1, -1);
-
for (let k = 0, xp, yp; k < this._ids.length; k++) {
const i = this._ids[k];
const x = coords[2 * i];
- const y = coords[2 * i + 1]; // skip near-duplicate points
+ const y = coords[2 * i + 1];
+ // skip near-duplicate points
if (k > 0 && Math.abs(x - xp) <= EPSILON && Math.abs(y - yp) <= EPSILON) continue;
xp = x;
- yp = y; // skip seed triangle points
+ yp = y;
- if (i === i0 || i === i1 || i === i2) continue; // find a visible edge on the convex hull using edge hash
+ // skip seed triangle points
+ if (i === i0 || i === i1 || i === i2) continue;
+ // find a visible edge on the convex hull using edge hash
let start = 0;
-
for (let j = 0, key = this._hashKey(x, y); j < this._hashSize; j++) {
start = hullHash[(key + j) % this._hashSize];
if (start !== -1 && start !== hullNext[start]) break;
}
-
start = hullPrev[start];
let e = start,
- q;
-
+ q;
while (q = hullNext[e], orient2d(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1]) >= 0) {
e = q;
-
if (e === start) {
e = -1;
break;
}
}
-
if (e === -1) continue; // likely a near-duplicate point; skip it
+
// add the first triangle from the point
+ let t = this._addTriangle(e, i, hullNext[e], -1, -1, hullTri[e]);
- let t = this._addTriangle(e, i, hullNext[e], -1, -1, hullTri[e]); // recursively flip triangles from the point until they satisfy the Delaunay condition
-
-
+ // recursively flip triangles from the point until they satisfy the Delaunay condition
hullTri[i] = this._legalize(t + 2);
hullTri[e] = t; // keep track of boundary triangles on the hull
+ hullSize++;
- hullSize++; // walk forward through the hull, adding more triangles and flipping recursively
-
+ // walk forward through the hull, adding more triangles and flipping recursively
let n = hullNext[e];
-
while (q = hullNext[n], orient2d(x, y, coords[2 * n], coords[2 * n + 1], coords[2 * q], coords[2 * q + 1]) < 0) {
t = this._addTriangle(n, i, q, hullTri[i], -1, hullTri[n]);
hullTri[i] = this._legalize(t + 2);
hullNext[n] = n; // mark as removed
-
hullSize--;
n = q;
- } // walk backward from the other side, adding more triangles and flipping
+ }
-
+ // walk backward from the other side, adding more triangles and flipping
if (e === start) {
while (q = hullPrev[e], orient2d(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1]) < 0) {
t = this._addTriangle(q, i, e, -1, hullTri[e], hullTri[q]);
-
this._legalize(t + 2);
-
hullTri[q] = t;
hullNext[e] = e; // mark as removed
-
hullSize--;
e = q;
}
- } // update the hull indices
+ }
-
+ // update the hull indices
this._hullStart = hullPrev[i] = e;
hullNext[e] = hullPrev[n] = i;
- hullNext[i] = n; // save the two new edges in the hash table
+ hullNext[i] = n;
+ // save the two new edges in the hash table
hullHash[this._hashKey(x, y)] = i;
hullHash[this._hashKey(coords[2 * e], coords[2 * e + 1])] = e;
}
-
this.hull = new Uint32Array(hullSize);
-
for (let i = 0, e = this._hullStart; i < hullSize; i++) {
this.hull[i] = e;
e = hullNext[e];
- } // trim typed triangle mesh arrays
+ }
-
+ // trim typed triangle mesh arrays
this.triangles = this._triangles.subarray(0, this.trianglesLen);
this.halfedges = this._halfedges.subarray(0, this.trianglesLen);
}
-
_hashKey(x, y) {
return Math.floor(pseudoAngle(x - this._cx, y - this._cy) * this._hashSize) % this._hashSize;
}
-
_legalize(a) {
const {
_triangles: triangles,
_halfedges: halfedges,
coords
} = this;
let i = 0;
- let ar = 0; // recursion eliminated with a fixed-size stack
+ let ar = 0;
+ // recursion eliminated with a fixed-size stack
while (true) {
const b = halfedges[a];
+
/* if the pair of triangles doesn't satisfy the Delaunay condition
* (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
* then do the same check/flip recursively for the new pair of triangles
*
* pl pl
@@ -34105,103 +30902,87 @@
* \ || / \ /
* ar\ || /br b\ /br
* \||/ \ /
* pr pr
*/
-
const a0 = a - a % 3;
ar = a0 + (a + 2) % 3;
-
if (b === -1) {
// convex hull edge
if (i === 0) break;
a = EDGE_STACK[--i];
continue;
}
-
const b0 = b - b % 3;
const al = a0 + (a + 1) % 3;
const bl = b0 + (b + 2) % 3;
const p0 = triangles[ar];
const pr = triangles[a];
const pl = triangles[al];
const p1 = triangles[bl];
const illegal = inCircle(coords[2 * p0], coords[2 * p0 + 1], coords[2 * pr], coords[2 * pr + 1], coords[2 * pl], coords[2 * pl + 1], coords[2 * p1], coords[2 * p1 + 1]);
-
if (illegal) {
triangles[a] = p1;
triangles[b] = p0;
- const hbl = halfedges[bl]; // edge swapped on the other side of the hull (rare); fix the halfedge reference
+ const hbl = halfedges[bl];
+ // edge swapped on the other side of the hull (rare); fix the halfedge reference
if (hbl === -1) {
let e = this._hullStart;
-
do {
if (this._hullTri[e] === bl) {
this._hullTri[e] = a;
break;
}
-
e = this._hullPrev[e];
} while (e !== this._hullStart);
}
-
this._link(a, hbl);
-
this._link(b, halfedges[ar]);
-
this._link(ar, bl);
+ const br = b0 + (b + 1) % 3;
- const br = b0 + (b + 1) % 3; // don't worry about hitting the cap: it can only happen on extremely degenerate input
-
+ // don't worry about hitting the cap: it can only happen on extremely degenerate input
if (i < EDGE_STACK.length) {
EDGE_STACK[i++] = br;
}
} else {
if (i === 0) break;
a = EDGE_STACK[--i];
}
}
-
return ar;
}
-
_link(a, b) {
this._halfedges[a] = b;
if (b !== -1) this._halfedges[b] = a;
- } // add a new triangle given vertex indices and adjacent half-edge ids
+ }
-
+ // add a new triangle given vertex indices and adjacent half-edge ids
_addTriangle(i0, i1, i2, a, b, c) {
const t = this.trianglesLen;
this._triangles[t] = i0;
this._triangles[t + 1] = i1;
this._triangles[t + 2] = i2;
-
this._link(t, a);
-
this._link(t + 1, b);
-
this._link(t + 2, c);
-
this.trianglesLen += 3;
return t;
}
+ }
- } // monotonically increases with real angle, but doesn't need expensive trigonometry
-
+ // monotonically increases with real angle, but doesn't need expensive trigonometry
function pseudoAngle(dx, dy) {
const p = dx / (Math.abs(dx) + Math.abs(dy));
return (dy > 0 ? 3 - p : 1 + p) / 4; // [0..1]
}
-
function dist(ax, ay, bx, by) {
const dx = ax - bx;
const dy = ay - by;
return dx * dx + dy * dy;
}
-
function inCircle(ax, ay, bx, by, cx, cy, px, py) {
const dx = ax - px;
const dy = ay - py;
const ex = bx - px;
const ey = by - py;
@@ -34210,11 +30991,10 @@
const ap = dx * dx + dy * dy;
const bp = ex * ex + ey * ey;
const cp = fx * fx + fy * fy;
return dx * (ey * cp - bp * fy) - dy * (ex * cp - bp * fx) + ap * (ex * fy - ey * fx) < 0;
}
-
function circumradius(ax, ay, bx, by, cx, cy) {
const dx = bx - ax;
const dy = by - ay;
const ex = cx - ax;
const ey = cy - ay;
@@ -34223,11 +31003,10 @@
const d = 0.5 / (dx * ey - dy * ex);
const x = (ey * bl - dy * cl) * d;
const y = (dx * cl - ex * bl) * d;
return x * x + y * y;
}
-
function circumcenter(ax, ay, bx, by, cx, cy) {
const dx = bx - ax;
const dy = by - ay;
const ex = cx - ax;
const ey = cy - ay;
@@ -34239,20 +31018,17 @@
return {
x,
y
};
}
-
function quicksort(ids, dists, left, right) {
if (right - left <= 20) {
for (let i = left + 1; i <= right; i++) {
const temp = ids[i];
const tempDist = dists[temp];
let j = i - 1;
-
while (j >= left && dists[ids[j]] > tempDist) ids[j + 1] = ids[j--];
-
ids[j + 1] = temp;
}
} else {
const median = left + right >> 1;
let i = left + 1;
@@ -34261,147 +31037,123 @@
if (dists[ids[left]] > dists[ids[right]]) swap(ids, left, right);
if (dists[ids[i]] > dists[ids[right]]) swap(ids, i, right);
if (dists[ids[left]] > dists[ids[i]]) swap(ids, left, i);
const temp = ids[i];
const tempDist = dists[temp];
-
while (true) {
do i++; while (dists[ids[i]] < tempDist);
-
do j--; while (dists[ids[j]] > tempDist);
-
if (j < i) break;
swap(ids, i, j);
}
-
ids[left + 1] = ids[j];
ids[j] = temp;
-
if (right - i + 1 >= j - left) {
quicksort(ids, dists, i, right);
quicksort(ids, dists, left, j - 1);
} else {
quicksort(ids, dists, left, j - 1);
quicksort(ids, dists, i, right);
}
}
}
-
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
-
function defaultGetX(p) {
return p[0];
}
-
function defaultGetY(p) {
return p[1];
}
const epsilon = 1e-6;
class Path {
constructor() {
- this._x0 = this._y0 = // start of current subpath
+ this._x0 = this._y0 =
+ // start of current subpath
this._x1 = this._y1 = null; // end of current subpath
-
this._ = "";
}
-
moveTo(x, y) {
- this._ += "M".concat(this._x0 = this._x1 = +x, ",").concat(this._y0 = this._y1 = +y);
+ this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`;
}
-
closePath() {
if (this._x1 !== null) {
this._x1 = this._x0, this._y1 = this._y0;
this._ += "Z";
}
}
-
lineTo(x, y) {
- this._ += "L".concat(this._x1 = +x, ",").concat(this._y1 = +y);
+ this._ += `L${this._x1 = +x},${this._y1 = +y}`;
}
-
arc(x, y, r) {
x = +x, y = +y, r = +r;
const x0 = x + r;
const y0 = y;
if (r < 0) throw new Error("negative radius");
- if (this._x1 === null) this._ += "M".concat(x0, ",").concat(y0);else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) this._ += "L" + x0 + "," + y0;
+ if (this._x1 === null) this._ += `M${x0},${y0}`;else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) this._ += "L" + x0 + "," + y0;
if (!r) return;
- this._ += "A".concat(r, ",").concat(r, ",0,1,1,").concat(x - r, ",").concat(y, "A").concat(r, ",").concat(r, ",0,1,1,").concat(this._x1 = x0, ",").concat(this._y1 = y0);
+ this._ += `A${r},${r},0,1,1,${x - r},${y}A${r},${r},0,1,1,${this._x1 = x0},${this._y1 = y0}`;
}
-
rect(x, y, w, h) {
- this._ += "M".concat(this._x0 = this._x1 = +x, ",").concat(this._y0 = this._y1 = +y, "h").concat(+w, "v").concat(+h, "h").concat(-w, "Z");
+ this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${+w}v${+h}h${-w}Z`;
}
-
value() {
return this._ || null;
}
-
}
class Polygon {
constructor() {
this._ = [];
}
-
moveTo(x, y) {
this._.push([x, y]);
}
-
closePath() {
this._.push(this._[0].slice());
}
-
lineTo(x, y) {
this._.push([x, y]);
}
-
value() {
return this._.length ? this._ : null;
}
-
}
- class Voronoi$1 {
+ let Voronoi$1 = class Voronoi {
constructor(delaunay) {
let [xmin, ymin, xmax, ymax] = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0, 0, 960, 500];
if (!((xmax = +xmax) >= (xmin = +xmin)) || !((ymax = +ymax) >= (ymin = +ymin))) throw new Error("invalid bounds");
this.delaunay = delaunay;
this._circumcenters = new Float64Array(delaunay.points.length * 2);
this.vectors = new Float64Array(delaunay.points.length * 2);
this.xmax = xmax, this.xmin = xmin;
this.ymax = ymax, this.ymin = ymin;
-
this._init();
}
-
update() {
this.delaunay.update();
-
this._init();
-
return this;
}
-
_init() {
const {
delaunay: {
points,
hull,
triangles
},
vectors
- } = this; // Compute circumcenters.
+ } = this;
+ let bx, by; // lazily computed barycenter of the hull
+ // Compute circumcenters.
const circumcenters = this.circumcenters = this._circumcenters.subarray(0, triangles.length / 3 * 2);
-
for (let i = 0, j = 0, n = triangles.length, x, y; i < n; i += 3, j += 2) {
const t1 = triangles[i] * 2;
const t2 = triangles[i + 1] * 2;
const t3 = triangles[i + 2] * 2;
const x1 = points[t1];
@@ -34413,56 +31165,50 @@
const dx = x2 - x1;
const dy = y2 - y1;
const ex = x3 - x1;
const ey = y3 - y1;
const ab = (dx * ey - dy * ex) * 2;
-
if (Math.abs(ab) < 1e-9) {
- // degenerate case (collinear diagram)
- // almost equal points (degenerate triangle)
- // the circumcenter is at the infinity, in a
- // direction that is:
- // 1. orthogonal to the halfedge.
- let a = 1e9; // 2. points away from the center; since the list of triangles starts
- // in the center, the first point of the first triangle
- // will be our reference
-
- const r = triangles[0] * 2;
- a *= Math.sign((points[r] - x1) * ey - (points[r + 1] - y1) * ex);
+ // For a degenerate triangle, the circumcenter is at the infinity, in a
+ // direction orthogonal to the halfedge and away from the “center” of
+ // the diagram <bx, by>, defined as the hull’s barycenter.
+ if (bx === undefined) {
+ bx = by = 0;
+ for (const i of hull) bx += points[i * 2], by += points[i * 2 + 1];
+ bx /= hull.length, by /= hull.length;
+ }
+ const a = 1e9 * Math.sign((bx - x1) * ey - (by - y1) * ex);
x = (x1 + x3) / 2 - a * ey;
y = (y1 + y3) / 2 + a * ex;
} else {
const d = 1 / ab;
const bl = dx * dx + dy * dy;
const cl = ex * ex + ey * ey;
x = x1 + (ey * bl - dy * cl) * d;
y = y1 + (dx * cl - ex * bl) * d;
}
-
circumcenters[j] = x;
circumcenters[j + 1] = y;
- } // Compute exterior cell rays.
+ }
-
+ // Compute exterior cell rays.
let h = hull[hull.length - 1];
let p0,
- p1 = h * 4;
+ p1 = h * 4;
let x0,
- x1 = points[2 * h];
+ x1 = points[2 * h];
let y0,
- y1 = points[2 * h + 1];
+ y1 = points[2 * h + 1];
vectors.fill(0);
-
for (let i = 0; i < hull.length; ++i) {
h = hull[i];
p0 = p1, x0 = x1, y0 = y1;
p1 = h * 4, x1 = points[2 * h], y1 = points[2 * h + 1];
vectors[p0 + 2] = vectors[p1] = y0 - y1;
vectors[p0 + 3] = vectors[p1 + 1] = x1 - x0;
}
}
-
render(context) {
const buffer = context == null ? context = new Path() : undefined;
const {
delaunay: {
halfedges,
@@ -34471,125 +31217,99 @@
},
circumcenters,
vectors
} = this;
if (hull.length <= 1) return null;
-
for (let i = 0, n = halfedges.length; i < n; ++i) {
const j = halfedges[i];
if (j < i) continue;
const ti = Math.floor(i / 3) * 2;
const tj = Math.floor(j / 3) * 2;
const xi = circumcenters[ti];
const yi = circumcenters[ti + 1];
const xj = circumcenters[tj];
const yj = circumcenters[tj + 1];
-
this._renderSegment(xi, yi, xj, yj, context);
}
-
let h0,
- h1 = hull[hull.length - 1];
-
+ h1 = hull[hull.length - 1];
for (let i = 0; i < hull.length; ++i) {
h0 = h1, h1 = hull[i];
const t = Math.floor(inedges[h1] / 3) * 2;
const x = circumcenters[t];
const y = circumcenters[t + 1];
const v = h0 * 4;
-
const p = this._project(x, y, vectors[v + 2], vectors[v + 3]);
-
if (p) this._renderSegment(x, y, p[0], p[1], context);
}
-
return buffer && buffer.value();
}
-
renderBounds(context) {
const buffer = context == null ? context = new Path() : undefined;
context.rect(this.xmin, this.ymin, this.xmax - this.xmin, this.ymax - this.ymin);
return buffer && buffer.value();
}
-
renderCell(i, context) {
const buffer = context == null ? context = new Path() : undefined;
-
const points = this._clip(i);
-
if (points === null || !points.length) return;
context.moveTo(points[0], points[1]);
let n = points.length;
-
while (points[0] === points[n - 2] && points[1] === points[n - 1] && n > 1) n -= 2;
-
for (let i = 2; i < n; i += 2) {
if (points[i] !== points[i - 2] || points[i + 1] !== points[i - 1]) context.lineTo(points[i], points[i + 1]);
}
-
context.closePath();
return buffer && buffer.value();
}
-
*cellPolygons() {
const {
delaunay: {
points
}
} = this;
-
for (let i = 0, n = points.length / 2; i < n; ++i) {
const cell = this.cellPolygon(i);
if (cell) cell.index = i, yield cell;
}
}
-
cellPolygon(i) {
const polygon = new Polygon();
this.renderCell(i, polygon);
return polygon.value();
}
-
_renderSegment(x0, y0, x1, y1, context) {
let S;
-
const c0 = this._regioncode(x0, y0);
-
const c1 = this._regioncode(x1, y1);
-
if (c0 === 0 && c1 === 0) {
context.moveTo(x0, y0);
context.lineTo(x1, y1);
} else if (S = this._clipSegment(x0, y0, x1, y1, c0, c1)) {
context.moveTo(S[0], S[1]);
context.lineTo(S[2], S[3]);
}
}
-
contains(i, x, y) {
if ((x = +x, x !== x) || (y = +y, y !== y)) return false;
return this.delaunay._step(i, x, y) === i;
}
-
*neighbors(i) {
const ci = this._clip(i);
-
if (ci) for (const j of this.delaunay.neighbors(i)) {
- const cj = this._clip(j); // find the common edge
-
-
+ const cj = this._clip(j);
+ // find the common edge
if (cj) loop: for (let ai = 0, li = ci.length; ai < li; ai += 2) {
for (let aj = 0, lj = cj.length; aj < lj; aj += 2) {
- if (ci[ai] == cj[aj] && ci[ai + 1] == cj[aj + 1] && ci[(ai + 2) % li] == cj[(aj + lj - 2) % lj] && ci[(ai + 3) % li] == cj[(aj + lj - 1) % lj]) {
+ if (ci[ai] === cj[aj] && ci[ai + 1] === cj[aj + 1] && ci[(ai + 2) % li] === cj[(aj + lj - 2) % lj] && ci[(ai + 3) % li] === cj[(aj + lj - 1) % lj]) {
yield j;
break loop;
}
}
}
}
}
-
_cell(i) {
const {
circumcenters,
delaunay: {
inedges,
@@ -34597,396 +31317,338 @@
triangles
}
} = this;
const e0 = inedges[i];
if (e0 === -1) return null; // coincident point
-
const points = [];
let e = e0;
-
do {
const t = Math.floor(e / 3);
points.push(circumcenters[t * 2], circumcenters[t * 2 + 1]);
e = e % 3 === 2 ? e - 2 : e + 1;
if (triangles[e] !== i) break; // bad triangulation
-
e = halfedges[e];
} while (e !== e0 && e !== -1);
-
return points;
}
-
_clip(i) {
// degenerate case (1 valid point: return the box)
if (i === 0 && this.delaunay.hull.length === 1) {
return [this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax, this.xmin, this.ymin];
}
-
const points = this._cell(i);
-
if (points === null) return null;
const {
vectors: V
} = this;
const v = i * 4;
- return V[v] || V[v + 1] ? this._clipInfinite(i, points, V[v], V[v + 1], V[v + 2], V[v + 3]) : this._clipFinite(i, points);
+ return this._simplify(V[v] || V[v + 1] ? this._clipInfinite(i, points, V[v], V[v + 1], V[v + 2], V[v + 3]) : this._clipFinite(i, points));
}
-
_clipFinite(i, points) {
const n = points.length;
let P = null;
let x0,
- y0,
- x1 = points[n - 2],
- y1 = points[n - 1];
-
+ y0,
+ x1 = points[n - 2],
+ y1 = points[n - 1];
let c0,
- c1 = this._regioncode(x1, y1);
-
+ c1 = this._regioncode(x1, y1);
let e0,
- e1 = 0;
-
+ e1 = 0;
for (let j = 0; j < n; j += 2) {
x0 = x1, y0 = y1, x1 = points[j], y1 = points[j + 1];
c0 = c1, c1 = this._regioncode(x1, y1);
-
if (c0 === 0 && c1 === 0) {
e0 = e1, e1 = 0;
if (P) P.push(x1, y1);else P = [x1, y1];
} else {
let S, sx0, sy0, sx1, sy1;
-
if (c0 === 0) {
if ((S = this._clipSegment(x0, y0, x1, y1, c0, c1)) === null) continue;
[sx0, sy0, sx1, sy1] = S;
} else {
if ((S = this._clipSegment(x1, y1, x0, y0, c1, c0)) === null) continue;
[sx1, sy1, sx0, sy0] = S;
e0 = e1, e1 = this._edgecode(sx0, sy0);
if (e0 && e1) this._edge(i, e0, e1, P, P.length);
if (P) P.push(sx0, sy0);else P = [sx0, sy0];
}
-
e0 = e1, e1 = this._edgecode(sx1, sy1);
if (e0 && e1) this._edge(i, e0, e1, P, P.length);
if (P) P.push(sx1, sy1);else P = [sx1, sy1];
}
}
-
if (P) {
e0 = e1, e1 = this._edgecode(P[0], P[1]);
if (e0 && e1) this._edge(i, e0, e1, P, P.length);
} else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) {
return [this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax, this.xmin, this.ymin];
}
-
return P;
}
-
_clipSegment(x0, y0, x1, y1, c0, c1) {
+ // for more robustness, always consider the segment in the same order
+ const flip = c0 < c1;
+ if (flip) [x0, y0, x1, y1, c0, c1] = [x1, y1, x0, y0, c1, c0];
while (true) {
- if (c0 === 0 && c1 === 0) return [x0, y0, x1, y1];
+ if (c0 === 0 && c1 === 0) return flip ? [x1, y1, x0, y0] : [x0, y0, x1, y1];
if (c0 & c1) return null;
let x,
- y,
- c = c0 || c1;
+ y,
+ c = c0 || c1;
if (c & 0b1000) x = x0 + (x1 - x0) * (this.ymax - y0) / (y1 - y0), y = this.ymax;else if (c & 0b0100) x = x0 + (x1 - x0) * (this.ymin - y0) / (y1 - y0), y = this.ymin;else if (c & 0b0010) y = y0 + (y1 - y0) * (this.xmax - x0) / (x1 - x0), x = this.xmax;else y = y0 + (y1 - y0) * (this.xmin - x0) / (x1 - x0), x = this.xmin;
if (c0) x0 = x, y0 = y, c0 = this._regioncode(x0, y0);else x1 = x, y1 = y, c1 = this._regioncode(x1, y1);
}
}
-
_clipInfinite(i, points, vx0, vy0, vxn, vyn) {
let P = Array.from(points),
- p;
+ p;
if (p = this._project(P[0], P[1], vx0, vy0)) P.unshift(p[0], p[1]);
if (p = this._project(P[P.length - 2], P[P.length - 1], vxn, vyn)) P.push(p[0], p[1]);
-
if (P = this._clipFinite(i, P)) {
for (let j = 0, n = P.length, c0, c1 = this._edgecode(P[n - 2], P[n - 1]); j < n; j += 2) {
c0 = c1, c1 = this._edgecode(P[j], P[j + 1]);
if (c0 && c1) j = this._edge(i, c0, c1, P, j), n = P.length;
}
} else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) {
P = [this.xmin, this.ymin, this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax];
}
-
return P;
}
-
_edge(i, e0, e1, P, j) {
while (e0 !== e1) {
let x, y;
-
switch (e0) {
case 0b0101:
e0 = 0b0100;
continue;
// top-left
-
case 0b0100:
e0 = 0b0110, x = this.xmax, y = this.ymin;
break;
// top
-
case 0b0110:
e0 = 0b0010;
continue;
// top-right
-
case 0b0010:
e0 = 0b1010, x = this.xmax, y = this.ymax;
break;
// right
-
case 0b1010:
e0 = 0b1000;
continue;
// bottom-right
-
case 0b1000:
e0 = 0b1001, x = this.xmin, y = this.ymax;
break;
// bottom
-
case 0b1001:
e0 = 0b0001;
continue;
// bottom-left
-
case 0b0001:
e0 = 0b0101, x = this.xmin, y = this.ymin;
break;
// left
- } // Note: this implicitly checks for out of bounds: if P[j] or P[j+1] are
+ }
+ // Note: this implicitly checks for out of bounds: if P[j] or P[j+1] are
// undefined, the conditional statement will be executed.
-
-
if ((P[j] !== x || P[j + 1] !== y) && this.contains(i, x, y)) {
P.splice(j, 0, x, y), j += 2;
}
}
-
- if (P.length > 4) {
- for (let i = 0; i < P.length; i += 2) {
- const j = (i + 2) % P.length,
- k = (i + 4) % P.length;
- if (P[i] === P[j] && P[j] === P[k] || P[i + 1] === P[j + 1] && P[j + 1] === P[k + 1]) P.splice(j, 2), i -= 2;
- }
- }
-
return j;
}
-
_project(x0, y0, vx, vy) {
let t = Infinity,
- c,
- x,
- y;
-
+ c,
+ x,
+ y;
if (vy < 0) {
// top
if (y0 <= this.ymin) return null;
if ((c = (this.ymin - y0) / vy) < t) y = this.ymin, x = x0 + (t = c) * vx;
} else if (vy > 0) {
// bottom
if (y0 >= this.ymax) return null;
if ((c = (this.ymax - y0) / vy) < t) y = this.ymax, x = x0 + (t = c) * vx;
}
-
if (vx > 0) {
// right
if (x0 >= this.xmax) return null;
if ((c = (this.xmax - x0) / vx) < t) x = this.xmax, y = y0 + (t = c) * vy;
} else if (vx < 0) {
// left
if (x0 <= this.xmin) return null;
if ((c = (this.xmin - x0) / vx) < t) x = this.xmin, y = y0 + (t = c) * vy;
}
-
return [x, y];
}
-
_edgecode(x, y) {
return (x === this.xmin ? 0b0001 : x === this.xmax ? 0b0010 : 0b0000) | (y === this.ymin ? 0b0100 : y === this.ymax ? 0b1000 : 0b0000);
}
-
_regioncode(x, y) {
return (x < this.xmin ? 0b0001 : x > this.xmax ? 0b0010 : 0b0000) | (y < this.ymin ? 0b0100 : y > this.ymax ? 0b1000 : 0b0000);
}
+ _simplify(P) {
+ if (P && P.length > 4) {
+ for (let i = 0; i < P.length; i += 2) {
+ const j = (i + 2) % P.length,
+ k = (i + 4) % P.length;
+ if (P[i] === P[j] && P[j] === P[k] || P[i + 1] === P[j + 1] && P[j + 1] === P[k + 1]) {
+ P.splice(j, 2), i -= 2;
+ }
+ }
+ if (!P.length) P = null;
+ }
+ return P;
+ }
+ };
- }
-
const tau = 2 * Math.PI,
- pow = Math.pow;
-
+ pow = Math.pow;
function pointX(p) {
return p[0];
}
-
function pointY(p) {
return p[1];
- } // A triangulation is collinear if all its triangles have a non-null area
+ }
-
+ // A triangulation is collinear if all its triangles have a non-null area
function collinear(d) {
const {
triangles,
coords
} = d;
-
for (let i = 0; i < triangles.length; i += 3) {
const a = 2 * triangles[i],
- b = 2 * triangles[i + 1],
- c = 2 * triangles[i + 2],
- cross = (coords[c] - coords[a]) * (coords[b + 1] - coords[a + 1]) - (coords[b] - coords[a]) * (coords[c + 1] - coords[a + 1]);
+ b = 2 * triangles[i + 1],
+ c = 2 * triangles[i + 2],
+ cross = (coords[c] - coords[a]) * (coords[b + 1] - coords[a + 1]) - (coords[b] - coords[a]) * (coords[c + 1] - coords[a + 1]);
if (cross > 1e-10) return false;
}
-
return true;
}
-
function jitter(x, y, r) {
return [x + Math.sin(x + y) * r, y + Math.cos(x - y) * r];
}
-
class Delaunay {
static from(points) {
let fx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : pointX;
let fy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : pointY;
let that = arguments.length > 3 ? arguments[3] : undefined;
return new Delaunay("length" in points ? flatArray(points, fx, fy, that) : Float64Array.from(flatIterable(points, fx, fy, that)));
}
-
constructor(points) {
this._delaunator = new Delaunator(points);
this.inedges = new Int32Array(points.length / 2);
this._hullIndex = new Int32Array(points.length / 2);
this.points = this._delaunator.coords;
-
this._init();
}
-
update() {
this._delaunator.update();
-
this._init();
-
return this;
}
-
_init() {
const d = this._delaunator,
- points = this.points; // check for collinear
+ points = this.points;
+ // check for collinear
if (d.hull && d.hull.length > 2 && collinear(d)) {
this.collinear = Int32Array.from({
length: points.length / 2
}, (_, i) => i).sort((i, j) => points[2 * i] - points[2 * j] || points[2 * i + 1] - points[2 * j + 1]); // for exact neighbors
-
const e = this.collinear[0],
- f = this.collinear[this.collinear.length - 1],
- bounds = [points[2 * e], points[2 * e + 1], points[2 * f], points[2 * f + 1]],
- r = 1e-8 * Math.hypot(bounds[3] - bounds[1], bounds[2] - bounds[0]);
-
+ f = this.collinear[this.collinear.length - 1],
+ bounds = [points[2 * e], points[2 * e + 1], points[2 * f], points[2 * f + 1]],
+ r = 1e-8 * Math.hypot(bounds[3] - bounds[1], bounds[2] - bounds[0]);
for (let i = 0, n = points.length / 2; i < n; ++i) {
const p = jitter(points[2 * i], points[2 * i + 1], r);
points[2 * i] = p[0];
points[2 * i + 1] = p[1];
}
-
this._delaunator = new Delaunator(points);
} else {
delete this.collinear;
}
-
const halfedges = this.halfedges = this._delaunator.halfedges;
const hull = this.hull = this._delaunator.hull;
const triangles = this.triangles = this._delaunator.triangles;
const inedges = this.inedges.fill(-1);
+ const hullIndex = this._hullIndex.fill(-1);
- const hullIndex = this._hullIndex.fill(-1); // Compute an index from each point to an (arbitrary) incoming halfedge
+ // Compute an index from each point to an (arbitrary) incoming halfedge
// Used to give the first neighbor of each point; for this reason,
// on the hull we give priority to exterior halfedges
-
-
for (let e = 0, n = halfedges.length; e < n; ++e) {
const p = triangles[e % 3 === 2 ? e - 2 : e + 1];
if (halfedges[e] === -1 || inedges[p] === -1) inedges[p] = e;
}
-
for (let i = 0, n = hull.length; i < n; ++i) {
hullIndex[hull[i]] = i;
- } // degenerate case: 1 or 2 (distinct) points
+ }
-
+ // degenerate case: 1 or 2 (distinct) points
if (hull.length <= 2 && hull.length > 0) {
this.triangles = new Int32Array(3).fill(-1);
this.halfedges = new Int32Array(3).fill(-1);
this.triangles[0] = hull[0];
inedges[hull[0]] = 1;
-
if (hull.length === 2) {
inedges[hull[1]] = 0;
this.triangles[1] = hull[1];
this.triangles[2] = hull[1];
}
}
}
-
voronoi(bounds) {
return new Voronoi$1(this, bounds);
}
-
*neighbors(i) {
const {
inedges,
hull,
_hullIndex,
halfedges,
triangles,
collinear
- } = this; // degenerate case with several collinear points
+ } = this;
+ // degenerate case with several collinear points
if (collinear) {
const l = collinear.indexOf(i);
if (l > 0) yield collinear[l - 1];
if (l < collinear.length - 1) yield collinear[l + 1];
return;
}
-
const e0 = inedges[i];
if (e0 === -1) return; // coincident point
-
let e = e0,
- p0 = -1;
-
+ p0 = -1;
do {
yield p0 = triangles[e];
e = e % 3 === 2 ? e - 2 : e + 1;
if (triangles[e] !== i) return; // bad triangulation
-
e = halfedges[e];
-
if (e === -1) {
const p = hull[(_hullIndex[i] + 1) % hull.length];
if (p !== p0) yield p;
return;
}
} while (e !== e0);
}
-
find(x, y) {
let i = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
if ((x = +x, x !== x) || (y = +y, y !== y)) return -1;
const i0 = i;
let c;
-
while ((c = this._step(i, x, y)) >= 0 && c !== i && c !== i0) i = c;
-
return c;
}
-
_step(i, x, y) {
const {
inedges,
hull,
_hullIndex,
@@ -34997,98 +31659,81 @@
if (inedges[i] === -1 || !points.length) return (i + 1) % (points.length >> 1);
let c = i;
let dc = pow(x - points[i * 2], 2) + pow(y - points[i * 2 + 1], 2);
const e0 = inedges[i];
let e = e0;
-
do {
let t = triangles[e];
const dt = pow(x - points[t * 2], 2) + pow(y - points[t * 2 + 1], 2);
if (dt < dc) dc = dt, c = t;
e = e % 3 === 2 ? e - 2 : e + 1;
if (triangles[e] !== i) break; // bad triangulation
-
e = halfedges[e];
-
if (e === -1) {
e = hull[(_hullIndex[i] + 1) % hull.length];
-
if (e !== t) {
if (pow(x - points[e * 2], 2) + pow(y - points[e * 2 + 1], 2) < dc) return e;
}
-
break;
}
} while (e !== e0);
-
return c;
}
-
render(context) {
const buffer = context == null ? context = new Path() : undefined;
const {
points,
halfedges,
triangles
} = this;
-
for (let i = 0, n = halfedges.length; i < n; ++i) {
const j = halfedges[i];
if (j < i) continue;
const ti = triangles[i] * 2;
const tj = triangles[j] * 2;
context.moveTo(points[ti], points[ti + 1]);
context.lineTo(points[tj], points[tj + 1]);
}
-
this.renderHull(context);
return buffer && buffer.value();
}
-
renderPoints(context, r) {
if (r === undefined && (!context || typeof context.moveTo !== "function")) r = context, context = null;
r = r == undefined ? 2 : +r;
const buffer = context == null ? context = new Path() : undefined;
const {
points
} = this;
-
for (let i = 0, n = points.length; i < n; i += 2) {
const x = points[i],
- y = points[i + 1];
+ y = points[i + 1];
context.moveTo(x + r, y);
context.arc(x, y, r, 0, tau);
}
-
return buffer && buffer.value();
}
-
renderHull(context) {
const buffer = context == null ? context = new Path() : undefined;
const {
hull,
points
} = this;
const h = hull[0] * 2,
- n = hull.length;
+ n = hull.length;
context.moveTo(points[h], points[h + 1]);
-
for (let i = 1; i < n; ++i) {
const h = 2 * hull[i];
context.lineTo(points[h], points[h + 1]);
}
-
context.closePath();
return buffer && buffer.value();
}
-
hullPolygon() {
const polygon = new Polygon();
this.renderHull(polygon);
return polygon.value();
}
-
renderTriangle(i, context) {
const buffer = context == null ? context = new Path() : undefined;
const {
points,
triangles
@@ -35100,56 +31745,46 @@
context.lineTo(points[t1], points[t1 + 1]);
context.lineTo(points[t2], points[t2 + 1]);
context.closePath();
return buffer && buffer.value();
}
-
*trianglePolygons() {
const {
triangles
} = this;
-
for (let i = 0, n = triangles.length / 3; i < n; ++i) {
yield this.trianglePolygon(i);
}
}
-
trianglePolygon(i) {
const polygon = new Polygon();
this.renderTriangle(i, polygon);
return polygon.value();
}
-
}
-
function flatArray(points, fx, fy, that) {
const n = points.length;
const array = new Float64Array(n * 2);
-
for (let i = 0; i < n; ++i) {
const p = points[i];
array[i * 2] = fx.call(that, p, i, points);
array[i * 2 + 1] = fy.call(that, p, i, points);
}
-
return array;
}
-
function* flatIterable(points, fx, fy, that) {
let i = 0;
-
for (const p of points) {
yield fx.call(that, p, i, points);
yield fy.call(that, p, i, points);
++i;
}
}
function Voronoi(params) {
Transform.call(this, null, params);
}
-
Voronoi.Definition = {
'type': 'Voronoi',
'metadata': {
'modifies': true
},
@@ -35185,37 +31820,40 @@
};
const defaultExtent = [-1e5, -1e5, 1e5, 1e5];
inherits(Voronoi, Transform, {
transform(_, pulse) {
const as = _.as || 'path',
- data = pulse.source; // nothing to do if no data
+ data = pulse.source;
- if (!data || !data.length) return pulse; // configure and construct voronoi diagram
+ // nothing to do if no data
+ if (!data || !data.length) return pulse;
+ // configure and construct voronoi diagram
let s = _.size;
s = s ? [0, 0, s[0], s[1]] : (s = _.extent) ? [s[0][0], s[0][1], s[1][0], s[1][1]] : defaultExtent;
- const voronoi = this.value = Delaunay.from(data, _.x, _.y).voronoi(s); // map polygons to paths
+ const voronoi = this.value = Delaunay.from(data, _.x, _.y).voronoi(s);
+ // map polygons to paths
for (let i = 0, n = data.length; i < n; ++i) {
const polygon = voronoi.cellPolygon(i);
- data[i][as] = polygon ? toPathString(polygon) : null;
+ data[i][as] = polygon && !isPoint(polygon) ? toPathString(polygon) : null;
}
-
return pulse.reflow(_.modified()).modifies(as);
}
+ });
- }); // suppress duplicated end point vertices
-
+ // suppress duplicated end point vertices
function toPathString(p) {
const x = p[0][0],
- y = p[0][1];
+ y = p[0][1];
let n = p.length - 1;
-
for (; p[n][0] === x && p[n][1] === y; --n);
-
return 'M' + p.slice(0, n + 1).join('L') + 'Z';
}
+ function isPoint(p) {
+ return p.length === 2 && p[0][0] === p[1][0] && p[0][1] === p[1][1];
+ }
var voronoi = /*#__PURE__*/Object.freeze({
__proto__: null,
voronoi: Voronoi
});
@@ -35246,81 +31884,76 @@
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+
// Word cloud layout by Jason Davies, https://www.jasondavies.com/wordcloud/
// Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf
var cloudRadians = Math.PI / 180,
- cw = 1 << 11 >> 5,
- ch = 1 << 11;
-
+ cw = 1 << 11 >> 5,
+ ch = 1 << 11;
function cloud() {
var size = [256, 256],
- text,
- font,
- fontSize,
- fontStyle,
- fontWeight,
- rotate,
- padding,
- spiral = archimedeanSpiral,
- words = [],
- random = Math.random,
- cloud = {};
-
+ text,
+ font,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ rotate,
+ padding,
+ spiral = archimedeanSpiral,
+ words = [],
+ random = Math.random,
+ cloud = {};
cloud.layout = function () {
var contextAndRatio = getContext(domCanvas()),
- board = zeroArray((size[0] >> 5) * size[1]),
- bounds = null,
- n = words.length,
- i = -1,
- tags = [],
- data = words.map(d => ({
- text: text(d),
- font: font(d),
- style: fontStyle(d),
- weight: fontWeight(d),
- rotate: rotate(d),
- size: ~~(fontSize(d) + 1e-14),
- padding: padding(d),
- xoff: 0,
- yoff: 0,
- x1: 0,
- y1: 0,
- x0: 0,
- y0: 0,
- hasText: false,
- sprite: null,
- datum: d
- })).sort((a, b) => b.size - a.size);
-
+ board = zeroArray((size[0] >> 5) * size[1]),
+ bounds = null,
+ n = words.length,
+ i = -1,
+ tags = [],
+ data = words.map(d => ({
+ text: text(d),
+ font: font(d),
+ style: fontStyle(d),
+ weight: fontWeight(d),
+ rotate: rotate(d),
+ size: ~~(fontSize(d) + 1e-14),
+ padding: padding(d),
+ xoff: 0,
+ yoff: 0,
+ x1: 0,
+ y1: 0,
+ x0: 0,
+ y0: 0,
+ hasText: false,
+ sprite: null,
+ datum: d
+ })).sort((a, b) => b.size - a.size);
while (++i < n) {
var d = data[i];
d.x = size[0] * (random() + .5) >> 1;
d.y = size[1] * (random() + .5) >> 1;
cloudSprite(contextAndRatio, d, data, i);
-
if (d.hasText && place(board, d, bounds)) {
tags.push(d);
if (bounds) cloudBounds(bounds, d);else bounds = [{
x: d.x + d.x0,
y: d.y + d.y0
}, {
x: d.x + d.x1,
y: d.y + d.y1
- }]; // Temporary hack
-
+ }];
+ // Temporary hack
d.x -= size[0] >> 1;
d.y -= size[1] >> 1;
}
}
-
return tags;
};
-
function getContext(canvas) {
canvas.width = canvas.height = 1;
var ratio = Math.sqrt(canvas.getContext('2d').getImageData(0, 0, 1, 1).data.length >> 2);
canvas.width = (cw << 5) / ratio;
canvas.height = ch / ratio;
@@ -35330,219 +31963,193 @@
return {
context: context,
ratio: ratio
};
}
-
function place(board, tag, bounds) {
var startX = tag.x,
- startY = tag.y,
- maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]),
- s = spiral(size),
- dt = random() < .5 ? 1 : -1,
- t = -dt,
- dxdy,
- dx,
- dy;
-
+ startY = tag.y,
+ maxDelta = Math.hypot(size[0], size[1]),
+ s = spiral(size),
+ dt = random() < .5 ? 1 : -1,
+ t = -dt,
+ dxdy,
+ dx,
+ dy;
while (dxdy = s(t += dt)) {
dx = ~~dxdy[0];
dy = ~~dxdy[1];
if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break;
tag.x = startX + dx;
tag.y = startY + dy;
- if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; // TODO only check for collisions within current bounds.
-
+ if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue;
+ // TODO only check for collisions within current bounds.
if (!bounds || !cloudCollide(tag, board, size[0])) {
if (!bounds || collideRects(tag, bounds)) {
var sprite = tag.sprite,
- w = tag.width >> 5,
- sw = size[0] >> 5,
- lx = tag.x - (w << 4),
- sx = lx & 0x7f,
- msx = 32 - sx,
- h = tag.y1 - tag.y0,
- x = (tag.y + tag.y0) * sw + (lx >> 5),
- last;
-
+ w = tag.width >> 5,
+ sw = size[0] >> 5,
+ lx = tag.x - (w << 4),
+ sx = lx & 0x7f,
+ msx = 32 - sx,
+ h = tag.y1 - tag.y0,
+ x = (tag.y + tag.y0) * sw + (lx >> 5),
+ last;
for (var j = 0; j < h; j++) {
last = 0;
-
for (var i = 0; i <= w; i++) {
board[x + i] |= last << msx | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
}
-
x += sw;
}
-
tag.sprite = null;
return true;
}
}
}
-
return false;
}
-
cloud.words = function (_) {
if (arguments.length) {
words = _;
return cloud;
} else {
return words;
}
};
-
cloud.size = function (_) {
if (arguments.length) {
size = [+_[0], +_[1]];
return cloud;
} else {
return size;
}
};
-
cloud.font = function (_) {
if (arguments.length) {
font = functor(_);
return cloud;
} else {
return font;
}
};
-
cloud.fontStyle = function (_) {
if (arguments.length) {
fontStyle = functor(_);
return cloud;
} else {
return fontStyle;
}
};
-
cloud.fontWeight = function (_) {
if (arguments.length) {
fontWeight = functor(_);
return cloud;
} else {
return fontWeight;
}
};
-
cloud.rotate = function (_) {
if (arguments.length) {
rotate = functor(_);
return cloud;
} else {
return rotate;
}
};
-
cloud.text = function (_) {
if (arguments.length) {
text = functor(_);
return cloud;
} else {
return text;
}
};
-
cloud.spiral = function (_) {
if (arguments.length) {
spiral = spirals[_] || _;
return cloud;
} else {
return spiral;
}
};
-
cloud.fontSize = function (_) {
if (arguments.length) {
fontSize = functor(_);
return cloud;
} else {
return fontSize;
}
};
-
cloud.padding = function (_) {
if (arguments.length) {
padding = functor(_);
return cloud;
} else {
return padding;
}
};
-
cloud.random = function (_) {
if (arguments.length) {
random = _;
return cloud;
} else {
return random;
}
};
-
return cloud;
- } // Fetches a monochrome sprite bitmap for the specified text.
- // Load in batches for speed.
+ }
-
+ // Fetches a monochrome sprite bitmap for the specified text.
+ // Load in batches for speed.
function cloudSprite(contextAndRatio, d, data, di) {
if (d.sprite) return;
var c = contextAndRatio.context,
- ratio = contextAndRatio.ratio;
+ ratio = contextAndRatio.ratio;
c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
var x = 0,
- y = 0,
- maxh = 0,
- n = data.length,
- w,
- w32,
- h,
- i,
- j;
+ y = 0,
+ maxh = 0,
+ n = data.length,
+ w,
+ w32,
+ h,
+ i,
+ j;
--di;
-
while (++di < n) {
d = data[di];
c.save();
c.font = d.style + ' ' + d.weight + ' ' + ~~((d.size + 1) / ratio) + 'px ' + d.font;
w = c.measureText(d.text + 'm').width * ratio;
h = d.size << 1;
-
if (d.rotate) {
var sr = Math.sin(d.rotate * cloudRadians),
- cr = Math.cos(d.rotate * cloudRadians),
- wcr = w * cr,
- wsr = w * sr,
- hcr = h * cr,
- hsr = h * sr;
+ cr = Math.cos(d.rotate * cloudRadians),
+ wcr = w * cr,
+ wsr = w * sr,
+ hcr = h * cr,
+ hsr = h * sr;
w = Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f >> 5 << 5;
h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
} else {
w = w + 0x1f >> 5 << 5;
}
-
if (h > maxh) maxh = h;
-
if (x + w >= cw << 5) {
x = 0;
y += maxh;
maxh = 0;
}
-
if (y + h >= ch) break;
c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
if (d.rotate) c.rotate(d.rotate * cloudRadians);
c.fillText(d.text, 0, 0);
-
if (d.padding) {
c.lineWidth = 2 * d.padding;
c.strokeText(d.text, 0, 0);
}
-
c.restore();
d.width = w;
d.height = h;
d.xoff = x;
d.yoff = y;
@@ -35551,152 +32158,128 @@
d.x0 = -d.x1;
d.y0 = -d.y1;
d.hasText = true;
x += w;
}
-
var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
- sprite = [];
-
+ sprite = [];
while (--di >= 0) {
d = data[di];
if (!d.hasText) continue;
w = d.width;
w32 = w >> 5;
- h = d.y1 - d.y0; // Zero the buffer
-
+ h = d.y1 - d.y0;
+ // Zero the buffer
for (i = 0; i < h * w32; i++) sprite[i] = 0;
-
x = d.xoff;
if (x == null) return;
y = d.yoff;
var seen = 0,
- seenRow = -1;
-
+ seenRow = -1;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
var k = w32 * j + (i >> 5),
- m = pixels[(y + j) * (cw << 5) + (x + i) << 2] ? 1 << 31 - i % 32 : 0;
+ m = pixels[(y + j) * (cw << 5) + (x + i) << 2] ? 1 << 31 - i % 32 : 0;
sprite[k] |= m;
seen |= m;
}
-
if (seen) seenRow = j;else {
d.y0++;
h--;
j--;
y++;
}
}
-
d.y1 = d.y0 + seenRow;
d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
}
- } // Use mask-based collision detection.
+ }
-
+ // Use mask-based collision detection.
function cloudCollide(tag, board, sw) {
sw >>= 5;
var sprite = tag.sprite,
- w = tag.width >> 5,
- lx = tag.x - (w << 4),
- sx = lx & 0x7f,
- msx = 32 - sx,
- h = tag.y1 - tag.y0,
- x = (tag.y + tag.y0) * sw + (lx >> 5),
- last;
-
+ w = tag.width >> 5,
+ lx = tag.x - (w << 4),
+ sx = lx & 0x7f,
+ msx = 32 - sx,
+ h = tag.y1 - tag.y0,
+ x = (tag.y + tag.y0) * sw + (lx >> 5),
+ last;
for (var j = 0; j < h; j++) {
last = 0;
-
for (var i = 0; i <= w; i++) {
if ((last << msx | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) & board[x + i]) return true;
}
-
x += sw;
}
-
return false;
}
-
function cloudBounds(bounds, d) {
var b0 = bounds[0],
- b1 = bounds[1];
+ b1 = bounds[1];
if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0;
if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0;
if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1;
if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1;
}
-
function collideRects(a, b) {
return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y;
}
-
function archimedeanSpiral(size) {
var e = size[0] / size[1];
return function (t) {
return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)];
};
}
-
function rectangularSpiral(size) {
var dy = 4,
- dx = dy * size[0] / size[1],
- x = 0,
- y = 0;
+ dx = dy * size[0] / size[1],
+ x = 0,
+ y = 0;
return function (t) {
- var sign = t < 0 ? -1 : 1; // See triangular numbers: T_n = n * (n + 1) / 2.
-
+ var sign = t < 0 ? -1 : 1;
+ // See triangular numbers: T_n = n * (n + 1) / 2.
switch (Math.sqrt(1 + 4 * sign * t) - sign & 3) {
case 0:
x += dx;
break;
-
case 1:
y += dy;
break;
-
case 2:
x -= dx;
break;
-
default:
y -= dy;
break;
}
-
return [x, y];
};
- } // TODO reuse arrays?
+ }
-
+ // TODO reuse arrays?
function zeroArray(n) {
var a = [],
- i = -1;
-
+ i = -1;
while (++i < n) a[i] = 0;
-
return a;
}
-
function functor(d) {
return typeof d === 'function' ? d : function () {
return d;
};
}
-
var spirals = {
archimedean: archimedeanSpiral,
rectangular: rectangularSpiral
};
const Output = ['x', 'y', 'font', 'fontSize', 'fontStyle', 'fontWeight', 'angle'];
const Params$1 = ['text', 'font', 'rotate', 'fontSize', 'fontStyle', 'fontWeight'];
-
function Wordcloud(params) {
Transform.call(this, cloud(), params);
}
-
Wordcloud.Definition = {
'type': 'Wordcloud',
'metadata': {
'modifies': true
},
@@ -35757,45 +32340,41 @@
inherits(Wordcloud, Transform, {
transform(_, pulse) {
if (_.size && !(_.size[0] && _.size[1])) {
error('Wordcloud size dimensions must be non-zero.');
}
-
function modp(param) {
const p = _[param];
return isFunction(p) && pulse.modified(p.fields);
}
-
const mod = _.modified();
-
if (!(mod || pulse.changed(pulse.ADD_REM) || Params$1.some(modp))) return;
const data = pulse.materialize(pulse.SOURCE).source,
- layout = this.value,
- as = _.as || Output;
+ layout = this.value,
+ as = _.as || Output;
let fontSize = _.fontSize || 14,
- range;
- isFunction(fontSize) ? range = _.fontSizeRange : fontSize = constant$4(fontSize); // create font size scaling function as needed
+ range;
+ isFunction(fontSize) ? range = _.fontSizeRange : fontSize = constant$5(fontSize);
+ // create font size scaling function as needed
if (range) {
const fsize = fontSize,
- sizeScale = scale$4('sqrt')().domain(extent(data, fsize)).range(range);
-
+ sizeScale = scale$4('sqrt')().domain(extent(data, fsize)).range(range);
fontSize = x => sizeScale(fsize(x));
}
-
data.forEach(t => {
t[as[0]] = NaN;
t[as[1]] = NaN;
t[as[3]] = 0;
- }); // configure layout
+ });
+ // configure layout
const words = layout.words(data).text(_.text).size(_.size || [500, 500]).padding(_.padding || 1).spiral(_.spiral || 'archimedean').rotate(_.rotate || 0).font(_.font || 'sans-serif').fontStyle(_.fontStyle || 'normal').fontWeight(_.fontWeight || 'normal').fontSize(fontSize).random(exports.random).layout();
const size = layout.size(),
- dx = size[0] >> 1,
- dy = size[1] >> 1,
- n = words.length;
-
+ dx = size[0] >> 1,
+ dy = size[1] >> 1,
+ n = words.length;
for (let i = 0, w, t; i < n; ++i) {
w = words[i];
t = w.datum;
t[as[0]] = w.x + dx;
t[as[1]] = w.y + dy;
@@ -35803,178 +32382,149 @@
t[as[3]] = w.size;
t[as[4]] = w.style;
t[as[5]] = w.weight;
t[as[6]] = w.rotate;
}
-
return pulse.reflow(mod).modifies(as);
}
-
});
var wordcloud = /*#__PURE__*/Object.freeze({
__proto__: null,
wordcloud: Wordcloud
});
const array8 = n => new Uint8Array(n);
-
const array16 = n => new Uint16Array(n);
-
const array32 = n => new Uint32Array(n);
+
/**
* Maintains CrossFilter state.
*/
-
-
function Bitmaps() {
let width = 8,
- data = [],
- seen = array32(0),
- curr = array$1(0, width),
- prev = array$1(0, width);
+ data = [],
+ seen = array32(0),
+ curr = array$1(0, width),
+ prev = array$1(0, width);
return {
data: () => data,
seen: () => seen = lengthen(seen, data.length),
-
add(array) {
for (let i = 0, j = data.length, n = array.length, t; i < n; ++i) {
t = array[i];
t._index = j++;
data.push(t);
}
},
-
remove(num, map) {
// map: index -> boolean (true => remove)
const n = data.length,
- copy = Array(n - num),
- reindex = data; // reuse old data array for index map
+ copy = Array(n - num),
+ reindex = data; // reuse old data array for index map
+ let t, i, j;
- let t, i, j; // seek forward to first removal
-
+ // seek forward to first removal
for (i = 0; !map[i] && i < n; ++i) {
copy[i] = data[i];
reindex[i] = i;
- } // condense arrays
+ }
-
+ // condense arrays
for (j = i; i < n; ++i) {
t = data[i];
-
if (!map[i]) {
reindex[i] = j;
curr[j] = curr[i];
prev[j] = prev[i];
copy[j] = t;
t._index = j++;
} else {
reindex[i] = -1;
}
-
curr[i] = 0; // clear unused bits
}
-
data = copy;
return reindex;
},
-
size: () => data.length,
curr: () => curr,
prev: () => prev,
reset: k => prev[k] = curr[k],
all: () => width < 0x101 ? 0xff : width < 0x10001 ? 0xffff : 0xffffffff,
-
set(k, one) {
curr[k] |= one;
},
-
clear(k, one) {
curr[k] &= ~one;
},
-
resize(n, m) {
const k = curr.length;
-
if (n > k || m > width) {
width = Math.max(m, width);
curr = array$1(n, width, curr);
prev = array$1(n, width);
}
}
-
};
}
-
function lengthen(array, length, copy) {
if (array.length >= length) return array;
copy = copy || new array.constructor(length);
copy.set(array);
return copy;
}
-
function array$1(n, m, array) {
const copy = (m < 0x101 ? array8 : m < 0x10001 ? array16 : array32)(n);
if (array) copy.set(array);
return copy;
}
-
function Dimension(index, i, query) {
const bit = 1 << i;
return {
one: bit,
zero: ~bit,
range: query.slice(),
bisect: index.bisect,
index: index.index,
size: index.size,
-
onAdd(added, curr) {
const dim = this,
- range = dim.bisect(dim.range, added.value),
- idx = added.index,
- lo = range[0],
- hi = range[1],
- n1 = idx.length;
+ range = dim.bisect(dim.range, added.value),
+ idx = added.index,
+ lo = range[0],
+ hi = range[1],
+ n1 = idx.length;
let i;
-
for (i = 0; i < lo; ++i) curr[idx[i]] |= bit;
-
for (i = hi; i < n1; ++i) curr[idx[i]] |= bit;
-
return dim;
}
-
};
}
+
/**
* Maintains a list of values, sorted by key.
*/
-
-
function SortedIndex() {
let index = array32(0),
- value = [],
- size = 0;
-
+ value = [],
+ size = 0;
function insert(key, data, base) {
if (!data.length) return [];
const n0 = size,
- n1 = data.length,
- addi = array32(n1);
+ n1 = data.length,
+ addi = array32(n1);
let addv = Array(n1),
- oldv,
- oldi,
- i;
-
+ oldv,
+ oldi,
+ i;
for (i = 0; i < n1; ++i) {
addv[i] = key(data[i]);
addi[i] = i;
}
-
addv = sort(addv, addi);
-
if (n0) {
oldv = value;
oldi = index;
value = Array(n0 + n1);
index = array32(n0 + n1);
@@ -35984,115 +32534,101 @@
addi[i] += base;
}
value = addv;
index = addi;
}
-
size = n0 + n1;
return {
index: addi,
value: addv
};
}
-
function remove(num, map) {
// map: index -> remove
const n = size;
- let idx, i, j; // seek forward to first removal
+ let idx, i, j;
- for (i = 0; !map[index[i]] && i < n; ++i); // condense index and value arrays
+ // seek forward to first removal
+ for (i = 0; !map[index[i]] && i < n; ++i);
-
+ // condense index and value arrays
for (j = i; i < n; ++i) {
if (!map[idx = index[i]]) {
index[j] = idx;
value[j] = value[i];
++j;
}
}
-
size = n - num;
}
-
function reindex(map) {
for (let i = 0, n = size; i < n; ++i) {
index[i] = map[index[i]];
}
}
-
function bisect(range, array) {
let n;
-
if (array) {
n = array.length;
} else {
array = value;
n = size;
}
-
return [bisectLeft$1(array, range[0], 0, n), bisectRight$1(array, range[1], 0, n)];
}
-
return {
insert: insert,
remove: remove,
bisect: bisect,
reindex: reindex,
index: () => index,
size: () => size
};
}
-
function sort(values, index) {
values.sort.call(index, (a, b) => {
const x = values[a],
- y = values[b];
+ y = values[b];
return x < y ? -1 : x > y ? 1 : 0;
});
return permute(values, index);
}
-
function merge$1(base, value0, index0, n0, value1, index1, n1, value, index) {
let i0 = 0,
- i1 = 0,
- i;
-
+ i1 = 0,
+ i;
for (i = 0; i0 < n0 && i1 < n1; ++i) {
if (value0[i0] < value1[i1]) {
value[i] = value0[i0];
index[i] = index0[i0++];
} else {
value[i] = value1[i1];
index[i] = index1[i1++] + base;
}
}
-
for (; i0 < n0; ++i0, ++i) {
value[i] = value0[i0];
index[i] = index0[i0];
}
-
for (; i1 < n1; ++i1, ++i) {
value[i] = value1[i1];
index[i] = index1[i1] + base;
}
}
+
/**
* An indexed multi-dimensional filter.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {Array<function(object): *>} params.fields - An array of dimension accessors to filter.
* @param {Array} params.query - An array of per-dimension range queries.
*/
-
-
function CrossFilter(params) {
Transform.call(this, Bitmaps(), params);
this._indices = null;
this._dims = null;
}
-
CrossFilter.Definition = {
'type': 'CrossFilter',
'metadata': {},
'params': [{
'name': 'fields',
@@ -36115,89 +32651,84 @@
transform(_, pulse) {
if (!this._dims) {
return this.init(_, pulse);
} else {
var init = _.modified('fields') || _.fields.some(f => pulse.modified(f.fields));
-
return init ? this.reinit(_, pulse) : this.eval(_, pulse);
}
},
-
init(_, pulse) {
const fields = _.fields,
- query = _.query,
- indices = this._indices = {},
- dims = this._dims = [],
- m = query.length;
+ query = _.query,
+ indices = this._indices = {},
+ dims = this._dims = [],
+ m = query.length;
let i = 0,
- key,
- index; // instantiate indices and dimensions
+ key,
+ index;
+ // instantiate indices and dimensions
for (; i < m; ++i) {
key = fields[i].fname;
index = indices[key] || (indices[key] = SortedIndex());
dims.push(Dimension(index, i, query[i]));
}
-
return this.eval(_, pulse);
},
-
reinit(_, pulse) {
const output = pulse.materialize().fork(),
- fields = _.fields,
- query = _.query,
- indices = this._indices,
- dims = this._dims,
- bits = this.value,
- curr = bits.curr(),
- prev = bits.prev(),
- all = bits.all(),
- out = output.rem = output.add,
- mod = output.mod,
- m = query.length,
- adds = {};
- let add, index, key, mods, remMap, modMap, i, n, f; // set prev to current state
+ fields = _.fields,
+ query = _.query,
+ indices = this._indices,
+ dims = this._dims,
+ bits = this.value,
+ curr = bits.curr(),
+ prev = bits.prev(),
+ all = bits.all(),
+ out = output.rem = output.add,
+ mod = output.mod,
+ m = query.length,
+ adds = {};
+ let add, index, key, mods, remMap, modMap, i, n, f;
- prev.set(curr); // if pulse has remove tuples, process them first
+ // set prev to current state
+ prev.set(curr);
+ // if pulse has remove tuples, process them first
if (pulse.rem.length) {
remMap = this.remove(_, pulse, output);
- } // if pulse has added tuples, add them to state
+ }
-
+ // if pulse has added tuples, add them to state
if (pulse.add.length) {
bits.add(pulse.add);
- } // if pulse has modified tuples, create an index map
+ }
-
+ // if pulse has modified tuples, create an index map
if (pulse.mod.length) {
modMap = {};
-
for (mods = pulse.mod, i = 0, n = mods.length; i < n; ++i) {
modMap[mods[i]._index] = 1;
}
- } // re-initialize indices as needed, update curr bitmap
+ }
-
+ // re-initialize indices as needed, update curr bitmap
for (i = 0; i < m; ++i) {
f = fields[i];
-
if (!dims[i] || _.modified('fields', i) || pulse.modified(f.fields)) {
key = f.fname;
-
if (!(add = adds[key])) {
indices[key] = index = SortedIndex();
adds[key] = add = index.insert(f, pulse.source, 0);
}
-
dims[i] = Dimension(index, i, query[i]).onAdd(add, curr);
}
- } // visit each tuple
+ }
+
+ // visit each tuple
// if filter state changed, push index to add/rem
// else if in mod and passes a filter, push index to mod
-
-
for (i = 0, n = bits.data().length; i < n; ++i) {
if (remMap[i]) {
// skip if removed tuple
continue;
} else if (prev[i] !== curr[i]) {
@@ -36206,149 +32737,135 @@
} else if (modMap[i] && curr[i] !== all) {
// otherwise, pass mods through
mod.push(i);
}
}
-
bits.mask = (1 << m) - 1;
return output;
},
-
eval(_, pulse) {
const output = pulse.materialize().fork(),
- m = this._dims.length;
+ m = this._dims.length;
let mask = 0;
-
if (pulse.rem.length) {
this.remove(_, pulse, output);
mask |= (1 << m) - 1;
}
-
if (_.modified('query') && !_.modified('fields')) {
mask |= this.update(_, pulse, output);
}
-
if (pulse.add.length) {
this.insert(_, pulse, output);
mask |= (1 << m) - 1;
}
-
if (pulse.mod.length) {
this.modify(pulse, output);
mask |= (1 << m) - 1;
}
-
this.value.mask = mask;
return output;
},
-
insert(_, pulse, output) {
const tuples = pulse.add,
- bits = this.value,
- dims = this._dims,
- indices = this._indices,
- fields = _.fields,
- adds = {},
- out = output.add,
- n = bits.size() + tuples.length,
- m = dims.length;
+ bits = this.value,
+ dims = this._dims,
+ indices = this._indices,
+ fields = _.fields,
+ adds = {},
+ out = output.add,
+ n = bits.size() + tuples.length,
+ m = dims.length;
let k = bits.size(),
- j,
- key,
- add; // resize bitmaps and add tuples as needed
+ j,
+ key,
+ add;
+ // resize bitmaps and add tuples as needed
bits.resize(n, m);
bits.add(tuples);
const curr = bits.curr(),
- prev = bits.prev(),
- all = bits.all(); // add to dimensional indices
+ prev = bits.prev(),
+ all = bits.all();
+ // add to dimensional indices
for (j = 0; j < m; ++j) {
key = fields[j].fname;
add = adds[key] || (adds[key] = indices[key].insert(fields[j], tuples, k));
dims[j].onAdd(add, curr);
- } // set previous filters, output if passes at least one filter
+ }
-
+ // set previous filters, output if passes at least one filter
for (; k < n; ++k) {
prev[k] = all;
if (curr[k] !== all) out.push(k);
}
},
-
modify(pulse, output) {
const out = output.mod,
- bits = this.value,
- curr = bits.curr(),
- all = bits.all(),
- tuples = pulse.mod;
+ bits = this.value,
+ curr = bits.curr(),
+ all = bits.all(),
+ tuples = pulse.mod;
let i, n, k;
-
for (i = 0, n = tuples.length; i < n; ++i) {
k = tuples[i]._index;
if (curr[k] !== all) out.push(k);
}
},
-
remove(_, pulse, output) {
const indices = this._indices,
- bits = this.value,
- curr = bits.curr(),
- prev = bits.prev(),
- all = bits.all(),
- map = {},
- out = output.rem,
- tuples = pulse.rem;
- let i, n, k, f; // process tuples, output if passes at least one filter
+ bits = this.value,
+ curr = bits.curr(),
+ prev = bits.prev(),
+ all = bits.all(),
+ map = {},
+ out = output.rem,
+ tuples = pulse.rem;
+ let i, n, k, f;
+ // process tuples, output if passes at least one filter
for (i = 0, n = tuples.length; i < n; ++i) {
k = tuples[i]._index;
map[k] = 1; // build index map
-
prev[k] = f = curr[k];
curr[k] = all;
if (f !== all) out.push(k);
- } // remove from dimensional indices
+ }
-
+ // remove from dimensional indices
for (k in indices) {
indices[k].remove(n, map);
}
-
this.reindex(pulse, n, map);
return map;
},
-
// reindex filters and indices after propagation completes
reindex(pulse, num, map) {
const indices = this._indices,
- bits = this.value;
+ bits = this.value;
pulse.runAfter(() => {
const indexMap = bits.remove(num, map);
-
for (const key in indices) indices[key].reindex(indexMap);
});
},
-
update(_, pulse, output) {
const dims = this._dims,
- query = _.query,
- stamp = pulse.stamp,
- m = dims.length;
+ query = _.query,
+ stamp = pulse.stamp,
+ m = dims.length;
let mask = 0,
- i,
- q; // survey how many queries have changed
+ i,
+ q;
+ // survey how many queries have changed
output.filters = 0;
-
for (q = 0; q < m; ++q) {
if (_.modified('query', q)) {
i = q;
++mask;
}
}
-
if (mask === 1) {
// only one query changed, use more efficient update
mask = dims[i].one;
this.incrementOne(dims[i], query[i], output.add, output.rem);
} else {
@@ -36358,98 +32875,88 @@
mask |= dims[q].one;
this.incrementAll(dims[q], query[q], stamp, output.add);
output.rem = output.add; // duplicate add/rem for downstream resolve
}
}
-
return mask;
},
-
incrementAll(dim, query, stamp, out) {
const bits = this.value,
- seen = bits.seen(),
- curr = bits.curr(),
- prev = bits.prev(),
- index = dim.index(),
- old = dim.bisect(dim.range),
- range = dim.bisect(query),
- lo1 = range[0],
- hi1 = range[1],
- lo0 = old[0],
- hi0 = old[1],
- one = dim.one;
- let i, j, k; // Fast incremental update based on previous lo index.
+ seen = bits.seen(),
+ curr = bits.curr(),
+ prev = bits.prev(),
+ index = dim.index(),
+ old = dim.bisect(dim.range),
+ range = dim.bisect(query),
+ lo1 = range[0],
+ hi1 = range[1],
+ lo0 = old[0],
+ hi0 = old[1],
+ one = dim.one;
+ let i, j, k;
+ // Fast incremental update based on previous lo index.
if (lo1 < lo0) {
for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
k = index[i];
-
if (seen[k] !== stamp) {
prev[k] = curr[k];
seen[k] = stamp;
out.push(k);
}
-
curr[k] ^= one;
}
} else if (lo1 > lo0) {
for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
k = index[i];
-
if (seen[k] !== stamp) {
prev[k] = curr[k];
seen[k] = stamp;
out.push(k);
}
-
curr[k] ^= one;
}
- } // Fast incremental update based on previous hi index.
+ }
-
+ // Fast incremental update based on previous hi index.
if (hi1 > hi0) {
for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
k = index[i];
-
if (seen[k] !== stamp) {
prev[k] = curr[k];
seen[k] = stamp;
out.push(k);
}
-
curr[k] ^= one;
}
} else if (hi1 < hi0) {
for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
k = index[i];
-
if (seen[k] !== stamp) {
prev[k] = curr[k];
seen[k] = stamp;
out.push(k);
}
-
curr[k] ^= one;
}
}
-
dim.range = query.slice();
},
-
incrementOne(dim, query, add, rem) {
const bits = this.value,
- curr = bits.curr(),
- index = dim.index(),
- old = dim.bisect(dim.range),
- range = dim.bisect(query),
- lo1 = range[0],
- hi1 = range[1],
- lo0 = old[0],
- hi0 = old[1],
- one = dim.one;
- let i, j, k; // Fast incremental update based on previous lo index.
+ curr = bits.curr(),
+ index = dim.index(),
+ old = dim.bisect(dim.range),
+ range = dim.bisect(query),
+ lo1 = range[0],
+ hi1 = range[1],
+ lo0 = old[0],
+ hi0 = old[1],
+ one = dim.one;
+ let i, j, k;
+ // Fast incremental update based on previous lo index.
if (lo1 < lo0) {
for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
k = index[i];
curr[k] ^= one;
add.push(k);
@@ -36458,13 +32965,13 @@
for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
k = index[i];
curr[k] ^= one;
rem.push(k);
}
- } // Fast incremental update based on previous hi index.
+ }
-
+ // Fast incremental update based on previous hi index.
if (hi1 > hi0) {
for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
k = index[i];
curr[k] ^= one;
add.push(k);
@@ -36474,29 +32981,26 @@
k = index[i];
curr[k] ^= one;
rem.push(k);
}
}
-
dim.range = query.slice();
}
-
});
+
/**
* Selectively filters tuples by resolving against a filter bitmap.
* Useful for processing the output of a cross-filter transform.
* @constructor
* @param {object} params - The parameters for this operator.
* @param {object} params.ignore - A bit mask indicating which filters to ignore.
* @param {object} params.filter - The per-tuple filter bitmaps. Typically this
* parameter value is a reference to a {@link CrossFilter} transform.
*/
-
function ResolveFilter(params) {
Transform.call(this, null, params);
}
-
ResolveFilter.Definition = {
'type': 'ResolveFilter',
'metadata': {},
'params': [{
'name': 'ignore',
@@ -36511,24 +33015,26 @@
}]
};
inherits(ResolveFilter, Transform, {
transform(_, pulse) {
const ignore = ~(_.ignore || 0),
- // bit mask where zeros -> dims to ignore
- bitmap = _.filter,
- mask = bitmap.mask; // exit early if no relevant filter changes
+ // bit mask where zeros -> dims to ignore
+ bitmap = _.filter,
+ mask = bitmap.mask;
+ // exit early if no relevant filter changes
if ((mask & ignore) === 0) return pulse.StopPropagation;
-
const output = pulse.fork(pulse.ALL),
- data = bitmap.data(),
- curr = bitmap.curr(),
- prev = bitmap.prev(),
- pass = k => !(curr[k] & ignore) ? data[k] : null; // propagate all mod tuples that pass the filter
+ data = bitmap.data(),
+ curr = bitmap.curr(),
+ prev = bitmap.prev(),
+ pass = k => !(curr[k] & ignore) ? data[k] : null;
+ // propagate all mod tuples that pass the filter
+ output.filter(output.MOD, pass);
- output.filter(output.MOD, pass); // determine add & rem tuples via filter functions
+ // determine add & rem tuples via filter functions
// for efficiency, we do *not* populate new arrays,
// instead we add filter functions applied downstream
if (!(mask & mask - 1)) {
// only one filter changed
@@ -36536,33 +33042,32 @@
output.filter(output.REM, k => (curr[k] & ignore) === mask ? data[k] : null);
} else {
// multiple filters changed
output.filter(output.ADD, k => {
const c = curr[k] & ignore,
- f = !c && c ^ prev[k] & ignore;
+ f = !c && c ^ prev[k] & ignore;
return f ? data[k] : null;
});
output.filter(output.REM, k => {
const c = curr[k] & ignore,
- f = c && !(c ^ (c ^ prev[k] & ignore));
+ f = c && !(c ^ (c ^ prev[k] & ignore));
return f ? data[k] : null;
});
- } // add filter to source data in case of reflow...
+ }
-
+ // add filter to source data in case of reflow...
return output.filter(output.SOURCE, t => pass(t._index));
}
-
});
var xf = /*#__PURE__*/Object.freeze({
__proto__: null,
crossfilter: CrossFilter,
resolvefilter: ResolveFilter
});
- var version = "5.22.1";
+ var version = "5.30.0";
const RawCode = 'RawCode';
const Literal = 'Literal';
const Property = 'Property';
const Identifier = 'Identifier';
@@ -36572,58 +33077,47 @@
const ConditionalExpression = 'ConditionalExpression';
const LogicalExpression = 'LogicalExpression';
const MemberExpression = 'MemberExpression';
const ObjectExpression = 'ObjectExpression';
const UnaryExpression = 'UnaryExpression';
-
function ASTNode(type) {
this.type = type;
}
-
ASTNode.prototype.visit = function (visitor) {
let c, i, n;
if (visitor(this)) return 1;
-
for (c = children(this), i = 0, n = c.length; i < n; ++i) {
if (c[i].visit(visitor)) return 1;
}
};
-
function children(node) {
switch (node.type) {
case ArrayExpression:
return node.elements;
-
case BinaryExpression:
case LogicalExpression:
return [node.left, node.right];
-
case CallExpression:
return [node.callee].concat(node.arguments);
-
case ConditionalExpression:
return [node.test, node.consequent, node.alternate];
-
case MemberExpression:
return [node.object, node.property];
-
case ObjectExpression:
return node.properties;
-
case Property:
return [node.key, node.value];
-
case UnaryExpression:
return [node.argument];
-
case Identifier:
case Literal:
case RawCode:
default:
return [];
}
}
+
/*
The following expression parser is based on Esprima (http://esprima.org/).
Original header comment and license for Esprima is included here:
Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
@@ -36655,22 +33149,20 @@
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
-
var TokenName, source, index, length, lookahead;
var TokenBooleanLiteral = 1,
- TokenEOF = 2,
- TokenIdentifier = 3,
- TokenKeyword = 4,
- TokenNullLiteral = 5,
- TokenNumericLiteral = 6,
- TokenPunctuator = 7,
- TokenStringLiteral = 8,
- TokenRegularExpression = 9;
+ TokenEOF = 2,
+ TokenIdentifier = 3,
+ TokenKeyword = 4,
+ TokenNullLiteral = 5,
+ TokenNumericLiteral = 6,
+ TokenPunctuator = 7,
+ TokenStringLiteral = 8,
+ TokenRegularExpression = 9;
TokenName = {};
TokenName[TokenBooleanLiteral] = 'Boolean';
TokenName[TokenEOF] = '<end>';
TokenName[TokenIdentifier] = 'Identifier';
TokenName[TokenKeyword] = 'Keyword';
@@ -36678,88 +33170,101 @@
TokenName[TokenNumericLiteral] = 'Numeric';
TokenName[TokenPunctuator] = 'Punctuator';
TokenName[TokenStringLiteral] = 'String';
TokenName[TokenRegularExpression] = 'RegularExpression';
var SyntaxArrayExpression = 'ArrayExpression',
- SyntaxBinaryExpression = 'BinaryExpression',
- SyntaxCallExpression = 'CallExpression',
- SyntaxConditionalExpression = 'ConditionalExpression',
- SyntaxIdentifier = 'Identifier',
- SyntaxLiteral = 'Literal',
- SyntaxLogicalExpression = 'LogicalExpression',
- SyntaxMemberExpression = 'MemberExpression',
- SyntaxObjectExpression = 'ObjectExpression',
- SyntaxProperty = 'Property',
- SyntaxUnaryExpression = 'UnaryExpression'; // Error messages should be identical to V8.
+ SyntaxBinaryExpression = 'BinaryExpression',
+ SyntaxCallExpression = 'CallExpression',
+ SyntaxConditionalExpression = 'ConditionalExpression',
+ SyntaxIdentifier = 'Identifier',
+ SyntaxLiteral = 'Literal',
+ SyntaxLogicalExpression = 'LogicalExpression',
+ SyntaxMemberExpression = 'MemberExpression',
+ SyntaxObjectExpression = 'ObjectExpression',
+ SyntaxProperty = 'Property',
+ SyntaxUnaryExpression = 'UnaryExpression';
+ // Error messages should be identical to V8.
var MessageUnexpectedToken = 'Unexpected token %0',
- MessageUnexpectedNumber = 'Unexpected number',
- MessageUnexpectedString = 'Unexpected string',
- MessageUnexpectedIdentifier = 'Unexpected identifier',
- MessageUnexpectedReserved = 'Unexpected reserved word',
- MessageUnexpectedEOS = 'Unexpected end of input',
- MessageInvalidRegExp = 'Invalid regular expression',
- MessageUnterminatedRegExp = 'Invalid regular expression: missing /',
- MessageStrictOctalLiteral = 'Octal literals are not allowed in strict mode.',
- MessageStrictDuplicateProperty = 'Duplicate data property in object literal not allowed in strict mode';
+ MessageUnexpectedNumber = 'Unexpected number',
+ MessageUnexpectedString = 'Unexpected string',
+ MessageUnexpectedIdentifier = 'Unexpected identifier',
+ MessageUnexpectedReserved = 'Unexpected reserved word',
+ MessageUnexpectedEOS = 'Unexpected end of input',
+ MessageInvalidRegExp = 'Invalid regular expression',
+ MessageUnterminatedRegExp = 'Invalid regular expression: missing /',
+ MessageStrictOctalLiteral = 'Octal literals are not allowed in strict mode.',
+ MessageStrictDuplicateProperty = 'Duplicate data property in object literal not allowed in strict mode';
var ILLEGAL$1 = 'ILLEGAL',
- DISABLED = 'Disabled.'; // See also tools/generate-unicode-regex.py.
+ DISABLED = 'Disabled.';
+ // See also tools/generate-unicode-regex.py.
var RegexNonAsciiIdentifierStart = new RegExp('[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]'),
- // eslint-disable-next-line no-misleading-character-class
- RegexNonAsciiIdentifierPart = new RegExp('[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u08A0-\\u08B2\\u08E4-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58\\u0C59\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C81-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D01-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D57\\u0D60-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1CD0-\\u1CD2\\u1CD4-\\u1CF6\\u1CF8\\u1CF9\\u1D00-\\u1DF5\\u1DFC-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u200C\\u200D\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA69D\\uA69F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C4\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2D\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]'); // Ensure the condition is true, otherwise throw an error.
+ // eslint-disable-next-line no-misleading-character-class
+ RegexNonAsciiIdentifierPart = new RegExp('[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u08A0-\\u08B2\\u08E4-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58\\u0C59\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C81-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D01-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D57\\u0D60-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1CD0-\\u1CD2\\u1CD4-\\u1CF6\\u1CF8\\u1CF9\\u1D00-\\u1DF5\\u1DFC-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u200C\\u200D\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA69D\\uA69F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C4\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2D\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]');
+
+ // Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert(condition, message) {
/* istanbul ignore next */
if (!condition) {
throw new Error('ASSERT: ' + message);
}
}
-
function isDecimalDigit(ch) {
return ch >= 0x30 && ch <= 0x39; // 0..9
}
-
function isHexDigit(ch) {
- return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
+ return '0123456789abcdefABCDEF'.includes(ch);
}
-
function isOctalDigit(ch) {
- return '01234567'.indexOf(ch) >= 0;
- } // 7.2 White Space
+ return '01234567'.includes(ch);
+ }
+ // 7.2 White Space
function isWhiteSpace(ch) {
- return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 || ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0;
- } // 7.3 Line Terminators
+ return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 || ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].includes(ch);
+ }
+ // 7.3 Line Terminators
function isLineTerminator(ch) {
return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029;
- } // 7.6 Identifier Names and Identifiers
+ }
+ // 7.6 Identifier Names and Identifiers
function isIdentifierStart(ch) {
- return ch === 0x24 || ch === 0x5F || // $ (dollar) and _ (underscore)
- ch >= 0x41 && ch <= 0x5A || // A..Z
- ch >= 0x61 && ch <= 0x7A || // a..z
- ch === 0x5C || // \ (backslash)
+ return ch === 0x24 || ch === 0x5F ||
+ // $ (dollar) and _ (underscore)
+ ch >= 0x41 && ch <= 0x5A ||
+ // A..Z
+ ch >= 0x61 && ch <= 0x7A ||
+ // a..z
+ ch === 0x5C ||
+ // \ (backslash)
ch >= 0x80 && RegexNonAsciiIdentifierStart.test(String.fromCharCode(ch));
}
-
function isIdentifierPart(ch) {
- return ch === 0x24 || ch === 0x5F || // $ (dollar) and _ (underscore)
- ch >= 0x41 && ch <= 0x5A || // A..Z
- ch >= 0x61 && ch <= 0x7A || // a..z
- ch >= 0x30 && ch <= 0x39 || // 0..9
- ch === 0x5C || // \ (backslash)
+ return ch === 0x24 || ch === 0x5F ||
+ // $ (dollar) and _ (underscore)
+ ch >= 0x41 && ch <= 0x5A ||
+ // A..Z
+ ch >= 0x61 && ch <= 0x7A ||
+ // a..z
+ ch >= 0x30 && ch <= 0x39 ||
+ // 0..9
+ ch === 0x5C ||
+ // \ (backslash)
ch >= 0x80 && RegexNonAsciiIdentifierPart.test(String.fromCharCode(ch));
- } // 7.6.1.1 Keywords
+ }
+ // 7.6.1.1 Keywords
const keywords = {
'if': 1,
'in': 1,
'do': 1,
@@ -36801,156 +33306,132 @@
'interface': 1,
'protected': 1,
'instanceof': 1,
'implements': 1
};
-
function skipComment() {
while (index < length) {
const ch = source.charCodeAt(index);
-
if (isWhiteSpace(ch) || isLineTerminator(ch)) {
++index;
} else {
break;
}
}
}
-
function scanHexEscape(prefix) {
var i,
- len,
- ch,
- code = 0;
+ len,
+ ch,
+ code = 0;
len = prefix === 'u' ? 4 : 2;
-
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
ch = source[index++];
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
}
-
return String.fromCharCode(code);
}
-
function scanUnicodeCodePointEscape() {
var ch, code, cu1, cu2;
ch = source[index];
- code = 0; // At least, one hex digit is required.
+ code = 0;
+ // At least, one hex digit is required.
if (ch === '}') {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
while (index < length) {
ch = source[index++];
-
if (!isHexDigit(ch)) {
break;
}
-
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
}
-
if (code > 0x10FFFF || ch !== '}') {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
- } // UTF-16 Encoding
+ }
-
+ // UTF-16 Encoding
if (code <= 0xFFFF) {
return String.fromCharCode(code);
}
-
cu1 = (code - 0x10000 >> 10) + 0xD800;
cu2 = (code - 0x10000 & 1023) + 0xDC00;
return String.fromCharCode(cu1, cu2);
}
-
function getEscapedIdentifier() {
var ch, id;
ch = source.charCodeAt(index++);
- id = String.fromCharCode(ch); // '\u' (U+005C, U+0075) denotes an escaped character.
+ id = String.fromCharCode(ch);
+ // '\u' (U+005C, U+0075) denotes an escaped character.
if (ch === 0x5C) {
if (source.charCodeAt(index) !== 0x75) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
++index;
ch = scanHexEscape('u');
-
if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
id = ch;
}
-
while (index < length) {
ch = source.charCodeAt(index);
-
if (!isIdentifierPart(ch)) {
break;
}
-
++index;
- id += String.fromCharCode(ch); // '\u' (U+005C, U+0075) denotes an escaped character.
+ id += String.fromCharCode(ch);
+ // '\u' (U+005C, U+0075) denotes an escaped character.
if (ch === 0x5C) {
id = id.substr(0, id.length - 1);
-
if (source.charCodeAt(index) !== 0x75) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
++index;
ch = scanHexEscape('u');
-
if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
id += ch;
}
}
-
return id;
}
-
function getIdentifier() {
var start, ch;
start = index++;
-
while (index < length) {
ch = source.charCodeAt(index);
-
if (ch === 0x5C) {
// Blackslash (U+005C) marks Unicode escape sequence.
index = start;
return getEscapedIdentifier();
}
-
if (isIdentifierPart(ch)) {
++index;
} else {
break;
}
}
-
return source.slice(start, index);
}
-
function scanIdentifier() {
var start, id, type;
- start = index; // Backslash (U+005C) starts an escaped character.
+ start = index;
- id = source.charCodeAt(index) === 0x5C ? getEscapedIdentifier() : getIdentifier(); // There is no keyword or literal with only one character.
- // Thus, it must be an identifier.
+ // Backslash (U+005C) starts an escaped character.
+ id = source.charCodeAt(index) === 0x5C ? getEscapedIdentifier() : getIdentifier();
+ // There is no keyword or literal with only one character.
+ // Thus, it must be an identifier.
if (id.length === 1) {
type = TokenIdentifier;
} else if (keywords.hasOwnProperty(id)) {
// eslint-disable-line no-prototype-builtins
type = TokenKeyword;
@@ -36959,382 +33440,324 @@
} else if (id === 'true' || id === 'false') {
type = TokenBooleanLiteral;
} else {
type = TokenIdentifier;
}
-
return {
type: type,
value: id,
start: start,
end: index
};
- } // 7.7 Punctuators
+ }
+ // 7.7 Punctuators
function scanPunctuator() {
var start = index,
- code = source.charCodeAt(index),
- code2,
- ch1 = source[index],
- ch2,
- ch3,
- ch4;
-
+ code = source.charCodeAt(index),
+ code2,
+ ch1 = source[index],
+ ch2,
+ ch3,
+ ch4;
switch (code) {
// Check for most common single-character punctuators.
case 0x2E: // . dot
-
case 0x28: // ( open bracket
-
case 0x29: // ) close bracket
-
case 0x3B: // ; semicolon
-
case 0x2C: // , comma
-
case 0x7B: // { open curly brace
-
case 0x7D: // } close curly brace
-
case 0x5B: // [
-
case 0x5D: // ]
-
case 0x3A: // :
-
case 0x3F: // ?
-
case 0x7E:
// ~
++index;
return {
type: TokenPunctuator,
value: String.fromCharCode(code),
start: start,
end: index
};
-
default:
- code2 = source.charCodeAt(index + 1); // '=' (U+003D) marks an assignment or comparison operator.
+ code2 = source.charCodeAt(index + 1);
+ // '=' (U+003D) marks an assignment or comparison operator.
if (code2 === 0x3D) {
switch (code) {
case 0x2B: // +
-
case 0x2D: // -
-
case 0x2F: // /
-
case 0x3C: // <
-
case 0x3E: // >
-
case 0x5E: // ^
-
case 0x7C: // |
-
case 0x25: // %
-
case 0x26: // &
-
case 0x2A:
// *
index += 2;
return {
type: TokenPunctuator,
value: String.fromCharCode(code) + String.fromCharCode(code2),
start: start,
end: index
};
-
case 0x21: // !
-
case 0x3D:
// =
- index += 2; // !== and ===
+ index += 2;
+ // !== and ===
if (source.charCodeAt(index) === 0x3D) {
++index;
}
-
return {
type: TokenPunctuator,
value: source.slice(start, index),
start: start,
end: index
};
}
}
+ }
- } // 4-character punctuator: >>>=
+ // 4-character punctuator: >>>=
-
ch4 = source.substr(index, 4);
-
if (ch4 === '>>>=') {
index += 4;
return {
type: TokenPunctuator,
value: ch4,
start: start,
end: index
};
- } // 3-character punctuators: === !== >>> <<= >>=
+ }
+ // 3-character punctuators: === !== >>> <<= >>=
ch3 = ch4.substr(0, 3);
-
if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') {
index += 3;
return {
type: TokenPunctuator,
value: ch3,
start: start,
end: index
};
- } // Other 2-character punctuators: ++ -- << >> && ||
+ }
-
+ // Other 2-character punctuators: ++ -- << >> && ||
ch2 = ch3.substr(0, 2);
-
- if (ch1 === ch2[1] && '+-<>&|'.indexOf(ch1) >= 0 || ch2 === '=>') {
+ if (ch1 === ch2[1] && '+-<>&|'.includes(ch1) || ch2 === '=>') {
index += 2;
return {
type: TokenPunctuator,
value: ch2,
start: start,
end: index
};
}
-
if (ch2 === '//') {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
- } // 1-character punctuators: < > = ! + - * % & | ^ /
+ }
+ // 1-character punctuators: < > = ! + - * % & | ^ /
- if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
+ if ('<>=!+-*%&|^/'.includes(ch1)) {
++index;
return {
type: TokenPunctuator,
value: ch1,
start: start,
end: index
};
}
-
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
- } // 7.8.3 Numeric Literals
+ }
+ // 7.8.3 Numeric Literals
function scanHexLiteral(start) {
let number = '';
-
while (index < length) {
if (!isHexDigit(source[index])) {
break;
}
-
number += source[index++];
}
-
if (number.length === 0) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
if (isIdentifierStart(source.charCodeAt(index))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
return {
type: TokenNumericLiteral,
value: parseInt('0x' + number, 16),
start: start,
end: index
};
}
-
function scanOctalLiteral(start) {
let number = '0' + source[index++];
-
while (index < length) {
if (!isOctalDigit(source[index])) {
break;
}
-
number += source[index++];
}
-
if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
return {
type: TokenNumericLiteral,
value: parseInt(number, 8),
octal: true,
start: start,
end: index
};
}
-
function scanNumericLiteral() {
var number, start, ch;
ch = source[index];
assert(isDecimalDigit(ch.charCodeAt(0)) || ch === '.', 'Numeric literal must start with a decimal digit or a decimal point');
start = index;
number = '';
-
if (ch !== '.') {
number = source[index++];
- ch = source[index]; // Hex number starts with '0x'.
- // Octal number starts with '0'.
+ ch = source[index];
+ // Hex number starts with '0x'.
+ // Octal number starts with '0'.
if (number === '0') {
if (ch === 'x' || ch === 'X') {
++index;
return scanHexLiteral(start);
}
-
if (isOctalDigit(ch)) {
return scanOctalLiteral(start);
- } // decimal number starts with '0' such as '09' is illegal.
+ }
-
+ // decimal number starts with '0' such as '09' is illegal.
if (ch && isDecimalDigit(ch.charCodeAt(0))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
}
-
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
-
ch = source[index];
}
-
if (ch === '.') {
number += source[index++];
-
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
-
ch = source[index];
}
-
if (ch === 'e' || ch === 'E') {
number += source[index++];
ch = source[index];
-
if (ch === '+' || ch === '-') {
number += source[index++];
}
-
if (isDecimalDigit(source.charCodeAt(index))) {
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
} else {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
}
-
if (isIdentifierStart(source.charCodeAt(index))) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
return {
type: TokenNumericLiteral,
value: parseFloat(number),
start: start,
end: index
};
- } // 7.8.4 String Literals
+ }
+ // 7.8.4 String Literals
function scanStringLiteral() {
var str = '',
- quote,
- start,
- ch,
- code,
- octal = false;
+ quote,
+ start,
+ ch,
+ code,
+ octal = false;
quote = source[index];
assert(quote === '\'' || quote === '"', 'String literal must starts with a quote');
start = index;
++index;
-
while (index < length) {
ch = source[index++];
-
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = source[index++];
-
if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'u':
case 'x':
if (source[index] === '{') {
++index;
str += scanUnicodeCodePointEscape();
} else {
str += scanHexEscape(ch);
}
-
break;
-
case 'n':
str += '\n';
break;
-
case 'r':
str += '\r';
break;
-
case 't':
str += '\t';
break;
-
case 'b':
str += '\b';
break;
-
case 'f':
str += '\f';
break;
-
case 'v':
str += '\x0B';
break;
-
default:
if (isOctalDigit(ch)) {
- code = '01234567'.indexOf(ch); // \0 is not octal escape sequence
+ code = '01234567'.indexOf(ch);
+ // \0 is not octal escape sequence
if (code !== 0) {
octal = true;
}
-
if (index < length && isOctalDigit(source[index])) {
octal = true;
- code = code * 8 + '01234567'.indexOf(source[index++]); // 3 digits are only allowed when string starts
- // with 0, 1, 2, 3
+ code = code * 8 + '01234567'.indexOf(source[index++]);
- if ('0123'.indexOf(ch) >= 0 && index < length && isOctalDigit(source[index])) {
+ // 3 digits are only allowed when string starts
+ // with 0, 1, 2, 3
+ if ('0123'.includes(ch) && index < length && isOctalDigit(source[index])) {
code = code * 8 + '01234567'.indexOf(source[index++]);
}
}
-
str += String.fromCharCode(code);
} else {
str += ch;
}
-
break;
}
} else {
if (ch === '\r' && source[index] === '\n') {
++index;
@@ -37344,28 +33767,24 @@
break;
} else {
str += ch;
}
}
-
if (quote !== '') {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
}
-
return {
type: TokenStringLiteral,
value: str,
octal: octal,
start: start,
end: index
};
}
-
function testRegExp(pattern, flags) {
let tmp = pattern;
-
- if (flags.indexOf('u') >= 0) {
+ if (flags.includes('u')) {
// Replace each astral symbol and every Unicode code point
// escape sequence with a single ASCII symbol to avoid throwing on
// regular expressions that are only valid in combination with the
// `/u` flag.
// Note: replacing with the ASCII symbol `x` might cause false
@@ -37374,51 +33793,46 @@
// would be replaced by `[x-b]` which throws an error.
tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, ($0, $1) => {
if (parseInt($1, 16) <= 0x10FFFF) {
return 'x';
}
-
throwError({}, MessageInvalidRegExp);
}).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 'x');
- } // First, detect invalid regular expressions.
+ }
-
+ // First, detect invalid regular expressions.
try {
new RegExp(tmp);
} catch (e) {
throwError({}, MessageInvalidRegExp);
- } // Return a regular expression object for this pattern-flag pair, or
+ }
+
+ // Return a regular expression object for this pattern-flag pair, or
// `null` in case the current environment doesn't support the flags it
// uses.
-
-
try {
return new RegExp(pattern, flags);
} catch (exception) {
return null;
}
}
-
function scanRegExpBody() {
var ch, str, classMarker, terminated, body;
ch = source[index];
assert(ch === '/', 'Regular expression literal must start with a slash');
str = source[index++];
classMarker = false;
terminated = false;
-
while (index < length) {
ch = source[index++];
str += ch;
-
if (ch === '\\') {
- ch = source[index++]; // ECMA-262 7.8.5
-
+ ch = source[index++];
+ // ECMA-262 7.8.5
if (isLineTerminator(ch.charCodeAt(0))) {
throwError({}, MessageUnterminatedRegExp);
}
-
str += ch;
} else if (isLineTerminator(ch.charCodeAt(0))) {
throwError({}, MessageUnterminatedRegExp);
} else if (classMarker) {
if (ch === ']') {
@@ -37431,55 +33845,46 @@
} else if (ch === '[') {
classMarker = true;
}
}
}
-
if (!terminated) {
throwError({}, MessageUnterminatedRegExp);
- } // Exclude leading and trailing slash.
+ }
-
+ // Exclude leading and trailing slash.
body = str.substr(1, str.length - 2);
return {
value: body,
literal: str
};
}
-
function scanRegExpFlags() {
var ch, str, flags;
str = '';
flags = '';
-
while (index < length) {
ch = source[index];
-
if (!isIdentifierPart(ch.charCodeAt(0))) {
break;
}
-
++index;
-
if (ch === '\\' && index < length) {
throwError({}, MessageUnexpectedToken, ILLEGAL$1);
} else {
flags += ch;
str += ch;
}
}
-
if (flags.search(/[^gimuy]/g) >= 0) {
throwError({}, MessageInvalidRegExp, flags);
}
-
return {
value: flags,
literal: str
};
}
-
function scanRegExp() {
var start, body, flags, value;
lookahead = null;
skipComment();
start = index;
@@ -37495,353 +33900,313 @@
},
start: start,
end: index
};
}
-
function isIdentifierName(token) {
return token.type === TokenIdentifier || token.type === TokenKeyword || token.type === TokenBooleanLiteral || token.type === TokenNullLiteral;
}
-
function advance() {
skipComment();
-
if (index >= length) {
return {
type: TokenEOF,
start: index,
end: index
};
}
-
const ch = source.charCodeAt(index);
-
if (isIdentifierStart(ch)) {
return scanIdentifier();
- } // Very common: ( and ) and ;
+ }
-
+ // Very common: ( and ) and ;
if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
return scanPunctuator();
- } // String literal starts with single quote (U+0027) or double quote (U+0022).
+ }
-
+ // String literal starts with single quote (U+0027) or double quote (U+0022).
if (ch === 0x27 || ch === 0x22) {
return scanStringLiteral();
- } // Dot (.) U+002E can also start a floating-point number, hence the need
- // to check the next character.
+ }
-
+ // Dot (.) U+002E can also start a floating-point number, hence the need
+ // to check the next character.
if (ch === 0x2E) {
if (isDecimalDigit(source.charCodeAt(index + 1))) {
return scanNumericLiteral();
}
-
return scanPunctuator();
}
-
if (isDecimalDigit(ch)) {
return scanNumericLiteral();
}
-
return scanPunctuator();
}
-
function lex() {
const token = lookahead;
index = token.end;
lookahead = advance();
index = token.end;
return token;
}
-
function peek() {
const pos = index;
lookahead = advance();
index = pos;
}
-
function finishArrayExpression(elements) {
const node = new ASTNode(SyntaxArrayExpression);
node.elements = elements;
return node;
}
-
function finishBinaryExpression(operator, left, right) {
const node = new ASTNode(operator === '||' || operator === '&&' ? SyntaxLogicalExpression : SyntaxBinaryExpression);
node.operator = operator;
node.left = left;
node.right = right;
return node;
}
-
function finishCallExpression(callee, args) {
const node = new ASTNode(SyntaxCallExpression);
node.callee = callee;
node.arguments = args;
return node;
}
-
function finishConditionalExpression(test, consequent, alternate) {
const node = new ASTNode(SyntaxConditionalExpression);
node.test = test;
node.consequent = consequent;
node.alternate = alternate;
return node;
}
-
function finishIdentifier(name) {
const node = new ASTNode(SyntaxIdentifier);
node.name = name;
return node;
}
-
function finishLiteral(token) {
const node = new ASTNode(SyntaxLiteral);
node.value = token.value;
node.raw = source.slice(token.start, token.end);
-
if (token.regex) {
if (node.raw === '//') {
node.raw = '/(?:)/';
}
-
node.regex = token.regex;
}
-
return node;
}
-
function finishMemberExpression(accessor, object, property) {
const node = new ASTNode(SyntaxMemberExpression);
node.computed = accessor === '[';
node.object = object;
node.property = property;
if (!node.computed) property.member = true;
return node;
}
-
function finishObjectExpression(properties) {
const node = new ASTNode(SyntaxObjectExpression);
node.properties = properties;
return node;
}
-
function finishProperty(kind, key, value) {
const node = new ASTNode(SyntaxProperty);
node.key = key;
node.value = value;
node.kind = kind;
return node;
}
-
function finishUnaryExpression(operator, argument) {
const node = new ASTNode(SyntaxUnaryExpression);
node.operator = operator;
node.argument = argument;
node.prefix = true;
return node;
- } // Throw an exception
+ }
+ // Throw an exception
function throwError(token, messageFormat) {
var error,
- args = Array.prototype.slice.call(arguments, 2),
- msg = messageFormat.replace(/%(\d)/g, (whole, index) => {
- assert(index < args.length, 'Message reference must be in range');
- return args[index];
- });
+ args = Array.prototype.slice.call(arguments, 2),
+ msg = messageFormat.replace(/%(\d)/g, (whole, index) => {
+ assert(index < args.length, 'Message reference must be in range');
+ return args[index];
+ });
error = new Error(msg);
error.index = index;
error.description = msg;
throw error;
- } // Throw an exception because of the token.
+ }
+ // Throw an exception because of the token.
function throwUnexpected(token) {
if (token.type === TokenEOF) {
throwError(token, MessageUnexpectedEOS);
}
-
if (token.type === TokenNumericLiteral) {
throwError(token, MessageUnexpectedNumber);
}
-
if (token.type === TokenStringLiteral) {
throwError(token, MessageUnexpectedString);
}
-
if (token.type === TokenIdentifier) {
throwError(token, MessageUnexpectedIdentifier);
}
-
if (token.type === TokenKeyword) {
throwError(token, MessageUnexpectedReserved);
- } // BooleanLiteral, NullLiteral, or Punctuator.
+ }
-
+ // BooleanLiteral, NullLiteral, or Punctuator.
throwError(token, MessageUnexpectedToken, token.value);
- } // Expect the next token to match the specified punctuator.
+ }
+
+ // Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
-
function expect(value) {
const token = lex();
-
if (token.type !== TokenPunctuator || token.value !== value) {
throwUnexpected(token);
}
- } // Return true if the next token matches the specified punctuator.
+ }
+ // Return true if the next token matches the specified punctuator.
function match(value) {
return lookahead.type === TokenPunctuator && lookahead.value === value;
- } // Return true if the next token matches the specified keyword
+ }
+ // Return true if the next token matches the specified keyword
function matchKeyword(keyword) {
return lookahead.type === TokenKeyword && lookahead.value === keyword;
- } // 11.1.4 Array Initialiser
+ }
+ // 11.1.4 Array Initialiser
function parseArrayInitialiser() {
const elements = [];
index = lookahead.start;
expect('[');
-
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else {
elements.push(parseConditionalExpression());
-
if (!match(']')) {
expect(',');
}
}
}
-
lex();
return finishArrayExpression(elements);
- } // 11.1.5 Object Initialiser
+ }
+ // 11.1.5 Object Initialiser
function parseObjectPropertyKey() {
index = lookahead.start;
- const token = lex(); // Note: This function is called only from parseObjectProperty(), where
+ const token = lex();
+
+ // Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
if (token.type === TokenStringLiteral || token.type === TokenNumericLiteral) {
if (token.octal) {
throwError(token, MessageStrictOctalLiteral);
}
-
return finishLiteral(token);
}
-
return finishIdentifier(token.value);
}
-
function parseObjectProperty() {
var token, key, id, value;
index = lookahead.start;
token = lookahead;
-
if (token.type === TokenIdentifier) {
id = parseObjectPropertyKey();
expect(':');
value = parseConditionalExpression();
return finishProperty('init', id, value);
}
-
if (token.type === TokenEOF || token.type === TokenPunctuator) {
throwUnexpected(token);
} else {
key = parseObjectPropertyKey();
expect(':');
value = parseConditionalExpression();
return finishProperty('init', key, value);
}
}
-
function parseObjectInitialiser() {
var properties = [],
- property,
- name,
- key,
- map = {},
- toString = String;
+ property,
+ name,
+ key,
+ map = {},
+ toString = String;
index = lookahead.start;
expect('{');
-
while (!match('}')) {
property = parseObjectProperty();
-
if (property.key.type === SyntaxIdentifier) {
name = property.key.name;
} else {
name = toString(property.key.value);
}
-
key = '$' + name;
-
if (Object.prototype.hasOwnProperty.call(map, key)) {
throwError({}, MessageStrictDuplicateProperty);
} else {
map[key] = true;
}
-
properties.push(property);
-
if (!match('}')) {
expect(',');
}
}
-
expect('}');
return finishObjectExpression(properties);
- } // 11.1.6 The Grouping Operator
+ }
+ // 11.1.6 The Grouping Operator
function parseGroupExpression() {
expect('(');
const expr = parseExpression();
expect(')');
return expr;
- } // 11.1 Primary Expressions
+ }
+ // 11.1 Primary Expressions
const legalKeywords = {
'if': 1
};
-
function parsePrimaryExpression() {
var type, token, expr;
-
if (match('(')) {
return parseGroupExpression();
}
-
if (match('[')) {
return parseArrayInitialiser();
}
-
if (match('{')) {
return parseObjectInitialiser();
}
-
type = lookahead.type;
index = lookahead.start;
-
if (type === TokenIdentifier || legalKeywords[lookahead.value]) {
expr = finishIdentifier(lex().value);
} else if (type === TokenStringLiteral || type === TokenNumericLiteral) {
if (lookahead.octal) {
throwError(lookahead, MessageStrictOctalLiteral);
}
-
expr = finishLiteral(lex());
} else if (type === TokenKeyword) {
throw new Error(DISABLED);
} else if (type === TokenBooleanLiteral) {
token = lex();
@@ -37855,62 +34220,51 @@
expr = finishLiteral(scanRegExp());
peek();
} else {
throwUnexpected(lex());
}
-
return expr;
- } // 11.2 Left-Hand-Side Expressions
+ }
+ // 11.2 Left-Hand-Side Expressions
function parseArguments() {
const args = [];
expect('(');
-
if (!match(')')) {
while (index < length) {
args.push(parseConditionalExpression());
-
if (match(')')) {
break;
}
-
expect(',');
}
}
-
expect(')');
return args;
}
-
function parseNonComputedProperty() {
index = lookahead.start;
const token = lex();
-
if (!isIdentifierName(token)) {
throwUnexpected(token);
}
-
return finishIdentifier(token.value);
}
-
function parseNonComputedMember() {
expect('.');
return parseNonComputedProperty();
}
-
function parseComputedMember() {
expect('[');
const expr = parseExpression();
expect(']');
return expr;
}
-
function parseLeftHandSideExpressionAllowCall() {
var expr, args, property;
expr = parsePrimaryExpression();
-
for (;;) {
if (match('.')) {
property = parseNonComputedMember();
expr = finishMemberExpression('.', expr, property);
} else if (match('(')) {
@@ -37921,31 +34275,29 @@
expr = finishMemberExpression('[', expr, property);
} else {
break;
}
}
-
return expr;
- } // 11.3 Postfix Expressions
+ }
+ // 11.3 Postfix Expressions
function parsePostfixExpression() {
const expr = parseLeftHandSideExpressionAllowCall();
-
if (lookahead.type === TokenPunctuator) {
if (match('++') || match('--')) {
throw new Error(DISABLED);
}
}
-
return expr;
- } // 11.4 Unary Operators
+ }
+ // 11.4 Unary Operators
function parseUnaryExpression() {
var token, expr;
-
if (lookahead.type !== TokenPunctuator && lookahead.type !== TokenKeyword) {
expr = parsePostfixExpression();
} else if (match('++') || match('--')) {
throw new Error(DISABLED);
} else if (match('+') || match('-') || match('~') || match('!')) {
@@ -37955,179 +34307,155 @@
} else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
throw new Error(DISABLED);
} else {
expr = parsePostfixExpression();
}
-
return expr;
}
-
function binaryPrecedence(token) {
let prec = 0;
-
if (token.type !== TokenPunctuator && token.type !== TokenKeyword) {
return 0;
}
-
switch (token.value) {
case '||':
prec = 1;
break;
-
case '&&':
prec = 2;
break;
-
case '|':
prec = 3;
break;
-
case '^':
prec = 4;
break;
-
case '&':
prec = 5;
break;
-
case '==':
case '!=':
case '===':
case '!==':
prec = 6;
break;
-
case '<':
case '>':
case '<=':
case '>=':
case 'instanceof':
case 'in':
prec = 7;
break;
-
case '<<':
case '>>':
case '>>>':
prec = 8;
break;
-
case '+':
case '-':
prec = 9;
break;
-
case '*':
case '/':
case '%':
prec = 11;
break;
}
-
return prec;
- } // 11.5 Multiplicative Operators
+ }
+
+ // 11.5 Multiplicative Operators
// 11.6 Additive Operators
// 11.7 Bitwise Shift Operators
// 11.8 Relational Operators
// 11.9 Equality Operators
// 11.10 Binary Bitwise Operators
// 11.11 Binary Logical Operators
-
function parseBinaryExpression() {
var marker, markers, expr, token, prec, stack, right, operator, left, i;
marker = lookahead;
left = parseUnaryExpression();
token = lookahead;
prec = binaryPrecedence(token);
-
if (prec === 0) {
return left;
}
-
token.prec = prec;
lex();
markers = [marker, lookahead];
right = parseUnaryExpression();
stack = [left, token, right];
-
while ((prec = binaryPrecedence(lookahead)) > 0) {
// Reduce: make a binary expression from the three topmost entries.
while (stack.length > 2 && prec <= stack[stack.length - 2].prec) {
right = stack.pop();
operator = stack.pop().value;
left = stack.pop();
markers.pop();
expr = finishBinaryExpression(operator, left, right);
stack.push(expr);
- } // Shift.
+ }
-
+ // Shift.
token = lex();
token.prec = prec;
stack.push(token);
markers.push(lookahead);
expr = parseUnaryExpression();
stack.push(expr);
- } // Final reduce to clean-up the stack.
+ }
-
+ // Final reduce to clean-up the stack.
i = stack.length - 1;
expr = stack[i];
markers.pop();
-
while (i > 1) {
markers.pop();
expr = finishBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
i -= 2;
}
-
return expr;
- } // 11.12 Conditional Operator
+ }
+ // 11.12 Conditional Operator
function parseConditionalExpression() {
var expr, consequent, alternate;
expr = parseBinaryExpression();
-
if (match('?')) {
lex();
consequent = parseConditionalExpression();
expect(':');
alternate = parseConditionalExpression();
expr = finishConditionalExpression(expr, consequent, alternate);
}
-
return expr;
- } // 11.14 Comma Operator
+ }
+ // 11.14 Comma Operator
function parseExpression() {
const expr = parseConditionalExpression();
-
if (match(',')) {
throw new Error(DISABLED); // no sequence expressions
}
-
return expr;
}
-
function parser$1(code) {
source = code;
index = 0;
length = source.length;
lookahead = null;
peek();
const expr = parseExpression();
-
if (lookahead.type !== TokenEOF) {
throw new Error('Unexpect token after expression.');
}
-
return expr;
}
-
var Constants = {
NaN: 'NaN',
E: 'Math.E',
LN2: 'Math.LN2',
LN10: 'Math.LN10',
@@ -38137,30 +34465,25 @@
SQRT1_2: 'Math.SQRT1_2',
SQRT2: 'Math.SQRT2',
MIN_VALUE: 'Number.MIN_VALUE',
MAX_VALUE: 'Number.MAX_VALUE'
};
-
function Functions(codegen) {
function fncall(name, args, cast, type) {
let obj = codegen(args[0]);
-
if (cast) {
obj = cast + '(' + obj + ')';
if (cast.lastIndexOf('new ', 0) === 0) obj = '(' + obj + ')';
}
-
return obj + '.' + name + (type < 0 ? '' : type === 0 ? '()' : '(' + args.slice(1).map(codegen).join(',') + ')');
}
-
function fn(name, cast, type) {
return args => fncall(name, args, cast, type);
}
-
const DATE = 'new Date',
- STRING = 'String',
- REGEXP = 'RegExp';
+ STRING = 'String',
+ REGEXP = 'RegExp';
return {
// MATH functions
isNaN: 'Number.isNaN',
isFinite: 'Number.isFinite',
abs: 'Math.abs',
@@ -38170,10 +34493,11 @@
atan2: 'Math.atan2',
ceil: 'Math.ceil',
cos: 'Math.cos',
exp: 'Math.exp',
floor: 'Math.floor',
+ hypot: 'Math.hypot',
log: 'Math.log',
max: 'Math.max',
min: 'Math.min',
pow: 'Math.pow',
random: 'Math.random',
@@ -38229,41 +34553,36 @@
const a = args.map(codegen);
return '(' + a[0] + '?' + a[1] + ':' + a[2] + ')';
}
};
}
-
function stripQuotes(s) {
const n = s && s.length - 1;
return n && (s[0] === '"' && s[n] === '"' || s[0] === '\'' && s[n] === '\'') ? s.slice(1, -1) : s;
}
-
function codegen(opt) {
opt = opt || {};
const allowed = opt.allowed ? toSet(opt.allowed) : {},
- forbidden = opt.forbidden ? toSet(opt.forbidden) : {},
- constants = opt.constants || Constants,
- functions = (opt.functions || Functions)(visit),
- globalvar = opt.globalvar,
- fieldvar = opt.fieldvar,
- outputGlobal = isFunction(globalvar) ? globalvar : id => "".concat(globalvar, "[\"").concat(id, "\"]");
+ forbidden = opt.forbidden ? toSet(opt.forbidden) : {},
+ constants = opt.constants || Constants,
+ functions = (opt.functions || Functions)(visit),
+ globalvar = opt.globalvar,
+ fieldvar = opt.fieldvar,
+ outputGlobal = isFunction(globalvar) ? globalvar : id => `${globalvar}["${id}"]`;
let globals = {},
- fields = {},
- memberDepth = 0;
-
+ fields = {},
+ memberDepth = 0;
function visit(ast) {
if (isString(ast)) return ast;
const generator = Generators[ast.type];
if (generator == null) error('Unsupported type: ' + ast.type);
return generator(ast);
}
-
const Generators = {
Literal: n => n.raw,
Identifier: n => {
const id = n.name;
-
if (memberDepth > 0) {
return id;
} else if (has$1(forbidden, id)) {
return error('Illegal identifier: ' + id);
} else if (has$1(constants, id)) {
@@ -38275,30 +34594,27 @@
return outputGlobal(id);
}
},
MemberExpression: n => {
const d = !n.computed,
- o = visit(n.object);
+ o = visit(n.object);
if (d) memberDepth += 1;
const p = visit(n.property);
-
if (o === fieldvar) {
// strip quotes to sanitize field name (#1653)
fields[stripQuotes(p)] = 1;
}
-
if (d) memberDepth -= 1;
return o + (d ? '.' + p : '[' + p + ']');
},
CallExpression: n => {
if (n.callee.type !== 'Identifier') {
error('Illegal callee type: ' + n.callee.type);
}
-
const callee = n.callee.name,
- args = n.arguments,
- fn = has$1(functions, callee) && functions[callee];
+ args = n.arguments,
+ fn = has$1(functions, callee) && functions[callee];
if (!fn) error('Unrecognized function: ' + callee);
return isFunction(fn) ? fn(args) : fn + '(' + args.map(visit).join(',') + ')';
},
ArrayExpression: n => '[' + n.elements.map(visit).join(',') + ']',
BinaryExpression: n => '(' + visit(n.left) + ' ' + n.operator + ' ' + visit(n.right) + ')',
@@ -38311,62 +34627,67 @@
const k = visit(n.key);
memberDepth -= 1;
return k + ':' + visit(n.value);
}
};
-
function codegen(ast) {
const result = {
code: visit(ast),
globals: Object.keys(globals),
fields: Object.keys(fields)
};
globals = {};
fields = {};
return result;
}
-
codegen.functions = functions;
codegen.constants = constants;
return codegen;
}
+ // Registers vega-util field accessors to protect against XSS attacks
+ const SELECTION_GETTER = Symbol('vega_selection_getter');
+ function getter(f) {
+ if (!f.getter || !f.getter[SELECTION_GETTER]) {
+ f.getter = field$1(f.field);
+ f.getter[SELECTION_GETTER] = true;
+ }
+ return f.getter;
+ }
const Intersect = 'intersect';
const Union = 'union';
const VlMulti = 'vlMulti';
const VlPoint = 'vlPoint';
const Or = 'or';
const And = 'and';
const SelectionId = '_vgsid_';
const $selectionId = field$1(SelectionId);
const TYPE_ENUM = 'E',
- TYPE_RANGE_INC = 'R',
- TYPE_RANGE_EXC = 'R-E',
- TYPE_RANGE_LE = 'R-LE',
- TYPE_RANGE_RE = 'R-RE',
- UNIT_INDEX = 'index:unit'; // TODO: revisit date coercion?
+ TYPE_RANGE_INC = 'R',
+ TYPE_RANGE_EXC = 'R-E',
+ TYPE_RANGE_LE = 'R-LE',
+ TYPE_RANGE_RE = 'R-RE',
+ UNIT_INDEX = 'index:unit';
+ // TODO: revisit date coercion?
function testPoint(datum, entry) {
var fields = entry.fields,
- values = entry.values,
- n = fields.length,
- i = 0,
- dval,
- f;
-
+ values = entry.values,
+ n = fields.length,
+ i = 0,
+ dval,
+ f;
for (; i < n; ++i) {
f = fields[i];
- f.getter = field$1.getter || field$1(f.field);
- dval = f.getter(datum);
+ dval = getter(f)(datum);
if (isDate$1(dval)) dval = toNumber(dval);
if (isDate$1(values[i])) values[i] = toNumber(values[i]);
- if (isDate$1(values[i][0])) values[i] = values[i].map(toNumber);
-
+ if (isArray(values[i]) && isDate$1(values[i][0])) values[i] = values[i].map(toNumber);
if (f.type === TYPE_ENUM) {
// Enumerated fields can either specify individual values (single/multi selections)
// or an array of values (interval selections).
- if (isArray(values[i]) ? values[i].indexOf(dval) < 0 : dval !== values[i]) {
+ if (isArray(values[i]) ? !values[i].includes(dval) : dval !== values[i]) {
return false;
}
} else {
if (f.type === TYPE_RANGE_INC) {
if (!inrange(dval, values[i])) return false;
@@ -38379,13 +34700,13 @@
} else if (f.type === TYPE_RANGE_LE) {
if (!inrange(dval, values[i], false, true)) return false;
}
}
}
-
return true;
}
+
/**
* Tests if a tuple is contained within an interactive selection.
* @param {string} name - The name of the data set representing the selection.
* Tuples in the dataset are of the form
* {unit: string, fields: array<fielddef>, values: array<*>}.
@@ -38396,91 +34717,86 @@
* @param {object} datum - The tuple to test for inclusion.
* @param {string} op - The set operation for combining selections.
* One of 'intersect' or 'union' (default).
* @return {boolean} - True if the datum is in the selection, false otherwise.
*/
-
-
function selectionTest(name, datum, op) {
var data = this.context.data[name],
- entries = data ? data.values.value : [],
- unitIdx = data ? data[UNIT_INDEX] && data[UNIT_INDEX].value : undefined,
- intersect = op === Intersect,
- n = entries.length,
- i = 0,
- entry,
- miss,
- count,
- unit,
- b;
-
+ entries = data ? data.values.value : [],
+ unitIdx = data ? data[UNIT_INDEX] && data[UNIT_INDEX].value : undefined,
+ intersect = op === Intersect,
+ n = entries.length,
+ i = 0,
+ entry,
+ miss,
+ count,
+ unit,
+ b;
for (; i < n; ++i) {
entry = entries[i];
-
if (unitIdx && intersect) {
// multi selections union within the same unit and intersect across units.
miss = miss || {};
- count = miss[unit = entry.unit] || 0; // if we've already matched this unit, skip.
+ count = miss[unit = entry.unit] || 0;
+ // if we've already matched this unit, skip.
if (count === -1) continue;
b = testPoint(datum, entry);
- miss[unit] = b ? -1 : ++count; // if we match and there are no other units return true
- // if we've missed against all tuples in this unit return false
+ miss[unit] = b ? -1 : ++count;
+ // if we match and there are no other units return true
+ // if we've missed against all tuples in this unit return false
if (b && unitIdx.size === 1) return true;
if (!b && count === unitIdx.get(unit).count) return false;
} else {
- b = testPoint(datum, entry); // if we find a miss and we do require intersection return false
- // if we find a match and we don't require intersection return true
+ b = testPoint(datum, entry);
+ // if we find a miss and we do require intersection return false
+ // if we find a match and we don't require intersection return true
if (intersect ^ b) return b;
}
- } // if intersecting and we made it here, then we saw no misses
+ }
+
+ // if intersecting and we made it here, then we saw no misses
// if not intersecting, then we saw no matches
// if no active selections, return false
-
-
return n && intersect;
}
-
const bisect = bisector($selectionId),
- bisectLeft = bisect.left,
- bisectRight = bisect.right;
-
+ bisectLeft = bisect.left,
+ bisectRight = bisect.right;
function selectionIdTest(name, datum, op) {
const data = this.context.data[name],
- entries = data ? data.values.value : [],
- unitIdx = data ? data[UNIT_INDEX] && data[UNIT_INDEX].value : undefined,
- intersect = op === Intersect,
- value = $selectionId(datum),
- index = bisectLeft(entries, value);
+ entries = data ? data.values.value : [],
+ unitIdx = data ? data[UNIT_INDEX] && data[UNIT_INDEX].value : undefined,
+ intersect = op === Intersect,
+ value = $selectionId(datum),
+ index = bisectLeft(entries, value);
if (index === entries.length) return false;
if ($selectionId(entries[index]) !== value) return false;
-
if (unitIdx && intersect) {
if (unitIdx.size === 1) return true;
if (bisectRight(entries, value) - index < unitIdx.size) return false;
}
-
return true;
}
+
/**
* Maps an array of scene graph items to an array of selection tuples.
* @param {string} name - The name of the dataset representing the selection.
* @param {string} base - The base object that generated tuples extend.
*
* @returns {array} An array of selection entries for the given unit.
*/
-
-
function selectionTuples(array, base) {
return array.map(x => extend$1(base.fields ? {
- values: base.fields.map(f => (f.getter || (f.getter = field$1(f.field)))(x.datum))
+ values: base.fields.map(f => getter(f)(x.datum))
} : {
[SelectionId]: $selectionId(x.datum)
}, base));
}
+
/**
* Resolves selection for use as a scale domain or reads via the API.
* @param {string} name - The name of the dataset representing the selection
* @param {string} [op='union'] - The set operation for combining selections.
* One of 'intersect' or 'union' (default).
@@ -38490,56 +34806,54 @@
* selections, and thus the resolved tuple should reflect this name.
* This parameter allows us to reflect this change without triggering
* a major version bump for Vega.
* @returns {object} An object of selected fields and values.
*/
-
-
function selectionResolve(name, op, isMulti, vl5) {
var data = this.context.data[name],
- entries = data ? data.values.value : [],
- resolved = {},
- multiRes = {},
- types = {},
- entry,
- fields,
- values,
- unit,
- field,
- value,
- res,
- resUnit,
- type,
- union,
- n = entries.length,
- i = 0,
- j,
- m; // First union all entries within the same unit.
+ entries = data ? data.values.value : [],
+ resolved = {},
+ multiRes = {},
+ types = {},
+ entry,
+ fields,
+ values,
+ unit,
+ field,
+ value,
+ res,
+ resUnit,
+ type,
+ union,
+ n = entries.length,
+ i = 0,
+ j,
+ m;
+ // First union all entries within the same unit.
for (; i < n; ++i) {
entry = entries[i];
unit = entry.unit;
fields = entry.fields;
values = entry.values;
-
if (fields && values) {
// Intentional selection stores
for (j = 0, m = fields.length; j < m; ++j) {
field = fields[j];
res = resolved[field.field] || (resolved[field.field] = {});
resUnit = res[unit] || (res[unit] = []);
types[field.field] = type = field.type.charAt(0);
- union = ops["".concat(type, "_union")];
+ union = ops[`${type}_union`];
res[unit] = union(resUnit, array$5(values[j]));
- } // If the same multi-selection is repeated over views and projected over
+ }
+
+ // If the same multi-selection is repeated over views and projected over
// an encoding, it may operate over different fields making it especially
// tricky to reliably resolve it. At best, we can de-dupe identical entries
// but doing so may be more computationally expensive than it is worth.
// Instead, for now, we simply transform our store representation into
// a more human-friendly one.
-
-
if (isMulti) {
resUnit = multiRes[unit] || (multiRes[unit] = []);
resUnit.push(array$5(values).reduce((obj, curr, j) => (obj[fields[j].field] = curr, obj), {}));
}
} else {
@@ -38547,216 +34861,185 @@
field = SelectionId;
value = $selectionId(entry);
res = resolved[field] || (resolved[field] = {});
resUnit = res[unit] || (res[unit] = []);
resUnit.push(value);
-
if (isMulti) {
resUnit = multiRes[unit] || (multiRes[unit] = []);
resUnit.push({
[SelectionId]: value
});
}
}
- } // Then resolve fields across units as per the op.
+ }
-
+ // Then resolve fields across units as per the op.
op = op || Union;
-
if (resolved[SelectionId]) {
- resolved[SelectionId] = ops["".concat(SelectionId, "_").concat(op)](...Object.values(resolved[SelectionId]));
+ resolved[SelectionId] = ops[`${SelectionId}_${op}`](...Object.values(resolved[SelectionId]));
} else {
Object.keys(resolved).forEach(field => {
- resolved[field] = Object.keys(resolved[field]).map(unit => resolved[field][unit]).reduce((acc, curr) => acc === undefined ? curr : ops["".concat(types[field], "_").concat(op)](acc, curr));
+ resolved[field] = Object.keys(resolved[field]).map(unit => resolved[field][unit]).reduce((acc, curr) => acc === undefined ? curr : ops[`${types[field]}_${op}`](acc, curr));
});
}
-
entries = Object.keys(multiRes);
-
if (isMulti && entries.length) {
const key = vl5 ? VlPoint : VlMulti;
resolved[key] = op === Union ? {
[Or]: entries.reduce((acc, k) => (acc.push(...multiRes[k]), acc), [])
} : {
[And]: entries.map(k => ({
[Or]: multiRes[k]
}))
};
}
-
return resolved;
}
-
var ops = {
- ["".concat(SelectionId, "_union")]: union,
- ["".concat(SelectionId, "_intersect")]: intersection,
+ [`${SelectionId}_union`]: union,
+ [`${SelectionId}_intersect`]: intersection,
E_union: function (base, value) {
if (!base.length) return value;
var i = 0,
- n = value.length;
-
- for (; i < n; ++i) if (base.indexOf(value[i]) < 0) base.push(value[i]);
-
+ n = value.length;
+ for (; i < n; ++i) if (!base.includes(value[i])) base.push(value[i]);
return base;
},
E_intersect: function (base, value) {
- return !base.length ? value : base.filter(v => value.indexOf(v) >= 0);
+ return !base.length ? value : base.filter(v => value.includes(v));
},
R_union: function (base, value) {
var lo = toNumber(value[0]),
- hi = toNumber(value[1]);
-
+ hi = toNumber(value[1]);
if (lo > hi) {
lo = value[1];
hi = value[0];
}
-
if (!base.length) return [lo, hi];
if (base[0] > lo) base[0] = lo;
if (base[1] < hi) base[1] = hi;
return base;
},
R_intersect: function (base, value) {
var lo = toNumber(value[0]),
- hi = toNumber(value[1]);
-
+ hi = toNumber(value[1]);
if (lo > hi) {
lo = value[1];
hi = value[0];
}
-
if (!base.length) return [lo, hi];
-
if (hi < base[0] || base[1] < lo) {
return [];
} else {
if (base[0] < lo) base[0] = lo;
if (base[1] > hi) base[1] = hi;
}
-
return base;
}
};
const DataPrefix$1 = ':',
- IndexPrefix$1 = '@';
-
+ IndexPrefix$1 = '@';
function selectionVisitor(name, args, scope, params) {
if (args[0].type !== Literal) error('First argument to selection functions must be a string literal.');
const data = args[0].value,
- op = args.length >= 2 && peek$1(args).value,
- field = 'unit',
- indexName = IndexPrefix$1 + field,
- dataName = DataPrefix$1 + data; // eslint-disable-next-line no-prototype-builtins
+ op = args.length >= 2 && peek$1(args).value,
+ field = 'unit',
+ indexName = IndexPrefix$1 + field,
+ dataName = DataPrefix$1 + data;
+ // eslint-disable-next-line no-prototype-builtins
if (op === Intersect && !has$1(params, indexName)) {
params[indexName] = scope.getData(data).indataRef(scope, field);
- } // eslint-disable-next-line no-prototype-builtins
+ }
-
+ // eslint-disable-next-line no-prototype-builtins
if (!has$1(params, dataName)) {
params[dataName] = scope.getData(data).tuplesRef();
}
}
function data$1(name) {
const data = this.context.data[name];
return data ? data.values.value : [];
}
-
function indata(name, field, value) {
const index = this.context.data[name]['index:' + field],
- entry = index ? index.value.get(value) : undefined;
+ entry = index ? index.value.get(value) : undefined;
return entry ? entry.count : entry;
}
-
function setdata(name, tuples) {
const df = this.context.dataflow,
- data = this.context.data[name],
- input = data.input;
+ data = this.context.data[name],
+ input = data.input;
df.pulse(input, df.changeset().remove(truthy).insert(tuples));
return 1;
}
-
function encode(item, name, retval) {
if (item) {
const df = this.context.dataflow,
- target = item.mark.source;
+ target = item.mark.source;
df.pulse(target, df.changeset().encode(item, name));
}
-
return retval !== undefined ? retval : item;
}
-
const wrap = method => function (value, spec) {
const locale = this.context.dataflow.locale();
- return locale[method](spec)(value);
+ return value === null ? 'null' : locale[method](spec)(value);
};
-
const format = wrap('format');
const timeFormat = wrap('timeFormat');
const utcFormat = wrap('utcFormat');
const timeParse = wrap('timeParse');
const utcParse = wrap('utcParse');
const dateObj = new Date(2000, 0, 1);
-
function time(month, day, specifier) {
if (!Number.isInteger(month) || !Number.isInteger(day)) return '';
dateObj.setYear(2000);
dateObj.setMonth(month);
dateObj.setDate(day);
return timeFormat.call(this, dateObj, specifier);
}
-
function monthFormat(month) {
return time.call(this, month, 1, '%B');
}
-
function monthAbbrevFormat(month) {
return time.call(this, month, 1, '%b');
}
-
function dayFormat(day) {
return time.call(this, 0, 2 + day, '%A');
}
-
function dayAbbrevFormat(day) {
return time.call(this, 0, 2 + day, '%a');
}
-
const DataPrefix = ':';
const IndexPrefix = '@';
const ScalePrefix = '%';
const SignalPrefix = '$';
-
function dataVisitor(name, args, scope, params) {
if (args[0].type !== Literal) {
error('First argument to data functions must be a string literal.');
}
-
const data = args[0].value,
- dataName = DataPrefix + data;
-
+ dataName = DataPrefix + data;
if (!has$1(dataName, params)) {
try {
params[dataName] = scope.getData(data).tuplesRef();
- } catch (err) {// if data set does not exist, there's nothing to track
+ } catch (err) {
+ // if data set does not exist, there's nothing to track
}
}
}
-
function indataVisitor(name, args, scope, params) {
if (args[0].type !== Literal) error('First argument to indata must be a string literal.');
if (args[1].type !== Literal) error('Second argument to indata must be a string literal.');
const data = args[0].value,
- field = args[1].value,
- indexName = IndexPrefix + field;
-
+ field = args[1].value,
+ indexName = IndexPrefix + field;
if (!has$1(indexName, params)) {
params[indexName] = scope.getData(data).indataRef(scope, field);
}
}
-
function scaleVisitor(name, args, scope, params) {
if (args[0].type === Literal) {
// add scale dependency
addScaleDependency(scope, params, args[0].value);
} else {
@@ -38764,47 +35047,50 @@
for (name in scope.scales) {
addScaleDependency(scope, params, name);
}
}
}
-
function addScaleDependency(scope, params, name) {
const scaleName = ScalePrefix + name;
-
if (!has$1(params, scaleName)) {
try {
params[scaleName] = scope.scaleRef(name);
- } catch (err) {// TODO: error handling? warning?
+ } catch (err) {
+ // TODO: error handling? warning?
}
}
}
-
- function getScale(name, ctx) {
- let s;
- return isFunction(name) ? name : isString(name) ? (s = ctx.scales[name]) && s.value : undefined;
+ function getScale(nameOrFunction, ctx) {
+ if (isFunction(nameOrFunction)) {
+ return nameOrFunction;
+ }
+ if (isString(nameOrFunction)) {
+ const maybeScale = ctx.scales[nameOrFunction];
+ return maybeScale && isRegisteredScale(maybeScale.value) ? maybeScale.value : undefined;
+ }
+ return undefined;
}
-
function internalScaleFunctions(codegen, fnctx, visitors) {
// add helper method to the 'this' expression function context
- fnctx.__bandwidth = s => s && s.bandwidth ? s.bandwidth() : 0; // register AST visitors for internal scale functions
+ fnctx.__bandwidth = s => s && s.bandwidth ? s.bandwidth() : 0;
-
+ // register AST visitors for internal scale functions
visitors._bandwidth = scaleVisitor;
visitors._range = scaleVisitor;
- visitors._scale = scaleVisitor; // resolve scale reference directly to the signal hash argument
+ visitors._scale = scaleVisitor;
- const ref = arg => '_[' + (arg.type === Literal ? $(ScalePrefix + arg.value) : $(ScalePrefix) + '+' + codegen(arg)) + ']'; // define and return internal scale function code generators
- // these internal functions are called by mark encoders
+ // resolve scale reference directly to the signal hash argument
+ const ref = arg => '_[' + (arg.type === Literal ? $(ScalePrefix + arg.value) : $(ScalePrefix) + '+' + codegen(arg)) + ']';
-
+ // define and return internal scale function code generators
+ // these internal functions are called by mark encoders
return {
- _bandwidth: args => "this.__bandwidth(".concat(ref(args[0]), ")"),
- _range: args => "".concat(ref(args[0]), ".range()"),
- _scale: args => "".concat(ref(args[0]), "(").concat(codegen(args[1]), ")")
+ _bandwidth: args => `this.__bandwidth(${ref(args[0])})`,
+ _range: args => `${ref(args[0])}.range()`,
+ _scale: args => `${ref(args[0])}(${codegen(args[1])})`
};
}
-
function geoMethod(methodName, globalMethod) {
return function (projection, geojson, group) {
if (projection) {
// projection defined, use it
const p = getScale(projection, (group || this).context);
@@ -38813,460 +35099,391 @@
// projection undefined, use global method
return globalMethod(geojson);
}
};
}
-
const geoArea = geoMethod('area', geoArea$1);
const geoBounds = geoMethod('bounds', geoBounds$1);
const geoCentroid = geoMethod('centroid', geoCentroid$1);
-
+ function geoScale(projection, group) {
+ const p = getScale(projection, (group || this).context);
+ return p && p.scale();
+ }
function inScope(item) {
const group = this.context.group;
let value = false;
if (group) while (item) {
if (item === group) {
value = true;
break;
}
-
item = item.mark.group;
}
return value;
}
-
function log(df, method, args) {
try {
df[method].apply(df, ['EXPRESSION'].concat([].slice.call(args)));
} catch (err) {
df.warn(err);
}
-
return args[args.length - 1];
}
-
function warn() {
return log(this.context.dataflow, 'warn', arguments);
}
-
function info() {
return log(this.context.dataflow, 'info', arguments);
}
-
function debug() {
return log(this.context.dataflow, 'debug', arguments);
}
+ // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
function channel_luminance_value(channelValue) {
const val = channelValue / 255;
-
if (val <= 0.03928) {
return val / 12.92;
}
-
return Math.pow((val + 0.055) / 1.055, 2.4);
}
-
function luminance(color) {
const c = rgb$1(color),
- r = channel_luminance_value(c.r),
- g = channel_luminance_value(c.g),
- b = channel_luminance_value(c.b);
+ r = channel_luminance_value(c.r),
+ g = channel_luminance_value(c.g),
+ b = channel_luminance_value(c.b);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
- } // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
+ }
-
+ // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
function contrast(color1, color2) {
const lum1 = luminance(color1),
- lum2 = luminance(color2),
- lumL = Math.max(lum1, lum2),
- lumD = Math.min(lum1, lum2);
+ lum2 = luminance(color2),
+ lumL = Math.max(lum1, lum2),
+ lumD = Math.min(lum1, lum2);
return (lumL + 0.05) / (lumD + 0.05);
}
-
function merge() {
const args = [].slice.call(arguments);
args.unshift({});
return extend$1(...args);
}
-
function equal(a, b) {
return a === b || a !== a && b !== b ? true : isArray(a) ? isArray(b) && a.length === b.length ? equalArray(a, b) : false : isObject(a) && isObject(b) ? equalObject(a, b) : false;
}
-
function equalArray(a, b) {
for (let i = 0, n = a.length; i < n; ++i) {
if (!equal(a[i], b[i])) return false;
}
-
return true;
}
-
function equalObject(a, b) {
for (const key in a) {
if (!equal(a[key], b[key])) return false;
}
-
return true;
}
-
function removePredicate(props) {
return _ => equalObject(props, _);
}
-
function modify(name, insert, remove, toggle, modify, values) {
const df = this.context.dataflow,
- data = this.context.data[name],
- input = data.input,
- stamp = df.stamp();
+ data = this.context.data[name],
+ input = data.input,
+ stamp = df.stamp();
let changes = data.changes,
- predicate,
- key;
-
+ predicate,
+ key;
if (df._trigger === false || !(input.value.length || insert || toggle)) {
// nothing to do!
return 0;
}
-
if (!changes || changes.stamp < stamp) {
data.changes = changes = df.changeset();
changes.stamp = stamp;
df.runAfter(() => {
data.modified = true;
df.pulse(input, changes).run();
}, true, 1);
}
-
if (remove) {
predicate = remove === true ? truthy : isArray(remove) || isTuple(remove) ? remove : removePredicate(remove);
changes.remove(predicate);
}
-
if (insert) {
changes.insert(insert);
}
-
if (toggle) {
predicate = removePredicate(toggle);
-
if (input.value.some(predicate)) {
changes.remove(predicate);
} else {
changes.insert(toggle);
}
}
-
if (modify) {
for (key in values) {
changes.modify(modify, key, values[key]);
}
}
-
return 1;
}
-
function pinchDistance(event) {
const t = event.touches,
- dx = t[0].clientX - t[1].clientX,
- dy = t[0].clientY - t[1].clientY;
- return Math.sqrt(dx * dx + dy * dy);
+ dx = t[0].clientX - t[1].clientX,
+ dy = t[0].clientY - t[1].clientY;
+ return Math.hypot(dx, dy);
}
-
function pinchAngle(event) {
const t = event.touches;
return Math.atan2(t[0].clientY - t[1].clientY, t[0].clientX - t[1].clientX);
}
+ // memoize accessor functions
const accessors = {};
-
function pluck(data, name) {
const accessor = accessors[name] || (accessors[name] = field$1(name));
return isArray(data) ? data.map(accessor) : accessor(data);
}
-
function array(seq) {
return isArray(seq) || ArrayBuffer.isView(seq) ? seq : null;
}
-
function sequence(seq) {
return array(seq) || (isString(seq) ? seq : null);
}
-
function join(seq) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
-
return array(seq).join(...args);
}
-
function indexof(seq) {
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
-
return sequence(seq).indexOf(...args);
}
-
function lastindexof(seq) {
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
-
return sequence(seq).lastIndexOf(...args);
}
-
function slice(seq) {
for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
-
return sequence(seq).slice(...args);
}
-
function replace(str, pattern, repl) {
if (isFunction(repl)) error('Function argument passed to replace.');
return String(str).replace(pattern, repl);
}
-
function reverse(seq) {
return array(seq).slice().reverse();
}
-
function bandspace(count, paddingInner, paddingOuter) {
return bandSpace(count || 0, paddingInner || 0, paddingOuter || 0);
}
-
function bandwidth(name, group) {
const s = getScale(name, (group || this).context);
return s && s.bandwidth ? s.bandwidth() : 0;
}
-
function copy(name, group) {
const s = getScale(name, (group || this).context);
return s ? s.copy() : undefined;
}
-
function domain(name, group) {
const s = getScale(name, (group || this).context);
return s ? s.domain() : [];
}
-
function invert(name, range, group) {
const s = getScale(name, (group || this).context);
return !s ? undefined : isArray(range) ? (s.invertRange || s.invert)(range) : (s.invert || s.invertExtent)(range);
}
-
function range$1(name, group) {
const s = getScale(name, (group || this).context);
return s && s.range ? s.range() : [];
}
-
function scale$2(name, value, group) {
const s = getScale(name, (group || this).context);
return s ? s(value) : undefined;
}
-
function scaleGradient(scale, p0, p1, count, group) {
scale = getScale(scale, (group || this).context);
const gradient = Gradient$1(p0, p1);
let stops = scale.domain(),
- min = stops[0],
- max = peek$1(stops),
- fraction = identity$6;
-
+ min = stops[0],
+ max = peek$1(stops),
+ fraction = identity$6;
if (!(max - min)) {
// expand scale if domain has zero span, fix #1479
scale = (scale.interpolator ? scale$4('sequential')().interpolator(scale.interpolator()) : scale$4('linear')().interpolate(scale.interpolate()).range(scale.range())).domain([min = 0, max = 1]);
} else {
fraction = scaleFraction(scale, min, max);
}
-
if (scale.ticks) {
stops = scale.ticks(+count || 15);
if (min !== stops[0]) stops.unshift(min);
if (max !== peek$1(stops)) stops.push(max);
}
-
stops.forEach(_ => gradient.stop(fraction(_), scale(_)));
return gradient;
}
-
function geoShape(projection, geojson, group) {
const p = getScale(projection, (group || this).context);
return function (context) {
return p ? p.path.context(context)(geojson) : '';
};
}
-
function pathShape(path) {
let p = null;
return function (context) {
return context ? pathRender(context, p = p || parse$3(path)) : path;
};
}
-
const datum = d => d.data;
-
function treeNodes(name, context) {
const tree = data$1.call(context, name);
return tree.root && tree.root.lookup || {};
}
-
function treePath(name, source, target) {
const nodes = treeNodes(name, this),
- s = nodes[source],
- t = nodes[target];
+ s = nodes[source],
+ t = nodes[target];
return s && t ? s.path(t).map(datum) : undefined;
}
-
function treeAncestors(name, node) {
const n = treeNodes(name, this)[node];
return n ? n.ancestors().map(datum) : undefined;
}
-
const _window = () => typeof window !== 'undefined' && window || null;
-
function screen() {
const w = _window();
-
return w ? w.screen : {};
}
-
function windowSize() {
const w = _window();
-
return w ? [w.innerWidth, w.innerHeight] : [undefined, undefined];
}
-
function containerSize() {
const view = this.context.dataflow,
- el = view.container && view.container();
+ el = view.container && view.container();
return el ? [el.clientWidth, el.clientHeight] : [undefined, undefined];
}
-
function intersect(b, opt, group) {
if (!b) return [];
const [u, v] = b,
- box = new Bounds().set(u[0], u[1], v[0], v[1]),
- scene = group || this.context.dataflow.scenegraph().root;
+ box = new Bounds().set(u[0], u[1], v[0], v[1]),
+ scene = group || this.context.dataflow.scenegraph().root;
return intersect$2(scene, box, filter(opt));
}
-
function filter(opt) {
let p = null;
-
if (opt) {
const types = array$5(opt.marktype),
- names = array$5(opt.markname);
-
+ names = array$5(opt.markname);
p = _ => (!types.length || types.some(t => _.marktype === t)) && (!names.length || names.some(s => _.name === s));
}
-
return p;
}
+
/**
* Appends a new point to the lasso
- *
+ *
* @param {*} lasso the lasso in pixel space
* @param {*} x the x coordinate in pixel space
* @param {*} y the y coordinate in pixel space
* @param {*} minDist the minimum distance, in pixels, that thenew point needs to be apart from the last point
* @returns a new array containing the lasso with the new point
*/
-
-
function lassoAppend(lasso, x, y) {
let minDist = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 5;
- const last = lasso[lasso.length - 1]; // Add point to lasso if distance to last point exceed minDist or its the first point
+ lasso = array$5(lasso);
+ const last = lasso[lasso.length - 1];
- if (last === undefined || Math.sqrt((last[0] - x) ** 2 + (last[1] - y) ** 2) > minDist) {
- lasso.push([x, y]);
- return [...lasso];
- }
-
- return lasso;
+ // Add point to lasso if its the first point or distance to last point exceed minDist
+ return last === undefined || Math.hypot(last[0] - x, last[1] - y) > minDist ? [...lasso, [x, y]] : lasso;
}
+
/**
* Generates a svg path command which draws a lasso
- *
+ *
* @param {*} lasso the lasso in pixel space in the form [[x,y], [x,y], ...]
* @returns the svg path command that draws the lasso
*/
-
-
function lassoPath(lasso) {
- return (lasso !== null && lasso !== void 0 ? lasso : []).reduce((svg, _ref, i) => {
+ return array$5(lasso).reduce((svg, _ref, i) => {
let [x, y] = _ref;
- return svg += i == 0 ? "M ".concat(x, ",").concat(y, " ") : i === lasso.length - 1 ? ' Z' : "L ".concat(x, ",").concat(y, " ");
+ return svg += i == 0 ? `M ${x},${y} ` : i === lasso.length - 1 ? ' Z' : `L ${x},${y} `;
}, '');
}
+
/**
* Inverts the lasso from pixel space to an array of vega scenegraph tuples
- *
+ *
* @param {*} data the dataset
* @param {*} pixelLasso the lasso in pixel space, [[x,y], [x,y], ...]
* @param {*} unit the unit where the lasso is defined
- *
+ *
* @returns an array of vega scenegraph tuples
*/
-
-
function intersectLasso(markname, pixelLasso, unit) {
const {
x,
y,
mark
} = unit;
- const bb = new Bounds().set(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); // Get bounding box around lasso
+ const bb = new Bounds().set(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
+ // Get bounding box around lasso
for (const [px, py] of pixelLasso) {
if (px < bb.x1) bb.x1 = px;
if (px > bb.x2) bb.x2 = px;
if (py < bb.y1) bb.y1 = py;
if (py > bb.y2) bb.y2 = py;
- } // Translate bb against unit coordinates
+ }
-
+ // Translate bb against unit coordinates
bb.translate(x, y);
- const intersection = intersect([[bb.x1, bb.y1], [bb.x2, bb.y2]], markname, mark); // Check every point against the lasso
+ const intersection = intersect([[bb.x1, bb.y1], [bb.x2, bb.y2]], markname, mark);
+ // Check every point against the lasso
return intersection.filter(tuple => pointInPolygon(tuple.x, tuple.y, pixelLasso));
}
+
/**
* Performs a test if a point is inside a polygon based on the idea from
* https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
- *
+ *
* This method will not need the same start/end point since it wraps around the edges of the array
- *
+ *
* @param {*} test a point to test against
* @param {*} polygon a polygon in the form [[x,y], [x,y], ...]
* @returns true if the point lies inside the polygon, false otherwise
*/
-
-
function pointInPolygon(testx, testy, polygon) {
let intersections = 0;
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const [prevX, prevY] = polygon[j];
- const [x, y] = polygon[i]; // count intersections
+ const [x, y] = polygon[i];
+ // count intersections
if (y > testy != prevY > testy && testx < (prevX - x) * (testy - y) / (prevY - y) + x) {
intersections++;
}
- } // point is in polygon if intersection count is odd
+ }
-
+ // point is in polygon if intersection count is odd
return intersections & 1;
}
+ // Expression function context object
const functionContext = {
random() {
return exports.random();
},
-
// override default
cumulativeNormal,
cumulativeLogNormal,
cumulativeUniform,
densityNormal,
@@ -39279,31 +35496,25 @@
sampleLogNormal,
sampleUniform,
isArray,
isBoolean: isBoolean$1,
isDate: isDate$1,
-
isDefined(_) {
return _ !== undefined;
},
-
isNumber: isNumber$1,
isObject,
isRegExp,
isString,
isTuple,
-
isValid(_) {
return _ != null && _ === _;
},
-
toBoolean,
-
toDate(_) {
return toDate(_);
},
-
// suppress extra arguments
toNumber,
toString,
indexof,
join,
@@ -39348,15 +35559,13 @@
dayofyear,
utcdayofyear,
warn,
info,
debug,
-
extent(_) {
return extent(_);
},
-
// suppress extra arguments
inScope,
intersect,
clampRange,
pinchDistance,
@@ -39380,59 +35589,61 @@
lassoAppend,
lassoPath,
intersectLasso
};
const eventFunctions = ['view', 'item', 'group', 'xy', 'x', 'y'],
- // event functions
- eventPrefix = 'event.vega.',
- // event function prefix
- thisPrefix = 'this.',
- // function context prefix
- astVisitors = {}; // AST visitors for dependency analysis
- // export code generator parameters
+ // event functions
+ eventPrefix = 'event.vega.',
+ // event function prefix
+ thisPrefix = 'this.',
+ // function context prefix
+ astVisitors = {}; // AST visitors for dependency analysis
+ // export code generator parameters
const codegenParams = {
forbidden: ['_'],
allowed: ['datum', 'event', 'item'],
fieldvar: 'datum',
- globalvar: id => "_[".concat($(SignalPrefix + id), "]"),
+ globalvar: id => `_[${$(SignalPrefix + id)}]`,
functions: buildFunctions,
constants: Constants,
visitors: astVisitors
- }; // export code generator
+ };
- const codeGenerator = codegen(codegenParams); // Build expression function registry
+ // export code generator
+ const codeGenerator = codegen(codegenParams);
+ // Build expression function registry
function buildFunctions(codegen) {
const fn = Functions(codegen);
eventFunctions.forEach(name => fn[name] = eventPrefix + name);
-
for (const name in functionContext) {
fn[name] = thisPrefix + name;
}
-
extend$1(fn, internalScaleFunctions(codegen, functionContext, astVisitors));
return fn;
- } // Register an expression function
+ }
-
+ // Register an expression function
function expressionFunction(name, fn, visitor) {
if (arguments.length === 1) {
return functionContext[name];
- } // register with the functionContext
+ }
+ // register with the functionContext
+ functionContext[name] = fn;
- functionContext[name] = fn; // if there is an astVisitor register that, too
+ // if there is an astVisitor register that, too
+ if (visitor) astVisitors[name] = visitor;
- if (visitor) astVisitors[name] = visitor; // if the code generator has already been initialized,
+ // if the code generator has already been initialized,
// we need to also register the function with it
-
if (codeGenerator) codeGenerator.functions[name] = thisPrefix + name;
return this;
- } // register expression functions with ast visitors
+ }
-
+ // register expression functions with ast visitors
expressionFunction('bandwidth', bandwidth, scaleVisitor);
expressionFunction('copy', copy, scaleVisitor);
expressionFunction('domain', domain, scaleVisitor);
expressionFunction('range', range$1, scaleVisitor);
expressionFunction('invert', invert, scaleVisitor);
@@ -39440,50 +35651,53 @@
expressionFunction('gradient', scaleGradient, scaleVisitor);
expressionFunction('geoArea', geoArea, scaleVisitor);
expressionFunction('geoBounds', geoBounds, scaleVisitor);
expressionFunction('geoCentroid', geoCentroid, scaleVisitor);
expressionFunction('geoShape', geoShape, scaleVisitor);
+ expressionFunction('geoScale', geoScale, scaleVisitor);
expressionFunction('indata', indata, indataVisitor);
expressionFunction('data', data$1, dataVisitor);
expressionFunction('treePath', treePath, dataVisitor);
- expressionFunction('treeAncestors', treeAncestors, dataVisitor); // register Vega-Lite selection functions
+ expressionFunction('treeAncestors', treeAncestors, dataVisitor);
+ // register Vega-Lite selection functions
expressionFunction('vlSelectionTest', selectionTest, selectionVisitor);
expressionFunction('vlSelectionIdTest', selectionIdTest, selectionVisitor);
expressionFunction('vlSelectionResolve', selectionResolve, selectionVisitor);
expressionFunction('vlSelectionTuples', selectionTuples);
-
function parser(expr, scope) {
- const params = {}; // parse the expression to an abstract syntax tree (ast)
+ const params = {};
+ // parse the expression to an abstract syntax tree (ast)
let ast;
-
try {
expr = isString(expr) ? expr : $(expr) + '';
ast = parser$1(expr);
} catch (err) {
error('Expression parse error: ' + expr);
- } // analyze ast function calls for dependencies
+ }
-
+ // analyze ast function calls for dependencies
ast.visit(node => {
if (node.type !== CallExpression) return;
const name = node.callee.name,
- visit = codegenParams.visitors[name];
+ visit = codegenParams.visitors[name];
if (visit) visit(name, node.arguments, scope, params);
- }); // perform code generation
+ });
- const gen = codeGenerator(ast); // collect signal dependencies
+ // perform code generation
+ const gen = codeGenerator(ast);
+ // collect signal dependencies
gen.globals.forEach(name => {
const signalName = SignalPrefix + name;
-
if (!has$1(params, signalName) && scope.getSignal(name)) {
params[signalName] = scope.signalRef(name);
}
- }); // return generated expression code and dependencies
+ });
+ // return generated expression code and dependencies
return {
$expr: extend$1({
code: gen.code
}, scope.options.ast ? {
ast
@@ -39494,253 +35708,220 @@
}
/**
* Parse a serialized dataflow specification.
*/
-
function parse$2(spec) {
const ctx = this,
- operators = spec.operators || []; // parse background
+ operators = spec.operators || [];
+ // parse background
if (spec.background) {
ctx.background = spec.background;
- } // parse event configuration
+ }
-
+ // parse event configuration
if (spec.eventConfig) {
ctx.eventConfig = spec.eventConfig;
- } // parse locale configuration
+ }
-
+ // parse locale configuration
if (spec.locale) {
ctx.locale = spec.locale;
- } // parse operators
+ }
+ // parse operators
+ operators.forEach(entry => ctx.parseOperator(entry));
- operators.forEach(entry => ctx.parseOperator(entry)); // parse operator parameters
+ // parse operator parameters
+ operators.forEach(entry => ctx.parseOperatorParameters(entry));
- operators.forEach(entry => ctx.parseOperatorParameters(entry)); // parse streams
+ // parse streams
+ (spec.streams || []).forEach(entry => ctx.parseStream(entry));
- (spec.streams || []).forEach(entry => ctx.parseStream(entry)); // parse updates
-
+ // parse updates
(spec.updates || []).forEach(entry => ctx.parseUpdate(entry));
return ctx.resolve();
}
-
const Skip$2 = toSet(['rule']),
- Swap = toSet(['group', 'image', 'rect']);
-
+ Swap = toSet(['group', 'image', 'rect']);
function adjustSpatial(encode, marktype) {
let code = '';
if (Skip$2[marktype]) return code;
-
if (encode.x2) {
if (encode.x) {
if (Swap[marktype]) {
code += 'if(o.x>o.x2)$=o.x,o.x=o.x2,o.x2=$;';
}
-
code += 'o.width=o.x2-o.x;';
} else {
code += 'o.x=o.x2-(o.width||0);';
}
}
-
if (encode.xc) {
code += 'o.x=o.xc-(o.width||0)/2;';
}
-
if (encode.y2) {
if (encode.y) {
if (Swap[marktype]) {
code += 'if(o.y>o.y2)$=o.y,o.y=o.y2,o.y2=$;';
}
-
code += 'o.height=o.y2-o.y;';
} else {
code += 'o.y=o.y2-(o.height||0);';
}
}
-
if (encode.yc) {
code += 'o.y=o.yc-(o.height||0)/2;';
}
-
return code;
}
-
function canonicalType(type) {
return (type + '').toLowerCase();
}
-
function isOperator(type) {
return canonicalType(type) === 'operator';
}
-
function isCollect(type) {
return canonicalType(type) === 'collect';
}
-
function expression(ctx, args, code) {
// wrap code in return statement if expression does not terminate
if (!code.endsWith(';')) {
code = 'return(' + code + ');';
}
-
const fn = Function(...args.concat(code));
return ctx && ctx.functions ? fn.bind(ctx.functions) : fn;
- } // generate code for comparing a single field
+ }
-
+ // generate code for comparing a single field
function _compare(u, v, lt, gt) {
- return "((u = ".concat(u, ") < (v = ").concat(v, ") || u == null) && v != null ? ").concat(lt, "\n : (u > v || v == null) && u != null ? ").concat(gt, "\n : ((v = v instanceof Date ? +v : v), (u = u instanceof Date ? +u : u)) !== u && v === v ? ").concat(lt, "\n : v !== v && u === u ? ").concat(gt, " : ");
+ return `((u = ${u}) < (v = ${v}) || u == null) && v != null ? ${lt}
+ : (u > v || v == null) && u != null ? ${gt}
+ : ((v = v instanceof Date ? +v : v), (u = u instanceof Date ? +u : u)) !== u && v === v ? ${lt}
+ : v !== v && u === u ? ${gt} : `;
}
-
var expressionCodegen = {
/**
* Parse an expression used to update an operator value.
*/
operator: (ctx, expr) => expression(ctx, ['_'], expr.code),
-
/**
* Parse an expression provided as an operator parameter value.
*/
parameter: (ctx, expr) => expression(ctx, ['datum', '_'], expr.code),
-
/**
* Parse an expression applied to an event stream.
*/
event: (ctx, expr) => expression(ctx, ['event'], expr.code),
-
/**
* Parse an expression used to handle an event-driven operator update.
*/
handler: (ctx, expr) => {
- const code = "var datum=event.item&&event.item.datum;return ".concat(expr.code, ";");
+ const code = `var datum=event.item&&event.item.datum;return ${expr.code};`;
return expression(ctx, ['_', 'event'], code);
},
-
/**
* Parse an expression that performs visual encoding.
*/
encode: (ctx, encode) => {
const {
marktype,
channels
} = encode;
let code = 'var o=item,datum=o.datum,m=0,$;';
-
for (const name in channels) {
const o = 'o[' + $(name) + ']';
- code += "$=".concat(channels[name].code, ";if(").concat(o, "!==$)").concat(o, "=$,m=1;");
+ code += `$=${channels[name].code};if(${o}!==$)${o}=$,m=1;`;
}
-
code += adjustSpatial(channels, marktype);
code += 'return m;';
return expression(ctx, ['item', '_'], code);
},
-
/**
* Optimized code generators for access and comparison.
*/
codegen: {
get(path) {
- const ref = "[".concat(path.map($).join(']['), "]");
- const get = Function('_', "return _".concat(ref, ";"));
+ const ref = `[${path.map($).join('][')}]`;
+ const get = Function('_', `return _${ref};`);
get.path = ref;
return get;
},
-
comparator(fields, orders) {
let t;
-
const map = (f, i) => {
const o = orders[i];
let u, v;
-
if (f.path) {
- u = "a".concat(f.path);
- v = "b".concat(f.path);
+ u = `a${f.path}`;
+ v = `b${f.path}`;
} else {
(t = t || {})['f' + i] = f;
- u = "this.f".concat(i, "(a)");
- v = "this.f".concat(i, "(b)");
+ u = `this.f${i}(a)`;
+ v = `this.f${i}(b)`;
}
-
return _compare(u, v, -o, o);
};
-
const fn = Function('a', 'b', 'var u, v; return ' + fields.map(map).join('') + '0;');
return t ? fn.bind(t) : fn;
}
-
}
};
+
/**
* Parse a dataflow operator.
*/
-
function parseOperator(spec) {
const ctx = this;
-
if (isOperator(spec.type) || !spec.type) {
ctx.operator(spec, spec.update ? ctx.operatorExpression(spec.update) : null);
} else {
ctx.transform(spec, spec.type);
}
}
+
/**
* Parse and assign operator parameters.
*/
-
-
function parseOperatorParameters(spec) {
const ctx = this;
-
if (spec.params) {
const op = ctx.get(spec.id);
if (!op) error('Invalid operator id: ' + spec.id);
ctx.dataflow.connect(op, op.parameters(ctx.parseParameters(spec.params), spec.react, spec.initonly));
}
}
+
/**
* Parse a set of operator parameters.
*/
-
-
function parseParameters$1(spec, params) {
params = params || {};
const ctx = this;
-
for (const key in spec) {
const value = spec[key];
params[key] = isArray(value) ? value.map(v => parseParameter$2(v, ctx, params)) : parseParameter$2(value, ctx, params);
}
-
return params;
}
+
/**
* Parse a single parameter.
*/
-
-
function parseParameter$2(spec, ctx, params) {
if (!spec || !isObject(spec)) return spec;
-
for (let i = 0, n = PARSERS.length, p; i < n; ++i) {
p = PARSERS[i];
-
if (has$1(spec, p.key)) {
return p.parse(spec, ctx, params);
}
}
-
return spec;
}
- /** Reference parsers. */
-
+ /** Reference parsers. */
var PARSERS = [{
key: '$ref',
parse: getOperator
}, {
key: '$key',
@@ -39765,223 +35946,189 @@
parse: getSubflow
}, {
key: '$tupleid',
parse: getTupleId
}];
+
/**
* Resolve an operator reference.
*/
-
function getOperator(_, ctx) {
return ctx.get(_.$ref) || error('Operator not defined: ' + _.$ref);
}
+
/**
* Resolve an expression reference.
*/
-
-
function getExpression(_, ctx, params) {
if (_.$params) {
// parse expression parameters
ctx.parseParameters(_.$params, params);
}
-
const k = 'e:' + _.$expr.code;
return ctx.fn[k] || (ctx.fn[k] = accessor(ctx.parameterExpression(_.$expr), _.$fields));
}
+
/**
* Resolve a key accessor reference.
*/
-
-
function getKey(_, ctx) {
const k = 'k:' + _.$key + '_' + !!_.$flat;
return ctx.fn[k] || (ctx.fn[k] = key(_.$key, _.$flat, ctx.expr.codegen));
}
+
/**
* Resolve a field accessor reference.
*/
-
-
function getField(_, ctx) {
if (!_.$field) return null;
const k = 'f:' + _.$field + '_' + _.$name;
return ctx.fn[k] || (ctx.fn[k] = field$1(_.$field, _.$name, ctx.expr.codegen));
}
+
/**
* Resolve a comparator function reference.
*/
-
-
function getCompare(_, ctx) {
// As of Vega 5.5.3, $tupleid sort is no longer used.
// Keep here for now for backwards compatibility.
const k = 'c:' + _.$compare + '_' + _.$order,
- c = array$5(_.$compare).map(_ => _ && _.$tupleid ? tupleid : _);
+ c = array$5(_.$compare).map(_ => _ && _.$tupleid ? tupleid : _);
return ctx.fn[k] || (ctx.fn[k] = compare$1(c, _.$order, ctx.expr.codegen));
}
+
/**
* Resolve an encode operator reference.
*/
-
-
function getEncode(_, ctx) {
const spec = _.$encode,
- encode = {};
-
+ encode = {};
for (const name in spec) {
const enc = spec[name];
encode[name] = accessor(ctx.encodeExpression(enc.$expr), enc.$fields);
encode[name].output = enc.$output;
}
-
return encode;
}
+
/**
* Resolve a context reference.
*/
-
-
function getContext(_, ctx) {
return ctx;
}
+
/**
* Resolve a recursive subflow specification.
*/
-
-
function getSubflow(_, ctx) {
const spec = _.$subflow;
return function (dataflow, key, parent) {
const subctx = ctx.fork().parse(spec),
- op = subctx.get(spec.operators[0].id),
- p = subctx.signals.parent;
+ op = subctx.get(spec.operators[0].id),
+ p = subctx.signals.parent;
if (p) p.set(parent);
-
op.detachSubflow = () => ctx.detach(subctx);
-
return op;
};
}
+
/**
* Resolve a tuple id reference.
*/
-
-
function getTupleId() {
return tupleid;
}
+
/**
* Parse an event stream specification.
*/
-
-
function parseStream$2(spec) {
var ctx = this,
- filter = spec.filter != null ? ctx.eventExpression(spec.filter) : undefined,
- stream = spec.stream != null ? ctx.get(spec.stream) : undefined,
- args;
-
+ filter = spec.filter != null ? ctx.eventExpression(spec.filter) : undefined,
+ stream = spec.stream != null ? ctx.get(spec.stream) : undefined,
+ args;
if (spec.source) {
stream = ctx.events(spec.source, spec.type, filter);
} else if (spec.merge) {
args = spec.merge.map(_ => ctx.get(_));
stream = args[0].merge.apply(args[0], args.slice(1));
}
-
if (spec.between) {
args = spec.between.map(_ => ctx.get(_));
stream = stream.between(args[0], args[1]);
}
-
if (spec.filter) {
stream = stream.filter(filter);
}
-
if (spec.throttle != null) {
stream = stream.throttle(+spec.throttle);
}
-
if (spec.debounce != null) {
stream = stream.debounce(+spec.debounce);
}
-
if (stream == null) {
error('Invalid stream definition: ' + JSON.stringify(spec));
}
-
if (spec.consume) stream.consume(true);
ctx.stream(spec, stream);
}
+
/**
* Parse an event-driven operator update.
*/
-
-
function parseUpdate$1(spec) {
var ctx = this,
- srcid = isObject(srcid = spec.source) ? srcid.$ref : srcid,
- source = ctx.get(srcid),
- target = null,
- update = spec.update,
- params = undefined;
+ srcid = isObject(srcid = spec.source) ? srcid.$ref : srcid,
+ source = ctx.get(srcid),
+ target = null,
+ update = spec.update,
+ params = undefined;
if (!source) error('Source not defined: ' + spec.source);
target = spec.target && spec.target.$expr ? ctx.eventExpression(spec.target.$expr) : ctx.get(spec.target);
-
if (update && update.$expr) {
if (update.$params) {
params = ctx.parseParameters(update.$params);
}
-
update = ctx.handlerExpression(update.$expr);
}
-
ctx.update(spec, source, target, update, params);
}
-
const SKIP = {
skip: true
};
-
function getState$1(options) {
var ctx = this,
- state = {};
-
+ state = {};
if (options.signals) {
var signals = state.signals = {};
Object.keys(ctx.signals).forEach(key => {
const op = ctx.signals[key];
-
if (options.signals(key, op)) {
signals[key] = op.value;
}
});
}
-
if (options.data) {
var data = state.data = {};
Object.keys(ctx.data).forEach(key => {
const dataset = ctx.data[key];
-
if (options.data(key, dataset)) {
data[key] = dataset.input.value;
}
});
}
-
if (ctx.subcontext && options.recurse !== false) {
state.subcontext = ctx.subcontext.map(ctx => ctx.getState(options));
}
-
return state;
}
-
function setState$1(state) {
var ctx = this,
- df = ctx.dataflow,
- data = state.data,
- signals = state.signals;
+ df = ctx.dataflow,
+ data = state.data,
+ signals = state.signals;
Object.keys(signals || {}).forEach(key => {
df.update(ctx.signals[key], signals[key], SKIP);
});
Object.keys(data || {}).forEach(key => {
df.pulse(ctx.data[key].input, df.changeset().remove(truthy).insert(data[key]));
@@ -39989,105 +36136,89 @@
(state.subcontext || []).forEach((substate, i) => {
const subctx = ctx.subcontext[i];
if (subctx) subctx.setState(substate);
});
}
+
/**
* Context objects store the current parse state.
* Enables lookup of parsed operators, event streams, accessors, etc.
* Provides a 'fork' method for creating child contexts for subflows.
*/
-
-
function context(df, transforms, functions, expr) {
return new Context(df, transforms, functions, expr);
}
-
function Context(df, transforms, functions, expr) {
this.dataflow = df;
this.transforms = transforms;
this.events = df.events.bind(df);
this.expr = expr || expressionCodegen, this.signals = {};
this.scales = {};
this.nodes = {};
this.data = {};
this.fn = {};
-
if (functions) {
this.functions = Object.create(functions);
this.functions.context = this;
}
}
-
function Subcontext(ctx) {
this.dataflow = ctx.dataflow;
this.transforms = ctx.transforms;
this.events = ctx.events;
this.expr = ctx.expr;
this.signals = Object.create(ctx.signals);
this.scales = Object.create(ctx.scales);
this.nodes = Object.create(ctx.nodes);
this.data = Object.create(ctx.data);
this.fn = Object.create(ctx.fn);
-
if (ctx.functions) {
this.functions = Object.create(ctx.functions);
this.functions.context = this;
}
}
-
Context.prototype = Subcontext.prototype = {
fork() {
const ctx = new Subcontext(this);
(this.subcontext || (this.subcontext = [])).push(ctx);
return ctx;
},
-
detach(ctx) {
- this.subcontext = this.subcontext.filter(c => c !== ctx); // disconnect all nodes in the subcontext
- // wipe out targets first for better efficiency
+ this.subcontext = this.subcontext.filter(c => c !== ctx);
+ // disconnect all nodes in the subcontext
+ // wipe out targets first for better efficiency
const keys = Object.keys(ctx.nodes);
-
for (const key of keys) ctx.nodes[key]._targets = null;
-
for (const key of keys) ctx.nodes[key].detach();
-
ctx.nodes = null;
},
-
get(id) {
return this.nodes[id];
},
-
set(id, node) {
return this.nodes[id] = node;
},
-
add(spec, op) {
const ctx = this,
- df = ctx.dataflow,
- data = spec.value;
+ df = ctx.dataflow,
+ data = spec.value;
ctx.set(spec.id, op);
-
if (isCollect(spec.type) && data) {
if (data.$ingest) {
df.ingest(op, data.$ingest, data.$format);
} else if (data.$request) {
df.preload(op, data.$request, data.$format);
} else {
df.pulse(op, df.changeset().insert(data));
}
}
-
if (spec.root) {
ctx.root = op;
}
-
if (spec.parent) {
let p = ctx.get(spec.parent.$ref);
-
if (p) {
df.connect(p, [op]);
op.targets().add(p);
} else {
(ctx.unresolved = ctx.unresolved || []).push(() => {
@@ -40095,70 +36226,56 @@
df.connect(p, [op]);
op.targets().add(p);
});
}
}
-
if (spec.signal) {
ctx.signals[spec.signal] = op;
}
-
if (spec.scale) {
ctx.scales[spec.scale] = op;
}
-
if (spec.data) {
for (const name in spec.data) {
const data = ctx.data[name] || (ctx.data[name] = {});
spec.data[name].forEach(role => data[role] = op);
}
}
},
-
resolve() {
(this.unresolved || []).forEach(fn => fn());
delete this.unresolved;
return this;
},
-
operator(spec, update) {
this.add(spec, this.dataflow.add(spec.value, update));
},
-
transform(spec, type) {
this.add(spec, this.dataflow.add(this.transforms[canonicalType(type)]));
},
-
stream(spec, stream) {
this.set(spec.id, stream);
},
-
update(spec, stream, target, update, params) {
this.dataflow.on(stream, target, update, params, spec.options);
},
-
// expression parsing
operatorExpression(expr) {
return this.expr.operator(this, expr);
},
-
parameterExpression(expr) {
return this.expr.parameter(this, expr);
},
-
eventExpression(expr) {
return this.expr.event(this, expr);
},
-
handlerExpression(expr) {
return this.expr.handler(this, expr);
},
-
encodeExpression(encode) {
return this.expr.encode(this, encode);
},
-
// parse methods
parse: parse$2,
parseOperator,
parseOperatorParameters,
parseParameters: parseParameters$1,
@@ -40167,146 +36284,126 @@
// state methods
getState: getState$1,
setState: setState$1
};
+ // initialize aria role and label attributes
function initializeAria(view) {
const el = view.container();
-
if (el) {
el.setAttribute('role', 'graphics-document');
el.setAttribute('aria-roleDescription', 'visualization');
ariaLabel(el, view.description());
}
- } // update aria-label if we have a DOM container element
+ }
-
+ // update aria-label if we have a DOM container element
function ariaLabel(el, desc) {
if (el) desc == null ? el.removeAttribute('aria-label') : el.setAttribute('aria-label', desc);
}
-
function background(view) {
// respond to background signal
view.add(null, _ => {
view._background = _.bg;
view._resize = 1;
return _.bg;
}, {
bg: view._signals.background
});
}
-
const Default = 'default';
-
function cursor(view) {
// get cursor signal, add to dataflow if needed
const cursor = view._signals.cursor || (view._signals.cursor = view.add({
user: Default,
item: null
- })); // evaluate cursor on each mousemove event
+ }));
- view.on(view.events('view', 'mousemove'), cursor, (_, event) => {
+ // evaluate cursor on each pointermove event
+ view.on(view.events('view', 'pointermove'), cursor, (_, event) => {
const value = cursor.value,
- user = value ? isString(value) ? value : value.user : Default,
- item = event.item && event.item.cursor || null;
+ user = value ? isString(value) ? value : value.user : Default,
+ item = event.item && event.item.cursor || null;
return value && user === value.user && item == value.item ? value : {
user: user,
item: item
};
- }); // when cursor signal updates, set visible cursor
+ });
+ // when cursor signal updates, set visible cursor
view.add(null, function (_) {
let user = _.cursor,
- item = this.value;
-
+ item = this.value;
if (!isString(user)) {
item = user.item;
user = user.user;
}
-
setCursor(view, user && user !== Default ? user : item || user);
return item;
}, {
cursor: cursor
});
}
-
function setCursor(view, cursor) {
const el = view.globalCursor() ? typeof document !== 'undefined' && document.body : view.container();
-
if (el) {
return cursor == null ? el.style.removeProperty('cursor') : el.style.cursor = cursor;
}
}
-
function dataref(view, name) {
var data = view._runtime.data;
-
if (!has$1(data, name)) {
error('Unrecognized data set: ' + name);
}
-
return data[name];
}
-
function data(name, values) {
return arguments.length < 2 ? dataref(this, name).values.value : change.call(this, name, changeset().remove(truthy).insert(values));
}
-
function change(name, changes) {
if (!isChangeSet(changes)) {
error('Second argument to changes must be a changeset.');
}
-
const dataset = dataref(this, name);
dataset.modified = true;
return this.pulse(dataset.input, changes);
}
-
function insert(name, _) {
return change.call(this, name, changeset().insert(_));
}
-
function remove(name, _) {
return change.call(this, name, changeset().remove(_));
}
-
function width(view) {
var padding = view.padding();
return Math.max(0, view._viewWidth + padding.left + padding.right);
}
-
function height(view) {
var padding = view.padding();
return Math.max(0, view._viewHeight + padding.top + padding.bottom);
}
-
function offset(view) {
var padding = view.padding(),
- origin = view._origin;
+ origin = view._origin;
return [padding.left + origin[0], padding.top + origin[1]];
}
-
function resizeRenderer(view) {
var origin = offset(view),
- w = width(view),
- h = height(view);
-
+ w = width(view),
+ h = height(view);
view._renderer.background(view.background());
-
view._renderer.resize(w, h, origin);
-
view._handler.origin(origin);
-
view._resizeListeners.forEach(handler => {
try {
handler(w, h);
} catch (error) {
view.error(error);
}
});
}
+
/**
* Extend an event with additional view-specific methods.
* Adds a new property ('vega') to an event that provides a number
* of methods for querying information about the current interaction.
* The vega object provides the following methods:
@@ -40329,148 +36426,127 @@
* y - Returns the current y-coordinate, accepts the same arguments as xy.
* @param {Event} event - The input event to extend.
* @param {Item} item - The currently active scenegraph item (if any).
* @return {Event} - The extended input event.
*/
-
-
function eventExtend(view, event, item) {
var r = view._renderer,
- el = r && r.canvas(),
- p,
- e,
- translate;
-
+ el = r && r.canvas(),
+ p,
+ e,
+ translate;
if (el) {
translate = offset(view);
e = event.changedTouches ? event.changedTouches[0] : event;
p = point(e, el);
p[0] -= translate[0];
p[1] -= translate[1];
}
-
event.dataflow = view;
event.item = item;
event.vega = extension(view, item, p);
return event;
}
-
function extension(view, item, point) {
const itemGroup = item ? item.mark.marktype === 'group' ? item : item.mark.group : null;
-
function group(name) {
var g = itemGroup,
- i;
+ i;
if (name) for (i = item; i; i = i.mark.group) {
if (i.mark.name === name) {
g = i;
break;
}
}
return g && g.mark && g.mark.interactive ? g : {};
}
-
function xy(item) {
if (!item) return point;
if (isString(item)) item = group(item);
const p = point.slice();
-
while (item) {
p[0] -= item.x || 0;
p[1] -= item.y || 0;
item = item.mark && item.mark.group;
}
-
return p;
}
-
return {
- view: constant$4(view),
- item: constant$4(item || {}),
+ view: constant$5(view),
+ item: constant$5(item || {}),
group: group,
xy: xy,
x: item => xy(item)[0],
y: item => xy(item)[1]
};
}
-
const VIEW$1 = 'view',
- TIMER = 'timer',
- WINDOW = 'window',
- NO_TRAP = {
- trap: false
- };
+ TIMER = 'timer',
+ WINDOW = 'window',
+ NO_TRAP = {
+ trap: false
+ };
+
/**
* Initialize event handling configuration.
* @param {object} config - The configuration settings.
* @return {object}
*/
-
function initializeEventConfig(config) {
const events = extend$1({
defaults: {}
}, config);
-
const unpack = (obj, keys) => {
keys.forEach(k => {
if (isArray(obj[k])) obj[k] = toSet(obj[k]);
});
};
-
unpack(events.defaults, ['prevent', 'allow']);
unpack(events, ['view', 'window', 'selector']);
return events;
}
-
function trackEventListener(view, sources, type, handler) {
view._eventListeners.push({
type: type,
sources: array$5(sources),
handler: handler
});
}
-
function prevent(view, type) {
var def = view._eventConfig.defaults,
- prevent = def.prevent,
- allow = def.allow;
+ prevent = def.prevent,
+ allow = def.allow;
return prevent === false || allow === true ? false : prevent === true || allow === false ? true : prevent ? prevent[type] : allow ? !allow[type] : view.preventDefault();
}
-
function permit(view, key, type) {
const rule = view._eventConfig && view._eventConfig[key];
-
if (rule === false || isObject(rule) && !rule[type]) {
- view.warn("Blocked ".concat(key, " ").concat(type, " event listener."));
+ view.warn(`Blocked ${key} ${type} event listener.`);
return false;
}
-
return true;
}
+
/**
* Create a new event stream from an event source.
* @param {object} source - The event source to monitor.
* @param {string} type - The event type.
* @param {function(object): boolean} [filter] - Event filter function.
* @return {EventStream}
*/
-
-
function events(source, type, filter) {
var view = this,
- s = new EventStream(filter),
- send = function (e, item) {
- view.runAsync(null, () => {
- if (source === VIEW$1 && prevent(view, type)) {
- e.preventDefault();
- }
-
- s.receive(eventExtend(view, e, item));
- });
- },
- sources;
-
+ s = new EventStream(filter),
+ send = function (e, item) {
+ view.runAsync(null, () => {
+ if (source === VIEW$1 && prevent(view, type)) {
+ e.preventDefault();
+ }
+ s.receive(eventExtend(view, e, item));
+ });
+ },
+ sources;
if (source === TIMER) {
if (permit(view, 'timer', type)) {
view.timer(send, type);
}
} else if (source === VIEW$1) {
@@ -40483,103 +36559,99 @@
if (permit(view, 'window', type) && typeof window !== 'undefined') {
sources = [window];
}
} else if (typeof document !== 'undefined') {
if (permit(view, 'selector', type)) {
- sources = document.querySelectorAll(source);
+ sources = Array.from(document.querySelectorAll(source));
}
}
-
if (!sources) {
view.warn('Can not resolve event source: ' + source);
} else {
for (var i = 0, n = sources.length; i < n; ++i) {
sources[i].addEventListener(type, send);
}
-
trackEventListener(view, sources, type, send);
}
}
-
return s;
}
-
function itemFilter(event) {
return event.item;
}
-
function markTarget(event) {
// grab upstream collector feeding the mark operator
return event.item.mark.source;
}
-
function invoke(name) {
return function (_, event) {
return event.vega.view().changeset().encode(event.item, name);
};
}
-
function hover(hoverSet, leaveSet) {
hoverSet = [hoverSet || 'hover'];
- leaveSet = [leaveSet || 'update', hoverSet[0]]; // invoke hover set upon mouseover
+ leaveSet = [leaveSet || 'update', hoverSet[0]];
- this.on(this.events('view', 'mouseover', itemFilter), markTarget, invoke(hoverSet)); // invoke leave set upon mouseout
+ // invoke hover set upon pointerover
+ this.on(this.events('view', 'pointerover', itemFilter), markTarget, invoke(hoverSet));
- this.on(this.events('view', 'mouseout', itemFilter), markTarget, invoke(leaveSet));
+ // invoke leave set upon pointerout
+ this.on(this.events('view', 'pointerout', itemFilter), markTarget, invoke(leaveSet));
return this;
}
+
/**
* Finalize a View instance that is being removed.
* Cancel any running timers.
* Remove all external event listeners.
* Remove any currently displayed tooltip.
*/
-
-
function finalize() {
var tooltip = this._tooltip,
- timers = this._timers,
- listeners = this._eventListeners,
- n,
- m,
- e;
+ timers = this._timers,
+ handlers = this._handler.handlers(),
+ listeners = this._eventListeners,
+ n,
+ m,
+ e,
+ h,
+ t;
n = timers.length;
-
while (--n >= 0) {
timers[n].stop();
}
-
n = listeners.length;
-
while (--n >= 0) {
e = listeners[n];
m = e.sources.length;
-
while (--m >= 0) {
e.sources[m].removeEventListener(e.type, e.handler);
}
}
-
if (tooltip) {
tooltip.call(this, this._handler, null, null, null);
}
+ // turn off all registered handlers
+ n = handlers.length;
+ while (--n >= 0) {
+ t = handlers[n].type;
+ h = handlers[n].handler;
+ this._handler.off(t, h);
+ }
return this;
}
-
function element(tag, attr, text) {
const el = document.createElement(tag);
-
for (const key in attr) el.setAttribute(key, attr[key]);
-
if (text != null) el.textContent = text;
return el;
}
-
const BindClass = 'vega-bind',
- NameClass = 'vega-bind-name',
- RadioClass = 'vega-bind-radio';
+ NameClass = 'vega-bind-name',
+ RadioClass = 'vega-bind-radio';
+
/**
* Bind a signal to an external HTML input element. The resulting two-way
* binding will propagate input changes to signals, and propagate signal
* changes to the input element state. If this view instance has no parent
* element, we assume the view is headless and no bindings are created.
@@ -40589,16 +36661,14 @@
* element of this view will be used as the element.
* @param {object} param - The binding parameters which specify the signal
* to bind to, the input element type, and type-specific configuration.
* @return {View} - This view instance.
*/
-
function bind(view, el, binding) {
if (!el) return;
const param = binding.param;
let bind = binding.state;
-
if (!bind) {
bind = binding.state = {
elements: null,
active: false,
set: null,
@@ -40609,61 +36679,56 @@
view.signal(param.signal, value);
});
}
}
};
-
if (param.debounce) {
bind.update = debounce(param.debounce, bind.update);
}
}
-
const create = param.input == null && param.element ? target : generate;
create(bind, el, param, view);
-
if (!bind.active) {
view.on(view._signals[param.signal], null, () => {
bind.source ? bind.source = false : bind.set(view.signal(param.signal));
});
bind.active = true;
}
-
return bind;
}
+
/**
* Bind the signal to an external EventTarget.
*/
-
-
function target(bind, node, param, view) {
const type = param.event || 'input';
+ const handler = () => bind.update(node.value);
- const handler = () => bind.update(node.value); // initialize signal value to external input value
+ // initialize signal value to external input value
+ view.signal(param.signal, node.value);
+ // listen for changes on the element
+ node.addEventListener(type, handler);
- view.signal(param.signal, node.value); // listen for changes on the element
+ // register with view, so we can remove it upon finalization
+ trackEventListener(view, node, type, handler);
- node.addEventListener(type, handler); // register with view, so we can remove it upon finalization
-
- trackEventListener(view, node, type, handler); // propagate change to element
-
+ // propagate change to element
bind.set = value => {
node.value = value;
node.dispatchEvent(event(type));
};
}
-
function event(type) {
return typeof Event !== 'undefined' ? new Event(type) : {
type
};
}
+
/**
* Generate an HTML input form element and bind it to a signal.
*/
-
-
function generate(bind, el, param, view) {
const value = view.signal(param.signal);
const div = element('div', {
'class': BindClass
});
@@ -40671,82 +36736,70 @@
wrapper.appendChild(element('span', {
'class': NameClass
}, param.name || param.signal));
el.appendChild(div);
let input = form;
-
switch (param.input) {
case 'checkbox':
input = checkbox;
break;
-
case 'select':
input = select;
break;
-
case 'radio':
input = radio;
break;
-
case 'range':
input = range;
break;
}
-
input(bind, wrapper, param, value);
}
+
/**
* Generates an arbitrary input form element.
* The input type is controlled via user-provided parameters.
*/
-
-
function form(bind, el, param, value) {
const node = element('input');
-
for (const key in param) {
if (key !== 'signal' && key !== 'element') {
node.setAttribute(key === 'input' ? 'type' : key, param[key]);
}
}
-
node.setAttribute('name', param.signal);
node.value = value;
el.appendChild(node);
node.addEventListener('input', () => bind.update(node.value));
bind.elements = [node];
-
bind.set = value => node.value = value;
}
+
/**
* Generates a checkbox input element.
*/
-
-
function checkbox(bind, el, param, value) {
const attr = {
type: 'checkbox',
name: param.signal
};
if (value) attr.checked = true;
const node = element('input', attr);
el.appendChild(node);
node.addEventListener('change', () => bind.update(node.checked));
bind.elements = [node];
-
bind.set = value => node.checked = !!value || null;
}
+
/**
* Generates a selection list input element.
*/
-
-
function select(bind, el, param, value) {
const node = element('select', {
- name: param.signal
- }),
- labels = param.labels || [];
+ name: param.signal
+ }),
+ labels = param.labels || [];
param.options.forEach((option, i) => {
const attr = {
value: option
};
if (valuesEqual(option, value)) attr.selected = true;
@@ -40755,30 +36808,28 @@
el.appendChild(node);
node.addEventListener('change', () => {
bind.update(param.options[node.selectedIndex]);
});
bind.elements = [node];
-
bind.set = value => {
for (let i = 0, n = param.options.length; i < n; ++i) {
if (valuesEqual(param.options[i], value)) {
node.selectedIndex = i;
return;
}
}
};
}
+
/**
* Generates a radio button group.
*/
-
-
function radio(bind, el, param, value) {
const group = element('span', {
- 'class': RadioClass
- }),
- labels = param.labels || [];
+ 'class': RadioClass
+ }),
+ labels = param.labels || [];
el.appendChild(group);
bind.elements = param.options.map((option, i) => {
const attr = {
type: 'radio',
name: param.signal,
@@ -40790,30 +36841,27 @@
const label = element('label', {}, (labels[i] || option) + '');
label.prepend(input);
group.appendChild(label);
return input;
});
-
bind.set = value => {
const nodes = bind.elements,
- n = nodes.length;
-
+ n = nodes.length;
for (let i = 0; i < n; ++i) {
if (valuesEqual(nodes[i].value, value)) nodes[i].checked = true;
}
};
}
+
/**
* Generates a slider input element.
*/
-
-
function range(bind, el, param, value) {
value = value !== undefined ? value : (+param.max + +param.min) / 2;
const max = param.max != null ? param.max : Math.max(100, +value) || 100,
- min = param.min || Math.min(0, max, +value) || 0,
- step = param.step || tickStep(min, max, 100);
+ min = param.min || Math.min(0, max, +value) || 0,
+ step = param.step || tickStep(min, max, 100);
const node = element('input', {
type: 'range',
name: param.signal,
min: min,
max: max,
@@ -40821,318 +36869,292 @@
});
node.value = value;
const span = element('span', {}, +value);
el.appendChild(node);
el.appendChild(span);
-
const update = () => {
span.textContent = node.value;
bind.update(+node.value);
- }; // subscribe to both input and change
+ };
-
+ // subscribe to both input and change
node.addEventListener('input', update);
node.addEventListener('change', update);
bind.elements = [node];
-
bind.set = value => {
node.value = value;
span.textContent = value;
};
}
-
function valuesEqual(a, b) {
return a === b || a + '' === b + '';
}
-
function initializeRenderer(view, r, el, constructor, scaleFactor, opt) {
r = r || new constructor(view.loader());
return r.initialize(el, width(view), height(view), offset(view), scaleFactor, opt).background(view.background());
}
-
function trap(view, fn) {
return !fn ? null : function () {
try {
fn.apply(this, arguments);
} catch (error) {
view.error(error);
}
};
}
-
function initializeHandler(view, prevHandler, el, constructor) {
// instantiate scenegraph handler
- const handler = new constructor(view.loader(), trap(view, view.tooltip())).scene(view.scenegraph().root).initialize(el, offset(view), view); // transfer event handlers
+ const handler = new constructor(view.loader(), trap(view, view.tooltip())).scene(view.scenegraph().root).initialize(el, offset(view), view);
+ // transfer event handlers
if (prevHandler) {
prevHandler.handlers().forEach(h => {
handler.on(h.type, h.handler);
});
}
-
return handler;
}
-
function initialize(el, elBind) {
const view = this,
- type = view._renderType,
- config = view._eventConfig.bind,
- module = renderModule(type); // containing dom element
+ type = view._renderType,
+ config = view._eventConfig.bind,
+ module = renderModule(type);
- el = view._el = el ? lookup$1(view, el, true) : null; // initialize aria attributes
+ // containing dom element
+ el = view._el = el ? lookup$1(view, el, true) : null;
- initializeAria(view); // select appropriate renderer & handler
+ // initialize aria attributes
+ initializeAria(view);
+ // select appropriate renderer & handler
if (!module) view.error('Unrecognized renderer type: ' + type);
const Handler = module.handler || CanvasHandler,
- Renderer = el ? module.renderer : module.headless; // initialize renderer and input handler
+ Renderer = el ? module.renderer : module.headless;
+ // initialize renderer and input handler
view._renderer = !Renderer ? null : initializeRenderer(view, view._renderer, el, Renderer);
view._handler = initializeHandler(view, view._handler, el, Handler);
- view._redraw = true; // initialize signal bindings
+ view._redraw = true;
+ // initialize signal bindings
if (el && config !== 'none') {
elBind = elBind ? view._elBind = lookup$1(view, elBind, true) : el.appendChild(element('form', {
'class': 'vega-bindings'
}));
-
view._bind.forEach(_ => {
if (_.param.element && config !== 'container') {
_.element = lookup$1(view, _.param.element, !!_.param.input);
}
});
-
view._bind.forEach(_ => {
bind(view, _.element || elBind, _);
});
}
-
return view;
}
-
function lookup$1(view, el, clear) {
if (typeof el === 'string') {
if (typeof document !== 'undefined') {
el = document.querySelector(el);
-
if (!el) {
view.error('Signal bind element not found: ' + el);
return null;
}
} else {
view.error('DOM document instance not found.');
return null;
}
}
-
if (el && clear) {
try {
el.textContent = '';
} catch (e) {
el = null;
view.error(e);
}
}
-
return el;
}
-
const number$1 = _ => +_ || 0;
-
const paddingObject$1 = _ => ({
top: _,
bottom: _,
left: _,
right: _
});
-
function padding(_) {
return isObject(_) ? {
top: number$1(_.top),
bottom: number$1(_.bottom),
left: number$1(_.left),
right: number$1(_.right)
} : paddingObject$1(number$1(_));
}
+
/**
* Render the current scene in a headless fashion.
* This method is asynchronous, returning a Promise instance.
* @return {Promise} - A Promise that resolves to a renderer.
*/
-
-
async function renderHeadless(view, type, scaleFactor, opt) {
const module = renderModule(type),
- ctr = module && module.headless;
+ ctr = module && module.headless;
if (!ctr) error('Unrecognized renderer type: ' + type);
await view.runAsync();
return initializeRenderer(view, null, null, ctr, scaleFactor, opt).renderAsync(view._scenegraph.root);
}
+
/**
* Produce an image URL for the visualization. Depending on the type
* parameter, the generated URL contains data for either a PNG or SVG image.
* The URL can be used (for example) to download images of the visualization.
* This method is asynchronous, returning a Promise instance.
* @param {string} type - The image type. One of 'svg', 'png' or 'canvas'.
* The 'canvas' and 'png' types are synonyms for a PNG image.
* @return {Promise} - A promise that resolves to an image URL.
*/
-
-
async function renderToImageURL(type, scaleFactor) {
if (type !== RenderType.Canvas && type !== RenderType.SVG && type !== RenderType.PNG) {
error('Unrecognized image type: ' + type);
}
-
const r = await renderHeadless(this, type, scaleFactor);
return type === RenderType.SVG ? toBlobURL(r.svg(), 'image/svg+xml') : r.canvas().toDataURL('image/png');
}
-
function toBlobURL(data, mime) {
const blob = new Blob([data], {
type: mime
});
return window.URL.createObjectURL(blob);
}
+
/**
* Produce a Canvas instance containing a rendered visualization.
* This method is asynchronous, returning a Promise instance.
* @return {Promise} - A promise that resolves to a Canvas instance.
*/
-
-
async function renderToCanvas(scaleFactor, opt) {
const r = await renderHeadless(this, RenderType.Canvas, scaleFactor, opt);
return r.canvas();
}
+
/**
* Produce a rendered SVG string of the visualization.
* This method is asynchronous, returning a Promise instance.
* @return {Promise} - A promise that resolves to an SVG string.
*/
-
-
async function renderToSVG(scaleFactor) {
const r = await renderHeadless(this, RenderType.SVG, scaleFactor);
return r.svg();
}
-
function runtime(view, spec, expr) {
return context(view, transforms, functionContext, expr).parse(spec);
}
-
function scale$1(name) {
var scales = this._runtime.scales;
-
if (!has$1(scales, name)) {
error('Unrecognized scale or projection: ' + name);
}
-
return scales[name].value;
}
-
var Width = 'width',
- Height = 'height',
- Padding = 'padding',
- Skip$1 = {
- skip: true
- };
-
+ Height = 'height',
+ Padding = 'padding',
+ Skip$1 = {
+ skip: true
+ };
function viewWidth(view, width) {
var a = view.autosize(),
- p = view.padding();
+ p = view.padding();
return width - (a && a.contains === Padding ? p.left + p.right : 0);
}
-
function viewHeight(view, height) {
var a = view.autosize(),
- p = view.padding();
+ p = view.padding();
return height - (a && a.contains === Padding ? p.top + p.bottom : 0);
}
-
function initializeResize(view) {
var s = view._signals,
- w = s[Width],
- h = s[Height],
- p = s[Padding];
-
+ w = s[Width],
+ h = s[Height],
+ p = s[Padding];
function resetSize() {
view._autosize = view._resize = 1;
- } // respond to width signal
+ }
-
+ // respond to width signal
view._resizeWidth = view.add(null, _ => {
view._width = _.size;
view._viewWidth = viewWidth(view, _.size);
resetSize();
}, {
size: w
- }); // respond to height signal
+ });
+ // respond to height signal
view._resizeHeight = view.add(null, _ => {
view._height = _.size;
view._viewHeight = viewHeight(view, _.size);
resetSize();
}, {
size: h
- }); // respond to padding signal
+ });
+ // respond to padding signal
const resizePadding = view.add(null, resetSize, {
pad: p
- }); // set rank to run immediately after source signal
+ });
+ // set rank to run immediately after source signal
view._resizeWidth.rank = w.rank + 1;
view._resizeHeight.rank = h.rank + 1;
resizePadding.rank = p.rank + 1;
}
-
function resizeView(viewWidth, viewHeight, width, height, origin, auto) {
this.runAfter(view => {
- let rerun = 0; // reset autosize flag
+ let rerun = 0;
- view._autosize = 0; // width value changed: update signal, skip resize op
+ // reset autosize flag
+ view._autosize = 0;
+ // width value changed: update signal, skip resize op
if (view.width() !== width) {
rerun = 1;
view.signal(Width, width, Skip$1); // set width, skip update calc
-
view._resizeWidth.skip(true); // skip width resize handler
+ }
- } // height value changed: update signal, skip resize op
-
-
+ // height value changed: update signal, skip resize op
if (view.height() !== height) {
rerun = 1;
view.signal(Height, height, Skip$1); // set height, skip update calc
-
view._resizeHeight.skip(true); // skip height resize handler
+ }
- } // view width changed: update view property, set resize flag
-
-
+ // view width changed: update view property, set resize flag
if (view._viewWidth !== viewWidth) {
view._resize = 1;
view._viewWidth = viewWidth;
- } // view height changed: update view property, set resize flag
+ }
-
+ // view height changed: update view property, set resize flag
if (view._viewHeight !== viewHeight) {
view._resize = 1;
view._viewHeight = viewHeight;
- } // origin changed: update view property, set resize flag
+ }
-
+ // origin changed: update view property, set resize flag
if (view._origin[0] !== origin[0] || view._origin[1] !== origin[1]) {
view._resize = 1;
view._origin = origin;
- } // run dataflow on width/height signal change
+ }
-
+ // run dataflow on width/height signal change
if (rerun) view.run('enter');
if (auto) view.runAfter(v => v.resize());
}, false, 1);
}
+
/**
* Get the current view state, consisting of signal values and/or data sets.
* @param {object} [options] - Options flags indicating which state to export.
* If unspecified, all signals and data sets will be exported.
* @param {function(string, Operator):boolean} [options.signals] - Optional
@@ -41145,417 +37167,378 @@
* explicitly modified will be included.
* @param {boolean} [options.recurse=true] - Flag indicating if the exported
* state should recursively include state from group mark sub-contexts.
* @return {object} - An object containing the exported state values.
*/
-
-
function getState(options) {
return this._runtime.getState(options || {
data: dataTest,
signals: signalTest,
recurse: true
});
}
-
function dataTest(name, data) {
- return data.modified && isArray(data.input.value) && name.indexOf('_:vega:_');
+ return data.modified && isArray(data.input.value) && !name.startsWith('_:vega:_');
}
-
function signalTest(name, op) {
return !(name === 'parent' || op instanceof transforms.proxy);
}
+
/**
* Sets the current view state and updates the view by invoking run.
* @param {object} state - A state object containing signal and/or
* data set values, following the format used by the getState method.
* @return {View} - This view instance.
*/
-
-
function setState(state) {
this.runAsync(null, v => {
v._trigger = false;
-
v._runtime.setState(state);
}, v => {
v._trigger = true;
});
return this;
}
-
function timer(callback, delay) {
function tick(elapsed) {
callback({
timestamp: Date.now(),
elapsed: elapsed
});
}
-
this._timers.push(interval(tick, delay));
}
-
function defaultTooltip(handler, event, item, value) {
const el = handler.element();
if (el) el.setAttribute('title', formatTooltip(value));
}
-
function formatTooltip(value) {
return value == null ? '' : isArray(value) ? formatArray(value) : isObject(value) && !isDate$1(value) ? formatObject(value) : value + '';
}
-
function formatObject(obj) {
return Object.keys(obj).map(key => {
const v = obj[key];
return key + ': ' + (isArray(v) ? formatArray(v) : formatValue(v));
}).join('\n');
}
-
function formatArray(value) {
return '[' + value.map(formatValue).join(', ') + ']';
}
-
function formatValue(value) {
return isArray(value) ? '[\u2026]' : isObject(value) && !isDate$1(value) ? '{\u2026}' : value;
}
+ function watchPixelRatio() {
+ // based on https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
+ if (this.renderer() === 'canvas' && this._renderer._canvas) {
+ let remove = null;
+ const updatePixelRatio = () => {
+ if (remove != null) {
+ remove();
+ }
+ const media = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
+ media.addEventListener('change', updatePixelRatio);
+ remove = () => {
+ media.removeEventListener('change', updatePixelRatio);
+ };
+ this._renderer._canvas.getContext('2d').pixelRatio = window.devicePixelRatio || 1;
+ this._redraw = true;
+ this._resize = 1;
+ this.resize().runAsync();
+ };
+ updatePixelRatio();
+ }
+ }
+
/**
* Create a new View instance from a Vega dataflow runtime specification.
* The generated View will not immediately be ready for display. Callers
* should also invoke the initialize method (e.g., to set the parent
* DOM element in browser-based deployment) and then invoke the run
* method to evaluate the dataflow graph. Rendering will automatically
* be performed upon dataflow runs.
* @constructor
* @param {object} spec - The Vega dataflow runtime specification.
*/
-
-
function View$1(spec, options) {
const view = this;
options = options || {};
Dataflow.call(view);
if (options.loader) view.loader(options.loader);
if (options.logger) view.logger(options.logger);
if (options.logLevel != null) view.logLevel(options.logLevel);
-
if (options.locale || spec.locale) {
const loc = extend$1({}, spec.locale, options.locale);
view.locale(locale(loc.number, loc.time));
}
-
view._el = null;
view._elBind = null;
view._renderType = options.renderer || RenderType.Canvas;
view._scenegraph = new Scenegraph();
- const root = view._scenegraph.root; // initialize renderer, handler and event management
+ const root = view._scenegraph.root;
+ // initialize renderer, handler and event management
view._renderer = null;
view._tooltip = options.tooltip || defaultTooltip, view._redraw = true;
view._handler = new CanvasHandler().scene(root);
view._globalCursor = false;
view._preventDefault = false;
view._timers = [];
view._eventListeners = [];
- view._resizeListeners = []; // initialize event configuration
+ view._resizeListeners = [];
+ // initialize event configuration
view._eventConfig = initializeEventConfig(spec.eventConfig);
- view.globalCursor(view._eventConfig.globalCursor); // initialize dataflow graph
+ view.globalCursor(view._eventConfig.globalCursor);
+ // initialize dataflow graph
const ctx = runtime(view, spec, options.expr);
view._runtime = ctx;
view._signals = ctx.signals;
view._bind = (spec.bindings || []).map(_ => ({
state: null,
param: extend$1({}, _)
- })); // initialize scenegraph
+ }));
+ // initialize scenegraph
if (ctx.root) ctx.root.set(root);
root.source = ctx.data.root.input;
- view.pulse(ctx.data.root.input, view.changeset().insert(root.items)); // initialize view size
+ view.pulse(ctx.data.root.input, view.changeset().insert(root.items));
+ // initialize view size
view._width = view.width();
view._height = view.height();
view._viewWidth = viewWidth(view, view._width);
view._viewHeight = viewHeight(view, view._height);
view._origin = [0, 0];
view._resize = 0;
view._autosize = 1;
- initializeResize(view); // initialize background color
+ initializeResize(view);
- background(view); // initialize cursor
+ // initialize background color
+ background(view);
- cursor(view); // initialize view description
+ // initialize cursor
+ cursor(view);
- view.description(spec.description); // initialize hover proessing, if requested
+ // initialize view description
+ view.description(spec.description);
- if (options.hover) view.hover(); // initialize DOM container(s) and renderer
+ // initialize hover proessing, if requested
+ if (options.hover) view.hover();
+ // initialize DOM container(s) and renderer
if (options.container) view.initialize(options.container, options.bind);
+ if (options.watchPixelRatio) view._watchPixelRatio();
}
-
function lookupSignal(view, name) {
return has$1(view._signals, name) ? view._signals[name] : error('Unrecognized signal name: ' + $(name));
}
-
function findOperatorHandler(op, handler) {
const h = (op._targets || []).filter(op => op._update && op._update.handler === handler);
return h.length ? h[0] : null;
}
-
function addOperatorListener(view, name, op, handler) {
let h = findOperatorHandler(op, handler);
-
if (!h) {
h = trap(view, () => handler(name, op.value));
h.handler = handler;
view.on(op, null, h);
}
-
return view;
}
-
function removeOperatorListener(view, op, handler) {
const h = findOperatorHandler(op, handler);
if (h) op._targets.remove(h);
return view;
}
-
inherits(View$1, Dataflow, {
// -- DATAFLOW / RENDERING ----
+
async evaluate(encode, prerun, postrun) {
// evaluate dataflow and prerun
- await Dataflow.prototype.evaluate.call(this, encode, prerun); // render as needed
+ await Dataflow.prototype.evaluate.call(this, encode, prerun);
+ // render as needed
if (this._redraw || this._resize) {
try {
if (this._renderer) {
if (this._resize) {
this._resize = 0;
resizeRenderer(this);
}
-
await this._renderer.renderAsync(this._scenegraph.root);
}
-
this._redraw = false;
} catch (e) {
this.error(e);
}
- } // evaluate postrun
+ }
-
+ // evaluate postrun
if (postrun) asyncCallback(this, postrun);
return this;
},
-
dirty(item) {
this._redraw = true;
this._renderer && this._renderer.dirty(item);
},
-
// -- GET / SET ----
+
description(text) {
if (arguments.length) {
const desc = text != null ? text + '' : null;
if (desc !== this._desc) ariaLabel(this._el, this._desc = desc);
return this;
}
-
return this._desc;
},
-
container() {
return this._el;
},
-
scenegraph() {
return this._scenegraph;
},
-
origin() {
return this._origin.slice();
},
-
signal(name, value, options) {
const op = lookupSignal(this, name);
return arguments.length === 1 ? op.value : this.update(op, value, options);
},
-
width(_) {
return arguments.length ? this.signal('width', _) : this.signal('width');
},
-
height(_) {
return arguments.length ? this.signal('height', _) : this.signal('height');
},
-
padding(_) {
return arguments.length ? this.signal('padding', padding(_)) : padding(this.signal('padding'));
},
-
autosize(_) {
return arguments.length ? this.signal('autosize', _) : this.signal('autosize');
},
-
background(_) {
return arguments.length ? this.signal('background', _) : this.signal('background');
},
-
renderer(type) {
if (!arguments.length) return this._renderType;
if (!renderModule(type)) error('Unrecognized renderer type: ' + type);
-
if (type !== this._renderType) {
this._renderType = type;
-
this._resetRenderer();
}
-
return this;
},
-
tooltip(handler) {
if (!arguments.length) return this._tooltip;
-
if (handler !== this._tooltip) {
this._tooltip = handler;
-
this._resetRenderer();
}
-
return this;
},
-
loader(loader) {
if (!arguments.length) return this._loader;
-
if (loader !== this._loader) {
Dataflow.prototype.loader.call(this, loader);
-
this._resetRenderer();
}
-
return this;
},
-
resize() {
// set flag to perform autosize
- this._autosize = 1; // touch autosize signal to ensure top-level ViewLayout runs
-
+ this._autosize = 1;
+ // touch autosize signal to ensure top-level ViewLayout runs
return this.touch(lookupSignal(this, 'autosize'));
},
-
_resetRenderer() {
if (this._renderer) {
this._renderer = null;
this.initialize(this._el, this._elBind);
}
},
-
// -- SIZING ----
_resizeView: resizeView,
-
// -- EVENT HANDLING ----
+
addEventListener(type, handler, options) {
let callback = handler;
-
if (!(options && options.trap === false)) {
// wrap callback in error handler
callback = trap(this, handler);
callback.raw = handler;
}
-
this._handler.on(type, callback);
-
return this;
},
-
removeEventListener(type, handler) {
var handlers = this._handler.handlers(type),
- i = handlers.length,
- h,
- t; // search registered handlers, remove if match found
+ i = handlers.length,
+ h,
+ t;
-
+ // search registered handlers, remove if match found
while (--i >= 0) {
t = handlers[i].type;
h = handlers[i].handler;
-
if (type === t && (handler === h || handler === h.raw)) {
this._handler.off(t, h);
-
break;
}
}
-
return this;
},
-
addResizeListener(handler) {
const l = this._resizeListeners;
-
- if (l.indexOf(handler) < 0) {
+ if (!l.includes(handler)) {
// add handler if it isn't already registered
// note: error trapping handled elsewhere, so
// no need to wrap handlers here
l.push(handler);
}
-
return this;
},
-
removeResizeListener(handler) {
var l = this._resizeListeners,
- i = l.indexOf(handler);
-
+ i = l.indexOf(handler);
if (i >= 0) {
l.splice(i, 1);
}
-
return this;
},
-
addSignalListener(name, handler) {
return addOperatorListener(this, name, lookupSignal(this, name), handler);
},
-
removeSignalListener(name, handler) {
return removeOperatorListener(this, lookupSignal(this, name), handler);
},
-
addDataListener(name, handler) {
return addOperatorListener(this, name, dataref(this, name).values, handler);
},
-
removeDataListener(name, handler) {
return removeOperatorListener(this, dataref(this, name).values, handler);
},
-
globalCursor(_) {
if (arguments.length) {
if (this._globalCursor !== !!_) {
const prev = setCursor(this, null); // clear previous cursor
-
this._globalCursor = !!_;
if (prev) setCursor(this, prev); // swap cursor
}
-
return this;
} else {
return this._globalCursor;
}
},
-
preventDefault(_) {
if (arguments.length) {
this._preventDefault = _;
return this;
} else {
return this._preventDefault;
}
},
-
timer,
events,
finalize,
hover,
// -- DATA ----
@@ -41571,219 +37554,194 @@
toImageURL: renderToImageURL,
toCanvas: renderToCanvas,
toSVG: renderToSVG,
// -- SAVE / RESTORE STATE ----
getState,
- setState
+ setState,
+ // RE-RENDER ON ZOOM
+ _watchPixelRatio: watchPixelRatio
});
const VIEW = 'view',
- LBRACK = '[',
- RBRACK = ']',
- LBRACE = '{',
- RBRACE = '}',
- COLON = ':',
- COMMA = ',',
- NAME = '@',
- GT = '>',
- ILLEGAL = /[[\]{}]/,
- DEFAULT_MARKS = {
- '*': 1,
- arc: 1,
- area: 1,
- group: 1,
- image: 1,
- line: 1,
- path: 1,
- rect: 1,
- rule: 1,
- shape: 1,
- symbol: 1,
- text: 1,
- trail: 1
- };
+ LBRACK = '[',
+ RBRACK = ']',
+ LBRACE = '{',
+ RBRACE = '}',
+ COLON = ':',
+ COMMA = ',',
+ NAME = '@',
+ GT = '>',
+ ILLEGAL = /[[\]{}]/,
+ DEFAULT_MARKS = {
+ '*': 1,
+ arc: 1,
+ area: 1,
+ group: 1,
+ image: 1,
+ line: 1,
+ path: 1,
+ rect: 1,
+ rule: 1,
+ shape: 1,
+ symbol: 1,
+ text: 1,
+ trail: 1
+ };
let DEFAULT_SOURCE, MARKS;
+
/**
* Parse an event selector string.
* Returns an array of event stream definitions.
*/
-
function eventSelector(selector, source, marks) {
DEFAULT_SOURCE = source || VIEW;
MARKS = marks || DEFAULT_MARKS;
return parseMerge(selector.trim()).map(parseSelector);
}
-
function isMarkType(type) {
return MARKS[type];
}
-
function find(s, i, endChar, pushChar, popChar) {
const n = s.length;
let count = 0,
- c;
-
+ c;
for (; i < n; ++i) {
c = s[i];
- if (!count && c === endChar) return i;else if (popChar && popChar.indexOf(c) >= 0) --count;else if (pushChar && pushChar.indexOf(c) >= 0) ++count;
+ if (!count && c === endChar) return i;else if (popChar && popChar.includes(c)) --count;else if (pushChar && pushChar.includes(c)) ++count;
}
-
return i;
}
-
function parseMerge(s) {
const output = [],
- n = s.length;
+ n = s.length;
let start = 0,
- i = 0;
-
+ i = 0;
while (i < n) {
i = find(s, i, COMMA, LBRACK + LBRACE, RBRACK + RBRACE);
output.push(s.substring(start, i).trim());
start = ++i;
}
-
if (output.length === 0) {
throw 'Empty event selector: ' + s;
}
-
return output;
}
-
function parseSelector(s) {
return s[0] === '[' ? parseBetween(s) : parseStream$1(s);
}
-
function parseBetween(s) {
const n = s.length;
let i = 1,
- b;
+ b;
i = find(s, i, RBRACK, LBRACK, RBRACK);
-
if (i === n) {
throw 'Empty between selector: ' + s;
}
-
b = parseMerge(s.substring(1, i));
-
if (b.length !== 2) {
throw 'Between selector must have two elements: ' + s;
}
-
s = s.slice(i + 1).trim();
-
if (s[0] !== GT) {
throw 'Expected \'>\' after between selector: ' + s;
}
-
b = b.map(parseSelector);
const stream = parseSelector(s.slice(1).trim());
-
if (stream.between) {
return {
between: b,
stream: stream
};
} else {
stream.between = b;
}
-
return stream;
}
-
function parseStream$1(s) {
const stream = {
- source: DEFAULT_SOURCE
- },
- source = [];
+ source: DEFAULT_SOURCE
+ },
+ source = [];
let throttle = [0, 0],
- markname = 0,
- start = 0,
- n = s.length,
- i = 0,
- j,
- filter; // extract throttle from end
+ markname = 0,
+ start = 0,
+ n = s.length,
+ i = 0,
+ j,
+ filter;
+ // extract throttle from end
if (s[n - 1] === RBRACE) {
i = s.lastIndexOf(LBRACE);
-
if (i >= 0) {
try {
throttle = parseThrottle(s.substring(i + 1, n - 1));
} catch (e) {
throw 'Invalid throttle specification: ' + s;
}
-
s = s.slice(0, i).trim();
n = s.length;
} else throw 'Unmatched right brace: ' + s;
-
i = 0;
}
+ if (!n) throw s;
- if (!n) throw s; // set name flag based on first char
+ // set name flag based on first char
+ if (s[0] === NAME) markname = ++i;
- if (s[0] === NAME) markname = ++i; // extract first part of multi-part stream selector
-
+ // extract first part of multi-part stream selector
j = find(s, i, COLON);
-
if (j < n) {
source.push(s.substring(start, j).trim());
start = i = ++j;
- } // extract remaining part of stream selector
+ }
-
+ // extract remaining part of stream selector
i = find(s, i, LBRACK);
-
if (i === n) {
source.push(s.substring(start, n).trim());
} else {
source.push(s.substring(start, i).trim());
filter = [];
start = ++i;
if (start === n) throw 'Unmatched left bracket: ' + s;
- } // extract filters
+ }
-
+ // extract filters
while (i < n) {
i = find(s, i, RBRACK);
if (i === n) throw 'Unmatched left bracket: ' + s;
filter.push(s.substring(start, i).trim());
if (i < n - 1 && s[++i] !== LBRACK) throw 'Expected left bracket: ' + s;
start = ++i;
- } // marshall event stream specification
+ }
-
+ // marshall event stream specification
if (!(n = source.length) || ILLEGAL.test(source[n - 1])) {
throw 'Invalid event selector: ' + s;
}
-
if (n > 1) {
stream.type = source[1];
-
if (markname) {
stream.markname = source[0].slice(1);
} else if (isMarkType(source[0])) {
stream.marktype = source[0];
} else {
stream.source = source[0];
}
} else {
stream.type = source[0];
}
-
if (stream.type.slice(-1) === '!') {
stream.consume = true;
stream.type = stream.type.slice(0, -1);
}
-
if (filter != null) stream.filter = filter;
if (throttle[0]) stream.throttle = throttle[0];
if (throttle[1]) stream.debounce = throttle[1];
return stream;
}
-
function parseThrottle(s) {
const a = s.split(COMMA);
if (!s.length || a.length > 2) throw s;
return a.map(_ => {
const x = +_;
@@ -41795,74 +37753,63 @@
function parseAutosize(spec) {
return isObject(spec) ? spec : {
type: spec || 'pad'
};
}
-
const number = _ => +_ || 0;
-
const paddingObject = _ => ({
top: _,
bottom: _,
left: _,
right: _
});
-
function parsePadding(spec) {
return !isObject(spec) ? paddingObject(number(spec)) : spec.signal ? spec : {
top: number(spec.top),
bottom: number(spec.bottom),
left: number(spec.left),
right: number(spec.right)
};
}
-
const encoder = _ => isObject(_) && !isArray(_) ? extend$1({}, _) : {
value: _
};
-
function addEncode(object, name, value, set) {
if (value != null) {
- const isEncoder = isObject(value) && !isArray(value) || isArray(value) && value.length && isObject(value[0]); // Always assign signal to update, even if the signal is from the enter block
+ const isEncoder = isObject(value) && !isArray(value) || isArray(value) && value.length && isObject(value[0]);
+ // Always assign signal to update, even if the signal is from the enter block
if (isEncoder) {
object.update[name] = value;
} else {
object[set || 'enter'][name] = {
value: value
};
}
-
return 1;
} else {
return 0;
}
}
-
function addEncoders(object, enter, update) {
for (const name in enter) {
addEncode(object, name, enter[name]);
}
-
for (const name in update) {
addEncode(object, name, update[name], 'update');
}
}
-
function extendEncode(encode, extra, skip) {
for (const name in extra) {
if (skip && has$1(skip, name)) continue;
encode[name] = extend$1(encode[name] || {}, extra[name]);
}
-
return encode;
}
-
function has(key, encode) {
return encode && (encode.enter && encode.enter[key] || encode.update && encode.update[key]);
}
-
const MarkRole = 'mark';
const FrameRole = 'frame';
const ScopeRole = 'scope';
const AxisRole = 'axis';
const AxisDomainRole = 'axis-domain';
@@ -41878,147 +37825,121 @@
const LegendSymbolRole = 'legend-symbol';
const LegendTitleRole = 'legend-title';
const TitleRole = 'title';
const TitleTextRole = 'title-text';
const TitleSubtitleRole = 'title-subtitle';
-
function applyDefaults(encode, type, role, style, config) {
const defaults = {},
- enter = {};
- let update, key, skip, props; // if text mark, apply global lineBreak settings (#2370)
+ enter = {};
+ let update, key, skip, props;
+ // if text mark, apply global lineBreak settings (#2370)
key = 'lineBreak';
-
if (type === 'text' && config[key] != null && !has(key, encode)) {
applyDefault(defaults, key, config[key]);
- } // ignore legend and axis roles
+ }
-
+ // ignore legend and axis roles
if (role == 'legend' || String(role).startsWith('axis')) {
role = null;
- } // resolve mark config
+ }
-
+ // resolve mark config
props = role === FrameRole ? config.group : role === MarkRole ? extend$1({}, config.mark, config[type]) : null;
-
for (key in props) {
// do not apply defaults if relevant fields are defined
skip = has(key, encode) || (key === 'fill' || key === 'stroke') && (has('fill', encode) || has('stroke', encode));
if (!skip) applyDefault(defaults, key, props[key]);
- } // resolve styles, apply with increasing precedence
+ }
-
+ // resolve styles, apply with increasing precedence
array$5(style).forEach(name => {
const props = config.style && config.style[name];
-
for (const key in props) {
if (!has(key, encode)) {
applyDefault(defaults, key, props[key]);
}
}
});
encode = extend$1({}, encode); // defensive copy
-
for (key in defaults) {
props = defaults[key];
-
if (props.signal) {
(update = update || {})[key] = props;
} else {
enter[key] = props;
}
}
-
encode.enter = extend$1(enter, encode.enter);
if (update) encode.update = extend$1(update, encode.update);
return encode;
}
-
function applyDefault(defaults, key, value) {
defaults[key] = value && value.signal ? {
signal: value.signal
} : {
value: value
};
}
-
- const scaleRef = scale => isString(scale) ? $(scale) : scale.signal ? "(".concat(scale.signal, ")") : field(scale);
-
+ const scaleRef = scale => isString(scale) ? $(scale) : scale.signal ? `(${scale.signal})` : field(scale);
function entry$1(enc) {
if (enc.gradient != null) {
return gradient(enc);
}
-
- let value = enc.signal ? "(".concat(enc.signal, ")") : enc.color ? color(enc.color) : enc.field != null ? field(enc.field) : enc.value !== undefined ? $(enc.value) : undefined;
-
+ let value = enc.signal ? `(${enc.signal})` : enc.color ? color(enc.color) : enc.field != null ? field(enc.field) : enc.value !== undefined ? $(enc.value) : undefined;
if (enc.scale != null) {
value = scale(enc, value);
}
-
if (value === undefined) {
value = null;
}
-
if (enc.exponent != null) {
- value = "pow(".concat(value, ",").concat(property(enc.exponent), ")");
+ value = `pow(${value},${property(enc.exponent)})`;
}
-
if (enc.mult != null) {
- value += "*".concat(property(enc.mult));
+ value += `*${property(enc.mult)}`;
}
-
if (enc.offset != null) {
- value += "+".concat(property(enc.offset));
+ value += `+${property(enc.offset)}`;
}
-
if (enc.round) {
- value = "round(".concat(value, ")");
+ value = `round(${value})`;
}
-
return value;
}
-
- const _color = (type, x, y, z) => "(".concat(type, "(").concat([x, y, z].map(entry$1).join(','), ")+'')");
-
+ const _color = (type, x, y, z) => `(${type}(${[x, y, z].map(entry$1).join(',')})+'')`;
function color(enc) {
return enc.c ? _color('hcl', enc.h, enc.c, enc.l) : enc.h || enc.s ? _color('hsl', enc.h, enc.s, enc.l) : enc.l || enc.a ? _color('lab', enc.l, enc.a, enc.b) : enc.r || enc.g || enc.b ? _color('rgb', enc.r, enc.g, enc.b) : null;
}
-
function gradient(enc) {
// map undefined to null; expression lang does not allow undefined
- const args = [enc.start, enc.stop, enc.count].map(_ => _ == null ? null : $(_)); // trim null inputs from the end
+ const args = [enc.start, enc.stop, enc.count].map(_ => _ == null ? null : $(_));
+ // trim null inputs from the end
while (args.length && peek$1(args) == null) args.pop();
-
args.unshift(scaleRef(enc.gradient));
- return "gradient(".concat(args.join(','), ")");
+ return `gradient(${args.join(',')})`;
}
-
function property(property) {
return isObject(property) ? '(' + entry$1(property) + ')' : property;
}
-
function field(ref) {
return resolveField(isObject(ref) ? ref : {
datum: ref
});
}
-
function resolveField(ref) {
let object, level, field;
-
if (ref.signal) {
object = 'datum';
field = ref.signal;
} else if (ref.group || ref.parent) {
level = Math.max(1, ref.level || 1);
object = 'item';
-
while (level-- > 0) {
object += '.mark.group';
}
-
if (ref.parent) {
field = ref.parent;
object += '.datum';
} else {
field = ref.group;
@@ -42027,414 +37948,351 @@
object = 'datum';
field = ref.datum;
} else {
error('Invalid field reference: ' + $(ref));
}
-
if (!ref.signal) {
field = isString(field) ? splitAccessPath(field).map($).join('][') : resolveField(field);
}
-
return object + '[' + field + ']';
}
-
function scale(enc, value) {
const scale = scaleRef(enc.scale);
-
if (enc.range != null) {
// pull value from scale range
- value = "lerp(_range(".concat(scale, "), ").concat(+enc.range, ")");
+ value = `lerp(_range(${scale}), ${+enc.range})`;
} else {
// run value through scale and/or pull scale bandwidth
- if (value !== undefined) value = "_scale(".concat(scale, ", ").concat(value, ")");
-
+ if (value !== undefined) value = `_scale(${scale}, ${value})`;
if (enc.band) {
- value = (value ? value + '+' : '') + "_bandwidth(".concat(scale, ")") + (+enc.band === 1 ? '' : '*' + property(enc.band));
-
+ value = (value ? value + '+' : '') + `_bandwidth(${scale})` + (+enc.band === 1 ? '' : '*' + property(enc.band));
if (enc.extra) {
// include logic to handle extraneous elements
- value = "(datum.extra ? _scale(".concat(scale, ", datum.extra.value) : ").concat(value, ")");
+ value = `(datum.extra ? _scale(${scale}, datum.extra.value) : ${value})`;
}
}
-
if (value == null) value = '0';
}
-
return value;
}
-
function rule(enc) {
let code = '';
enc.forEach(rule => {
const value = entry$1(rule);
- code += rule.test ? "(".concat(rule.test, ")?").concat(value, ":") : value;
- }); // if no else clause, terminate with null (#1366)
+ code += rule.test ? `(${rule.test})?${value}:` : value;
+ });
+ // if no else clause, terminate with null (#1366)
if (peek$1(code) === ':') {
code += 'null';
}
-
return code;
}
-
function parseEncode(encode, type, role, style, scope, params) {
const enc = {};
params = params || {};
params.encoders = {
$encode: enc
};
encode = applyDefaults(encode, type, role, style, scope.config);
-
for (const key in encode) {
enc[key] = parseBlock(encode[key], type, params, scope);
}
-
return params;
}
-
function parseBlock(block, marktype, params, scope) {
const channels = {},
- fields = {};
-
+ fields = {};
for (const name in block) {
if (block[name] != null) {
// skip any null entries
channels[name] = parse$1(expr(block[name]), scope, params, fields);
}
}
-
return {
$expr: {
marktype,
channels
},
$fields: Object.keys(fields),
$output: Object.keys(block)
};
}
-
function expr(enc) {
return isArray(enc) ? rule(enc) : entry$1(enc);
}
-
function parse$1(code, scope, params, fields) {
const expr = parser(code, scope);
expr.$fields.forEach(name => fields[name] = 1);
extend$1(params, expr.$params);
return expr.$expr;
}
-
const OUTER = 'outer',
- OUTER_INVALID = ['value', 'update', 'init', 'react', 'bind'];
-
+ OUTER_INVALID = ['value', 'update', 'init', 'react', 'bind'];
function outerError(prefix, name) {
error(prefix + ' for "outer" push: ' + $(name));
}
-
function parseSignal(signal, scope) {
const name = signal.name;
-
if (signal.push === OUTER) {
// signal must already be defined, raise error if not
- if (!scope.signals[name]) outerError('No prior signal definition', name); // signal push must not use properties reserved for standard definition
-
+ if (!scope.signals[name]) outerError('No prior signal definition', name);
+ // signal push must not use properties reserved for standard definition
OUTER_INVALID.forEach(prop => {
if (signal[prop] !== undefined) outerError('Invalid property ', prop);
});
} else {
// define a new signal in the current scope
const op = scope.addSignal(name, signal.value);
if (signal.react === false) op.react = false;
if (signal.bind) scope.addBinding(name, signal.bind);
}
}
-
function Entry(type, value, params, parent) {
this.id = -1;
this.type = type;
this.value = value;
this.params = params;
if (parent) this.parent = parent;
}
-
function entry(type, value, params, parent) {
return new Entry(type, value, params, parent);
}
-
function operator(value, params) {
return entry('operator', value, params);
- } // -----
+ }
+ // -----
function ref(op) {
const ref = {
$ref: op.id
- }; // if operator not yet registered, cache ref to resolve later
-
+ };
+ // if operator not yet registered, cache ref to resolve later
if (op.id < 0) (op.refs = op.refs || []).push(ref);
return ref;
}
-
function fieldRef$1(field, name) {
return name ? {
$field: field,
$name: name
} : {
$field: field
};
}
-
const keyFieldRef = fieldRef$1('key');
-
function compareRef(fields, orders) {
return {
$compare: fields,
$order: orders
};
}
-
function keyRef(fields, flat) {
const ref = {
$key: fields
};
if (flat) ref.$flat = true;
return ref;
- } // -----
+ }
+ // -----
const Ascending = 'ascending';
const Descending = 'descending';
-
function sortKey(sort) {
return !isObject(sort) ? '' : (sort.order === Descending ? '-' : '+') + aggrField(sort.op, sort.field);
}
-
function aggrField(op, field) {
return (op && op.signal ? '$' + op.signal : op || '') + (op && field ? '_' : '') + (field && field.signal ? '$' + field.signal : field || '');
- } // -----
+ }
+ // -----
const Scope$1 = 'scope';
const View = 'view';
-
function isSignal(_) {
return _ && _.signal;
}
-
function isExpr$1(_) {
return _ && _.expr;
}
-
function hasSignal(_) {
if (isSignal(_)) return true;
if (isObject(_)) for (const key in _) {
if (hasSignal(_[key])) return true;
}
return false;
}
-
function value(specValue, defaultValue) {
return specValue != null ? specValue : defaultValue;
}
-
function deref(v) {
return v && v.signal || v;
}
-
const Timer = 'timer';
-
function parseStream(stream, scope) {
const method = stream.merge ? mergeStream : stream.stream ? nestedStream : stream.type ? eventStream : error('Invalid stream specification: ' + $(stream));
return method(stream, scope);
}
-
function eventSource(source) {
return source === Scope$1 ? View : source || View;
}
-
function mergeStream(stream, scope) {
const list = stream.merge.map(s => parseStream(s, scope)),
- entry = streamParameters({
- merge: list
- }, stream, scope);
+ entry = streamParameters({
+ merge: list
+ }, stream, scope);
return scope.addStream(entry).id;
}
-
function nestedStream(stream, scope) {
const id = parseStream(stream.stream, scope),
- entry = streamParameters({
- stream: id
- }, stream, scope);
+ entry = streamParameters({
+ stream: id
+ }, stream, scope);
return scope.addStream(entry).id;
}
-
function eventStream(stream, scope) {
let id;
-
if (stream.type === Timer) {
id = scope.event(Timer, stream.throttle);
stream = {
between: stream.between,
filter: stream.filter
};
} else {
id = scope.event(eventSource(stream.source), stream.type);
}
-
const entry = streamParameters({
stream: id
}, stream, scope);
return Object.keys(entry).length === 1 ? id : scope.addStream(entry).id;
}
-
function streamParameters(entry, stream, scope) {
let param = stream.between;
-
if (param) {
if (param.length !== 2) {
error('Stream "between" parameter must have 2 entries: ' + $(stream));
}
-
entry.between = [parseStream(param[0], scope), parseStream(param[1], scope)];
}
-
param = stream.filter ? [].concat(stream.filter) : [];
-
if (stream.marktype || stream.markname || stream.markrole) {
// add filter for mark type, name and/or role
param.push(filterMark(stream.marktype, stream.markname, stream.markrole));
}
-
if (stream.source === Scope$1) {
// add filter to limit events from sub-scope only
param.push('inScope(event.item)');
}
-
if (param.length) {
entry.filter = parser('(' + param.join(')&&(') + ')', scope).$expr;
}
-
if ((param = stream.throttle) != null) {
entry.throttle = +param;
}
-
if ((param = stream.debounce) != null) {
entry.debounce = +param;
}
-
if (stream.consume) {
entry.consume = true;
}
-
return entry;
}
-
function filterMark(type, name, role) {
const item = 'event.item';
return item + (type && type !== '*' ? '&&' + item + '.mark.marktype===\'' + type + '\'' : '') + (role ? '&&' + item + '.mark.role===\'' + role + '\'' : '') + (name ? '&&' + item + '.mark.name===\'' + name + '\'' : '');
}
+ // bypass expression parser for internal operator references
const OP_VALUE_EXPR = {
code: '_.$value',
ast: {
type: 'Identifier',
value: 'value'
}
};
-
function parseUpdate(spec, scope, target) {
const encode = spec.encode,
- entry = {
- target: target
- };
+ entry = {
+ target: target
+ };
let events = spec.events,
- update = spec.update,
- sources = [];
-
+ update = spec.update,
+ sources = [];
if (!events) {
error('Signal update missing events specification.');
- } // interpret as an event selector string
+ }
-
+ // interpret as an event selector string
if (isString(events)) {
events = eventSelector(events, scope.isSubscope() ? Scope$1 : View);
- } // separate event streams from signal updates
+ }
+ // separate event streams from signal updates
+ events = array$5(events).filter(s => s.signal || s.scale ? (sources.push(s), 0) : 1);
- events = array$5(events).filter(s => s.signal || s.scale ? (sources.push(s), 0) : 1); // merge internal operator listeners
-
+ // merge internal operator listeners
if (sources.length > 1) {
sources = [mergeSources(sources)];
- } // merge event streams, include as source
+ }
-
+ // merge event streams, include as source
if (events.length) {
sources.push(events.length > 1 ? {
merge: events
} : events[0]);
}
-
if (encode != null) {
if (update) error('Signal encode and update are mutually exclusive.');
update = 'encode(item(),' + $(encode) + ')';
- } // resolve update value
+ }
-
+ // resolve update value
entry.update = isString(update) ? parser(update, scope) : update.expr != null ? parser(update.expr, scope) : update.value != null ? update.value : update.signal != null ? {
$expr: OP_VALUE_EXPR,
$params: {
$value: scope.signalRef(update.signal)
}
} : error('Invalid signal update specification.');
-
if (spec.force) {
entry.options = {
force: true
};
}
-
sources.forEach(source => scope.addUpdate(extend$1(streamSource(source, scope), entry)));
}
-
function streamSource(stream, scope) {
return {
source: stream.signal ? scope.signalRef(stream.signal) : stream.scale ? scope.scaleRef(stream.scale) : parseStream(stream, scope)
};
}
-
function mergeSources(sources) {
return {
signal: '[' + sources.map(s => s.scale ? 'scale("' + s.scale + '")' : s.signal) + ']'
};
}
-
function parseSignalUpdates(signal, scope) {
const op = scope.getSignal(signal.name);
let expr = signal.update;
-
if (signal.init) {
if (expr) {
error('Signals can not include both init and update expressions.');
} else {
expr = signal.init;
op.initonly = true;
}
}
-
if (expr) {
expr = parser(expr, scope);
op.update = expr.$expr;
op.params = expr.$params;
}
-
if (signal.on) {
signal.on.forEach(_ => parseUpdate(_, scope, op.id));
}
}
-
const transform = name => (params, value, parent) => entry(name, value, params || undefined, parent);
-
const Aggregate = transform('aggregate');
const AxisTicks = transform('axisticks');
const Bound = transform('bound');
const Collect = transform('collect');
const Compare = transform('compare');
@@ -42465,156 +38323,137 @@
const MULTIDOMAIN_SORT_OPS = {
min: 'min',
max: 'max',
count: 'sum'
};
-
function initScale(spec, scope) {
const type = spec.type || 'linear';
-
if (!isValidScaleType(type)) {
error('Unrecognized scale type: ' + $(type));
}
-
scope.addScale(spec.name, {
type,
domain: undefined
});
}
-
function parseScale(spec, scope) {
const params = scope.getScale(spec.name).params;
let key;
params.domain = parseScaleDomain(spec.domain, spec, scope);
-
if (spec.range != null) {
params.range = parseScaleRange(spec, scope, params);
}
-
if (spec.interpolate != null) {
parseScaleInterpolate(spec.interpolate, params);
}
-
if (spec.nice != null) {
- params.nice = parseScaleNice(spec.nice);
+ params.nice = parseScaleNice(spec.nice, scope);
}
-
if (spec.bins != null) {
params.bins = parseScaleBins(spec.bins, scope);
}
-
for (key in spec) {
if (has$1(params, key) || key === 'name') continue;
params[key] = parseLiteral(spec[key], scope);
}
}
-
function parseLiteral(v, scope) {
return !isObject(v) ? v : v.signal ? scope.signalRef(v.signal) : error('Unsupported object: ' + $(v));
}
-
function parseArray(v, scope) {
return v.signal ? scope.signalRef(v.signal) : v.map(v => parseLiteral(v, scope));
}
-
function dataLookupError(name) {
error('Can not find data set: ' + $(name));
- } // -- SCALE DOMAIN ----
+ }
+ // -- SCALE DOMAIN ----
function parseScaleDomain(domain, spec, scope) {
if (!domain) {
if (spec.domainMin != null || spec.domainMax != null) {
error('No scale domain defined for domainMin/domainMax to override.');
}
-
return; // default domain
}
-
return domain.signal ? scope.signalRef(domain.signal) : (isArray(domain) ? explicitDomain : domain.fields ? multipleDomain : singularDomain)(domain, spec, scope);
}
-
function explicitDomain(domain, spec, scope) {
return domain.map(v => parseLiteral(v, scope));
}
-
function singularDomain(domain, spec, scope) {
const data = scope.getData(domain.data);
if (!data) dataLookupError(domain.data);
return isDiscrete(spec.type) ? data.valuesRef(scope, domain.field, parseSort(domain.sort, false)) : isQuantile(spec.type) ? data.domainRef(scope, domain.field) : data.extentRef(scope, domain.field);
}
-
function multipleDomain(domain, spec, scope) {
const data = domain.data,
- fields = domain.fields.reduce((dom, d) => {
- d = isString(d) ? {
- data: data,
- field: d
- } : isArray(d) || d.signal ? fieldRef(d, scope) : d;
- dom.push(d);
- return dom;
- }, []);
+ fields = domain.fields.reduce((dom, d) => {
+ d = isString(d) ? {
+ data: data,
+ field: d
+ } : isArray(d) || d.signal ? fieldRef(d, scope) : d;
+ dom.push(d);
+ return dom;
+ }, []);
return (isDiscrete(spec.type) ? ordinalMultipleDomain : isQuantile(spec.type) ? quantileMultipleDomain : numericMultipleDomain)(domain, scope, fields);
}
-
function fieldRef(data, scope) {
const name = '_:vega:_' + FIELD_REF_ID++,
- coll = Collect({});
-
+ coll = Collect({});
if (isArray(data)) {
coll.value = {
$ingest: data
};
} else if (data.signal) {
const code = 'setdata(' + $(name) + ',' + data.signal + ')';
coll.params.input = scope.signalRef(code);
}
-
scope.addDataPipeline(name, [coll, Sieve({})]);
return {
data: name,
field: 'data'
};
}
-
function ordinalMultipleDomain(domain, scope, fields) {
const sort = parseSort(domain.sort, true);
- let a, v; // get value counts for each domain field
+ let a, v;
+ // get value counts for each domain field
const counts = fields.map(f => {
const data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.countsRef(scope, f.field, sort);
- }); // aggregate the results from each domain field
+ });
+ // aggregate the results from each domain field
const p = {
groupby: keyFieldRef,
pulse: counts
};
-
if (sort) {
a = sort.op || 'count';
v = sort.field ? aggrField(a, sort.field) : 'count';
p.ops = [MULTIDOMAIN_SORT_OPS[a]];
p.fields = [scope.fieldRef(v)];
p.as = [v];
}
+ a = scope.add(Aggregate(p));
- a = scope.add(Aggregate(p)); // collect aggregate output
-
+ // collect aggregate output
const c = scope.add(Collect({
pulse: ref(a)
- })); // extract values for combined domain
+ }));
+ // extract values for combined domain
v = scope.add(Values({
field: keyFieldRef,
sort: scope.sortRef(sort),
pulse: ref(c)
}));
return ref(v);
}
-
function parseSort(sort, multidomain) {
if (sort) {
if (!sort.field && !sort.op) {
if (isObject(sort)) sort.field = 'key';else sort = {
field: 'key'
@@ -42625,67 +38464,68 @@
if (sort.op && !MULTIDOMAIN_SORT_OPS[sort.op]) {
error('Multiple domain scales can not be sorted using ' + sort.op);
}
}
}
-
return sort;
}
-
function quantileMultipleDomain(domain, scope, fields) {
// get value arrays for each domain field
const values = fields.map(f => {
const data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.domainRef(scope, f.field);
- }); // combine value arrays
+ });
+ // combine value arrays
return ref(scope.add(MultiValues({
values: values
})));
}
-
function numericMultipleDomain(domain, scope, fields) {
// get extents for each domain field
const extents = fields.map(f => {
const data = scope.getData(f.data);
if (!data) dataLookupError(f.data);
return data.extentRef(scope, f.field);
- }); // combine extents
+ });
+ // combine extents
return ref(scope.add(MultiExtent({
extents: extents
})));
- } // -- SCALE BINS -----
+ }
+ // -- SCALE BINS -----
function parseScaleBins(v, scope) {
return v.signal || isArray(v) ? parseArray(v, scope) : scope.objectProperty(v);
- } // -- SCALE NICE -----
+ }
+ // -- SCALE NICE -----
- function parseScaleNice(nice) {
- return isObject(nice) ? {
+ function parseScaleNice(nice, scope) {
+ return nice.signal ? scope.signalRef(nice.signal) : isObject(nice) ? {
interval: parseLiteral(nice.interval),
step: parseLiteral(nice.step)
} : parseLiteral(nice);
- } // -- SCALE INTERPOLATION -----
+ }
+ // -- SCALE INTERPOLATION -----
function parseScaleInterpolate(interpolate, params) {
params.interpolate = parseLiteral(interpolate.type || interpolate);
-
if (interpolate.gamma != null) {
params.interpolateGamma = parseLiteral(interpolate.gamma);
}
- } // -- SCALE RANGE -----
+ }
+ // -- SCALE RANGE -----
function parseScaleRange(spec, scope, params) {
const config = scope.config.range;
let range = spec.range;
-
if (range.signal) {
return scope.signalRef(range.signal);
} else if (isString(range)) {
if (config && has$1(config, range)) {
spec = extend$1({}, spec, {
@@ -42716,37 +38556,31 @@
} else if (isDiscrete(spec.type) && !isArray(range)) {
return parseScaleDomain(range, spec, scope);
} else if (!isArray(range)) {
error('Unsupported range type: ' + $(range));
}
-
return range.map(v => (isArray(v) ? parseArray : parseLiteral)(v, scope));
}
-
function parseProjection(proj, scope) {
const config = scope.config.projection || {},
- params = {};
-
+ params = {};
for (const name in proj) {
if (name === 'name') continue;
params[name] = parseParameter$1(proj[name], name, scope);
- } // apply projection defaults from config
+ }
-
+ // apply projection defaults from config
for (const name in config) {
if (params[name] == null) {
params[name] = parseParameter$1(config[name], name, scope);
}
}
-
scope.addProjection(proj.name, params);
}
-
function parseParameter$1(_, name, scope) {
return isArray(_) ? _.map(_ => parseParameter$1(_, name, scope)) : !isObject(_) ? _ : _.signal ? scope.signalRef(_.signal) : name === 'fit' ? _ : error('Unsupported parameter object: ' + $(_));
}
-
const Top = 'top';
const Left = 'left';
const Right = 'right';
const Bottom = 'bottom';
const Center = 'center';
@@ -42771,13 +38605,14 @@
const Shape = 'shape';
const Fill = 'fill';
const Stroke = 'stroke';
const StrokeWidth = 'strokeWidth';
const StrokeDash = 'strokeDash';
- const Opacity = 'opacity'; // Encoding channels supported by legends
- // In priority order of 'canonical' scale
+ const Opacity = 'opacity';
+ // Encoding channels supported by legends
+ // In priority order of 'canonical' scale
const LegendScales = [Size, Shape, Fill, Stroke, StrokeWidth, StrokeDash, Opacity];
const Skip = {
name: 1,
style: 1,
interactive: 1
@@ -42791,70 +38626,55 @@
const GroupMark = 'group';
const RectMark = 'rect';
const RuleMark = 'rule';
const SymbolMark = 'symbol';
const TextMark = 'text';
-
function guideGroup(mark) {
mark.type = GroupMark;
mark.interactive = mark.interactive || false;
return mark;
}
-
function lookup(spec, config) {
const _ = (name, dflt) => value(spec[name], value(config[name], dflt));
-
_.isVertical = s => Vertical === value(spec.direction, config.direction || (s ? config.symbolDirection : config.gradientDirection));
-
_.gradientLength = () => value(spec.gradientLength, config.gradientLength || config.gradientWidth);
-
_.gradientThickness = () => value(spec.gradientThickness, config.gradientThickness || config.gradientHeight);
-
_.entryColumns = () => value(spec.columns, value(config.columns, +_.isVertical(true)));
-
return _;
}
-
function getEncoding(name, encode) {
const v = encode && (encode.update && encode.update[name] || encode.enter && encode.enter[name]);
return v && v.signal ? v : v ? v.value : null;
}
-
function getStyle(name, scope, style) {
const s = scope.config.style[style];
return s && s[name];
}
-
function anchorExpr(s, e, m) {
- return "item.anchor === '".concat(Start, "' ? ").concat(s, " : item.anchor === '").concat(End, "' ? ").concat(e, " : ").concat(m);
+ return `item.anchor === '${Start}' ? ${s} : item.anchor === '${End}' ? ${e} : ${m}`;
}
-
const alignExpr$1 = anchorExpr($(Left), $(Right), $(Center));
-
function tickBand(_) {
const v = _('tickBand');
-
let offset = _('tickOffset'),
- band,
- extra;
-
+ band,
+ extra;
if (!v) {
// if no tick band entry, fall back on other properties
band = _('bandPosition');
extra = _('tickExtra');
} else if (v.signal) {
// if signal, augment code to interpret values
band = {
- signal: "(".concat(v.signal, ") === 'extent' ? 1 : 0.5")
+ signal: `(${v.signal}) === 'extent' ? 1 : 0.5`
};
extra = {
- signal: "(".concat(v.signal, ") === 'extent'")
+ signal: `(${v.signal}) === 'extent'`
};
-
if (!isObject(offset)) {
offset = {
- signal: "(".concat(v.signal, ") === 'extent' ? 0 : ").concat(offset)
+ signal: `(${v.signal}) === 'extent' ? 0 : ${offset}`
};
}
} else if (v === 'extent') {
// if constant, simply set values
band = 1;
@@ -42862,48 +38682,41 @@
offset = 0;
} else {
band = 0.5;
extra = false;
}
-
return {
extra,
band,
offset
};
}
-
function extendOffset(value, offset) {
return !offset ? value : !value ? offset : !isObject(value) ? {
value,
offset
} : Object.assign({}, value, {
offset: extendOffset(value.offset, offset)
});
}
-
function guideMark(mark, extras) {
if (extras) {
mark.name = extras.name;
mark.style = extras.style || mark.style;
mark.interactive = !!extras.interactive;
mark.encode = extendEncode(mark.encode, extras, Skip);
} else {
mark.interactive = false;
}
-
return mark;
}
-
function legendGradient(spec, scale, config, userEncode) {
const _ = lookup(spec, config),
- vertical = _.isVertical(),
- thickness = _.gradientThickness(),
- length = _.gradientLength();
-
+ vertical = _.isVertical(),
+ thickness = _.gradientThickness(),
+ length = _.gradientLength();
let enter, start, stop, width, height;
-
if (vertical) {
start = [0, 1];
stop = [0, 0];
width = thickness;
height = length;
@@ -42911,11 +38724,10 @@
start = [0, 0];
stop = [1, 0];
width = length;
height = thickness;
}
-
const encode = {
enter: enter = {
opacity: zero,
x: zero,
y: zero,
@@ -42945,22 +38757,20 @@
type: RectMark,
role: LegendGradientRole,
encode
}, userEncode);
}
-
function legendGradientDiscrete(spec, scale, config, userEncode, dataRef) {
const _ = lookup(spec, config),
- vertical = _.isVertical(),
- thickness = _.gradientThickness(),
- length = _.gradientLength();
-
+ vertical = _.isVertical(),
+ thickness = _.gradientThickness(),
+ length = _.gradientLength();
let u,
- v,
- uu,
- vv,
- adjust = '';
+ v,
+ uu,
+ vv,
+ adjust = '';
vertical ? (u = 'y', uu = 'y2', v = 'x', vv = 'width', adjust = '1-') : (u = 'x', uu = 'x2', v = 'y', vv = 'height');
const enter = {
opacity: zero,
fill: {
scale: scale,
@@ -42999,27 +38809,23 @@
key: Value,
from: dataRef,
encode
}, userEncode);
}
-
- const alignExpr = "datum.".concat(Perc, "<=0?\"").concat(Left, "\":datum.").concat(Perc, ">=1?\"").concat(Right, "\":\"").concat(Center, "\""),
- baselineExpr = "datum.".concat(Perc, "<=0?\"").concat(Bottom, "\":datum.").concat(Perc, ">=1?\"").concat(Top, "\":\"").concat(Middle, "\"");
-
+ const alignExpr = `datum.${Perc}<=0?"${Left}":datum.${Perc}>=1?"${Right}":"${Center}"`,
+ baselineExpr = `datum.${Perc}<=0?"${Bottom}":datum.${Perc}>=1?"${Top}":"${Middle}"`;
function legendGradientLabels(spec, config, userEncode, dataRef) {
const _ = lookup(spec, config),
- vertical = _.isVertical(),
- thickness = encoder(_.gradientThickness()),
- length = _.gradientLength();
-
+ vertical = _.isVertical(),
+ thickness = encoder(_.gradientThickness()),
+ length = _.gradientLength();
let overlap = _('labelOverlap'),
- enter,
- update,
- u,
- v,
- adjust = '';
-
+ enter,
+ update,
+ u,
+ v,
+ adjust = '';
const encode = {
enter: enter = {
opacity: zero
},
update: update = {
@@ -43039,11 +38845,10 @@
fontSize: _('labelFontSize'),
fontStyle: _('labelFontStyle'),
fontWeight: _('labelFontWeight'),
limit: value(spec.labelLimit, config.gradientLabelLimit)
});
-
if (vertical) {
enter.align = {
value: 'left'
};
enter.baseline = update.baseline = {
@@ -43060,23 +38865,23 @@
value: 'top'
};
u = 'x';
v = 'y';
}
-
enter[u] = update[u] = {
signal: adjust + 'datum.' + Perc,
mult: length
};
enter[v] = update[v] = thickness;
thickness.offset = value(spec.labelOffset, config.gradientLabelOffset) || 0;
overlap = overlap ? {
separation: _('labelSeparation'),
method: overlap,
order: 'datum.' + Index
- } : undefined; // type, role, style, key, dataRef, encode, extras
+ } : undefined;
+ // type, role, style, key, dataRef, encode, extras
return guideMark({
type: TextMark,
role: LegendLabelRole,
style: GuideLabelStyle,
key: Value,
@@ -43084,30 +38889,31 @@
encode,
overlap
}, userEncode);
}
+ // userEncode is top-level, includes entries, symbols, labels
function legendSymbolGroups(spec, config, userEncode, dataRef, columns) {
const _ = lookup(spec, config),
- entries = userEncode.entries,
- interactive = !!(entries && entries.interactive),
- name = entries ? entries.name : undefined,
- height = _('clipHeight'),
- symbolOffset = _('symbolOffset'),
- valueRef = {
- data: 'value'
- },
- xSignal = "(".concat(columns, ") ? datum.").concat(Offset, " : datum.").concat(Size),
- yEncode = height ? encoder(height) : {
- field: Size
- },
- index = "datum.".concat(Index),
- ncols = "max(1, ".concat(columns, ")");
-
+ entries = userEncode.entries,
+ interactive = !!(entries && entries.interactive),
+ name = entries ? entries.name : undefined,
+ height = _('clipHeight'),
+ symbolOffset = _('symbolOffset'),
+ valueRef = {
+ data: 'value'
+ },
+ xSignal = `(${columns}) ? datum.${Offset} : datum.${Size}`,
+ yEncode = height ? encoder(height) : {
+ field: Size
+ },
+ index = `datum.${Index}`,
+ ncols = `max(1, ${columns})`;
let encode, enter, update, nrows, sort;
- yEncode.mult = 0.5; // -- LEGEND SYMBOLS --
+ yEncode.mult = 0.5;
+ // -- LEGEND SYMBOLS --
encode = {
enter: enter = {
opacity: zero,
x: {
signal: xSignal,
@@ -43124,17 +38930,15 @@
exit: {
opacity: zero
}
};
let baseFill = null,
- baseStroke = null;
-
+ baseStroke = null;
if (!spec.fill) {
baseFill = config.symbolBaseFillColor;
baseStroke = config.symbolBaseStrokeColor;
}
-
addEncoders(encode, {
fill: _('symbolFillColor', baseFill),
shape: _('symbolType'),
size: _('symbolSize'),
stroke: _('symbolStrokeColor', baseStroke),
@@ -43158,12 +38962,13 @@
role: LegendSymbolRole,
key: Value,
from: valueRef,
clip: height ? true : undefined,
encode
- }, userEncode.symbols); // -- LEGEND LABELS --
+ }, userEncode.symbols);
+ // -- LEGEND LABELS --
const labelOffset = encoder(symbolOffset);
labelOffset.offset = _('labelOffset');
encode = {
enter: enter = {
opacity: zero,
@@ -43201,12 +39006,13 @@
role: LegendLabelRole,
style: GuideLabelStyle,
key: Value,
from: valueRef,
encode
- }, userEncode.labels); // -- LEGEND ENTRY GROUPS --
+ }, userEncode.labels);
+ // -- LEGEND ENTRY GROUPS --
encode = {
enter: {
noBound: {
value: !height
},
@@ -43225,30 +39031,31 @@
},
column: {
signal: null
}
}
- }; // annotate and sort groups to ensure correct ordering
+ };
+ // annotate and sort groups to ensure correct ordering
if (_.isVertical(true)) {
- nrows = "ceil(item.mark.items.length / ".concat(ncols, ")");
- update.row.signal = "".concat(index, "%").concat(nrows);
- update.column.signal = "floor(".concat(index, " / ").concat(nrows, ")");
+ nrows = `ceil(item.mark.items.length / ${ncols})`;
+ update.row.signal = `${index}%${nrows}`;
+ update.column.signal = `floor(${index} / ${nrows})`;
sort = {
field: ['row', index]
};
} else {
- update.row.signal = "floor(".concat(index, " / ").concat(ncols, ")");
- update.column.signal = "".concat(index, " % ").concat(ncols);
+ update.row.signal = `floor(${index} / ${ncols})`;
+ update.column.signal = `${index} % ${ncols}`;
sort = {
field: index
};
- } // handle zero column case (implies infinite columns)
+ }
+ // handle zero column case (implies infinite columns)
+ update.column.signal = `(${columns})?${update.column.signal}:${index}`;
-
- update.column.signal = "(".concat(columns, ")?").concat(update.column.signal, ":").concat(index); // facet legend entries into sub-groups
-
+ // facet legend entries into sub-groups
dataRef = {
facet: {
data: dataRef,
name: 'value',
groupby: Index
@@ -43262,15 +39069,14 @@
name,
interactive,
sort
});
}
-
function legendSymbolLayout(spec, config) {
- const _ = lookup(spec, config); // layout parameters for legend entries
+ const _ = lookup(spec, config);
-
+ // layout parameters for legend entries
return {
align: _('gridAlign'),
columns: _.entryColumns(),
center: {
row: true,
@@ -43281,24 +39087,23 @@
column: _('columnPadding')
}
};
}
+ // expression logic for align, anchor, angle, and baseline calculation
const isL = 'item.orient === "left"',
- isR = 'item.orient === "right"',
- isLR = "(".concat(isL, " || ").concat(isR, ")"),
- isVG = "datum.vgrad && ".concat(isLR),
- baseline = anchorExpr('"top"', '"bottom"', '"middle"'),
- alignFlip = anchorExpr('"right"', '"left"', '"center"'),
- exprAlign = "datum.vgrad && ".concat(isR, " ? (").concat(alignFlip, ") : (").concat(isLR, " && !(datum.vgrad && ").concat(isL, ")) ? \"left\" : ").concat(alignExpr$1),
- exprAnchor = "item._anchor || (".concat(isLR, " ? \"middle\" : \"start\")"),
- exprAngle = "".concat(isVG, " ? (").concat(isL, " ? -90 : 90) : 0"),
- exprBaseline = "".concat(isLR, " ? (datum.vgrad ? (").concat(isR, " ? \"bottom\" : \"top\") : ").concat(baseline, ") : \"top\"");
-
+ isR = 'item.orient === "right"',
+ isLR = `(${isL} || ${isR})`,
+ isVG = `datum.vgrad && ${isLR}`,
+ baseline = anchorExpr('"top"', '"bottom"', '"middle"'),
+ alignFlip = anchorExpr('"right"', '"left"', '"center"'),
+ exprAlign = `datum.vgrad && ${isR} ? (${alignFlip}) : (${isLR} && !(datum.vgrad && ${isL})) ? "left" : ${alignExpr$1}`,
+ exprAnchor = `item._anchor || (${isLR} ? "middle" : "start")`,
+ exprAngle = `${isVG} ? (${isL} ? -90 : 90) : 0`,
+ exprBaseline = `${isLR} ? (datum.vgrad ? (${isR} ? "bottom" : "top") : ${baseline}) : "top"`;
function legendTitle(spec, config, userEncode, dataRef) {
const _ = lookup(spec, config);
-
const encode = {
enter: {
opacity: zero
},
update: {
@@ -43353,201 +39158,176 @@
style: GuideTitleStyle,
from: dataRef,
encode
}, userEncode);
}
-
function clip(clip, scope) {
let expr;
-
if (isObject(clip)) {
if (clip.signal) {
expr = clip.signal;
} else if (clip.path) {
expr = 'pathShape(' + param(clip.path) + ')';
} else if (clip.sphere) {
expr = 'geoShape(' + param(clip.sphere) + ', {type: "Sphere"})';
}
}
-
return expr ? scope.signalRef(expr) : !!clip;
}
-
function param(value) {
return isObject(value) && value.signal ? value.signal : $(value);
}
-
function getRole(spec) {
const role = spec.role || '';
- return !role.indexOf('axis') || !role.indexOf('legend') || !role.indexOf('title') ? role : spec.type === GroupMark ? ScopeRole : role || MarkRole;
+ return role.startsWith('axis') || role.startsWith('legend') || role.startsWith('title') ? role : spec.type === GroupMark ? ScopeRole : role || MarkRole;
}
-
function definition(spec) {
return {
marktype: spec.type,
name: spec.name || undefined,
role: spec.role || getRole(spec),
zindex: +spec.zindex || undefined,
aria: spec.aria,
description: spec.description
};
}
-
function interactive(spec, scope) {
return spec && spec.signal ? scope.signalRef(spec.signal) : spec === false ? false : true;
}
+
/**
* Parse a data transform specification.
*/
-
-
function parseTransform(spec, scope) {
const def = definition$1(spec.type);
if (!def) error('Unrecognized transform type: ' + $(spec.type));
const t = entry(def.type.toLowerCase(), null, parseParameters(def, spec, scope));
if (spec.signal) scope.addSignal(spec.signal, scope.proxy(t));
t.metadata = def.metadata || {};
return t;
}
+
/**
* Parse all parameters of a data transform.
*/
-
-
function parseParameters(def, spec, scope) {
const params = {},
- n = def.params.length;
-
+ n = def.params.length;
for (let i = 0; i < n; ++i) {
const pdef = def.params[i];
params[pdef.name] = parseParameter(pdef, spec, scope);
}
-
return params;
}
+
/**
* Parse a data transform parameter.
*/
-
-
function parseParameter(def, spec, scope) {
const type = def.type,
- value = spec[def.name];
-
+ value = spec[def.name];
if (type === 'index') {
return parseIndexParameter(def, spec, scope);
} else if (value === undefined) {
if (def.required) {
error('Missing required ' + $(spec.type) + ' parameter: ' + $(def.name));
}
-
return;
} else if (type === 'param') {
return parseSubParameters(def, spec, scope);
} else if (type === 'projection') {
return scope.projectionRef(spec[def.name]);
}
-
return def.array && !isSignal(value) ? value.map(v => parameterValue(def, v, scope)) : parameterValue(def, value, scope);
}
+
/**
* Parse a single parameter value.
*/
-
-
function parameterValue(def, value, scope) {
const type = def.type;
-
if (isSignal(value)) {
return isExpr(type) ? error('Expression references can not be signals.') : isField(type) ? scope.fieldRef(value) : isCompare(type) ? scope.compareRef(value) : scope.signalRef(value.signal);
} else {
const expr = def.expr || isField(type);
return expr && outerExpr(value) ? scope.exprRef(value.expr, value.as) : expr && outerField(value) ? fieldRef$1(value.field, value.as) : isExpr(type) ? parser(value, scope) : isData(type) ? ref(scope.getData(value).values) : isField(type) ? fieldRef$1(value) : isCompare(type) ? scope.compareRef(value) : value;
}
}
+
/**
* Parse parameter for accessing an index of another data set.
*/
-
-
function parseIndexParameter(def, spec, scope) {
if (!isString(spec.from)) {
error('Lookup "from" parameter must be a string literal.');
}
-
return scope.getData(spec.from).lookupRef(scope, spec.key);
}
+
/**
* Parse a parameter that contains one or more sub-parameter objects.
*/
-
-
function parseSubParameters(def, spec, scope) {
const value = spec[def.name];
-
if (def.array) {
if (!isArray(value)) {
// signals not allowed!
error('Expected an array of sub-parameters. Instead: ' + $(value));
}
-
return value.map(v => parseSubParameter(def, v, scope));
} else {
return parseSubParameter(def, value, scope);
}
}
+
/**
* Parse a sub-parameter object.
*/
-
-
function parseSubParameter(def, value, scope) {
const n = def.params.length;
- let pdef; // loop over defs to find matching key
+ let pdef;
+ // loop over defs to find matching key
for (let i = 0; i < n; ++i) {
pdef = def.params[i];
-
for (const k in pdef.key) {
if (pdef.key[k] !== value[k]) {
pdef = null;
break;
}
}
-
if (pdef) break;
- } // raise error if matching key not found
+ }
+ // raise error if matching key not found
+ if (!pdef) error('Unsupported parameter: ' + $(value));
-
- if (!pdef) error('Unsupported parameter: ' + $(value)); // parse params, create Params transform, return ref
-
+ // parse params, create Params transform, return ref
const params = extend$1(parseParameters(pdef, value, scope), pdef.key);
return ref(scope.add(Params(params)));
- } // -- Utilities -----
+ }
+ // -- Utilities -----
const outerExpr = _ => _ && _.expr;
-
const outerField = _ => _ && _.field;
-
const isData = _ => _ === 'data';
-
const isExpr = _ => _ === 'expr';
-
const isField = _ => _ === 'field';
-
const isCompare = _ => _ === 'compare';
-
function parseData$1(from, group, scope) {
- let facet, key, op, dataRef, parent; // if no source data, generate singleton datum
+ let facet, key, op, dataRef, parent;
+ // if no source data, generate singleton datum
if (!from) {
dataRef = ref(scope.add(Collect(null, [{}])));
- } // if faceted, process facet specification
+ }
+
+ // if faceted, process facet specification
else if (facet = from.facet) {
- if (!group) error('Only group marks can be faceted.'); // use pre-faceted source data, if available
+ if (!group) error('Only group marks can be faceted.');
+ // use pre-faceted source data, if available
if (facet.field != null) {
dataRef = parent = getDataRef(facet, scope);
} else {
// generate facet aggregates if no direct data specification
if (!from.data) {
@@ -43559,108 +39339,91 @@
op.params.pulse = getDataRef(facet, scope);
dataRef = parent = ref(scope.add(op));
} else {
parent = ref(scope.getData(from.data).aggregate);
}
-
key = scope.keyRef(facet.groupby, true);
}
- } // if not yet defined, get source data reference
+ }
-
+ // if not yet defined, get source data reference
if (!dataRef) {
dataRef = getDataRef(from, scope);
}
-
return {
key: key,
pulse: dataRef,
parent: parent
};
}
-
function getDataRef(from, scope) {
return from.$ref ? from : from.data && from.data.$ref ? from.data : ref(scope.getData(from.data).output);
}
-
function DataScope(scope, input, output, values, aggr) {
this.scope = scope; // parent scope object
-
this.input = input; // first operator in pipeline (tuple input)
-
this.output = output; // last operator in pipeline (tuple output)
-
this.values = values; // operator for accessing tuples (but not tuple flow)
+
// last aggregate in transform pipeline
+ this.aggregate = aggr;
- this.aggregate = aggr; // lookup table of field indices
-
+ // lookup table of field indices
this.index = {};
}
-
DataScope.fromEntries = function (scope, entries) {
const n = entries.length,
- values = entries[n - 1],
- output = entries[n - 2];
+ values = entries[n - 1],
+ output = entries[n - 2];
let input = entries[0],
- aggr = null,
- i = 1;
-
+ aggr = null,
+ i = 1;
if (input && input.type === 'load') {
input = entries[1];
- } // add operator entries to this scope, wire up pulse chain
+ }
-
+ // add operator entries to this scope, wire up pulse chain
scope.add(entries[0]);
-
for (; i < n; ++i) {
entries[i].params.pulse = ref(entries[i - 1]);
scope.add(entries[i]);
if (entries[i].type === 'aggregate') aggr = entries[i];
}
-
return new DataScope(scope, input, output, values, aggr);
};
-
function fieldKey(field) {
return isString(field) ? field : null;
}
-
function addSortField(scope, p, sort) {
const as = aggrField(sort.op, sort.field);
let s;
-
if (p.ops) {
for (let i = 0, n = p.as.length; i < n; ++i) {
if (p.as[i] === as) return;
}
} else {
p.ops = ['count'];
p.fields = [null];
p.as = ['count'];
}
-
if (sort.op) {
p.ops.push((s = sort.op.signal) ? scope.signalRef(s) : sort.op);
p.fields.push(scope.fieldRef(sort.field));
p.as.push(as);
}
}
-
function cache(scope, ds, name, optype, field, counts, index) {
const cache = ds[name] || (ds[name] = {}),
- sort = sortKey(counts);
+ sort = sortKey(counts);
let k = fieldKey(field),
- v,
- op;
-
+ v,
+ op;
if (k != null) {
scope = ds.scope;
k = k + (sort ? '|' + sort : '');
v = cache[k];
}
-
if (!v) {
const params = counts ? {
field: keyFieldRef,
pulse: ds.countsRef(scope, field, counts)
} : {
@@ -43671,26 +39434,22 @@
op = scope.add(entry(optype, undefined, params));
if (index) ds.index[field] = op;
v = ref(op);
if (k != null) cache[k] = v;
}
-
return v;
}
-
DataScope.prototype = {
countsRef(scope, field, sort) {
const ds = this,
- cache = ds.counts || (ds.counts = {}),
- k = fieldKey(field);
+ cache = ds.counts || (ds.counts = {}),
+ k = fieldKey(field);
let v, a, p;
-
if (k != null) {
scope = ds.scope;
v = cache[k];
}
-
if (!v) {
p = {
groupby: scope.fieldRef(field, 'key'),
pulse: ref(ds.output)
};
@@ -43705,54 +39464,42 @@
};
if (k != null) cache[k] = v;
} else if (sort && sort.field) {
addSortField(scope, v.agg.params, sort);
}
-
return v.ref;
},
-
tuplesRef() {
return ref(this.values);
},
-
extentRef(scope, field) {
return cache(scope, this, 'extent', 'extent', field, false);
},
-
domainRef(scope, field) {
return cache(scope, this, 'domain', 'values', field, false);
},
-
valuesRef(scope, field, sort) {
return cache(scope, this, 'vals', 'values', field, sort || true);
},
-
lookupRef(scope, field) {
return cache(scope, this, 'lookup', 'tupleindex', field, false);
},
-
indataRef(scope, field) {
return cache(scope, this, 'indata', 'tupleindex', field, true, true);
}
-
};
-
function parseFacet(spec, scope, group) {
const facet = spec.from.facet,
- name = facet.name,
- data = getDataRef(facet, scope);
+ name = facet.name,
+ data = getDataRef(facet, scope);
let op;
-
if (!facet.name) {
error('Facet must have a name: ' + $(facet));
}
-
if (!facet.data) {
error('Facet must reference a data set: ' + $(facet));
}
-
if (facet.field) {
op = scope.add(PreFacet({
field: scope.fieldRef(facet.field),
pulse: data
}));
@@ -43762,80 +39509,83 @@
group: ref(scope.proxy(group.parent)),
pulse: data
}));
} else {
error('Facet must specify groupby or field: ' + $(facet));
- } // initialize facet subscope
+ }
-
+ // initialize facet subscope
const subscope = scope.fork(),
- source = subscope.add(Collect()),
- values = subscope.add(Sieve({
- pulse: ref(source)
- }));
+ source = subscope.add(Collect()),
+ values = subscope.add(Sieve({
+ pulse: ref(source)
+ }));
subscope.addData(name, new DataScope(subscope, source, source, values));
- subscope.addSignal('parent', null); // parse faceted subflow
+ subscope.addSignal('parent', null);
+ // parse faceted subflow
op.params.subflow = {
$subflow: subscope.parse(spec).toRuntime()
};
}
-
function parseSubflow(spec, scope, input) {
const op = scope.add(PreFacet({
- pulse: input.pulse
- })),
- subscope = scope.fork();
+ pulse: input.pulse
+ })),
+ subscope = scope.fork();
subscope.add(Sieve());
- subscope.addSignal('parent', null); // parse group mark subflow
+ subscope.addSignal('parent', null);
+ // parse group mark subflow
op.params.subflow = {
$subflow: subscope.parse(spec).toRuntime()
};
}
-
function parseTrigger(spec, scope, name) {
const remove = spec.remove,
- insert = spec.insert,
- toggle = spec.toggle,
- modify = spec.modify,
- values = spec.values,
- op = scope.add(operator());
+ insert = spec.insert,
+ toggle = spec.toggle,
+ modify = spec.modify,
+ values = spec.values,
+ op = scope.add(operator());
const update = 'if(' + spec.trigger + ',modify("' + name + '",' + [insert, remove, toggle, modify, values].map(_ => _ == null ? 'null' : _).join(',') + '),0)';
const expr = parser(update, scope);
op.update = expr.$expr;
op.params = expr.$params;
}
-
function parseMark(spec, scope) {
const role = getRole(spec),
- group = spec.type === GroupMark,
- facet = spec.from && spec.from.facet,
- overlap = spec.overlap;
+ group = spec.type === GroupMark,
+ facet = spec.from && spec.from.facet,
+ overlap = spec.overlap;
let layout = spec.layout || role === ScopeRole || role === FrameRole,
- ops,
- op,
- store,
- enc,
- name,
- layoutRef,
- boundRef;
- const nested = role === MarkRole || layout || facet; // resolve input data
+ ops,
+ op,
+ store,
+ enc,
+ name,
+ layoutRef,
+ boundRef;
+ const nested = role === MarkRole || layout || facet;
- const input = parseData$1(spec.from, group, scope); // data join to map tuples to visual items
+ // resolve input data
+ const input = parseData$1(spec.from, group, scope);
+ // data join to map tuples to visual items
op = scope.add(DataJoin({
key: input.key || (spec.key ? fieldRef$1(spec.key) : undefined),
pulse: input.pulse,
clean: !group
}));
- const joinRef = ref(op); // collect visual items
+ const joinRef = ref(op);
+ // collect visual items
op = store = scope.add(Collect({
pulse: joinRef
- })); // connect visual items to scenegraph
+ }));
+ // connect visual items to scenegraph
op = scope.add(Mark({
markdef: definition(spec),
interactive: interactive(spec.interactive, scope),
clip: clip(spec.clip, scope),
context: {
@@ -43844,217 +39594,220 @@
groups: scope.lookup(),
parent: scope.signals.parent ? scope.signalRef('parent') : null,
index: scope.markpath(),
pulse: ref(op)
}));
- const markRef = ref(op); // add visual encoders
+ const markRef = ref(op);
+ // add visual encoders
op = enc = scope.add(Encode(parseEncode(spec.encode, spec.type, role, spec.style, scope, {
mod: false,
pulse: markRef
- }))); // monitor parent marks to propagate changes
+ })));
- op.params.parent = scope.encode(); // add post-encoding transforms, if defined
+ // monitor parent marks to propagate changes
+ op.params.parent = scope.encode();
+ // add post-encoding transforms, if defined
if (spec.transform) {
spec.transform.forEach(_ => {
const tx = parseTransform(_, scope),
- md = tx.metadata;
-
+ md = tx.metadata;
if (md.generates || md.changes) {
error('Mark transforms should not generate new data.');
}
-
if (!md.nomod) enc.params.mod = true; // update encode mod handling
-
tx.params.pulse = ref(op);
scope.add(op = tx);
});
- } // if item sort specified, perform post-encoding
+ }
-
+ // if item sort specified, perform post-encoding
if (spec.sort) {
op = scope.add(SortItems({
sort: scope.compareRef(spec.sort),
pulse: ref(op)
}));
}
+ const encodeRef = ref(op);
- const encodeRef = ref(op); // add view layout operator if needed
-
+ // add view layout operator if needed
if (facet || layout) {
layout = scope.add(ViewLayout({
layout: scope.objectProperty(spec.layout),
legends: scope.legends,
mark: markRef,
pulse: encodeRef
}));
layoutRef = ref(layout);
- } // compute bounding boxes
+ }
-
+ // compute bounding boxes
const bound = scope.add(Bound({
mark: markRef,
pulse: layoutRef || encodeRef
}));
- boundRef = ref(bound); // if group mark, recurse to parse nested content
+ boundRef = ref(bound);
+ // if group mark, recurse to parse nested content
if (group) {
// juggle layout & bounds to ensure they run *after* any faceting transforms
if (nested) {
ops = scope.operators;
ops.pop();
if (layout) ops.pop();
}
-
scope.pushState(encodeRef, layoutRef || boundRef, joinRef);
facet ? parseFacet(spec, scope, input) // explicit facet
: nested ? parseSubflow(spec, scope, input) // standard mark group
: scope.parse(spec); // guide group, we can avoid nested scopes
-
scope.popState();
-
if (nested) {
if (layout) ops.push(layout);
ops.push(bound);
}
- } // if requested, add overlap removal transform
+ }
-
+ // if requested, add overlap removal transform
if (overlap) {
boundRef = parseOverlap(overlap, boundRef, scope);
- } // render / sieve items
+ }
-
+ // render / sieve items
const render = scope.add(Render({
- pulse: boundRef
- })),
- sieve = scope.add(Sieve({
- pulse: ref(render)
- }, undefined, scope.parent())); // if mark is named, make accessible as reactive geometry
- // add trigger updates if defined
+ pulse: boundRef
+ })),
+ sieve = scope.add(Sieve({
+ pulse: ref(render)
+ }, undefined, scope.parent()));
+ // if mark is named, make accessible as reactive geometry
+ // add trigger updates if defined
if (spec.name != null) {
name = spec.name;
scope.addData(name, new DataScope(scope, store, render, sieve));
if (spec.on) spec.on.forEach(on => {
if (on.insert || on.remove || on.toggle) {
error('Marks only support modify triggers.');
}
-
parseTrigger(on, scope, name);
});
}
}
-
function parseOverlap(overlap, source, scope) {
const method = overlap.method,
- bound = overlap.bound,
- sep = overlap.separation;
+ bound = overlap.bound,
+ sep = overlap.separation;
const params = {
separation: isSignal(sep) ? scope.signalRef(sep.signal) : sep,
method: isSignal(method) ? scope.signalRef(method.signal) : method,
pulse: source
};
-
if (overlap.order) {
params.sort = scope.compareRef({
field: overlap.order
});
}
-
if (bound) {
const tol = bound.tolerance;
params.boundTolerance = isSignal(tol) ? scope.signalRef(tol.signal) : +tol;
params.boundScale = scope.scaleRef(bound.scale);
params.boundOrient = bound.orient;
}
-
return ref(scope.add(Overlap(params)));
}
-
function parseLegend(spec, scope) {
const config = scope.config.legend,
- encode = spec.encode || {},
- _ = lookup(spec, config),
- legendEncode = encode.legend || {},
- name = legendEncode.name || undefined,
- interactive = legendEncode.interactive,
- style = legendEncode.style,
- scales = {};
-
+ encode = spec.encode || {},
+ _ = lookup(spec, config),
+ legendEncode = encode.legend || {},
+ name = legendEncode.name || undefined,
+ interactive = legendEncode.interactive,
+ style = legendEncode.style,
+ scales = {};
let scale = 0,
- entryLayout,
- params,
- children; // resolve scales and 'canonical' scale name
+ entryLayout,
+ params,
+ children;
+ // resolve scales and 'canonical' scale name
LegendScales.forEach(s => spec[s] ? (scales[s] = spec[s], scale = scale || spec[s]) : 0);
- if (!scale) error('Missing valid scale for legend.'); // resolve legend type (symbol, gradient, or discrete gradient)
+ if (!scale) error('Missing valid scale for legend.');
- const type = legendType(spec, scope.scaleType(scale)); // single-element data source for legend group
+ // resolve legend type (symbol, gradient, or discrete gradient)
+ const type = legendType(spec, scope.scaleType(scale));
+ // single-element data source for legend group
const datum = {
title: spec.title != null,
scales: scales,
type: type,
vgrad: type !== 'symbol' && _.isVertical()
};
- const dataRef = ref(scope.add(Collect(null, [datum]))); // encoding properties for legend entry sub-group
+ const dataRef = ref(scope.add(Collect(null, [datum])));
+ // encoding properties for legend entry sub-group
const entryEncode = {
enter: {
x: {
value: 0
},
y: {
value: 0
}
}
- }; // data source for legend values
+ };
+ // data source for legend values
const entryRef = ref(scope.add(LegendEntries(params = {
type: type,
scale: scope.scaleRef(scale),
count: scope.objectProperty(_('tickCount')),
limit: scope.property(_('symbolLimit')),
values: scope.objectProperty(spec.values),
minstep: scope.property(spec.tickMinStep),
formatType: scope.property(spec.formatType),
formatSpecifier: scope.property(spec.format)
- }))); // continuous gradient legend
+ })));
+ // continuous gradient legend
if (type === Gradient) {
- children = [legendGradient(spec, scale, config, encode.gradient), legendGradientLabels(spec, config, encode.labels, entryRef)]; // adjust default tick count based on the gradient length
+ children = [legendGradient(spec, scale, config, encode.gradient), legendGradientLabels(spec, config, encode.labels, entryRef)];
+ // adjust default tick count based on the gradient length
+ params.count = params.count || scope.signalRef(`max(2,2*floor((${deref(_.gradientLength())})/100))`);
+ }
- params.count = params.count || scope.signalRef("max(2,2*floor((".concat(deref(_.gradientLength()), ")/100))"));
- } // discrete gradient legend
+ // discrete gradient legend
else if (type === Discrete) {
children = [legendGradientDiscrete(spec, scale, config, encode.gradient, entryRef), legendGradientLabels(spec, config, encode.labels, entryRef)];
- } // symbol legend
+ }
+
+ // symbol legend
else {
// determine legend symbol group layout
entryLayout = legendSymbolLayout(spec, config);
- children = [legendSymbolGroups(spec, config, encode, entryRef, deref(entryLayout.columns))]; // pass symbol size information to legend entry generator
-
+ children = [legendSymbolGroups(spec, config, encode, entryRef, deref(entryLayout.columns))];
+ // pass symbol size information to legend entry generator
params.size = sizeExpression(spec, scope, children[0].marks);
- } // generate legend marks
+ }
-
+ // generate legend marks
children = [guideGroup({
role: LegendEntryRole,
from: dataRef,
encode: entryEncode,
marks: children,
layout: entryLayout,
interactive
- })]; // include legend title if defined
+ })];
+ // include legend title if defined
if (datum.title) {
children.push(legendTitle(spec, config, encode.title, dataRef));
- } // parse legend specification
+ }
-
+ // parse legend specification
return parseMark(guideGroup({
role: LegendRole,
from: dataRef,
encode: extendEncode(buildLegendEncode(_, spec, config), legendEncode, Skip),
marks: children,
@@ -44064,25 +39817,20 @@
name,
interactive,
style
}), scope);
}
-
function legendType(spec, scaleType) {
let type = spec.type || Symbols;
-
if (!spec.type && scaleCount(spec) === 1 && (spec.fill || spec.stroke)) {
type = isContinuous(scaleType) ? Gradient : isDiscretizing(scaleType) ? Discrete : Symbols;
}
-
return type !== Gradient ? type : isDiscretizing(scaleType) ? Discrete : Gradient;
}
-
function scaleCount(spec) {
return LegendScales.reduce((count, type) => count + (spec[type] ? 1 : 0), 0);
}
-
function buildLegendEncode(_, spec, config) {
const encode = {
enter: {},
update: {}
};
@@ -44102,52 +39850,48 @@
format: spec.format,
formatType: spec.formatType
});
return encode;
}
-
function sizeExpression(spec, scope, marks) {
const size = deref(getChannel('size', spec, marks)),
- strokeWidth = deref(getChannel('strokeWidth', spec, marks)),
- fontSize = deref(getFontSize(marks[1].encode, scope, GuideLabelStyle));
- return parser("max(ceil(sqrt(".concat(size, ")+").concat(strokeWidth, "),").concat(fontSize, ")"), scope);
+ strokeWidth = deref(getChannel('strokeWidth', spec, marks)),
+ fontSize = deref(getFontSize(marks[1].encode, scope, GuideLabelStyle));
+ return parser(`max(ceil(sqrt(${size})+${strokeWidth}),${fontSize})`, scope);
}
-
function getChannel(name, spec, marks) {
- return spec[name] ? "scale(\"".concat(spec[name], "\",datum)") : getEncoding(name, marks[0].encode);
+ return spec[name] ? `scale("${spec[name]}",datum)` : getEncoding(name, marks[0].encode);
}
-
function getFontSize(encode, scope, style) {
return getEncoding('fontSize', encode) || getStyle('fontSize', scope, style);
}
-
- const angleExpr = "item.orient===\"".concat(Left, "\"?-90:item.orient===\"").concat(Right, "\"?90:0");
-
+ const angleExpr = `item.orient==="${Left}"?-90:item.orient==="${Right}"?90:0`;
function parseTitle(spec, scope) {
spec = isString(spec) ? {
text: spec
} : spec;
-
const _ = lookup(spec, scope.config.title),
- encode = spec.encode || {},
- userEncode = encode.group || {},
- name = userEncode.name || undefined,
- interactive = userEncode.interactive,
- style = userEncode.style,
- children = []; // single-element data source for group title
+ encode = spec.encode || {},
+ userEncode = encode.group || {},
+ name = userEncode.name || undefined,
+ interactive = userEncode.interactive,
+ style = userEncode.style,
+ children = [];
-
+ // single-element data source for group title
const datum = {},
- dataRef = ref(scope.add(Collect(null, [datum]))); // include title text
+ dataRef = ref(scope.add(Collect(null, [datum])));
- children.push(buildTitle(spec, _, titleEncode(spec), dataRef)); // include subtitle text
+ // include title text
+ children.push(buildTitle(spec, _, titleEncode(spec), dataRef));
+ // include subtitle text
if (spec.subtitle) {
children.push(buildSubTitle(spec, _, encode.subtitle, dataRef));
- } // parse title specification
+ }
-
+ // parse title specification
return parseMark(guideGroup({
role: TitleRole,
from: dataRef,
encode: groupEncode(_, userEncode),
marks: children,
@@ -44156,23 +39900,22 @@
zindex: _('zindex'),
name,
interactive,
style
}), scope);
- } // provide backwards-compatibility for title custom encode;
- // the top-level encode block has been *deprecated*.
+ }
-
+ // provide backwards-compatibility for title custom encode;
+ // the top-level encode block has been *deprecated*.
function titleEncode(spec) {
const encode = spec.encode;
return encode && encode.title || extend$1({
name: spec.name,
interactive: spec.interactive,
style: spec.style
}, encode);
}
-
function groupEncode(_, userEncode) {
const encode = {
enter: {},
update: {}
};
@@ -44190,29 +39933,28 @@
offset: _('offset') || 0,
padding: _('subtitlePadding')
});
return extendEncode(encode, userEncode, Skip);
}
-
function buildTitle(spec, _, userEncode, dataRef) {
const zero = {
- value: 0
- },
- text = spec.text,
- encode = {
- enter: {
- opacity: zero
+ value: 0
},
- update: {
- opacity: {
- value: 1
+ text = spec.text,
+ encode = {
+ enter: {
+ opacity: zero
+ },
+ update: {
+ opacity: {
+ value: 1
+ }
+ },
+ exit: {
+ opacity: zero
}
- },
- exit: {
- opacity: zero
- }
- };
+ };
addEncoders(encode, {
text: text,
align: {
signal: 'item.mark.group.align'
},
@@ -44243,29 +39985,28 @@
style: GroupTitleStyle,
from: dataRef,
encode
}, userEncode);
}
-
function buildSubTitle(spec, _, userEncode, dataRef) {
const zero = {
- value: 0
- },
- text = spec.subtitle,
- encode = {
- enter: {
- opacity: zero
+ value: 0
},
- update: {
- opacity: {
- value: 1
+ text = spec.subtitle,
+ encode = {
+ enter: {
+ opacity: zero
+ },
+ update: {
+ opacity: {
+ value: 1
+ }
+ },
+ exit: {
+ opacity: zero
}
- },
- exit: {
- opacity: zero
- }
- };
+ };
addEncoders(encode, {
text: text,
align: {
signal: 'item.mark.group.align'
},
@@ -44296,44 +40037,38 @@
style: GroupSubtitleStyle,
from: dataRef,
encode
}, userEncode);
}
-
function parseData(data, scope) {
const transforms = [];
-
if (data.transform) {
data.transform.forEach(tx => {
transforms.push(parseTransform(tx, scope));
});
}
-
if (data.on) {
data.on.forEach(on => {
parseTrigger(on, scope, data.name);
});
}
-
scope.addDataPipeline(data.name, analyze(data, scope, transforms));
}
+
/**
* Analyze a data pipeline, add needed operators.
*/
-
-
function analyze(data, scope, ops) {
const output = [];
let source = null,
- modify = false,
- generate = false,
- upstream,
- i,
- n,
- t,
- m;
-
+ modify = false,
+ generate = false,
+ upstream,
+ i,
+ n,
+ t,
+ m;
if (data.values) {
// hard-wired input data set
if (isSignal(data.values) || hasSignal(data.format)) {
// if either values is signal or format has signal, use dynamic loader
output.push(load(scope, data));
@@ -44360,226 +40095,184 @@
}
} else if (data.source) {
// derives from one or more other data sets
source = upstream = array$5(data.source).map(d => ref(scope.getData(d).output));
output.push(null); // populate later
- } // scan data transforms, add collectors as needed
+ }
-
+ // scan data transforms, add collectors as needed
for (i = 0, n = ops.length; i < n; ++i) {
t = ops[i];
m = t.metadata;
-
if (!source && !m.source) {
output.push(source = collect());
}
-
output.push(t);
if (m.generates) generate = true;
if (m.modifies && !generate) modify = true;
if (m.source) source = t;else if (m.changes) source = null;
}
-
if (upstream) {
n = upstream.length - 1;
output[0] = Relay({
derive: modify,
pulse: n ? upstream : upstream[0]
});
-
if (modify || n) {
// collect derived and multi-pulse tuples
output.splice(1, 0, collect());
}
}
-
if (!source) output.push(collect());
output.push(Sieve({}));
return output;
}
-
function collect(values) {
const s = Collect({}, values);
s.metadata = {
source: true
};
return s;
}
-
function load(scope, data) {
return Load({
url: data.url ? scope.property(data.url) : undefined,
async: data.async ? scope.property(data.async) : undefined,
values: data.values ? scope.property(data.values) : undefined,
format: scope.objectProperty(data.format)
});
}
+ const isX = orient => orient === Bottom || orient === Top;
- const isX = orient => orient === Bottom || orient === Top; // get sign coefficient based on axis orient
+ // get sign coefficient based on axis orient
+ const getSign = (orient, a, b) => isSignal(orient) ? ifLeftTopExpr(orient.signal, a, b) : orient === Left || orient === Top ? a : b;
+ // condition on axis x-direction
+ const ifX = (orient, a, b) => isSignal(orient) ? ifXEnc(orient.signal, a, b) : isX(orient) ? a : b;
- const getSign = (orient, a, b) => isSignal(orient) ? ifLeftTopExpr(orient.signal, a, b) : orient === Left || orient === Top ? a : b; // condition on axis x-direction
-
-
- const ifX = (orient, a, b) => isSignal(orient) ? ifXEnc(orient.signal, a, b) : isX(orient) ? a : b; // condition on axis y-direction
-
-
+ // condition on axis y-direction
const ifY = (orient, a, b) => isSignal(orient) ? ifYEnc(orient.signal, a, b) : isX(orient) ? b : a;
-
const ifTop = (orient, a, b) => isSignal(orient) ? ifTopExpr(orient.signal, a, b) : orient === Top ? {
value: a
} : {
value: b
};
-
const ifRight = (orient, a, b) => isSignal(orient) ? ifRightExpr(orient.signal, a, b) : orient === Right ? {
value: a
} : {
value: b
};
-
- const ifXEnc = ($orient, a, b) => ifEnc("".concat($orient, " === '").concat(Top, "' || ").concat($orient, " === '").concat(Bottom, "'"), a, b);
-
- const ifYEnc = ($orient, a, b) => ifEnc("".concat($orient, " !== '").concat(Top, "' && ").concat($orient, " !== '").concat(Bottom, "'"), a, b);
-
- const ifLeftTopExpr = ($orient, a, b) => ifExpr("".concat($orient, " === '").concat(Left, "' || ").concat($orient, " === '").concat(Top, "'"), a, b);
-
- const ifTopExpr = ($orient, a, b) => ifExpr("".concat($orient, " === '").concat(Top, "'"), a, b);
-
- const ifRightExpr = ($orient, a, b) => ifExpr("".concat($orient, " === '").concat(Right, "'"), a, b);
-
+ const ifXEnc = ($orient, a, b) => ifEnc(`${$orient} === '${Top}' || ${$orient} === '${Bottom}'`, a, b);
+ const ifYEnc = ($orient, a, b) => ifEnc(`${$orient} !== '${Top}' && ${$orient} !== '${Bottom}'`, a, b);
+ const ifLeftTopExpr = ($orient, a, b) => ifExpr(`${$orient} === '${Left}' || ${$orient} === '${Top}'`, a, b);
+ const ifTopExpr = ($orient, a, b) => ifExpr(`${$orient} === '${Top}'`, a, b);
+ const ifRightExpr = ($orient, a, b) => ifExpr(`${$orient} === '${Right}'`, a, b);
const ifEnc = (test, a, b) => {
// ensure inputs are encoder objects (or null)
a = a != null ? encoder(a) : a;
b = b != null ? encoder(b) : b;
-
if (isSimple(a) && isSimple(b)) {
// if possible generate simple signal expression
a = a ? a.signal || $(a.value) : null;
b = b ? b.signal || $(b.value) : null;
return {
- signal: "".concat(test, " ? (").concat(a, ") : (").concat(b, ")")
+ signal: `${test} ? (${a}) : (${b})`
};
} else {
// otherwise generate rule set
return [extend$1({
test
}, a)].concat(b || []);
}
};
-
const isSimple = enc => enc == null || Object.keys(enc).length === 1;
-
const ifExpr = (test, a, b) => ({
- signal: "".concat(test, " ? (").concat(toExpr(a), ") : (").concat(toExpr(b), ")")
+ signal: `${test} ? (${toExpr(a)}) : (${toExpr(b)})`
});
-
const ifOrient = ($orient, t, b, l, r) => ({
- signal: (l != null ? "".concat($orient, " === '").concat(Left, "' ? (").concat(toExpr(l), ") : ") : '') + (b != null ? "".concat($orient, " === '").concat(Bottom, "' ? (").concat(toExpr(b), ") : ") : '') + (r != null ? "".concat($orient, " === '").concat(Right, "' ? (").concat(toExpr(r), ") : ") : '') + (t != null ? "".concat($orient, " === '").concat(Top, "' ? (").concat(toExpr(t), ") : ") : '') + '(null)'
+ signal: (l != null ? `${$orient} === '${Left}' ? (${toExpr(l)}) : ` : '') + (b != null ? `${$orient} === '${Bottom}' ? (${toExpr(b)}) : ` : '') + (r != null ? `${$orient} === '${Right}' ? (${toExpr(r)}) : ` : '') + (t != null ? `${$orient} === '${Top}' ? (${toExpr(t)}) : ` : '') + '(null)'
});
-
const toExpr = v => isSignal(v) ? v.signal : v == null ? null : $(v);
-
const mult = (sign, value) => value === 0 ? 0 : isSignal(sign) ? {
- signal: "(".concat(sign.signal, ") * ").concat(value)
+ signal: `(${sign.signal}) * ${value}`
} : {
value: sign * value
};
-
const patch = (value, base) => {
const s = value.signal;
return s && s.endsWith('(null)') ? {
signal: s.slice(0, -6) + base.signal
} : value;
};
-
function fallback(prop, config, axisConfig, style) {
let styleProp;
-
if (config && has$1(config, prop)) {
return config[prop];
} else if (has$1(axisConfig, prop)) {
return axisConfig[prop];
} else if (prop.startsWith('title')) {
switch (prop) {
case 'titleColor':
styleProp = 'fill';
break;
-
case 'titleFont':
case 'titleFontSize':
case 'titleFontWeight':
styleProp = prop[5].toLowerCase() + prop.slice(6);
}
-
return style[GuideTitleStyle][styleProp];
} else if (prop.startsWith('label')) {
switch (prop) {
case 'labelColor':
styleProp = 'fill';
break;
-
case 'labelFont':
case 'labelFontSize':
styleProp = prop[5].toLowerCase() + prop.slice(6);
}
-
return style[GuideLabelStyle][styleProp];
}
-
return null;
}
-
function keys(objects) {
const map = {};
-
for (const obj of objects) {
if (!obj) continue;
-
for (const key in obj) map[key] = 1;
}
-
return Object.keys(map);
}
-
function axisConfig(spec, scope) {
var config = scope.config,
- style = config.style,
- axis = config.axis,
- band = scope.scaleType(spec.scale) === 'band' && config.axisBand,
- orient = spec.orient,
- xy,
- or,
- key;
-
+ style = config.style,
+ axis = config.axis,
+ band = scope.scaleType(spec.scale) === 'band' && config.axisBand,
+ orient = spec.orient,
+ xy,
+ or,
+ key;
if (isSignal(orient)) {
const xyKeys = keys([config.axisX, config.axisY]),
- orientKeys = keys([config.axisTop, config.axisBottom, config.axisLeft, config.axisRight]);
+ orientKeys = keys([config.axisTop, config.axisBottom, config.axisLeft, config.axisRight]);
xy = {};
-
for (key of xyKeys) {
xy[key] = ifX(orient, fallback(key, config.axisX, axis, style), fallback(key, config.axisY, axis, style));
}
-
or = {};
-
for (key of orientKeys) {
or[key] = ifOrient(orient.signal, fallback(key, config.axisTop, axis, style), fallback(key, config.axisBottom, axis, style), fallback(key, config.axisLeft, axis, style), fallback(key, config.axisRight, axis, style));
}
} else {
xy = orient === Top || orient === Bottom ? config.axisX : config.axisY;
or = config['axis' + orient[0].toUpperCase() + orient.slice(1)];
}
-
const result = xy || or || band ? extend$1({}, axis, xy, or, band) : axis;
return result;
}
-
function axisDomain(spec, config, userEncode, dataRef) {
const _ = lookup(spec, config),
- orient = spec.orient;
-
+ orient = spec.orient;
let enter, update;
const encode = {
enter: enter = {
opacity: zero
},
@@ -44609,25 +40302,22 @@
role: AxisDomainRole,
from: dataRef,
encode
}, userEncode);
}
-
function position(spec, pos) {
return {
scale: spec.scale,
range: pos
};
}
-
function axisGrid(spec, config, userEncode, dataRef, band) {
const _ = lookup(spec, config),
- orient = spec.orient,
- vscale = spec.gridScale,
- sign = getSign(orient, 1, -1),
- offset = offsetValue(spec.offset, sign);
-
+ orient = spec.orient,
+ vscale = spec.gridScale,
+ sign = getSign(orient, 1, -1),
+ offset = offsetValue(spec.offset, sign);
let enter, exit, update;
const encode = {
enter: enter = {
opacity: zero
},
@@ -44689,41 +40379,36 @@
key: Value,
from: dataRef,
encode
}, userEncode);
}
-
function offsetValue(offset, sign) {
if (sign === 1) ;else if (!isObject(offset)) {
offset = isSignal(sign) ? {
- signal: "(".concat(sign.signal, ") * (").concat(offset || 0, ")")
+ signal: `(${sign.signal}) * (${offset || 0})`
} : sign * (offset || 0);
} else {
let entry = offset = extend$1({}, offset);
-
while (entry.mult != null) {
if (!isObject(entry.mult)) {
entry.mult = isSignal(sign) // no offset if sign === 1
? {
- signal: "(".concat(entry.mult, ") * (").concat(sign.signal, ")")
+ signal: `(${entry.mult}) * (${sign.signal})`
} : entry.mult * sign;
return offset;
} else {
entry = entry.mult = extend$1({}, entry.mult);
}
}
-
entry.mult = sign;
}
return offset;
}
-
function axisTicks(spec, config, userEncode, dataRef, size, band) {
const _ = lookup(spec, config),
- orient = spec.orient,
- sign = getSign(orient, -1, 1);
-
+ orient = spec.orient,
+ sign = getSign(orient, -1, 1);
let enter, exit, update;
const encode = {
enter: enter = {
opacity: zero
},
@@ -44764,29 +40449,26 @@
key: Value,
from: dataRef,
encode
}, userEncode);
}
-
function flushExpr(scale, threshold, a, b, c) {
return {
signal: 'flush(range("' + scale + '"), ' + 'scale("' + scale + '", datum.value), ' + threshold + ',' + a + ',' + b + ',' + c + ')'
};
}
-
function axisLabels(spec, config, userEncode, dataRef, size, band) {
const _ = lookup(spec, config),
- orient = spec.orient,
- scale = spec.scale,
- sign = getSign(orient, -1, 1),
- flush = deref(_('labelFlush')),
- flushOffset = deref(_('labelFlushOffset')),
- labelAlign = _('labelAlign'),
- labelBaseline = _('labelBaseline');
-
+ orient = spec.orient,
+ scale = spec.scale,
+ sign = getSign(orient, -1, 1),
+ flush = deref(_('labelFlush')),
+ flushOffset = deref(_('labelFlushOffset')),
+ labelAlign = _('labelAlign'),
+ labelBaseline = _('labelBaseline');
let flushOn = flush === 0 || !!flush,
- update;
+ update;
const tickSize = encoder(size);
tickSize.mult = sign;
tickSize.offset = encoder(_('labelPadding') || 0);
tickSize.offset.mult = sign;
const tickPos = {
@@ -44799,11 +40481,11 @@
value: 'center'
}, ifRight(orient, 'left', 'right'));
const baseline = ifX(orient, ifTop(orient, 'bottom', 'top'), flushOn ? flushExpr(scale, flush, '"top"', '"bottom"', '"middle"') : {
value: 'middle'
});
- const offsetExpr = flushExpr(scale, flush, "-(".concat(flushOffset, ")"), flushOffset, 0);
+ const offsetExpr = flushExpr(scale, flush, `-(${flushOffset})`, flushOffset, 0);
flushOn = flushOn && flushOffset;
const enter = {
opacity: zero,
x: ifX(orient, tickPos, tickSize),
y: ifY(orient, tickPos, tickSize)
@@ -44842,51 +40524,44 @@
lineHeight: _('labelLineHeight')
}, {
align: labelAlign,
baseline: labelBaseline
});
-
const bound = _('labelBound');
+ let overlap = _('labelOverlap');
- let overlap = _('labelOverlap'); // if overlap method or bound defined, request label overlap removal
-
-
+ // if overlap method or bound defined, request label overlap removal
overlap = overlap || bound ? {
separation: _('labelSeparation'),
method: overlap,
order: 'datum.index',
bound: bound ? {
scale,
orient,
tolerance: bound
} : null
} : undefined;
-
if (update.align !== align) {
update.align = patch(update.align, align);
}
-
if (update.baseline !== baseline) {
update.baseline = patch(update.baseline, baseline);
}
-
return guideMark({
type: TextMark,
role: AxisLabelRole,
style: GuideLabelStyle,
key: Value,
from: dataRef,
encode,
overlap
}, userEncode);
}
-
function axisTitle(spec, config, userEncode, dataRef) {
const _ = lookup(spec, config),
- orient = spec.orient,
- sign = getSign(orient, -1, 1);
-
+ orient = spec.orient,
+ sign = getSign(orient, -1, 1);
let enter, update;
const encode = {
enter: enter = {
opacity: zero,
anchor: encoder(_('titleAnchor', null)),
@@ -44901,11 +40576,11 @@
exit: {
opacity: zero
}
};
const titlePos = {
- signal: "lerp(range(\"".concat(spec.scale, "\"), ").concat(anchorExpr(0, 1, 0.5), ")")
+ signal: `lerp(range("${spec.scale}"), ${anchorExpr(0, 1, 0.5)})`
};
update.x = ifX(orient, titlePos);
update.y = ifY(orient, titlePos);
enter.angle = ifX(orient, zero, mult(sign, 90));
enter.baseline = ifX(orient, ifTop(orient, Bottom, Top), {
@@ -44938,80 +40613,80 @@
style: GuideTitleStyle,
from: dataRef,
encode
}, userEncode);
}
-
function autoLayout(_, orient, encode, userEncode) {
const auto = (value, dim) => value != null ? (encode.update[dim] = patch(encoder(value), encode.update[dim]), false) : !has(dim, userEncode) ? true : false;
-
const autoY = auto(_('titleX'), 'x'),
- autoX = auto(_('titleY'), 'y');
+ autoX = auto(_('titleY'), 'y');
encode.enter.auto = autoX === autoY ? encoder(autoX) : ifX(orient, encoder(autoX), encoder(autoY));
}
-
function parseAxis(spec, scope) {
const config = axisConfig(spec, scope),
- encode = spec.encode || {},
- axisEncode = encode.axis || {},
- name = axisEncode.name || undefined,
- interactive = axisEncode.interactive,
- style = axisEncode.style,
- _ = lookup(spec, config),
- band = tickBand(_); // single-element data source for axis group
+ encode = spec.encode || {},
+ axisEncode = encode.axis || {},
+ name = axisEncode.name || undefined,
+ interactive = axisEncode.interactive,
+ style = axisEncode.style,
+ _ = lookup(spec, config),
+ band = tickBand(_);
-
+ // single-element data source for axis group
const datum = {
scale: spec.scale,
ticks: !!_('ticks'),
labels: !!_('labels'),
grid: !!_('grid'),
domain: !!_('domain'),
title: spec.title != null
};
- const dataRef = ref(scope.add(Collect({}, [datum]))); // data source for axis ticks
+ const dataRef = ref(scope.add(Collect({}, [datum])));
+ // data source for axis ticks
const ticksRef = ref(scope.add(AxisTicks({
scale: scope.scaleRef(spec.scale),
extra: scope.property(band.extra),
count: scope.objectProperty(spec.tickCount),
values: scope.objectProperty(spec.values),
minstep: scope.property(spec.tickMinStep),
formatType: scope.property(spec.formatType),
formatSpecifier: scope.property(spec.format)
- }))); // generate axis marks
+ })));
+ // generate axis marks
const children = [];
- let size; // include axis gridlines if requested
+ let size;
+ // include axis gridlines if requested
if (datum.grid) {
children.push(axisGrid(spec, config, encode.grid, ticksRef, band));
- } // include axis ticks if requested
+ }
-
+ // include axis ticks if requested
if (datum.ticks) {
size = _('tickSize');
children.push(axisTicks(spec, config, encode.ticks, ticksRef, size, band));
- } // include axis labels if requested
+ }
-
+ // include axis labels if requested
if (datum.labels) {
size = datum.ticks ? size : 0;
children.push(axisLabels(spec, config, encode.labels, ticksRef, size, band));
- } // include axis domain path if requested
+ }
-
+ // include axis domain path if requested
if (datum.domain) {
children.push(axisDomain(spec, config, encode.domain, dataRef));
- } // include axis title if defined
+ }
-
+ // include axis title if defined
if (datum.title) {
children.push(axisTitle(spec, config, encode.title, dataRef));
- } // parse axis specification
+ }
-
+ // parse axis specification
return parseMark(guideGroup({
role: AxisRole,
from: dataRef,
encode: extendEncode(buildAxisEncode(_, spec), axisEncode, Skip),
marks: children,
@@ -45021,11 +40696,10 @@
name,
interactive,
style
}), scope);
}
-
function buildAxisEncode(_, spec) {
const encode = {
enter: {},
update: {}
};
@@ -45035,48 +40709,57 @@
position: value(spec.position, 0),
titlePadding: _('titlePadding'),
minExtent: _('minExtent'),
maxExtent: _('maxExtent'),
range: {
- signal: "abs(span(range(\"".concat(spec.scale, "\")))")
+ signal: `abs(span(range("${spec.scale}")))`
},
translate: _('translate'),
// accessibility support
format: spec.format,
formatType: spec.formatType
});
return encode;
}
-
function parseScope(spec, scope, preprocessed) {
const signals = array$5(spec.signals),
- scales = array$5(spec.scales); // parse signal definitions, if not already preprocessed
+ scales = array$5(spec.scales);
- if (!preprocessed) signals.forEach(_ => parseSignal(_, scope)); // parse cartographic projection definitions
+ // parse signal definitions, if not already preprocessed
+ if (!preprocessed) signals.forEach(_ => parseSignal(_, scope));
- array$5(spec.projections).forEach(_ => parseProjection(_, scope)); // initialize scale references
+ // parse cartographic projection definitions
+ array$5(spec.projections).forEach(_ => parseProjection(_, scope));
- scales.forEach(_ => initScale(_, scope)); // parse data sources
+ // initialize scale references
+ scales.forEach(_ => initScale(_, scope));
- array$5(spec.data).forEach(_ => parseData(_, scope)); // parse scale definitions
+ // parse data sources
+ array$5(spec.data).forEach(_ => parseData(_, scope));
- scales.forEach(_ => parseScale(_, scope)); // parse signal updates
+ // parse scale definitions
+ scales.forEach(_ => parseScale(_, scope));
- (preprocessed || signals).forEach(_ => parseSignalUpdates(_, scope)); // parse axis definitions
+ // parse signal updates
+ (preprocessed || signals).forEach(_ => parseSignalUpdates(_, scope));
- array$5(spec.axes).forEach(_ => parseAxis(_, scope)); // parse mark definitions
+ // parse axis definitions
+ array$5(spec.axes).forEach(_ => parseAxis(_, scope));
- array$5(spec.marks).forEach(_ => parseMark(_, scope)); // parse legend definitions
+ // parse mark definitions
+ array$5(spec.marks).forEach(_ => parseMark(_, scope));
- array$5(spec.legends).forEach(_ => parseLegend(_, scope)); // parse title, if defined
+ // parse legend definitions
+ array$5(spec.legends).forEach(_ => parseLegend(_, scope));
- if (spec.title) parseTitle(spec.title, scope); // parse collected lambda (anonymous) expressions
+ // parse title, if defined
+ if (spec.title) parseTitle(spec.title, scope);
+ // parse collected lambda (anonymous) expressions
scope.parseLambdas();
return scope;
}
-
const rootEncode = spec => extendEncode({
enter: {
x: {
value: 0
},
@@ -45091,67 +40774,75 @@
height: {
signal: 'height'
}
}
}, spec);
-
function parseView(spec, scope) {
- const config = scope.config; // add scenegraph root
+ const config = scope.config;
- const root = ref(scope.root = scope.add(operator())); // parse top-level signal definitions
+ // add scenegraph root
+ const root = ref(scope.root = scope.add(operator()));
+ // parse top-level signal definitions
const signals = collectSignals(spec, config);
- signals.forEach(_ => parseSignal(_, scope)); // assign description, event, legend, and locale configuration
+ signals.forEach(_ => parseSignal(_, scope));
+ // assign description, event, legend, and locale configuration
scope.description = spec.description || config.description;
scope.eventConfig = config.events;
scope.legends = scope.objectProperty(config.legend && config.legend.layout);
- scope.locale = config.locale; // store root group item
+ scope.locale = config.locale;
- const input = scope.add(Collect()); // encode root group item
+ // store root group item
+ const input = scope.add(Collect());
+ // encode root group item
const encode = scope.add(Encode(parseEncode(rootEncode(spec.encode), GroupMark, FrameRole, spec.style, scope, {
pulse: ref(input)
- }))); // perform view layout
+ })));
+ // perform view layout
const parent = scope.add(ViewLayout({
layout: scope.objectProperty(spec.layout),
legends: scope.legends,
autosize: scope.signalRef('autosize'),
mark: root,
pulse: ref(encode)
}));
- scope.operators.pop(); // parse remainder of specification
+ scope.operators.pop();
+ // parse remainder of specification
scope.pushState(ref(encode), ref(parent), null);
parseScope(spec, scope, signals);
- scope.operators.push(parent); // bound / render / sieve root item
+ scope.operators.push(parent);
+ // bound / render / sieve root item
let op = scope.add(Bound({
mark: root,
pulse: ref(parent)
}));
op = scope.add(Render({
pulse: ref(op)
}));
op = scope.add(Sieve({
pulse: ref(op)
- })); // track metadata for root item
+ }));
+ // track metadata for root item
scope.addData('root', new DataScope(scope, input, input, op));
return scope;
}
-
function signalObject(name, value) {
return value && value.signal ? {
name,
update: value.signal
} : {
name,
value
};
}
+
/**
* Collect top-level signals, merging values as needed. Signals
* defined in the config signals arrays are added only if that
* signal is not explicitly defined in the specification.
* Built-in signals (autosize, background, padding, width, height)
@@ -45162,40 +40853,37 @@
* signal 'update' property. If the spec's top-level signal array
* contains an entry that matches a built-in signal, that entry
* will be merged with the built-in specification, potentially
* overwriting existing 'value' or 'update' properties.
*/
-
-
function collectSignals(spec, config) {
const _ = name => value(spec[name], config[name]),
- signals = [signalObject('background', _('background')), signalObject('autosize', parseAutosize(_('autosize'))), signalObject('padding', parsePadding(_('padding'))), signalObject('width', _('width') || 0), signalObject('height', _('height') || 0)],
- pre = signals.reduce((p, s) => (p[s.name] = s, p), {}),
- map = {}; // add spec signal array
+ signals = [signalObject('background', _('background')), signalObject('autosize', parseAutosize(_('autosize'))), signalObject('padding', parsePadding(_('padding'))), signalObject('width', _('width') || 0), signalObject('height', _('height') || 0)],
+ pre = signals.reduce((p, s) => (p[s.name] = s, p), {}),
+ map = {};
-
+ // add spec signal array
array$5(spec.signals).forEach(s => {
if (has$1(pre, s.name)) {
// merge if built-in signal
s = extend$1(pre[s.name], s);
} else {
// otherwise add to signal list
signals.push(s);
}
-
map[s.name] = s;
- }); // add config signal array
+ });
+ // add config signal array
array$5(config.signals).forEach(s => {
if (!has$1(map, s.name) && !has$1(pre, s.name)) {
// add to signal list if not already defined
signals.push(s);
}
});
return signals;
}
-
function Scope(config, options) {
this.config = config || {};
this.options = options || {};
this.bindings = [];
this.field = {};
@@ -45215,11 +40903,10 @@
this._parent = [];
this._encode = [];
this._lookup = [];
this._markpath = [];
}
-
function Subscope(scope) {
this.config = scope.config;
this.options = scope.options;
this.legends = scope.legends;
this.field = Object.create(scope.field);
@@ -45237,24 +40924,20 @@
this._parent = scope._parent.slice();
this._encode = scope._encode.slice();
this._lookup = scope._lookup.slice();
this._markpath = scope._markpath;
}
-
Scope.prototype = Subscope.prototype = {
parse(spec) {
return parseScope(spec, this);
},
-
fork() {
return new Subscope(this);
},
-
isSubscope() {
return this._subid > 0;
},
-
toRuntime() {
this.finish();
return {
description: this.description,
operators: this.operators,
@@ -45263,385 +40946,317 @@
bindings: this.bindings,
eventConfig: this.eventConfig,
locale: this.locale
};
},
-
id() {
return (this._subid ? this._subid + ':' : 0) + this._id++;
},
-
add(op) {
this.operators.push(op);
- op.id = this.id(); // if pre-registration references exist, resolve them now
-
+ op.id = this.id();
+ // if pre-registration references exist, resolve them now
if (op.refs) {
op.refs.forEach(ref => {
ref.$ref = op.id;
});
op.refs = null;
}
-
return op;
},
-
proxy(op) {
const vref = op instanceof Entry ? ref(op) : op;
return this.add(Proxy({
value: vref
}));
},
-
addStream(stream) {
this.streams.push(stream);
stream.id = this.id();
return stream;
},
-
addUpdate(update) {
this.updates.push(update);
return update;
},
-
// Apply metadata
finish() {
- let name, ds; // annotate root
+ let name, ds;
- if (this.root) this.root.root = true; // annotate signals
+ // annotate root
+ if (this.root) this.root.root = true;
+ // annotate signals
for (name in this.signals) {
this.signals[name].signal = name;
- } // annotate scales
+ }
-
+ // annotate scales
for (name in this.scales) {
this.scales[name].scale = name;
- } // annotate data sets
+ }
-
+ // annotate data sets
function annotate(op, name, type) {
let data, list;
-
if (op) {
data = op.data || (op.data = {});
list = data[name] || (data[name] = []);
list.push(type);
}
}
-
for (name in this.data) {
ds = this.data[name];
annotate(ds.input, name, 'input');
annotate(ds.output, name, 'output');
annotate(ds.values, name, 'values');
-
for (const field in ds.index) {
annotate(ds.index[field], name, 'index:' + field);
}
}
-
return this;
},
-
// ----
+
pushState(encode, parent, lookup) {
this._encode.push(ref(this.add(Sieve({
pulse: encode
}))));
-
this._parent.push(parent);
-
this._lookup.push(lookup ? ref(this.proxy(lookup)) : null);
-
this._markpath.push(-1);
},
-
popState() {
this._encode.pop();
-
this._parent.pop();
-
this._lookup.pop();
-
this._markpath.pop();
},
-
parent() {
return peek$1(this._parent);
},
-
encode() {
return peek$1(this._encode);
},
-
lookup() {
return peek$1(this._lookup);
},
-
markpath() {
const p = this._markpath;
return ++p[p.length - 1];
},
-
// ----
+
fieldRef(field, name) {
if (isString(field)) return fieldRef$1(field, name);
-
if (!field.signal) {
error('Unsupported field reference: ' + $(field));
}
-
const s = field.signal;
let f = this.field[s];
-
if (!f) {
const params = {
name: this.signalRef(s)
};
if (name) params.as = name;
this.field[s] = f = ref(this.add(Field(params)));
}
-
return f;
},
-
compareRef(cmp) {
let signal = false;
-
const check = _ => isSignal(_) ? (signal = true, this.signalRef(_.signal)) : isExpr$1(_) ? (signal = true, this.exprRef(_.expr)) : _;
-
const fields = array$5(cmp.field).map(check),
- orders = array$5(cmp.order).map(check);
+ orders = array$5(cmp.order).map(check);
return signal ? ref(this.add(Compare({
fields: fields,
orders: orders
}))) : compareRef(fields, orders);
},
-
keyRef(fields, flat) {
let signal = false;
-
const check = _ => isSignal(_) ? (signal = true, ref(sig[_.signal])) : _;
-
const sig = this.signals;
fields = array$5(fields).map(check);
return signal ? ref(this.add(Key({
fields: fields,
flat: flat
}))) : keyRef(fields, flat);
},
-
sortRef(sort) {
- if (!sort) return sort; // including id ensures stable sorting
+ if (!sort) return sort;
+ // including id ensures stable sorting
const a = aggrField(sort.op, sort.field),
- o = sort.order || Ascending;
+ o = sort.order || Ascending;
return o.signal ? ref(this.add(Compare({
fields: a,
orders: this.signalRef(o.signal)
}))) : compareRef(a, o);
},
-
// ----
+
event(source, type) {
const key = source + ':' + type;
-
if (!this.events[key]) {
const id = this.id();
this.streams.push({
id: id,
source: source,
type: type
});
this.events[key] = id;
}
-
return this.events[key];
},
-
// ----
+
hasOwnSignal(name) {
return has$1(this.signals, name);
},
-
addSignal(name, value) {
if (this.hasOwnSignal(name)) {
error('Duplicate signal name: ' + $(name));
}
-
const op = value instanceof Entry ? value : this.add(operator(value));
return this.signals[name] = op;
},
-
getSignal(name) {
if (!this.signals[name]) {
error('Unrecognized signal name: ' + $(name));
}
-
return this.signals[name];
},
-
signalRef(s) {
if (this.signals[s]) {
return ref(this.signals[s]);
} else if (!has$1(this.lambdas, s)) {
this.lambdas[s] = this.add(operator(null));
}
-
return ref(this.lambdas[s]);
},
-
parseLambdas() {
const code = Object.keys(this.lambdas);
-
for (let i = 0, n = code.length; i < n; ++i) {
const s = code[i],
- e = parser(s, this),
- op = this.lambdas[s];
+ e = parser(s, this),
+ op = this.lambdas[s];
op.params = e.$params;
op.update = e.$expr;
}
},
-
property(spec) {
return spec && spec.signal ? this.signalRef(spec.signal) : spec;
},
-
objectProperty(spec) {
return !spec || !isObject(spec) ? spec : this.signalRef(spec.signal || propertyLambda(spec));
},
-
exprRef(code, name) {
const params = {
expr: parser(code, this)
};
if (name) params.expr.$name = name;
return ref(this.add(Expression(params)));
},
-
addBinding(name, bind) {
if (!this.bindings) {
error('Nested signals do not support binding: ' + $(name));
}
-
this.bindings.push(extend$1({
signal: name
}, bind));
},
-
// ----
+
addScaleProj(name, transform) {
if (has$1(this.scales, name)) {
error('Duplicate scale or projection name: ' + $(name));
}
-
this.scales[name] = this.add(transform);
},
-
addScale(name, params) {
this.addScaleProj(name, Scale(params));
},
-
addProjection(name, params) {
this.addScaleProj(name, Projection(params));
},
-
getScale(name) {
if (!this.scales[name]) {
error('Unrecognized scale name: ' + $(name));
}
-
return this.scales[name];
},
-
scaleRef(name) {
return ref(this.getScale(name));
},
-
scaleType(name) {
return this.getScale(name).params.type;
},
-
projectionRef(name) {
return this.scaleRef(name);
},
-
projectionType(name) {
return this.scaleType(name);
},
-
// ----
+
addData(name, dataScope) {
if (has$1(this.data, name)) {
error('Duplicate data set name: ' + $(name));
}
-
return this.data[name] = dataScope;
},
-
getData(name) {
if (!this.data[name]) {
error('Undefined data set name: ' + $(name));
}
-
return this.data[name];
},
-
addDataPipeline(name, entries) {
if (has$1(this.data, name)) {
error('Duplicate data set name: ' + $(name));
}
-
return this.addData(name, DataScope.fromEntries(this, entries));
}
-
};
-
function propertyLambda(spec) {
return (isArray(spec) ? arrayLambda : objectLambda)(spec);
}
-
function arrayLambda(array) {
const n = array.length;
let code = '[';
-
for (let i = 0; i < n; ++i) {
const value = array[i];
code += (i > 0 ? ',' : '') + (isObject(value) ? value.signal || propertyLambda(value) : $(value));
}
-
return code + ']';
}
-
function objectLambda(obj) {
let code = '{',
- i = 0,
- key,
- value;
-
+ i = 0,
+ key,
+ value;
for (key in obj) {
value = obj[key];
code += (++i > 1 ? ',' : '') + $(key) + ':' + (isObject(value) ? value.signal || propertyLambda(value) : $(value));
}
-
return code + '}';
}
+
/**
* Standard configuration defaults for Vega specification parsing.
* Users can provide their own (sub-)set of these default values
* by passing in a config object to the top-level parse method.
*/
-
-
function defaults() {
const defaultFont = 'sans-serif',
- defaultSymbolSize = 30,
- defaultStrokeWidth = 2,
- defaultColor = '#4c78a8',
- black = '#000',
- gray = '#888',
- lightGray = '#ddd';
+ defaultSymbolSize = 30,
+ defaultStrokeWidth = 2,
+ defaultColor = '#4c78a8',
+ black = '#000',
+ gray = '#888',
+ lightGray = '#ddd';
return {
// default visualization description
description: 'Vega visualization',
// default padding around visualization
padding: 0,
@@ -45746,10 +41361,13 @@
},
// defaults for styled group marks in Vega-Lite
cell: {
fill: 'transparent',
stroke: lightGray
+ },
+ view: {
+ fill: 'transparent'
}
},
// defaults for title
title: {
orient: 'top',
@@ -45849,23 +41467,22 @@
},
symbol: ['circle', 'square', 'triangle-up', 'cross', 'diamond', 'triangle-right', 'triangle-down', 'triangle-left']
}
};
}
-
function parse(spec, config, options) {
if (!isObject(spec)) {
error('Input Vega specification must be an object.');
}
-
config = mergeConfig(defaults(), config, spec.config);
return parseView(spec, new Scope(config, options)).toRuntime();
}
// -- Transforms -----
- extend$1(transforms, tx, vtx, encode$1, geo, force, label, tree, reg, voronoi, wordcloud, xf); // -- Exports -----
+ extend$1(transforms, tx, vtx, encode$1, geo, force, label, tree, reg, voronoi, wordcloud, xf);
+
exports.Bounds = Bounds;
exports.CanvasHandler = CanvasHandler;
exports.CanvasRenderer = CanvasRenderer;
exports.DATE = DATE;
exports.DAY = DAY;
@@ -45876,10 +41493,12 @@
exports.EventStream = EventStream;
exports.Gradient = Gradient$1;
exports.GroupItem = GroupItem;
exports.HOURS = HOURS;
exports.Handler = Handler;
+ exports.HybridHandler = HybridHandler;
+ exports.HybridRenderer = HybridRenderer;
exports.Info = Info;
exports.Item = Item;
exports.MILLISECONDS = MILLISECONDS;
exports.MINUTES = MINUTES;
exports.MONTH = MONTH;
@@ -45919,11 +41538,11 @@
exports.boundStroke = boundStroke;
exports.changeset = changeset;
exports.clampRange = clampRange;
exports.codegenExpression = codegen;
exports.compare = compare$1;
- exports.constant = constant$4;
+ exports.constant = constant$5;
exports.cumulativeLogNormal = cumulativeLogNormal;
exports.cumulativeNormal = cumulativeNormal;
exports.cumulativeUniform = cumulativeUniform;
exports.dayofyear = dayofyear;
exports.debounce = debounce;
@@ -46016,16 +41635,17 @@
exports.quantizeInterpolator = quantizeInterpolator;
exports.quarter = quarter;
exports.quartiles = quartiles;
exports.randomInteger = integer;
exports.randomKDE = kde;
- exports.randomLCG = lcg$1;
+ exports.randomLCG = lcg$2;
exports.randomLogNormal = lognormal;
exports.randomMixture = mixture$1;
exports.randomNormal = gaussian;
exports.randomUniform = uniform;
exports.read = read;
+ exports.regressionConstant = constant$4;
exports.regressionExp = exp$1;
exports.regressionLinear = linear$2;
exports.regressionLoess = loess;
exports.regressionLog = log$3;
exports.regressionPoly = poly;
@@ -46049,10 +41669,11 @@
exports.sceneToJSON = sceneToJSON;
exports.sceneVisit = visit;
exports.sceneZOrder = zorder;
exports.scheme = scheme;
exports.serializeXML = serializeXML;
+ exports.setHybridRendererOptions = setHybridRendererOptions;
exports.setRandom = setRandom;
exports.span = span;
exports.splitAccessPath = splitAccessPath;
exports.stringValue = $;
exports.textMetrics = textMetrics;
@@ -46084,14 +41705,12 @@
exports.utcweek = utcweek;
exports.version = version;
exports.visitArray = visitArray;
exports.week = week;
exports.writeConfig = writeConfig;
- exports.zero = zero$2;
+ exports.zero = zero$3;
exports.zoomLinear = zoomLinear;
exports.zoomLog = zoomLog;
exports.zoomPow = zoomPow;
exports.zoomSymlog = zoomSymlog;
-
- Object.defineProperty(exports, '__esModule', { value: true });
}));