(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vega-util'), require('vega-canvas'), require('vega-loader'), require('d3-shape'), require('d3-path')) : typeof define === 'function' && define.amd ? define(['exports', 'vega-util', 'vega-canvas', 'vega-loader', 'd3-shape', 'd3-path'], factory) : (global = global || self, factory(global.vega = {}, global.vega, global.vega, global.vega, global.d3, global.d3)); }(this, (function (exports, vegaUtil, vegaCanvas, vegaLoader, d3Shape, d3Path) { 'use strict'; function Bounds(b) { this.clear(); if (b) this.union(b); } var prototype = Bounds.prototype; prototype.clone = function() { return new Bounds(this); }; prototype.clear = function() { this.x1 = +Number.MAX_VALUE; this.y1 = +Number.MAX_VALUE; this.x2 = -Number.MAX_VALUE; this.y2 = -Number.MAX_VALUE; return this; }; prototype.empty = function() { return ( this.x1 === +Number.MAX_VALUE && this.y1 === +Number.MAX_VALUE && this.x2 === -Number.MAX_VALUE && this.y2 === -Number.MAX_VALUE ); }; prototype.equals = function(b) { return ( this.x1 === b.x1 && this.y1 === b.y1 && this.x2 === b.x2 && this.y2 === b.y2 ); }; prototype.set = function(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; }; prototype.add = function(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; }; prototype.expand = function(d) { this.x1 -= d; this.y1 -= d; this.x2 += d; this.y2 += d; return this; }; prototype.round = function() { 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; }; prototype.translate = function(dx, dy) { this.x1 += dx; this.x2 += dx; this.y1 += dy; this.y2 += dy; return this; }; prototype.rotate = function(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]); }; prototype.rotatedPoints = function(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; 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 ]; }; prototype.union = function(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; }; prototype.intersect = function(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; }; prototype.encloses = function(b) { return b && ( this.x1 <= b.x1 && this.x2 >= b.x2 && this.y1 <= b.y1 && this.y2 >= b.y2 ); }; prototype.alignsWith = function(b) { return b && ( this.x1 == b.x1 || this.x2 == b.x2 || this.y1 == b.y1 || this.y2 == b.y2 ); }; prototype.intersects = function(b) { return b && !( this.x2 < b.x1 || this.x1 > b.x2 || this.y2 < b.y1 || this.y1 > b.y2 ); }; prototype.contains = function(x, y) { return !( x < this.x1 || x > this.x2 || y < this.y1 || y > this.y2 ); }; prototype.width = function() { return this.x2 - this.x1; }; prototype.height = function() { return this.y2 - this.y1; }; var gradient_id = 0; const patternPrefix = 'p_'; function isGradient(value) { return value && value.gradient; } function gradientRef(g, defs, base) { let id = g.id, type = g.gradient, 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(g.x1, 0.5); g.y1 = get(g.y1, 0.5); g.r1 = get(g.r1, 0); g.x2 = get(g.x2, 0.5); g.y2 = get(g.y2, 0.5); g.r2 = get(g.r2, 0.5); prefix = patternPrefix; } else { g.x1 = get(g.x1, 0); g.y1 = get(g.y1, 0); g.x2 = get(g.x2, 1); g.y2 = get(g.y2, 0); } } // register definition defs[id] = g; // return url reference return 'url(' + (base || '') + '#' + prefix + id + ')'; } function get(val, def) { return val != null ? val : def; } function Gradient(p0, p1) { var stops = [], gradient; return gradient = { gradient: 'linear', x1: p0 ? p0[0] : 0, y1: p0 ? p0[1] : 0, x2: p1 ? p1[0] : 1, y2: p1 ? p1[1] : 0, stops: stops, stop: function(offset, color) { stops.push({offset: offset, color: color}); return gradient; } }; } function Item(mark) { this.mark = mark; this.bounds = (this.bounds || new Bounds()); } function GroupItem(mark) { Item.call(this, mark); this.items = (this.items || []); } vegaUtil.inherits(GroupItem, Item); function ResourceLoader(customLoader) { this._pending = 0; this._loader = customLoader || vegaLoader.loader(); } var prototype$1 = ResourceLoader.prototype; prototype$1.pending = function() { return this._pending; }; function increment(loader) { loader._pending += 1; } function decrement(loader) { loader._pending -= 1; } prototype$1.sanitizeURL = function(uri) { var loader = this; increment(loader); return loader._loader.sanitize(uri, {context:'href'}) .then(function(opt) { decrement(loader); return opt; }) .catch(function() { decrement(loader); return null; }); }; prototype$1.loadImage = function(uri) { var loader = this, Image = vegaCanvas.image(); increment(loader); return loader._loader .sanitize(uri, {context: 'image'}) .then(function(opt) { var url = opt.href; if (!url || !Image) throw {url: url}; var img = new Image(); img.onload = function() { decrement(loader); }; img.onerror = function() { decrement(loader); }; img.src = url; return img; }) .catch(function(e) { decrement(loader); return {complete: false, width: 0, height: 0, src: e && e.url || ''}; }); }; prototype$1.ready = function() { var loader = this; return new Promise(function(accept) { function poll(value) { if (!loader.pending()) accept(value); else setTimeout(function() { poll(true); }, 10); } poll(false); }); }; var lookup = { 'basis': { curve: d3Shape.curveBasis }, 'basis-closed': { curve: d3Shape.curveBasisClosed }, 'basis-open': { curve: d3Shape.curveBasisOpen }, 'bundle': { curve: d3Shape.curveBundle, tension: 'beta', value: 0.85 }, 'cardinal': { curve: d3Shape.curveCardinal, tension: 'tension', value: 0 }, 'cardinal-open': { curve: d3Shape.curveCardinalOpen, tension: 'tension', value: 0 }, 'cardinal-closed': { curve: d3Shape.curveCardinalClosed, tension: 'tension', value: 0 }, 'catmull-rom': { curve: d3Shape.curveCatmullRom, tension: 'alpha', value: 0.5 }, 'catmull-rom-closed': { curve: d3Shape.curveCatmullRomClosed, tension: 'alpha', value: 0.5 }, 'catmull-rom-open': { curve: d3Shape.curveCatmullRomOpen, tension: 'alpha', value: 0.5 }, 'linear': { curve: d3Shape.curveLinear }, 'linear-closed': { curve: d3Shape.curveLinearClosed }, 'monotone': { horizontal: d3Shape.curveMonotoneY, vertical: d3Shape.curveMonotoneX }, 'natural': { curve: d3Shape.curveNatural }, 'step': { curve: d3Shape.curveStep }, 'step-after': { curve: d3Shape.curveStepAfter }, 'step-before': { curve: d3Shape.curveStepBefore } }; function curves(type, orientation, tension) { var entry = vegaUtil.hasOwnProperty(lookup, type) && lookup[type], curve = null; if (entry) { curve = entry.curve || entry[orientation || 'vertical']; if (entry.tension && tension != null) { curve = curve[entry.tension](tension); } } return curve; } // Path parsing and rendering code adapted from fabric.js -- Thanks! var cmdlen = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 }, regexp = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)([-+])/g, /\s|,|###/]; function pathParse(pathstr) { var result = [], path, curr, chunks, parsed, param, cmd, len, i, j, n, m; // First, break path into command sequence path = pathstr .slice() .replace(regexp[0], '###$1') .split(regexp[1]) .slice(1); // Next, parse each command in turn for (i=0, n=path.length; i len) { for (j=1, m=parsed.length; j 1) { pl = Math.sqrt(pl); rx *= pl; ry *= pl; } var a00 = cos_th / rx; var a01 = sin_th / rx; var a10 = (-sin_th) / ry; var a11 = (cos_th) / ry; var x0 = a00 * ox + a01 * oy; var y0 = a10 * ox + a11 * oy; var x1 = a00 * x + a01 * y; var y1 = a10 * x + a11 * y; var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0); var sfactor_sq = 1 / d - 0.25; if (sfactor_sq < 0) sfactor_sq = 0; var sfactor = Math.sqrt(sfactor_sq); if (sweep == large) sfactor = -sfactor; var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0); var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0); var th0 = Math.atan2(y0-yc, x0-xc); var th1 = Math.atan2(y1-yc, x1-xc); var th_arc = th1-th0; if (th_arc < 0 && sweep === 1) { th_arc += Tau; } else if (th_arc > 0 && sweep === 0) { th_arc -= Tau; } var segs = Math.ceil(Math.abs(th_arc / (HalfPi + 0.001))); var result = []; for (var i=0; i +_; } 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(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); if (!context) context = buffer = d3Path.path(); if (tl <= 0 && tr <= 0 && bl <= 0 && br <= 0) { context.rect(x1, y1, w, h); } else { var x2 = x1 + w, 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); context.lineTo(x1 + bl, y2); 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(_); return rectangle; } else { return x; } }; rectangle.y = function(_) { if (arguments.length) { y = number(_); return rectangle; } else { return y; } }; rectangle.width = function(_) { if (arguments.length) { width = number(_); return rectangle; } else { return width; } }; rectangle.height = function(_) { if (arguments.length) { height = number(_); return rectangle; } else { return height; } }; rectangle.cornerRadius = function(tl, tr, br, bl) { if (arguments.length) { crTL = number(tl); crTR = tr != null ? number(tr) : crTL; crBR = br != null ? number(br) : crTL; crBL = bl != null ? number(bl) : crTR; 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; function point(x2, y2, w2) { var r2 = w2 / 2; if (ready) { var ux = y1 - y2, 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 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; if (context == null) context = buffer = d3Path.path(); 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(a, b) { return a != null ? a : b; } const x = item => item.x || 0, y = 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(item.cornerRadiusTopLeft, item.cornerRadius) || 0, tr = item => value(item.cornerRadiusTopRight, item.cornerRadius) || 0, br = item => value(item.cornerRadiusBottomRight, item.cornerRadius) || 0, bl = item => value(item.cornerRadiusBottomLeft, item.cornerRadius) || 0, sz = item => value(item.size, 64), ts = item => item.size || 1, def = item => !(item.defined === false), type = item => symbols(item.shape || 'circle'); const arcShape = d3Shape.arc().startAngle(sa).endAngle(ea).padAngle(pa) .innerRadius(ir).outerRadius(or).cornerRadius(cr), areavShape = d3Shape.area().x(x).y1(y).y0(yh).defined(def), areahShape = d3Shape.area().y(y).x1(x).x0(xw).defined(def), lineShape = d3Shape.line().x(x).y(y).defined(def), rectShape = vg_rect().x(x).y(y).width(w).height(h) .cornerRadius(tl, tr, br, bl), symbolShape = d3Shape.symbol().type(type).size(sz), trailShape = vg_trail().x(x).y(y).defined(def).size(ts); function hasCornerRadius(item) { return item.cornerRadius || item.cornerRadiusTopLeft || item.cornerRadiusTopRight || item.cornerRadiusBottomRight || item.cornerRadiusBottomLeft; } function arc(context, item) { return arcShape.context(context)(item); } function area(context, items) { var item = items[0], interp = item.interpolate || 'linear'; return (item.orient === 'horizontal' ? areahShape : areavShape) .curve(curves(interp, item.orient, item.tension)) .context(context)(items); } function line(context, items) { var item = items[0], 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(context, item) { return (item.mark.shape || item.shape) .context(context)(item); } function symbol(context, item) { return symbolShape.context(context)(item); } function trail(context, items) { return trailShape.context(context)(items); } 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; } var bounds, lx, ly, circleThreshold = Tau - 1e-8; function context(_) { bounds = _; return context; } function noop() {} function add(x, y) { bounds.add(x, y); } function addL(x, y) { add(lx = x, ly = y); } function addX(x) { add(x, bounds.y1); } function addY(y) { add(bounds.x1, y); } context.beginPath = noop; context.closePath = noop; context.moveTo = addL; context.lineTo = addL; context.rect = function(x, y, w, h) { add(x + w, y + h); addL(x, y); }; context.quadraticCurveTo = function(x1, y1, x2, y2) { quadExtrema(lx, x1, x2, addX); quadExtrema(ly, y1, y2, addY); addL(x2, y2); }; function quadExtrema(x0, x1, x2, cb) { const t = (x0 - x1) / (x0 + x2 - 2 * x1); if (0 < t && t < 1) cb(x0 + (x1 - x0) * t); } context.bezierCurveTo = function(x1, y1, x2, y2, x3, y3) { cubicExtrema(lx, x1, x2, x3, addX); cubicExtrema(ly, y1, y2, y3, addY); addL(x3, y3); }; function cubicExtrema(x0, x1, x2, x3, cb) { const a = x3 - x0 + 3 * x1 - 3 * x2, b = x0 + x2 - 2 * x1, c = x0 - x1; let t0 = 0, 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 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; return (s2 * s * x0) + (3 * s2 * t * x1) + (3 * s * t2 * x2) + (t2 * t * x3); } context.arc = function(cx, cy, r, sa, ea, ccw) { // 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(cx - r, cy - r); add(cx + r, cy + r); } else { const update = a => add(r * Math.cos(a) + cx, r * Math.sin(a) + cy); let s, i; // sample end points update(sa); 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 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 gradient(context, gradient, bounds) { const w = bounds.width(), h = bounds.height(), stop = gradient.stops, n = stop.length; const canvasGradient = gradient.gradient === 'radial' ? context.createRadialGradient( bounds.x1 + v(gradient.x1, 0.5) * w, bounds.y1 + v(gradient.y1, 0.5) * h, Math.max(w, h) * v(gradient.r1, 0), bounds.x1 + v(gradient.x2, 0.5) * w, bounds.y1 + v(gradient.y2, 0.5) * h, Math.max(w, h) * v(gradient.r2, 0.5) ) : context.createLinearGradient( bounds.x1 + v(gradient.x1, 0) * w, bounds.y1 + v(gradient.y1, 0) * h, bounds.x1 + v(gradient.x2, 1) * w, bounds.y1 + v(gradient.y2, 0) * h ); for (let i=0; i 0) { context.globalAlpha = opacity; context.fillStyle = color(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(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; for (i=0, n=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, function(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; if (item.fill && fill(context, item, opacity)) { context.fill(); } if (item.stroke && stroke(context, item, opacity)) { context.stroke(); } } function pick(test) { test = test || vegaUtil.truthy; return function(context, scene, x, y, gx, gy) { x *= context.pixelRatio; y *= context.pixelRatio; return pickVisit(scene, function(item) { var 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; 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(hitPath(path)); } function translate(x, y) { return 'translate(' + x + ',' + y + ')'; } function rotate(a) { return 'rotate(' + a + ')'; } function scale$1(scaleX, scaleY){ return 'scale('+ scaleX + ',' + scaleY+')'; } function translateItem(item) { return translate(item.x || 0, item.y || 0); } function transformItem(item) { return translate(item.x || 0, item.y || 0) + (item.angle ? ' ' + rotate(item.angle) : '') + (item.scaleX || item.scaleY ? ' ' + scale$1(item.scaleX || 1, item.scaleY || 1) : ''); } function markItemPath(type, shape, isect) { function attr(emit, item) { emit('transform', transformItem(item)); emit('d', shape(null, item)); } function bound(bounds, item) { var x = item.x || 0, y = item.y || 0; shape(context(bounds), item); boundStroke(bounds, item).translate(x, y); if (item.angle) { bounds.rotate(item.angle * DegToRad, x, y); } return bounds; } function draw(context, item) { var x = item.x || 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, bound: bound, draw: drawAll(draw), pick: pickPath(draw), isect: isect || intersectPath(draw) }; } var arc$1 = markItemPath('arc', arc); 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; 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; 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; 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(context(bounds), items); return boundStroke(bounds, items[0]); } } function draw(context, items) { context.beginPath(); shape(context, items); } var hit = hitPath(draw); function pick(context, scene, x, y, gx, gy) { var items = scene.items, 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, bound: bound, draw: drawOne(draw), pick: pick, isect: intersectPoint, tip: tip }; } var area$1 = markMultiItemPath('area', area, pickArea); function clip(context, scene) { var clip = scene.clip; context.save(); if (vegaUtil.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(); } var clip_id = 1; function resetSVGClipId() { clip_id = 1; } function clip$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}); if (vegaUtil.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 offset(item) { var sw = (sw = item.strokeWidth) != null ? sw : 1; return item.strokeOffset != null ? item.strokeOffset : item.stroke && sw > 0.5 && sw < 1.5 ? 0.5 - Math.abs(sw - 1) : 0; } function attr(emit, item) { emit('transform', translateItem(item)); } function emitRectangle(emit, item) { var off = offset(item); emit('d', rectangle(null, item, off, off)); } function background(emit, item) { emit('class', 'background'); emitRectangle(emit, item); } function foreground(emit, item) { emit('class', 'foreground'); if (item.strokeForeground) { emitRectangle(emit, item); } else { emit('d', ''); } } function content(emit, item, renderer) { var url = item.clip ? clip$1(renderer, item, item) : null; emit('clip-path', url); } function bound(bounds, group) { if (!group.clip && group.items) { var items = group.items; for (var j=0, m=items.length; j dw || gy < dx || gy > dh)) return; // adjust coordinate system context.save(); context.translate(dx, dy); dx = gx - dx; dy = gy - dy; // test background for rounded corner clip if (c && hasCornerRadius(group) && !hitBackground(context, group, cx, cy)) { context.restore(); return null; } fore = group.strokeForeground; 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 = pickVisit(group, function(mark) { return pickMark(mark, dx, dy) ? handler.pick(mark, x, y, dx, dy) : null; }); // hit test against group background if (!hit && ix && (group.fill || (!fore && group.stroke)) && hitBackground(context, group, cx, cy)) { hit = group; } // 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, bound: bound, draw: draw, pick: pick$1, isect: intersectRect, content: content, background: background, foreground: foreground }; function getImage(item, renderer) { var image = item.image; if (!image || item.url && item.url !== image.url) { image = {complete: false, width: 0, height: 0}; 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$1(emit, item, renderer) { var image = getImage(item, renderer), x = item.x || 0, y = item.y || 0, w = imageWidth(item, image), h = imageHeight(item, image), a = item.aspect === false ? 'none' : 'xMidYMid'; x -= imageXOffset(item.align, w); y -= imageYOffset(item.baseline, h); if (!image.src && image.toDataURL) { emit('href', image.toDataURL(), 'http://www.w3.org/1999/xlink', 'xlink:href'); } else { emit('href', image.src || '', 'http://www.w3.org/1999/xlink', 'xlink:href'); } emit('transform', translate(x, y)); emit('width', w); emit('height', h); emit('preserveAspectRatio', a); } function bound$1(bounds, item) { var image = item.image, x = item.x || 0, y = item.y || 0, w = imageWidth(item, image), h = imageHeight(item, image); x -= imageXOffset(item.align, w); y -= imageYOffset(item.baseline, h); return bounds.set(x, y, x + w, y + h); } function draw$1(context, scene, bounds) { var renderer = this; visit(scene, function(item) { if (bounds && !bounds.intersects(item.bounds)) return; // bounds check var image = getImage(item, renderer), x = item.x || 0, y = item.y || 0, w = imageWidth(item, image), h = imageHeight(item, image), opacity, ar0, ar1, t; x -= imageXOffset(item.align, w); y -= imageYOffset(item.baseline, h); if (item.aspect !== false) { ar0 = image.width / image.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; } else { t = h * ar0; x += (w - t) / 2; w = t; } } } if (image.complete || image.toDataURL) { context.globalAlpha = (opacity = item.opacity) != null ? opacity : 1; context.imageSmoothingEnabled = item.smooth !== false; context.drawImage(image, x, y, w, h); } }); } var image = { type: 'image', tag: 'image', nested: false, attr: attr$1, bound: bound$1, draw: draw$1, pick: pick(), isect: vegaUtil.truthy, // bounds check is sufficient get: getImage, xOffset: imageXOffset, yOffset: imageYOffset }; var line$1 = markMultiItemPath('line', line, pickLine); function attr$2(emit, item) { var sx = item.scaleX || 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(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; if (!cache || cache.path !== path) { (item.pathCache = cache = pathParse(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$2(bounds, item) { path(context(bounds), item) ? bounds.set(0, 0, 0, 0) : boundStroke(bounds, item, true); if (item.angle) { bounds.rotate(item.angle * DegToRad, item.x || 0, item.y || 0); } return bounds; } var path$1 = { type: 'path', tag: 'path', nested: false, attr: attr$2, bound: bound$2, draw: drawAll(path), pick: pickPath(path), isect: intersectPath(path) }; function attr$3(emit, item) { emit('d', rectangle(null, item)); } function bound$3(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$3, bound: bound$3, draw: drawAll(draw$2), pick: pickPath(draw$2), isect: intersectRect }; function attr$4(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$4(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$2(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$3(context, scene, bounds) { visit(scene, function(item) { if (bounds && !bounds.intersects(item.bounds)) return; // bounds check var opacity = item.opacity == null ? 1 : item.opacity; if (opacity && path$2(context, item, opacity)) { context.stroke(); } }); } function hit(context, item, x, y) { if (!context.isPointInStroke) return false; return path$2(context, item, 1) && context.isPointInStroke(x, y); } var rule = { type: 'rule', tag: 'line', nested: false, attr: attr$4, bound: bound$4, draw: draw$3, pick: pick(hit), isect: intersectRule }; var shape$1 = markItemPath('shape', shape); var symbol$1 = markItemPath('symbol', symbol, intersectPoint); var currFontHeight; var textMetrics = { height: fontSize, measureWidth: measureWidth, estimateWidth: estimateWidth, width: estimateWidth, canvas: useCanvas }; useCanvas(true); // make dumb, simple estimate if no canvas is available function estimateWidth(item, text) { currFontHeight = fontSize(item); return estimate(textValue(item, text)); } function estimate(text) { return ~~(0.8 * text.length * currFontHeight); } // measure text width if canvas is available function measureWidth(item, text) { return fontSize(item) <= 0 ? 0 : (context$1.font = font(item), measure(textValue(item, text))); } function measure(text) { return context$1.measureText(text).width; } function fontSize(item) { return item.fontSize != null ? item.fontSize : 11; } function useCanvas(use) { textMetrics.width = (use && context$1) ? measureWidth : estimateWidth; } function lineHeight(item) { return item.lineHeight != null ? item.lineHeight : (fontSize(item) + 2); } function lineArray(_) { return vegaUtil.isArray(_) ? _.length > 1 ? _ : _[0] : _; } function textLines(item) { return lineArray( item.lineBreak && item.text && !vegaUtil.isArray(item.text) ? item.text.split(item.lineBreak) : item.text ); } function multiLineOffset(item) { const tl = textLines(item); return (vegaUtil.isArray(tl) ? (tl.length - 1) : 0) * lineHeight(item); } function textValue(item, line) { return line == null ? '' : item.limit > 0 ? truncate(item, line) : line + ''; } function truncate(item, line) { var limit = +item.limit, text = line + '', width; if (textMetrics.width === measureWidth) { // we are using canvas context$1.font = font(item); width = measure; } else { // we are relying on estimates currFontHeight = fontSize(item); width = estimate; } if (width(text) < limit) return text; var ellipsis = item.ellipsis || '\u2026', 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$1(item) { // perform our own font baseline calculation // why? not all browsers support SVG 1.1 'alignment-baseline' :( var baseline = item.baseline, h = fontSize(item); return Math.round( baseline === 'top' ? 0.79*h : baseline === 'middle' ? 0.30*h : baseline === 'bottom' ? -0.21*h : 0 ); } var textAlign = { 'left': 'start', 'center': 'middle', 'right': 'end' }; var tempBounds = new Bounds(); function anchorPoint(item) { var x = item.x || 0, 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.x1 = x; tempBounds.y1 = y; return tempBounds; } function attr$5(emit, item) { var dx = item.dx || 0, dy = (item.dy || 0) + offset$1(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(x, y) + ' ' + rotate(a); if (dx || dy) t += ' ' + translate(dx, dy); } else { t = translate(x + dx, y + dy); } emit('transform', t); } function bound$5(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$1(item) - Math.round(0.8*h), // use 4/5 offset tl = textLines(item), w; // get dimensions if (vegaUtil.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 if (a === 'center') { dx -= (w / 2); } else if (a === 'right') { dx -= w; } 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$4(context, scene, bounds) { visit(scene, function(item) { var opacity = item.opacity == null ? 1 : item.opacity, 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$1(item); tl = textLines(item); if (vegaUtil.isArray(tl)) { lh = lineHeight(item); for (i=0; i index) el.removeChild(nodes[--curr]); return el; } // 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) { var 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; 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 */ function Handler(customLoader, customTooltip) { this._active = null; this._handlers = {}; this._loader = customLoader || vegaLoader.loader(); this._tooltip = customTooltip || defaultTooltip; } // The default tooltip display handler. // Sets the HTML title attribute on the visualization container. function defaultTooltip(handler, event, item, value) { handler.element().setAttribute('title', value || ''); } var prototype$3 = Handler.prototype; /** * Initialize a new Handler instance. * @param {DOMElement} el - The containing DOM element for the display. * @param {Array} origin - The origin of the display, in pixels. * The coordinate system will be translated to this point. * @param {object} [obj] - Optional context object that should serve as * the "this" context for event callbacks. * @return {Handler} - This handler instance. */ prototype$3.initialize = function(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. */ prototype$3.element = function() { 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. */ prototype$3.canvas = function() { return this._el && this._el.firstChild; }; /** * Get / set the origin coordinates of the visualization. */ prototype$3.origin = function(origin) { if (arguments.length) { this._origin = origin || [0, 0]; return this; } else { return this._origin.slice(); } }; /** * Get / set the scenegraph root. */ prototype$3.scene = function(scene) { if (!arguments.length) return this._scene; this._scene = scene; return this; }; /** * Add an event handler. Subclasses should override this method. */ prototype$3.on = function(/*type, handler*/) {}; /** * Remove an event handler. Subclasses should override this method. */ prototype$3.off = function(/*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. * @param {function} handler - The event handler instance to find. * @return {number} - The handler's array index or -1 if not registered. */ prototype$3._handlerIndex = function(h, type, handler) { for (var 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 * be ignored and the method returns all "click" handlers. If type is * null or unspecified, this method returns handlers for all types. * @return {Array} - A new array containing all registered event handlers. */ prototype$3.handlers = function(type) { var h = this._handlers, a = [], k; if (type) { a.push.apply(a, h[this.eventName(type)]); } else { for (k in h) { a.push.apply(a, 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. */ prototype$3.eventName = function(name) { var 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. * @param {string} href - The URL to navigate to. */ prototype$3.handleHref = function(event, item, href) { this._loader .sanitize(href, {context:'href'}) .then(function(opt) { var e = new MouseEvent(event.type, event), a = domCreate(null, 'a'); for (var name in opt) a.setAttribute(name, opt[name]); a.dispatchEvent(e); }) .catch(function() { /* do nothing */ }); }; /** * Handle tooltip display in response to an item.tooltip value. * @param {Event} event - The event triggering tooltip display. * @param {Item} item - The scenegraph item. * @param {boolean} show - A boolean flag indicating whether * to show or hide a tooltip for the given item. */ prototype$3.handleTooltip = function(event, item, show) { if (item && item.tooltip != null) { item = resolveItem(item, event, this.canvas(), this._origin); var 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. * @return {object} - A bounding box object (compatible with the * DOMRect type) consisting of x, y, width, heigh, top, left, * right, and bottom properties. */ prototype$3.getItemBoundingClientRect = function(item) { if (!(el = this.canvas())) return; var el, rect = el.getBoundingClientRect(), origin = this._origin, itemBounds = item.bounds, x = itemBounds.x1 + origin[0] + rect.left, y = itemBounds.y1 + origin[1] + rect.top, w = itemBounds.width(), h = itemBounds.height(); // 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 { x: x, y: y, width: w, height: h, left: x, top: y, right: x + w, bottom: y + h }; }; /** * 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); } var prototype$4 = 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. * @param {Array} 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 {Renderer} - This renderer instance. */ prototype$4.initialize = function(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. */ prototype$4.element = function() { 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. */ prototype$4.canvas = function() { return this._el && this._el.firstChild; }; /** * Get / set the background color. */ prototype$4.background = function(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. * @param {Array} 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 {Renderer} - This renderer instance; */ prototype$4.resize = function(width, height, origin, scaleFactor) { 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. */ prototype$4.dirty = function(/*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. * @return {Renderer} - This renderer instance. */ prototype$4.render = function(scene) { var 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._call(); // 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. */ prototype$4._render = function(/*scene*/) { // 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. * @return {Promise} - A Promise that resolves when rendering is complete. */ prototype$4.renderAsync = function(scene) { var r = this.render(scene); return this._ready ? this._ready.then(function() { return 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. * @param {string} method - The method name to invoke on the ImageLoader. * @param {string} uri - The URI for the requested resource. * @return {Promise} - A Promise that resolves to the requested resource. */ prototype$4._load = function(method, uri) { var r = this, p = r._loader[method](uri); if (!r._ready) { // re-render the scene when loading completes var call = r._call; r._ready = r._loader.ready() .then(function(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. */ prototype$4.sanitizeURL = function(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. * @param {string} uri - The URI string of the image. * @return {Promise} - A Promise that resolves to the loaded Image. */ prototype$4.loadImage = function(uri) { return this._load('loadImage', uri); }; var Events = [ 'keydown', 'keypress', 'keyup', 'dragenter', 'dragleave', 'dragover', 'mousedown', 'mouseup', 'mousemove', 'mouseout', 'mouseover', 'click', 'dblclick', 'wheel', 'mousewheel', 'touchstart', 'touchmove', 'touchend' ]; var TooltipShowEvent = 'mousemove'; var TooltipHideEvent = 'mouseout'; var HrefEvent = 'click'; function CanvasHandler(loader, tooltip) { Handler.call(this, loader, tooltip); this._down = null; this._touch = null; this._first = true; } var prototype$5 = vegaUtil.inherits(CanvasHandler, Handler); prototype$5.initialize = function(el, origin, obj) { // add event listeners var canvas = this._canvas = el && domFind(el, 'canvas'); if (canvas) { var that = this; this.events.forEach(function(type) { canvas.addEventListener(type, function(evt) { if (prototype$5[type]) { prototype$5[type].call(that, evt); } else { that.fire(type, evt); } }); }); } return Handler.prototype.initialize.call(this, el, origin, obj); }; // return the backing canvas instance prototype$5.canvas = function() { return this._canvas; }; // retrieve the current canvas context prototype$5.context = function() { return this._canvas.getContext('2d'); }; // supported events prototype$5.events = Events; // to keep old versions of firefox happy prototype$5.DOMMouseScroll = function(evt) { this.fire('mousewheel', evt); }; function move(moveEvent, overEvent, outEvent) { return function(evt) { var 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; }; } prototype$5.mousemove = move('mousemove', 'mouseover', 'mouseout'); prototype$5.dragover = move('dragover', 'dragenter', 'dragleave'); prototype$5.mouseout = inactive('mouseout'); prototype$5.dragleave = inactive('dragleave'); prototype$5.mousedown = function(evt) { this._down = this._active; this.fire('mousedown', evt); }; prototype$5.click = function(evt) { if (this._down === this._active) { this.fire('click', evt); this._down = null; } }; prototype$5.touchstart = function(evt) { this._touch = this.pickEvent(evt.changedTouches[0]); if (this._first) { this._active = this._touch; this._first = false; } this.fire('touchstart', evt, true); }; prototype$5.touchmove = function(evt) { this.fire('touchmove', evt, true); }; prototype$5.touchend = function(evt) { this.fire('touchend', evt, true); this._touch = null; }; // fire an event prototype$5.fire = function(type, evt, touch) { var a = touch ? this._touch : this._active, h = this._handlers[type], i, len; // 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 if (h) { for (i=0, len=h.length; i= 0) { h.splice(i, 1); } return this; }; prototype$5.pickEvent = function(evt) { var p = point(evt, this._canvas), 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 // gx, gy -- the relative coordinates within the current group prototype$5.pick = function(scene, x, y, gx, gy) { var g = this.context(), mark = Marks[scene.marktype]; return mark.pick.call(this, g, scene, x, y, gx, gy); }; function devicePixelRatio() { return typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1; } var pixelRatio = devicePixelRatio(); function resize(canvas, width, height, origin, scaleFactor, opt) { var inDOM = typeof HTMLElement !== 'undefined' && canvas instanceof HTMLElement && canvas.parentNode != null; var context = canvas.getContext('2d'), ratio = inDOM ? pixelRatio : scaleFactor, key; canvas.width = width * ratio; canvas.height = height * ratio; for (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._redraw = false; this._dirty = new Bounds(); } var prototype$6 = vegaUtil.inherits(CanvasRenderer, Renderer), base = Renderer.prototype, tempBounds$1 = new Bounds(); prototype$6.initialize = function(el, width, height, origin, scaleFactor, options) { this._options = options; this._canvas = vegaCanvas.canvas(1, 1, options && options.type); // instantiate a small canvas if (el) { domClear(el, 0).appendChild(this._canvas); this._canvas.setAttribute('class', 'marks'); } // this method will invoke resize to size the canvas appropriately return base.initialize.call(this, el, width, height, origin, scaleFactor); }; prototype$6.resize = function(width, height, origin, scaleFactor) { base.resize.call(this, width, height, origin, scaleFactor); resize(this._canvas, this._width, this._height, this._origin, this._scale, this._options && this._options.context); this._redraw = true; return this; }; prototype$6.canvas = function() { return this._canvas; }; prototype$6.context = function() { return this._canvas ? this._canvas.getContext('2d') : null; }; prototype$6.dirty = function(item) { var b = translate$1(item.bounds, item.mark.group); this._dirty.union(b); }; function clipToBounds(g, b, origin) { // expand bounds by 1 pixel, then round to pixel boundaries b.expand(1).round(); // to avoid artifacts translate if origin has fractional pixels b.translate(-(origin[0] % 1), -(origin[1] % 1)); // set clipping path g.beginPath(); g.rect(b.x1, b.y1, b.width(), b.height()); g.clip(); return b; } function viewBounds(origin, width, height) { return tempBounds$1 .set(0, 0, width, height) .translate(-origin[0], -origin[1]); } function translate$1(bounds, group) { if (group == null) return bounds; var b = tempBounds$1.clear().union(bounds); for (; group != null; group = group.mark.group) { b.translate(group.x || 0, group.y || 0); } return b; } prototype$6._render = function(scene) { var g = this.context(), o = this._origin, w = this._width, h = this._height, b = this._dirty; // setup g.save(); if (this._redraw || b.empty()) { this._redraw = false; b = viewBounds(o, w, h).expand(1); } else { b = clipToBounds(g, b.intersect(viewBounds(o, w, h)), o); } this.clear(-o[0], -o[1], w, h); // render this.draw(g, scene, b); // takedown g.restore(); this._dirty.clear(); return this; }; prototype$6.draw = function(ctx, scene, bounds) { var mark = Marks[scene.marktype]; if (scene.clip) clip(ctx, scene); mark.draw.call(this, ctx, scene, bounds); if (scene.clip) ctx.restore(); }; prototype$6.clear = function(x, y, w, h) { var g = this.context(); g.clearRect(x, y, w, h); if (this._bgcolor != null) { g.fillStyle = this._bgcolor; g.fillRect(x, y, w, h); } }; function SVGHandler(loader, tooltip) { Handler.call(this, loader, tooltip); var h = this; h._hrefHandler = listener(h, function(evt, item) { if (item && item.href) h.handleHref(evt, item, item.href); }); h._tooltipHandler = listener(h, function(evt, item) { h.handleTooltip(evt, item, evt.type !== TooltipHideEvent); }); } var prototype$7 = vegaUtil.inherits(SVGHandler, Handler); prototype$7.initialize = function(el, origin, obj) { var 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); }; prototype$7.canvas = function() { return this._svg; }; // wrap an event listener for the SVG DOM function listener(context, handler) { return function(evt) { var target = evt.target, item = target.__data__; evt.vegaType = evt.type; item = Array.isArray(item) ? item[0] : item; handler.call(context._obj, evt, item); }; } // add an event handler prototype$7.on = function(type, handler) { var name = this.eventName(type), h = this._handlers, i = this._handlerIndex(h[name], type, handler); if (i < 0) { var x = { type: type, handler: 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 prototype$7.off = function(type, handler) { var name = this.eventName(type), 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; }; // generate string for an opening xml tag // tag: the name of the xml tag // attr: hash of attribute name-value pairs to include // raw: additional raw string to include in tag markup function openTag(tag, attr, raw) { var s = '<' + tag, key, val; if (attr) { for (key in attr) { val = attr[key]; if (val != null) { s += ' ' + key + '="' + val + '"'; } } } if (raw) s += ' ' + raw; return s + '>'; } // generate string for closing xml tag // tag: the name of the xml tag function closeTag(tag) { return ''; } var metadata = { 'version': '1.1', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink' }; var styles = { 'fill': 'fill', 'fillOpacity': 'fill-opacity', 'stroke': 'stroke', 'strokeOpacity': 'stroke-opacity', 'strokeWidth': 'stroke-width', 'strokeCap': 'stroke-linecap', 'strokeJoin': 'stroke-linejoin', 'strokeDash': 'stroke-dasharray', 'strokeDashOffset': 'stroke-dashoffset', 'strokeMiterLimit': 'stroke-miterlimit', 'opacity': 'opacity' }; var styleProperties = Object.keys(styles); var ns = metadata.xmlns; function SVGRenderer(loader) { Renderer.call(this, loader); this._dirtyID = 0; this._dirty = []; this._svg = null; this._root = null; this._defs = null; } var prototype$8 = vegaUtil.inherits(SVGRenderer, Renderer); var base$1 = Renderer.prototype; prototype$8.initialize = function(el, width, height, padding) { if (el) { this._svg = domChild(el, 0, 'svg', ns); this._svg.setAttribute('class', 'marks'); domClear(el, 1); // set the svg root group this._root = domChild(this._svg, 0, 'g', ns); domClear(this._svg, 1); } // create the svg definitions cache this._defs = { gradient: {}, clipping: {} }; // set background color if defined this.background(this._bgcolor); return base$1.initialize.call(this, el, width, height, padding); }; prototype$8.background = function(bgcolor) { if (arguments.length && this._svg) { this._svg.style.setProperty('background-color', bgcolor); } return base$1.background.apply(this, arguments); }; prototype$8.resize = function(width, height, origin, scaleFactor) { base$1.resize.call(this, width, height, origin, scaleFactor); if (this._svg) { this._svg.setAttribute('width', this._width * this._scale); this._svg.setAttribute('height', this._height * this._scale); this._svg.setAttribute('viewBox', '0 0 ' + this._width + ' ' + this._height); this._root.setAttribute('transform', 'translate(' + this._origin + ')'); } this._dirty = []; return this; }; prototype$8.canvas = function() { return this._svg; }; prototype$8.svg = function() { if (!this._svg) return null; var attr = { class: 'marks', width: this._width * this._scale, height: this._height * this._scale, viewBox: '0 0 ' + this._width + ' ' + this._height }; for (var key in metadata) { attr[key] = metadata[key]; } var bg = !this._bgcolor ? '' : (openTag('rect', { width: this._width, height: this._height, style: 'fill: ' + this._bgcolor + ';' }) + closeTag('rect')); return openTag('svg', attr) + bg + this._svg.innerHTML + closeTag('svg'); }; // -- Render entry point -- prototype$8._render = function(scene) { // perform spot updates and re-render markup if (this._dirtyCheck()) { if (this._dirtyAll) this._resetDefs(); this.draw(this._root, scene); domClear(this._root, 1); } this.updateDefs(); this._dirty = []; ++this._dirtyID; return this; }; // -- Manage SVG definitions ('defs') block -- prototype$8.updateDefs = function() { var svg = this._svg, defs = this._defs, el = defs.el, index = 0, id; for (id in defs.gradient) { if (!el) defs.el = (el = domChild(svg, 0, 'defs', ns)); index = updateGradient(el, defs.gradient[id], index); } for (id in defs.clipping) { if (!el) defs.el = (el = domChild(svg, 0, 'defs', ns)); index = updateClipping(el, defs.clipping[id], index); } // clean-up if (el) { if (index === 0) { svg.removeChild(el); defs.el = null; } else { domClear(el, index); } } }; function updateGradient(el, grad, index) { var 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. // So we wrap the radial gradient in a pattern element, allowing us // to mantain a circular gradient that matches what canvas provides. var pt = domChild(el, index++, 'pattern', ns); pt.setAttribute('id', patternPrefix + grad.id); pt.setAttribute('viewBox', '0,0,1,1'); pt.setAttribute('width', '100%'); pt.setAttribute('height', '100%'); pt.setAttribute('preserveAspectRatio', 'xMidYMid slice'); pt = domChild(pt, 0, 'rect', ns); pt.setAttribute('width', '1'); pt.setAttribute('height', '1'); pt.setAttribute('fill', 'url(' + href() + '#' + grad.id + ')'); el = domChild(el, index++, 'radialGradient', ns); el.setAttribute('id', grad.id); el.setAttribute('fx', grad.x1); el.setAttribute('fy', grad.y1); el.setAttribute('fr', grad.r1); el.setAttribute('cx', grad.x2); el.setAttribute('cy', grad.y2); el.setAttribute( 'r', grad.r2); } else { el = domChild(el, index++, 'linearGradient', ns); el.setAttribute('id', grad.id); el.setAttribute('x1', grad.x1); el.setAttribute('x2', grad.x2); el.setAttribute('y1', grad.y1); el.setAttribute('y2', grad.y2); } for (i=0, n=grad.stops.length; i 1 && node.previousSibling != sibling; // treat null/undefined the same } // -- Set attributes & styles on SVG elements --- var element = null, // temp var for current SVG element values = null; // temp var for current values hash // Extra configuration for certain mark types var mark_extras = { group: function(mdef, el, item) { var fg, bg; element = fg = el.childNodes[2]; values = fg.__values__; mdef.foreground(emit, item, this); values = el.__values__; // use parent's values hash element = el.childNodes[1]; mdef.content(emit, item, this); element = bg = el.childNodes[0]; mdef.background(emit, item, this); var value = item.mark.interactive === false ? 'none' : null; if (value !== values.events) { fg.style.setProperty('pointer-events', value); bg.style.setProperty('pointer-events', value); values.events = value; } if (item.strokeForeground && item.stroke) { const fill = item.fill; fg.style.removeProperty('display'); // set style of background this.style(bg, item); bg.style.removeProperty('stroke'); // 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 element = null; } else { // ensure foreground is ignored fg.style.setProperty('display', 'none'); fg.style.setProperty('fill', 'none'); } }, image: function(mdef, el, item) { if (item.smooth === false) { setStyle(el, 'image-rendering', 'optimizeSpeed'); setStyle(el, 'image-rendering', 'pixelated'); } else { setStyle(el, 'image-rendering', null); } }, text: function(mdef, el, item) { var tl = textLines(item), key, value, doc, lh; if (vegaUtil.isArray(tl)) { // multi-line text value = tl.map(_ => textValue(item, _)); key = value.join('\n'); // content cache key if (key !== values.text) { domClear(el, 0); doc = el.ownerDocument; lh = lineHeight(item); value.forEach((t, i) => { const ts = domCreate(doc, 'tspan', ns); 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; } } setStyle(el, 'font-family', fontFamily(item)); setStyle(el, 'font-size', fontSize(item) + 'px'); setStyle(el, 'font-style', item.fontStyle); setStyle(el, 'font-variant', item.fontVariant); setStyle(el, 'font-weight', item.fontWeight); } }; 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; } } prototype$8._update = function(mdef, el, item) { // set dom element and values cache // provides access to emit method element = el; values = el.__values__; // apply svg attributes mdef.attr(emit, item, this); // some marks need special treatment var extra = mark_extras[mdef.type]; if (extra) extra.call(this, mdef, el, item); // apply svg css styles // note: element may be modified by 'extra' method if (element) this.style(element, item); }; function emit(name, value, ns) { // early exit if value is unchanged if (value === values[name]) return; if (value != null) { // if value is provided, update DOM attribute if (ns) { element.setAttributeNS(ns, name, value); } else { element.setAttribute(name, value); } } else { // else remove DOM attribute if (ns) { element.removeAttributeNS(ns, name); } else { element.removeAttribute(name); } } // note current value for future comparison values[name] = value; } prototype$8.style = function(el, o) { if (o == null) return; var i, n, prop, name, value; for (i=0, n=styleProperties.length; i 0) ? openTag('defs') + defs + closeTag('defs') : ''; }; var object; function emit$1(name, value, ns, prefixed) { object[prefixed || name] = value; } prototype$9.attributes = function(attr, item) { object = {}; attr(emit$1, item, this); return object; }; prototype$9.href = function(item) { var that = this, href = item.href, attr; if (href) { if (attr = that._hrefs && that._hrefs[href]) { return attr; } else { that.sanitizeURL(href).then(function(attr) { // rewrite to use xlink namespace // note that this will be deprecated in SVG 2.0 attr['xlink:href'] = attr.href; attr.href = null; (that._hrefs || (that._hrefs = {}))[href] = attr; }); } } return null; }; prototype$9.mark = function(scene) { var renderer = this, mdef = Marks[scene.marktype], tag = mdef.tag, defs = this._defs, str = '', style; if (tag !== 'g' && scene.interactive === false) { style = 'style="pointer-events: none;"'; } // render opening group tag str += openTag('g', { 'class': cssClass(scene), 'clip-path': scene.clip ? clip$1(renderer, scene, scene.group) : null }, style); // render contained elements function process(item) { var href = renderer.href(item); if (href) str += openTag('a', href); style = (tag !== 'g') ? applyStyles(item, scene, tag, defs) : null; str += openTag(tag, renderer.attributes(mdef.attr, item), style); if (tag === 'text') { const tl = textLines(item); if (vegaUtil.isArray(tl)) { // multi-line text const attrs = {x: 0, dy: lineHeight(item)}; for (let i=0; i/g, '>'); } var Canvas = 'canvas'; var PNG = 'png'; var SVG = 'svg'; var None = 'none'; var RenderType = { Canvas: Canvas, PNG: PNG, SVG: SVG, None: None }; var modules = {}; modules[Canvas] = modules[PNG] = { renderer: CanvasRenderer, headless: CanvasRenderer, handler: CanvasHandler }; modules[SVG] = { renderer: SVGRenderer, headless: SVGStringRenderer, handler: SVGHandler }; modules[None] = {}; function renderModule(name, _) { name = String(name || '').toLowerCase(); if (arguments.length > 1) { modules[name] = _; return this; } else { return modules[name]; } } function intersect(scene, bounds, filter) { const hits = [], // 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) : vegaUtil.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; let i = 0; if (type === 'group') { for (; 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; } exports.Bounds = Bounds; exports.CanvasHandler = CanvasHandler; exports.CanvasRenderer = CanvasRenderer; exports.Gradient = Gradient; exports.GroupItem = GroupItem; exports.Handler = Handler; exports.Item = Item; exports.Marks = Marks; exports.RenderType = RenderType; exports.Renderer = Renderer; exports.ResourceLoader = ResourceLoader; exports.SVGHandler = SVGHandler; exports.SVGRenderer = SVGRenderer; exports.SVGStringRenderer = SVGStringRenderer; exports.Scenegraph = Scenegraph; exports.boundClip = boundClip; exports.boundContext = context; exports.boundItem = boundItem; exports.boundMark = boundMark; exports.boundStroke = boundStroke; exports.closeTag = closeTag; exports.domChild = domChild; exports.domClear = domClear; exports.domCreate = domCreate; exports.domFind = domFind; exports.font = font; exports.fontFamily = fontFamily; exports.fontSize = fontSize; exports.intersect = intersect; exports.intersectBoxLine = intersectBoxLine; exports.intersectPath = intersectPath; exports.intersectPoint = intersectPoint; exports.intersectRule = intersectRule; exports.lineHeight = lineHeight; exports.multiLineOffset = multiLineOffset; exports.openTag = openTag; exports.pathCurves = curves; exports.pathEqual = pathEqual; exports.pathParse = pathParse; exports.pathRectangle = vg_rect; exports.pathRender = pathRender; exports.pathSymbols = symbols; exports.pathTrail = vg_trail; exports.point = point; exports.renderModule = renderModule; exports.resetSVGClipId = resetSVGClipId; exports.sceneEqual = sceneEqual; exports.sceneFromJSON = sceneFromJSON; exports.scenePickVisit = pickVisit; exports.sceneToJSON = sceneToJSON; exports.sceneVisit = visit; exports.sceneZOrder = zorder; exports.textMetrics = textMetrics; Object.defineProperty(exports, '__esModule', { value: true }); })));