You've already forked wakapi-readme-stats
Bar graph added.
This commit is contained in:
178
node_modules/vega-scenegraph/src/Bounds.js
generated
vendored
Normal file
178
node_modules/vega-scenegraph/src/Bounds.js
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
export default 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.scale = function(s) {
|
||||
this.x1 *= s;
|
||||
this.y1 *= s;
|
||||
this.x2 *= s;
|
||||
this.y2 *= s;
|
||||
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;
|
||||
};
|
||||
208
node_modules/vega-scenegraph/src/CanvasHandler.js
generated
vendored
Normal file
208
node_modules/vega-scenegraph/src/CanvasHandler.js
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
import Handler from './Handler';
|
||||
import Marks from './marks/index';
|
||||
import {
|
||||
ClickEvent, DragEnterEvent, DragLeaveEvent, DragOverEvent, Events,
|
||||
HrefEvent, MouseDownEvent, MouseMoveEvent, MouseOutEvent, MouseOverEvent,
|
||||
MouseWheelEvent, TooltipHideEvent, TooltipShowEvent,
|
||||
TouchEndEvent, TouchMoveEvent, TouchStartEvent
|
||||
} from './util/events';
|
||||
import point from './util/point';
|
||||
import {domFind} from './util/dom';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
export default function CanvasHandler(loader, tooltip) {
|
||||
Handler.call(this, loader, tooltip);
|
||||
this._down = null;
|
||||
this._touch = null;
|
||||
this._first = true;
|
||||
this._events = {};
|
||||
}
|
||||
|
||||
const prototype = inherits(CanvasHandler, Handler);
|
||||
|
||||
prototype.initialize = function(el, origin, obj) {
|
||||
this._canvas = el && domFind(el, 'canvas');
|
||||
|
||||
// add minimal events required for proper state management
|
||||
[ClickEvent, MouseDownEvent, MouseMoveEvent, MouseOutEvent, DragLeaveEvent]
|
||||
.forEach(type => eventListenerCheck(this, type));
|
||||
|
||||
return Handler.prototype.initialize.call(this, el, origin, obj);
|
||||
};
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// return the backing canvas instance
|
||||
prototype.canvas = function() {
|
||||
return this._canvas;
|
||||
};
|
||||
|
||||
// retrieve the current canvas context
|
||||
prototype.context = function() {
|
||||
return this._canvas.getContext('2d');
|
||||
};
|
||||
|
||||
// supported events
|
||||
prototype.events = Events;
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
// to keep old versions of firefox happy
|
||||
prototype.DOMMouseScroll = function(evt) {
|
||||
this.fire(MouseWheelEvent, evt);
|
||||
};
|
||||
|
||||
prototype.mousemove = move(MouseMoveEvent, MouseOverEvent, MouseOutEvent);
|
||||
prototype.dragover = move(DragOverEvent, DragEnterEvent, DragLeaveEvent);
|
||||
|
||||
prototype.mouseout = inactive(MouseOutEvent);
|
||||
prototype.dragleave = inactive(DragLeaveEvent);
|
||||
|
||||
prototype.mousedown = function(evt) {
|
||||
this._down = this._active;
|
||||
this.fire(MouseDownEvent, evt);
|
||||
};
|
||||
|
||||
prototype.click = function(evt) {
|
||||
if (this._down === this._active) {
|
||||
this.fire(ClickEvent, evt);
|
||||
this._down = null;
|
||||
}
|
||||
};
|
||||
|
||||
prototype.touchstart = function(evt) {
|
||||
this._touch = this.pickEvent(evt.changedTouches[0]);
|
||||
|
||||
if (this._first) {
|
||||
this._active = this._touch;
|
||||
this._first = false;
|
||||
}
|
||||
|
||||
this.fire(TouchStartEvent, evt, true);
|
||||
};
|
||||
|
||||
prototype.touchmove = function(evt) {
|
||||
this.fire(TouchMoveEvent, evt, true);
|
||||
};
|
||||
|
||||
prototype.touchend = function(evt) {
|
||||
this.fire(TouchEndEvent, evt, true);
|
||||
this._touch = null;
|
||||
};
|
||||
|
||||
// fire an event
|
||||
prototype.fire = function(type, evt, touch) {
|
||||
const a = touch ? this._touch : this._active,
|
||||
h = this._handlers[type];
|
||||
|
||||
// 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 (let i=0, len=h.length; i<len; ++i) {
|
||||
h[i].handler.call(this._obj, evt, a);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// add an event handler
|
||||
prototype.on = function(type, handler) {
|
||||
const name = this.eventName(type),
|
||||
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
|
||||
prototype.off = function(type, handler) {
|
||||
const name = this.eventName(type),
|
||||
h = this._handlers[name],
|
||||
i = this._handlerIndex(h, type, handler);
|
||||
|
||||
if (i >= 0) {
|
||||
h.splice(i, 1);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
prototype.pickEvent = function(evt) {
|
||||
const 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.pick = function(scene, x, y, gx, gy) {
|
||||
const g = this.context(),
|
||||
mark = Marks[scene.marktype];
|
||||
return mark.pick.call(this, g, scene, x, y, gx, gy);
|
||||
};
|
||||
149
node_modules/vega-scenegraph/src/CanvasRenderer.js
generated
vendored
Normal file
149
node_modules/vega-scenegraph/src/CanvasRenderer.js
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
import Renderer from './Renderer';
|
||||
import Bounds from './Bounds';
|
||||
import marks from './marks/index';
|
||||
|
||||
import {domClear} from './util/dom';
|
||||
import clip from './util/canvas/clip';
|
||||
import resize from './util/canvas/resize';
|
||||
import {canvas} from 'vega-canvas';
|
||||
import {error, inherits} from 'vega-util';
|
||||
|
||||
export default function CanvasRenderer(loader) {
|
||||
Renderer.call(this, loader);
|
||||
this._options = {};
|
||||
this._redraw = false;
|
||||
this._dirty = new Bounds();
|
||||
this._tempb = new Bounds();
|
||||
}
|
||||
|
||||
const prototype = inherits(CanvasRenderer, Renderer),
|
||||
base = Renderer.prototype;
|
||||
|
||||
prototype.initialize = function(el, width, height, origin, scaleFactor, options) {
|
||||
this._options = options || {};
|
||||
|
||||
this._canvas = this._options.externalContext
|
||||
? null
|
||||
: canvas(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.initialize.call(this, el, width, height, origin, scaleFactor);
|
||||
};
|
||||
|
||||
prototype.resize = function(width, height, origin, scaleFactor) {
|
||||
base.resize.call(this, 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;
|
||||
};
|
||||
|
||||
prototype.canvas = function() {
|
||||
return this._canvas;
|
||||
};
|
||||
|
||||
prototype.context = function() {
|
||||
return this._options.externalContext
|
||||
|| (this._canvas ? this._canvas.getContext('2d') : null);
|
||||
};
|
||||
|
||||
prototype.dirty = function(item) {
|
||||
let b = this._tempb.clear().union(item.bounds),
|
||||
g = item.mark.group;
|
||||
|
||||
while (g) {
|
||||
b.translate(g.x || 0, g.y || 0);
|
||||
g = g.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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const viewBounds = (origin, width, height) => new Bounds()
|
||||
.set(0, 0, width, height)
|
||||
.translate(-origin[0], -origin[1]);
|
||||
|
||||
prototype._render = function(scene) {
|
||||
const g = this.context(),
|
||||
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.draw(g, scene, b);
|
||||
|
||||
// takedown
|
||||
g.restore();
|
||||
db.clear();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
prototype.draw = function(ctx, scene, bounds) {
|
||||
const mark = marks[scene.marktype];
|
||||
if (scene.clip) clip(ctx, scene);
|
||||
mark.draw.call(this, ctx, scene, bounds);
|
||||
if (scene.clip) ctx.restore();
|
||||
};
|
||||
|
||||
prototype.clear = function(x, y, w, h) {
|
||||
const opt = this._options,
|
||||
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);
|
||||
}
|
||||
};
|
||||
62
node_modules/vega-scenegraph/src/Gradient.js
generated
vendored
Normal file
62
node_modules/vega-scenegraph/src/Gradient.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
var gradient_id = 0;
|
||||
|
||||
export function resetSVGGradientId() {
|
||||
gradient_id = 0;
|
||||
}
|
||||
|
||||
export const patternPrefix = 'p_';
|
||||
|
||||
export function isGradient(value) {
|
||||
return value && value.gradient;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
export default function(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;
|
||||
}
|
||||
};
|
||||
}
|
||||
9
node_modules/vega-scenegraph/src/GroupItem.js
generated
vendored
Normal file
9
node_modules/vega-scenegraph/src/GroupItem.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import Item from './Item';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
export default function GroupItem(mark) {
|
||||
Item.call(this, mark);
|
||||
this.items = (this.items || []);
|
||||
}
|
||||
|
||||
inherits(GroupItem, Item);
|
||||
202
node_modules/vega-scenegraph/src/Handler.js
generated
vendored
Normal file
202
node_modules/vega-scenegraph/src/Handler.js
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
import {domCreate} from './util/dom';
|
||||
import resolveItem from './util/resolveItem';
|
||||
import {loader} from 'vega-loader';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export default function Handler(customLoader, customTooltip) {
|
||||
this._active = null;
|
||||
this._handlers = {};
|
||||
this._loader = customLoader || 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 || '');
|
||||
}
|
||||
|
||||
const prototype = 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.
|
||||
* @param {object} [obj] - Optional context object that should serve as
|
||||
* the "this" context for event callbacks.
|
||||
* @return {Handler} - This handler instance.
|
||||
*/
|
||||
prototype.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.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.canvas = function() {
|
||||
return this._el && this._el.firstChild;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get / set the origin coordinates of the visualization.
|
||||
*/
|
||||
prototype.origin = function(origin) {
|
||||
if (arguments.length) {
|
||||
this._origin = origin || [0, 0];
|
||||
return this;
|
||||
} else {
|
||||
return this._origin.slice();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get / set the scenegraph root.
|
||||
*/
|
||||
prototype.scene = function(scene) {
|
||||
if (!arguments.length) return this._scene;
|
||||
this._scene = scene;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an event handler. Subclasses should override this method.
|
||||
*/
|
||||
prototype.on = function(/*type, handler*/) {};
|
||||
|
||||
/**
|
||||
* Remove an event handler. Subclasses should override this method.
|
||||
*/
|
||||
prototype.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._handlerIndex = function(h, type, handler) {
|
||||
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
|
||||
* 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.handlers = function(type) {
|
||||
const h = this._handlers, a = [];
|
||||
if (type) {
|
||||
a.push.apply(a, h[this.eventName(type)]);
|
||||
} else {
|
||||
for (const 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.eventName = function(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.
|
||||
* @param {string} href - The URL to navigate to.
|
||||
*/
|
||||
prototype.handleHref = function(event, item, href) {
|
||||
this._loader
|
||||
.sanitize(href, {context:'href'})
|
||||
.then(opt => {
|
||||
const e = new MouseEvent(event.type, event),
|
||||
a = domCreate(null, 'a');
|
||||
for (const 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.handleTooltip = function(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.
|
||||
* @return {object} - A bounding box object (compatible with the
|
||||
* DOMRect type) consisting of x, y, width, heigh, top, left,
|
||||
* right, and bottom properties.
|
||||
*/
|
||||
prototype.getItemBoundingClientRect = function(item) {
|
||||
const el = this.canvas();
|
||||
if (!el) return;
|
||||
|
||||
const rect = el.getBoundingClientRect(),
|
||||
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
|
||||
while (item.mark && (item = item.mark.group)) {
|
||||
x += item.x || 0;
|
||||
y += item.y || 0;
|
||||
}
|
||||
|
||||
// return DOMRect-compatible bounding box
|
||||
return {
|
||||
x, y, width, height,
|
||||
left: x, top: y, right: x + width, bottom: y + height
|
||||
};
|
||||
};
|
||||
6
node_modules/vega-scenegraph/src/Item.js
generated
vendored
Normal file
6
node_modules/vega-scenegraph/src/Item.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import Bounds from './Bounds';
|
||||
|
||||
export default function Item(mark) {
|
||||
this.mark = mark;
|
||||
this.bounds = (this.bounds || new Bounds());
|
||||
}
|
||||
183
node_modules/vega-scenegraph/src/Renderer.js
generated
vendored
Normal file
183
node_modules/vega-scenegraph/src/Renderer.js
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
import ResourceLoader from './ResourceLoader';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export default function Renderer(loader) {
|
||||
this._el = null;
|
||||
this._bgcolor = null;
|
||||
this._loader = new ResourceLoader(loader);
|
||||
}
|
||||
|
||||
var prototype = 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<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 {Renderer} - This renderer instance.
|
||||
*/
|
||||
prototype.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.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.canvas = function() {
|
||||
return this._el && this._el.firstChild;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get / set the background color.
|
||||
*/
|
||||
prototype.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<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 {Renderer} - This renderer instance;
|
||||
*/
|
||||
prototype.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.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.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._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.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._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.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.loadImage = function(uri) {
|
||||
return this._load('loadImage', uri);
|
||||
};
|
||||
79
node_modules/vega-scenegraph/src/ResourceLoader.js
generated
vendored
Normal file
79
node_modules/vega-scenegraph/src/ResourceLoader.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
import {image} from 'vega-canvas';
|
||||
import {loader} from 'vega-loader';
|
||||
import {hasOwnProperty} from 'vega-util';
|
||||
|
||||
export default function ResourceLoader(customLoader) {
|
||||
this._pending = 0;
|
||||
this._loader = customLoader || loader();
|
||||
}
|
||||
|
||||
var prototype = ResourceLoader.prototype;
|
||||
|
||||
prototype.pending = function() {
|
||||
return this._pending;
|
||||
};
|
||||
|
||||
function increment(loader) {
|
||||
loader._pending += 1;
|
||||
}
|
||||
|
||||
function decrement(loader) {
|
||||
loader._pending -= 1;
|
||||
}
|
||||
|
||||
prototype.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.loadImage = function(uri) {
|
||||
const loader = this,
|
||||
Image = image();
|
||||
increment(loader);
|
||||
|
||||
return loader._loader
|
||||
.sanitize(uri, {context: 'image'})
|
||||
.then(function(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 cors = hasOwnProperty(opt, 'crossOrigin') ? opt.crossOrigin : 'anonymous';
|
||||
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(function(e) {
|
||||
decrement(loader);
|
||||
return {complete: false, width: 0, height: 0, src: e && e.url || ''};
|
||||
});
|
||||
};
|
||||
|
||||
prototype.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);
|
||||
});
|
||||
};
|
||||
83
node_modules/vega-scenegraph/src/SVGHandler.js
generated
vendored
Normal file
83
node_modules/vega-scenegraph/src/SVGHandler.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import Handler from './Handler';
|
||||
import {domFind} from './util/dom';
|
||||
import {HrefEvent, TooltipHideEvent, TooltipShowEvent} from './util/events';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
export default 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);
|
||||
});
|
||||
}
|
||||
|
||||
const prototype = inherits(SVGHandler, Handler);
|
||||
|
||||
prototype.initialize = function(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);
|
||||
};
|
||||
|
||||
prototype.canvas = function() {
|
||||
return this._svg;
|
||||
};
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
// add an event handler
|
||||
prototype.on = function(type, handler) {
|
||||
const name = this.eventName(type),
|
||||
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
|
||||
prototype.off = function(type, handler) {
|
||||
const 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;
|
||||
};
|
||||
613
node_modules/vega-scenegraph/src/SVGRenderer.js
generated
vendored
Normal file
613
node_modules/vega-scenegraph/src/SVGRenderer.js
generated
vendored
Normal file
@@ -0,0 +1,613 @@
|
||||
import Renderer from './Renderer';
|
||||
import {gradientRef, isGradient, patternPrefix} from './Gradient';
|
||||
import marks from './marks/index';
|
||||
import {ariaItemAttributes, ariaMarkAttributes} from './util/aria';
|
||||
import {cssClass, domChild, domClear, domCreate} from './util/dom';
|
||||
import {closeTag, openTag} from './util/tags';
|
||||
import {fontFamily, fontSize, lineHeight, textLines, textValue} from './util/text';
|
||||
import {visit} from './util/visit';
|
||||
import clip from './util/svg/clip';
|
||||
import metadata from './util/svg/metadata';
|
||||
import {rootAttributes, styles} from './util/svg/styles';
|
||||
import {inherits, isArray} from 'vega-util';
|
||||
|
||||
const RootIndex = 0,
|
||||
ns = metadata.xmlns;
|
||||
|
||||
export default function SVGRenderer(loader) {
|
||||
Renderer.call(this, loader);
|
||||
this._dirtyID = 0;
|
||||
this._dirty = [];
|
||||
this._svg = null;
|
||||
this._root = null;
|
||||
this._defs = null;
|
||||
}
|
||||
|
||||
var prototype = inherits(SVGRenderer, Renderer);
|
||||
var base = Renderer.prototype;
|
||||
|
||||
prototype.initialize = function(el, width, height, padding) {
|
||||
// create the svg definitions cache
|
||||
this._defs = {
|
||||
gradient: {},
|
||||
clipping: {}
|
||||
};
|
||||
|
||||
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, RootIndex, 'g', ns);
|
||||
for (const attr in rootAttributes) {
|
||||
this._root.setAttribute(attr, rootAttributes[attr]);
|
||||
}
|
||||
|
||||
// ensure no additional child elements
|
||||
domClear(this._svg, RootIndex + 1);
|
||||
}
|
||||
|
||||
// set background color if defined
|
||||
this.background(this._bgcolor);
|
||||
|
||||
return base.initialize.call(this, el, width, height, padding);
|
||||
};
|
||||
|
||||
prototype.background = function(bgcolor) {
|
||||
if (arguments.length && this._svg) {
|
||||
this._svg.style.setProperty('background-color', bgcolor);
|
||||
}
|
||||
return base.background.apply(this, arguments);
|
||||
};
|
||||
|
||||
prototype.resize = function(width, height, origin, scaleFactor) {
|
||||
base.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.canvas = function() {
|
||||
return this._svg;
|
||||
};
|
||||
|
||||
prototype.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,
|
||||
fill: this._bgcolor
|
||||
}) + closeTag('rect'));
|
||||
|
||||
return openTag('svg', attr)
|
||||
+ (this._defs.el ? this._defs.el.outerHTML : '')
|
||||
+ bg
|
||||
+ this._root.outerHTML
|
||||
+ closeTag('svg');
|
||||
};
|
||||
|
||||
|
||||
// -- Render entry point --
|
||||
|
||||
prototype._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.updateDefs = function() {
|
||||
const svg = this._svg,
|
||||
defs = this._defs;
|
||||
|
||||
let el = defs.el,
|
||||
index = 0;
|
||||
|
||||
for (const id in defs.gradient) {
|
||||
if (!el) defs.el = (el = domChild(svg, RootIndex, 'defs', ns));
|
||||
index = updateGradient(el, defs.gradient[id], index);
|
||||
}
|
||||
|
||||
for (const id in defs.clipping) {
|
||||
if (!el) defs.el = (el = domChild(svg, RootIndex, 'defs', ns));
|
||||
index = updateClipping(el, defs.clipping[id], index);
|
||||
}
|
||||
|
||||
// clean-up
|
||||
if (el) {
|
||||
index === 0
|
||||
? (svg.removeChild(el), defs.el = null)
|
||||
: 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.
|
||||
// We wrap the radial gradient in a pattern element, allowing us to
|
||||
// maintain 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<n; ++i) {
|
||||
stop = domChild(el, i, 'stop', ns);
|
||||
stop.setAttribute('offset', grad.stops[i].offset);
|
||||
stop.setAttribute('stop-color', grad.stops[i].color);
|
||||
}
|
||||
domClear(el, i);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function updateClipping(el, clip, index) {
|
||||
var mask;
|
||||
|
||||
el = domChild(el, index, 'clipPath', ns);
|
||||
el.setAttribute('id', clip.id);
|
||||
|
||||
if (clip.path) {
|
||||
mask = domChild(el, 0, 'path', ns);
|
||||
mask.setAttribute('d', clip.path);
|
||||
} else {
|
||||
mask = domChild(el, 0, 'rect', ns);
|
||||
mask.setAttribute('x', 0);
|
||||
mask.setAttribute('y', 0);
|
||||
mask.setAttribute('width', clip.width);
|
||||
mask.setAttribute('height', clip.height);
|
||||
}
|
||||
domClear(el, 1);
|
||||
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
prototype._resetDefs = function() {
|
||||
var def = this._defs;
|
||||
def.gradient = {};
|
||||
def.clipping = {};
|
||||
};
|
||||
|
||||
|
||||
// -- Manage rendering of items marked as dirty --
|
||||
|
||||
prototype.dirty = function(item) {
|
||||
if (item.dirty !== this._dirtyID) {
|
||||
item.dirty = this._dirtyID;
|
||||
this._dirty.push(item);
|
||||
}
|
||||
};
|
||||
|
||||
prototype.isDirty = function(item) {
|
||||
return this._dirtyAll
|
||||
|| !item._svg
|
||||
|| item.dirty === this._dirtyID;
|
||||
};
|
||||
|
||||
prototype._dirtyCheck = function() {
|
||||
this._dirtyAll = true;
|
||||
var items = this._dirty;
|
||||
if (!items.length || !this._dirtyID) return true;
|
||||
|
||||
var id = ++this._dirtyID,
|
||||
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(function(i) { i.dirty = id; });
|
||||
}
|
||||
if (mark.zdirty) continue; // handle in standard drawing pass
|
||||
|
||||
if (item.exit) { // EXIT
|
||||
if (mdef.nested && mark.items.length) {
|
||||
// if nested mark with remaining points, update instead
|
||||
o = mark.items[0];
|
||||
if (o._svg) this._update(mdef, o._svg, o);
|
||||
} 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
|
||||
this._dirtyAll = false;
|
||||
dirtyParents(item, id);
|
||||
} else {
|
||||
// IN-PLACE UPDATE
|
||||
this._update(mdef, item._svg, item);
|
||||
}
|
||||
item._update = id;
|
||||
}
|
||||
return !this._dirtyAll;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- Construct & maintain scenegraph to SVG mapping ---
|
||||
|
||||
// Draw a mark container.
|
||||
prototype.draw = function(el, scene, prev) {
|
||||
if (!this.isDirty(scene)) return scene._svg;
|
||||
|
||||
var svg = this._svg,
|
||||
mdef = marks[scene.marktype],
|
||||
events = scene.interactive === false ? 'none' : null,
|
||||
isGroup = mdef.tag === 'g',
|
||||
sibling = null,
|
||||
i = 0,
|
||||
parent;
|
||||
|
||||
parent = bind(scene, el, prev, 'g', 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(this, scene, scene.group) : null);
|
||||
|
||||
const process = item => {
|
||||
const dirty = this.isDirty(item),
|
||||
node = bind(item, parent, sibling, mdef.tag, svg);
|
||||
|
||||
if (dirty) {
|
||||
this._update(mdef, node, item);
|
||||
if (isGroup) recurse(this, node, item);
|
||||
}
|
||||
|
||||
sibling = node;
|
||||
++i;
|
||||
};
|
||||
|
||||
if (mdef.nested) {
|
||||
if (scene.items.length) process(scene.items[0]);
|
||||
} else {
|
||||
visit(scene, process);
|
||||
}
|
||||
|
||||
domClear(parent, i);
|
||||
return parent;
|
||||
};
|
||||
|
||||
// Recursively process group contents.
|
||||
function recurse(renderer, el, group) {
|
||||
el = el.lastChild.previousSibling;
|
||||
let prev, idx = 0;
|
||||
|
||||
visit(group, item => {
|
||||
prev = renderer.draw(el, item, prev);
|
||||
++idx;
|
||||
});
|
||||
|
||||
// remove any extraneous DOM elements
|
||||
domClear(el, 1 + idx);
|
||||
}
|
||||
|
||||
// Bind a scenegraph item to an SVG DOM element.
|
||||
// Create new SVG elements as needed.
|
||||
function bind(item, el, sibling, tag, svg) {
|
||||
let node = item._svg, doc;
|
||||
|
||||
// create a new dom node if needed
|
||||
if (!node) {
|
||||
doc = el.ownerDocument;
|
||||
node = domCreate(doc, tag, ns);
|
||||
item._svg = node;
|
||||
|
||||
if (item.mark) {
|
||||
node.__data__ = item;
|
||||
node.__values__ = {fill: 'default'};
|
||||
|
||||
// if group, create background, content, and foreground elements
|
||||
if (tag === 'g') {
|
||||
const bg = domCreate(doc, 'path', ns);
|
||||
node.appendChild(bg);
|
||||
bg.__data__ = item;
|
||||
|
||||
const cg = domCreate(doc, 'g', ns);
|
||||
node.appendChild(cg);
|
||||
cg.__data__ = item;
|
||||
|
||||
const fg = domCreate(doc, 'path', ns);
|
||||
node.appendChild(fg);
|
||||
fg.__data__ = item;
|
||||
fg.__values__ = {fill: 'default'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (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;
|
||||
}
|
||||
|
||||
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 ---
|
||||
|
||||
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) {
|
||||
const fg = element = 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);
|
||||
|
||||
const bg = element = 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
|
||||
this.style(bg, item);
|
||||
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
|
||||
element = null;
|
||||
} else {
|
||||
// ensure foreground is ignored
|
||||
setAttribute(fg, 'display', '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) {
|
||||
let tl = textLines(item),
|
||||
key, value, doc, lh;
|
||||
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
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 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._update = function(mdef, el, item) {
|
||||
// set dom element and values cache
|
||||
// provides access to emit method
|
||||
element = el;
|
||||
values = el.__values__;
|
||||
|
||||
// apply aria-specific properties
|
||||
ariaItemAttributes(emit, item);
|
||||
|
||||
// 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 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;
|
||||
|
||||
// use appropriate method given namespace (ns)
|
||||
if (ns) {
|
||||
setAttributeNS(element, name, value, ns);
|
||||
} else {
|
||||
setAttribute(element, name, value);
|
||||
}
|
||||
|
||||
// note current value for future comparison
|
||||
values[name] = value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
prototype.style = function(el, o) {
|
||||
if (o == null) return;
|
||||
|
||||
for (const prop in styles) {
|
||||
let value = prop === 'font' ? fontFamily(o) : o[prop];
|
||||
if (value === values[prop]) continue;
|
||||
|
||||
const name = styles[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;
|
||||
}
|
||||
};
|
||||
|
||||
function href() {
|
||||
let loc;
|
||||
return typeof window === 'undefined' ? ''
|
||||
: (loc = window.location).hash ? loc.href.slice(0, -loc.hash.length)
|
||||
: loc.href;
|
||||
}
|
||||
365
node_modules/vega-scenegraph/src/SVGStringRenderer.js
generated
vendored
Normal file
365
node_modules/vega-scenegraph/src/SVGStringRenderer.js
generated
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
import Renderer from './Renderer';
|
||||
import {gradientRef, isGradient, patternPrefix} from './Gradient';
|
||||
import marks from './marks/index';
|
||||
import {ariaItemAttributes, ariaMarkAttributes} from './util/aria';
|
||||
import {cssClass} from './util/dom';
|
||||
import {closeTag, openTag} from './util/tags';
|
||||
import {fontFamily, fontSize, lineHeight, textLines, textValue} from './util/text';
|
||||
import {visit} from './util/visit';
|
||||
import clip from './util/svg/clip';
|
||||
import metadata from './util/svg/metadata';
|
||||
import {rootAttributes, styles} from './util/svg/styles';
|
||||
import {extend, inherits, isArray} from 'vega-util';
|
||||
|
||||
export default function SVGStringRenderer(loader) {
|
||||
Renderer.call(this, loader);
|
||||
|
||||
this._text = {
|
||||
head: '',
|
||||
bg: '',
|
||||
root: '',
|
||||
foot: '',
|
||||
defs: '',
|
||||
body: ''
|
||||
};
|
||||
|
||||
this._defs = {
|
||||
gradient: {},
|
||||
clipping: {}
|
||||
};
|
||||
}
|
||||
|
||||
var prototype = inherits(SVGStringRenderer, Renderer);
|
||||
var base = Renderer.prototype;
|
||||
|
||||
prototype.resize = function(width, height, origin, scaleFactor) {
|
||||
base.resize.call(this, width, height, origin, scaleFactor);
|
||||
var o = this._origin,
|
||||
t = this._text;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
t.head = openTag('svg', attr);
|
||||
|
||||
var bg = this._bgcolor;
|
||||
if (bg === 'transparent' || bg === 'none') bg = null;
|
||||
|
||||
if (bg) {
|
||||
t.bg = openTag('rect', {
|
||||
width: this._width,
|
||||
height: this._height,
|
||||
fill: bg
|
||||
}) + closeTag('rect');
|
||||
} else {
|
||||
t.bg = '';
|
||||
}
|
||||
|
||||
t.root = openTag('g', extend(
|
||||
{}, rootAttributes, {transform: 'translate(' + o + ')'}
|
||||
));
|
||||
|
||||
t.foot = closeTag('g') + closeTag('svg');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
prototype.background = function() {
|
||||
var rv = base.background.apply(this, arguments);
|
||||
if (arguments.length && this._text.head) {
|
||||
this.resize(this._width, this._height, this._origin, this._scale);
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
prototype.svg = function() {
|
||||
var t = this._text;
|
||||
return t.head + t.defs + t.bg + t.root + t.body + t.foot;
|
||||
};
|
||||
|
||||
prototype._render = function(scene) {
|
||||
this._text.body = this.mark(scene);
|
||||
this._text.defs = this.buildDefs();
|
||||
return this;
|
||||
};
|
||||
|
||||
prototype.buildDefs = function() {
|
||||
let defs = '', tag;
|
||||
|
||||
for (const id in this._defs.gradient) {
|
||||
const def = this._defs.gradient[id],
|
||||
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.
|
||||
|
||||
defs += openTag(tag = 'pattern', {
|
||||
id: patternPrefix + id,
|
||||
viewBox: '0,0,1,1',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
preserveAspectRatio: 'xMidYMid slice'
|
||||
});
|
||||
|
||||
defs += openTag('rect', {
|
||||
width: '1',
|
||||
height: '1',
|
||||
fill: 'url(#' + id + ')'
|
||||
}) + closeTag('rect');
|
||||
|
||||
defs += closeTag(tag);
|
||||
|
||||
defs += openTag(tag = 'radialGradient', {
|
||||
id: id,
|
||||
fx: def.x1,
|
||||
fy: def.y1,
|
||||
fr: def.r1,
|
||||
cx: def.x2,
|
||||
cy: def.y2,
|
||||
r: def.r2
|
||||
});
|
||||
} else {
|
||||
defs += openTag(tag = 'linearGradient', {
|
||||
id: id,
|
||||
x1: def.x1,
|
||||
x2: def.x2,
|
||||
y1: def.y1,
|
||||
y2: def.y2
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < stops.length; ++i) {
|
||||
defs += openTag('stop', {
|
||||
offset: stops[i].offset,
|
||||
'stop-color': stops[i].color
|
||||
}) + closeTag('stop');
|
||||
}
|
||||
|
||||
defs += closeTag(tag);
|
||||
}
|
||||
|
||||
for (const id in this._defs.clipping) {
|
||||
const def = this._defs.clipping[id];
|
||||
|
||||
defs += openTag('clipPath', {id: id});
|
||||
|
||||
if (def.path) {
|
||||
defs += openTag('path', {
|
||||
d: def.path
|
||||
}) + closeTag('path');
|
||||
} else {
|
||||
defs += openTag('rect', {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: def.width,
|
||||
height: def.height
|
||||
}) + closeTag('rect');
|
||||
}
|
||||
|
||||
defs += closeTag('clipPath');
|
||||
}
|
||||
|
||||
return defs ? (openTag('defs') + defs + closeTag('defs')) : '';
|
||||
};
|
||||
|
||||
prototype.attr = function(scene, item, attrs, tag) {
|
||||
const object = {},
|
||||
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
|
||||
if (tag) {
|
||||
applyStyles(object, item, scene, tag, this._defs);
|
||||
}
|
||||
|
||||
return object;
|
||||
};
|
||||
|
||||
prototype.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(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.mark = function(scene) {
|
||||
const mdef = marks[scene.marktype],
|
||||
tag = mdef.tag,
|
||||
attrList = [ariaItemAttributes, mdef.attr];
|
||||
|
||||
let str = '';
|
||||
|
||||
// render opening group tag
|
||||
str += openTag('g', extend(
|
||||
{
|
||||
'class': cssClass(scene),
|
||||
'clip-path': scene.clip ? clip(this, scene, scene.group) : null
|
||||
},
|
||||
ariaMarkAttributes(scene),
|
||||
{
|
||||
'pointer-events': tag !== 'g' && scene.interactive === false ? 'none' : null
|
||||
}
|
||||
));
|
||||
|
||||
// render contained elements
|
||||
const process = item => {
|
||||
const href = this.href(item);
|
||||
if (href) str += openTag('a', href);
|
||||
|
||||
str += openTag(
|
||||
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) {
|
||||
str += openTag('tspan', i ? attrs: null)
|
||||
+ escape_text(textValue(item, tl[i]))
|
||||
+ closeTag('tspan');
|
||||
}
|
||||
} else {
|
||||
// single-line text
|
||||
str += escape_text(textValue(item, tl));
|
||||
}
|
||||
} else if (tag === 'g') {
|
||||
const fore = item.strokeForeground,
|
||||
fill = item.fill,
|
||||
stroke = item.stroke;
|
||||
|
||||
if (fore && stroke) {
|
||||
item.stroke = null;
|
||||
}
|
||||
|
||||
str += openTag(
|
||||
'path',
|
||||
this.attr(scene, item, mdef.background, 'bgrect')
|
||||
) + closeTag('path');
|
||||
|
||||
str += openTag('g', this.attr(scene, item, mdef.content))
|
||||
+ this.markGroup(item)
|
||||
+ closeTag('g');
|
||||
|
||||
if (fore && stroke) {
|
||||
if (fill) item.fill = null;
|
||||
item.stroke = stroke;
|
||||
|
||||
str += openTag(
|
||||
'path',
|
||||
this.attr(scene, item, mdef.foreground, 'bgrect')
|
||||
) + closeTag('path');
|
||||
|
||||
if (fill) item.fill = fill;
|
||||
} else {
|
||||
str += openTag(
|
||||
'path',
|
||||
this.attr(scene, item, mdef.foreground, 'bgfore')
|
||||
) + closeTag('path');
|
||||
}
|
||||
}
|
||||
|
||||
str += closeTag(tag);
|
||||
if (href) str += closeTag('a');
|
||||
};
|
||||
|
||||
if (mdef.nested) {
|
||||
if (scene.items && scene.items.length) process(scene.items[0]);
|
||||
} else {
|
||||
visit(scene, process);
|
||||
}
|
||||
|
||||
// render closing group tag
|
||||
return str + closeTag('g');
|
||||
};
|
||||
|
||||
prototype.markGroup = function(scene) {
|
||||
let str = '';
|
||||
visit(scene, item => { str += this.mark(item); });
|
||||
return str;
|
||||
};
|
||||
|
||||
function applyStyles(s, item, scene, tag, defs) {
|
||||
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) {
|
||||
s.style = 'image-rendering: optimizeSpeed; image-rendering: pixelated;';
|
||||
}
|
||||
|
||||
if (tag === 'text') {
|
||||
s['font-family'] = fontFamily(item);
|
||||
s['font-size'] = fontSize(item) + 'px';
|
||||
if (item.fontStyle) s['font-style'] = item.fontStyle;
|
||||
if (item.fontVariant) s['font-variant'] = item.fontVariant;
|
||||
if (item.fontWeight) s['font-weight'] = item.fontWeight;
|
||||
}
|
||||
|
||||
for (const prop in styles) {
|
||||
let value = item[prop];
|
||||
const name = styles[prop];
|
||||
|
||||
if (value === 'transparent' && (name === 'fill' || name === 'stroke')) {
|
||||
// transparent is not a legal SVG value
|
||||
// we can skip it to rely on default 'none' instead
|
||||
} else if (value != null) {
|
||||
if (isGradient(value)) {
|
||||
value = gradientRef(value, defs.gradient, '');
|
||||
}
|
||||
s[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function escape_text(s) {
|
||||
return s.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
54
node_modules/vega-scenegraph/src/Scenegraph.js
generated
vendored
Normal file
54
node_modules/vega-scenegraph/src/Scenegraph.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import Bounds from './Bounds';
|
||||
import GroupItem from './GroupItem';
|
||||
import {sceneFromJSON, sceneToJSON} from './util/serialize';
|
||||
|
||||
export default 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)];
|
||||
}
|
||||
}
|
||||
|
||||
var prototype = Scenegraph.prototype;
|
||||
|
||||
prototype.toJSON = function(indent) {
|
||||
return sceneToJSON(this.root, indent || 0);
|
||||
};
|
||||
|
||||
prototype.mark = function(markdef, group, index) {
|
||||
group = group || this.root.items[0];
|
||||
var 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,
|
||||
interactive: def.interactive === false ? false : true,
|
||||
items: [],
|
||||
marktype: def.marktype,
|
||||
name: def.name || undefined,
|
||||
role: def.role || undefined,
|
||||
zindex: def.zindex || 0
|
||||
};
|
||||
|
||||
// add accessibility properties if defined
|
||||
if (def.aria != null) {
|
||||
mark.aria = def.aria;
|
||||
}
|
||||
if (def.description) {
|
||||
mark.description = def.description;
|
||||
}
|
||||
|
||||
return mark;
|
||||
}
|
||||
17
node_modules/vega-scenegraph/src/bound/boundClip.js
generated
vendored
Normal file
17
node_modules/vega-scenegraph/src/bound/boundClip.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import Bounds from '../Bounds';
|
||||
import boundContext from './boundContext';
|
||||
import {isFunction} from 'vega-util';
|
||||
|
||||
var clipBounds = new Bounds();
|
||||
|
||||
export default function(mark) {
|
||||
var 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);
|
||||
}
|
||||
119
node_modules/vega-scenegraph/src/bound/boundContext.js
generated
vendored
Normal file
119
node_modules/vega-scenegraph/src/bound/boundContext.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
import {Epsilon, HalfPi, Tau} from '../util/constants';
|
||||
|
||||
var bounds, lx, ly,
|
||||
circleThreshold = Tau - 1e-8;
|
||||
|
||||
export default 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<ea; ++i, s=s+HalfPi) update(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
10
node_modules/vega-scenegraph/src/bound/boundItem.js
generated
vendored
Normal file
10
node_modules/vega-scenegraph/src/bound/boundItem.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import Bounds from '../Bounds';
|
||||
import marks from '../marks/index';
|
||||
|
||||
export default function(item, func, opt) {
|
||||
var type = marks[item.mark.marktype],
|
||||
bound = func || type.bound;
|
||||
if (type.nested) item = item.mark;
|
||||
|
||||
return bound(item.bounds || (item.bounds = new Bounds()), item, opt);
|
||||
}
|
||||
38
node_modules/vega-scenegraph/src/bound/boundMark.js
generated
vendored
Normal file
38
node_modules/vega-scenegraph/src/bound/boundMark.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import Bounds from '../Bounds';
|
||||
import boundItem from './boundItem';
|
||||
import marks from '../marks/index';
|
||||
|
||||
var DUMMY = {mark: null};
|
||||
|
||||
export default function(mark, bounds, opt) {
|
||||
var type = marks[mark.marktype],
|
||||
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(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(items[i], bound, opt));
|
||||
}
|
||||
}
|
||||
|
||||
return mark.bounds = bounds;
|
||||
}
|
||||
12
node_modules/vega-scenegraph/src/bound/boundStroke.js
generated
vendored
Normal file
12
node_modules/vega-scenegraph/src/bound/boundStroke.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function(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;
|
||||
}
|
||||
78
node_modules/vega-scenegraph/src/intersect.js
generated
vendored
Normal file
78
node_modules/vega-scenegraph/src/intersect.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
import Marks from './marks/index';
|
||||
import {error} from 'vega-util';
|
||||
import Bounds from './Bounds';
|
||||
|
||||
export 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)
|
||||
: 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<n; ++i) {
|
||||
intersectGroup(items[i], box, filter, hits);
|
||||
}
|
||||
} else {
|
||||
for (const test = Marks[type].isect; i<n; ++i) {
|
||||
let 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
|
||||
const marks = group.items,
|
||||
n = marks && marks.length;
|
||||
|
||||
if (n) {
|
||||
const x = group.x || 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));
|
||||
}
|
||||
|
||||
4
node_modules/vega-scenegraph/src/marks/arc.js
generated
vendored
Normal file
4
node_modules/vega-scenegraph/src/marks/arc.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import {arc} from '../path/shapes';
|
||||
import markItemPath from './markItemPath';
|
||||
|
||||
export default markItemPath('arc', arc);
|
||||
5
node_modules/vega-scenegraph/src/marks/area.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/marks/area.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import {area} from '../path/shapes';
|
||||
import {pickArea} from '../util/pickPath';
|
||||
import markMultiItemPath from './markMultiItemPath';
|
||||
|
||||
export default markMultiItemPath('area', area, pickArea);
|
||||
202
node_modules/vega-scenegraph/src/marks/group.js
generated
vendored
Normal file
202
node_modules/vega-scenegraph/src/marks/group.js
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
import {hasCornerRadius, rectangle} from '../path/shapes';
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import {intersectRect} from '../util/intersect';
|
||||
import value from '../util/value';
|
||||
import {pickVisit, visit} from '../util/visit';
|
||||
import blend from '../util/canvas/blend';
|
||||
import {clipGroup} from '../util/canvas/clip';
|
||||
import fill from '../util/canvas/fill';
|
||||
import stroke from '../util/canvas/stroke';
|
||||
import {hitPath} from '../util/canvas/pick';
|
||||
import clip from '../util/svg/clip';
|
||||
import {translateItem} from '../util/svg/transform';
|
||||
|
||||
function offset(item) {
|
||||
const sw = value(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(emit, item) {
|
||||
emit('transform', translateItem(item));
|
||||
}
|
||||
|
||||
function emitRectangle(emit, item) {
|
||||
const off = offset(item);
|
||||
emit('d', rectangle(null, item, off, off));
|
||||
}
|
||||
|
||||
function background(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(renderer, item, item) : null;
|
||||
emit('clip-path', url);
|
||||
}
|
||||
|
||||
function bound(bounds, group) {
|
||||
if (!group.clip && group.items) {
|
||||
const items = group.items, 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(group);
|
||||
context.beginPath();
|
||||
rectangle(context, group, (x || 0) + off, (y || 0) + off);
|
||||
}
|
||||
|
||||
const hitBackground = hitPath(rectanglePath);
|
||||
const hitForeground = hitPath(rectanglePath, false);
|
||||
|
||||
function draw(context, scene, bounds) {
|
||||
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
|
||||
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
|
||||
context.save();
|
||||
context.translate(gx, gy);
|
||||
if (group.clip) clipGroup(context, group);
|
||||
if (bounds) bounds.translate(-gx, -gy);
|
||||
|
||||
// draw group contents
|
||||
visit(group, item => {
|
||||
this.draw(context, item, bounds);
|
||||
});
|
||||
|
||||
// restore graphics context
|
||||
if (bounds) bounds.translate(gx, gy);
|
||||
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;
|
||||
|
||||
return pickVisit(scene, group => {
|
||||
let hit, fore, ix, dx, dy, dw, dh, b, c;
|
||||
|
||||
// first hit test bounding box
|
||||
b = group.bounds;
|
||||
if (b && !b.contains(gx, gy)) return;
|
||||
|
||||
// passed bounds check, test rectangular clip
|
||||
dx = group.x || 0;
|
||||
dy = group.y || 0;
|
||||
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
|
||||
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, mark => pickMark(mark, dx, dy)
|
||||
? this.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);
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'group',
|
||||
tag: 'g',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: draw,
|
||||
pick: pick,
|
||||
isect: intersectRect,
|
||||
content: content,
|
||||
background: background,
|
||||
foreground: foreground
|
||||
};
|
||||
115
node_modules/vega-scenegraph/src/marks/image.js
generated
vendored
Normal file
115
node_modules/vega-scenegraph/src/marks/image.js
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
import {visit} from '../util/visit';
|
||||
import blend from '../util/canvas/blend';
|
||||
import {pick} from '../util/canvas/pick';
|
||||
import metadata from '../util/svg/metadata';
|
||||
import {translate} from '../util/svg/transform';
|
||||
import {truthy} from 'vega-util';
|
||||
|
||||
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(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 || '';
|
||||
|
||||
emit('href', i, metadata['xmlns:xlink'], 'xlink:href');
|
||||
emit('transform', translate(x, y));
|
||||
emit('width', w);
|
||||
emit('height', h);
|
||||
emit('preserveAspectRatio', item.aspect === false ? 'none' : 'xMidYMid');
|
||||
}
|
||||
|
||||
function bound(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);
|
||||
|
||||
return bounds.set(x, y, x + w, y + h);
|
||||
}
|
||||
|
||||
function draw(context, scene, bounds) {
|
||||
visit(scene, item => {
|
||||
if (bounds && !bounds.intersects(item.bounds)) return; // bounds check
|
||||
|
||||
let img = getImage(item, this),
|
||||
w = imageWidth(item, img),
|
||||
h = imageHeight(item, img),
|
||||
x = (item.x || 0) - imageXOffset(item.align, w),
|
||||
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;
|
||||
} else {
|
||||
t = h * ar0;
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'image',
|
||||
tag: 'image',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: draw,
|
||||
pick: pick(),
|
||||
isect: truthy, // bounds check is sufficient
|
||||
get: getImage,
|
||||
xOffset: imageXOffset,
|
||||
yOffset: imageYOffset
|
||||
};
|
||||
27
node_modules/vega-scenegraph/src/marks/index.js
generated
vendored
Normal file
27
node_modules/vega-scenegraph/src/marks/index.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import arc from './arc';
|
||||
import area from './area';
|
||||
import group from './group';
|
||||
import image from './image';
|
||||
import line from './line';
|
||||
import path from './path';
|
||||
import rect from './rect';
|
||||
import rule from './rule';
|
||||
import shape from './shape';
|
||||
import symbol from './symbol';
|
||||
import text from './text';
|
||||
import trail from './trail';
|
||||
|
||||
export default {
|
||||
arc: arc,
|
||||
area: area,
|
||||
group: group,
|
||||
image: image,
|
||||
line: line,
|
||||
path: path,
|
||||
rect: rect,
|
||||
rule: rule,
|
||||
shape: shape,
|
||||
symbol: symbol,
|
||||
text: text,
|
||||
trail: trail
|
||||
};
|
||||
5
node_modules/vega-scenegraph/src/marks/line.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/marks/line.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import {line} from '../path/shapes';
|
||||
import {pickLine} from '../util/pickPath';
|
||||
import markMultiItemPath from './markMultiItemPath';
|
||||
|
||||
export default markMultiItemPath('line', line, pickLine);
|
||||
53
node_modules/vega-scenegraph/src/marks/markItemPath.js
generated
vendored
Normal file
53
node_modules/vega-scenegraph/src/marks/markItemPath.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import context from '../bound/boundContext';
|
||||
import {intersectPath} from '../util/intersect';
|
||||
import {drawAll} from '../util/canvas/draw';
|
||||
import {pickPath} from '../util/canvas/pick';
|
||||
import {transformItem} from '../util/svg/transform';
|
||||
import {DegToRad} from '../util/constants';
|
||||
|
||||
export default function(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)
|
||||
};
|
||||
|
||||
}
|
||||
58
node_modules/vega-scenegraph/src/marks/markMultiItemPath.js
generated
vendored
Normal file
58
node_modules/vega-scenegraph/src/marks/markMultiItemPath.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import context from '../bound/boundContext';
|
||||
import {intersectPoint} from '../util/intersect';
|
||||
import {drawOne} from '../util/canvas/draw';
|
||||
import {hitPath} from '../util/canvas/pick';
|
||||
|
||||
export default function(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
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
68
node_modules/vega-scenegraph/src/marks/path.js
generated
vendored
Normal file
68
node_modules/vega-scenegraph/src/marks/path.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import context from '../bound/boundContext';
|
||||
import pathParse from '../path/parse';
|
||||
import pathRender from '../path/render';
|
||||
import {intersectPath} from '../util/intersect';
|
||||
import {drawAll} from '../util/canvas/draw';
|
||||
import {pickPath} from '../util/canvas/pick';
|
||||
import {transformItem} from '../util/svg/transform';
|
||||
import {DegToRad} from '../util/constants';
|
||||
|
||||
function attr(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(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;
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'path',
|
||||
tag: 'path',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: drawAll(path),
|
||||
pick: pickPath(path),
|
||||
isect: intersectPath(path)
|
||||
};
|
||||
35
node_modules/vega-scenegraph/src/marks/rect.js
generated
vendored
Normal file
35
node_modules/vega-scenegraph/src/marks/rect.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import {rectangle} from '../path/shapes';
|
||||
import {intersectRect} from '../util/intersect';
|
||||
import {drawAll} from '../util/canvas/draw';
|
||||
import {pickPath} from '../util/canvas/pick';
|
||||
|
||||
function attr(emit, item) {
|
||||
emit('d', rectangle(null, item));
|
||||
}
|
||||
|
||||
function bound(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(context, item) {
|
||||
context.beginPath();
|
||||
rectangle(context, item);
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'rect',
|
||||
tag: 'path',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: drawAll(draw),
|
||||
pick: pickPath(draw),
|
||||
isect: intersectRect
|
||||
};
|
||||
66
node_modules/vega-scenegraph/src/marks/rule.js
generated
vendored
Normal file
66
node_modules/vega-scenegraph/src/marks/rule.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import boundStroke from '../bound/boundStroke';
|
||||
import {intersectRule} from '../util/intersect';
|
||||
import {visit} from '../util/visit';
|
||||
import blend from '../util/canvas/blend';
|
||||
import {pick} from '../util/canvas/pick';
|
||||
import stroke from '../util/canvas/stroke';
|
||||
import {translateItem} from '../util/svg/transform';
|
||||
|
||||
function attr(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(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(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(context, item, opacity)) {
|
||||
blend(context, item);
|
||||
context.stroke();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hit(context, item, x, y) {
|
||||
if (!context.isPointInStroke) return false;
|
||||
return path(context, item, 1) && context.isPointInStroke(x, y);
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'rule',
|
||||
tag: 'line',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: draw,
|
||||
pick: pick(hit),
|
||||
isect: intersectRule
|
||||
};
|
||||
4
node_modules/vega-scenegraph/src/marks/shape.js
generated
vendored
Normal file
4
node_modules/vega-scenegraph/src/marks/shape.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import {shape} from '../path/shapes';
|
||||
import markItemPath from './markItemPath';
|
||||
|
||||
export default markItemPath('shape', shape);
|
||||
5
node_modules/vega-scenegraph/src/marks/symbol.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/marks/symbol.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import {symbol} from '../path/shapes';
|
||||
import {intersectPoint} from '../util/intersect';
|
||||
import markItemPath from './markItemPath';
|
||||
|
||||
export default markItemPath('symbol', symbol, intersectPoint);
|
||||
184
node_modules/vega-scenegraph/src/marks/text.js
generated
vendored
Normal file
184
node_modules/vega-scenegraph/src/marks/text.js
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
import Bounds from '../Bounds';
|
||||
import {DegToRad, HalfPi} from '../util/constants';
|
||||
import {font, lineHeight, offset, textLines, textMetrics, textValue} from '../util/text';
|
||||
import {intersectBoxLine} from '../util/intersect';
|
||||
import {visit} from '../util/visit';
|
||||
import blend from '../util/canvas/blend';
|
||||
import fill from '../util/canvas/fill';
|
||||
import {pick} from '../util/canvas/pick';
|
||||
import stroke from '../util/canvas/stroke';
|
||||
import {rotate, translate} from '../util/svg/transform';
|
||||
import {isArray} from 'vega-util';
|
||||
|
||||
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(emit, item) {
|
||||
var dx = item.dx || 0,
|
||||
dy = (item.dy || 0) + offset(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(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(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
|
||||
if (a === 'center') {
|
||||
dx -= (w / 2);
|
||||
} else if (a === 'right') {
|
||||
dx -= w;
|
||||
} else {
|
||||
// left by default, do nothing
|
||||
}
|
||||
|
||||
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(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(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
|
||||
var p = anchorPoint(item),
|
||||
ax = p.x1,
|
||||
ay = p.y1,
|
||||
b = bound(tempBounds, 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) {
|
||||
var p = bound(tempBounds, 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]);
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'text',
|
||||
tag: 'text',
|
||||
nested: false,
|
||||
attr: attr,
|
||||
bound: bound,
|
||||
draw: draw,
|
||||
pick: pick(hit),
|
||||
isect: intersectText
|
||||
};
|
||||
5
node_modules/vega-scenegraph/src/marks/trail.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/marks/trail.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import {trail} from '../path/shapes';
|
||||
import {pickTrail} from '../util/pickPath';
|
||||
import markMultiItemPath from './markMultiItemPath';
|
||||
|
||||
export default markMultiItemPath('trail', trail, pickTrail);
|
||||
43
node_modules/vega-scenegraph/src/modules.js
generated
vendored
Normal file
43
node_modules/vega-scenegraph/src/modules.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import CanvasHandler from './CanvasHandler';
|
||||
import CanvasRenderer from './CanvasRenderer';
|
||||
import SVGHandler from './SVGHandler';
|
||||
import SVGRenderer from './SVGRenderer';
|
||||
import SVGStringRenderer from './SVGStringRenderer';
|
||||
|
||||
var Canvas = 'canvas';
|
||||
var PNG = 'png';
|
||||
var SVG = 'svg';
|
||||
var None = 'none';
|
||||
|
||||
export 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] = {};
|
||||
|
||||
export function renderModule(name, _) {
|
||||
name = String(name || '').toLowerCase();
|
||||
if (arguments.length > 1) {
|
||||
modules[name] = _;
|
||||
return this;
|
||||
} else {
|
||||
return modules[name];
|
||||
}
|
||||
}
|
||||
107
node_modules/vega-scenegraph/src/path/arc.js
generated
vendored
Normal file
107
node_modules/vega-scenegraph/src/path/arc.js
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
import {DegToRad, HalfPi, Tau} from '../util/constants';
|
||||
|
||||
export var segmentCache = {};
|
||||
export var bezierCache = {};
|
||||
|
||||
var join = [].join;
|
||||
|
||||
// Copied from Inkscape svgtopdf, thanks!
|
||||
export function segments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
||||
var key = join.call(arguments);
|
||||
if (segmentCache[key]) {
|
||||
return segmentCache[key];
|
||||
}
|
||||
|
||||
var th = rotateX * DegToRad;
|
||||
var sin_th = Math.sin(th);
|
||||
var cos_th = Math.cos(th);
|
||||
rx = Math.abs(rx);
|
||||
ry = Math.abs(ry);
|
||||
var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
||||
var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
||||
var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
|
||||
if (pl > 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<segs; ++i) {
|
||||
var th2 = th0 + i * th_arc / segs;
|
||||
var th3 = th0 + (i+1) * th_arc / segs;
|
||||
result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
|
||||
}
|
||||
|
||||
return (segmentCache[key] = result);
|
||||
}
|
||||
|
||||
export function bezier(params) {
|
||||
var key = join.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];
|
||||
|
||||
var a00 = cos_th * rx;
|
||||
var a01 = -sin_th * ry;
|
||||
var a10 = sin_th * rx;
|
||||
var a11 = cos_th * ry;
|
||||
|
||||
var cos_th0 = Math.cos(th0);
|
||||
var sin_th0 = Math.sin(th0);
|
||||
var cos_th1 = Math.cos(th1);
|
||||
var sin_th1 = Math.sin(th1);
|
||||
|
||||
var th_half = 0.5 * (th1 - th0);
|
||||
var sin_th_h2 = Math.sin(th_half * 0.5);
|
||||
var t = (8/3) * sin_th_h2 * sin_th_h2 / Math.sin(th_half);
|
||||
var x1 = cx + cos_th0 - t * sin_th0;
|
||||
var y1 = cy + sin_th0 + t * cos_th0;
|
||||
var x3 = cx + cos_th1;
|
||||
var y3 = cy + sin_th1;
|
||||
var x2 = x3 + t * sin_th1;
|
||||
var 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
|
||||
]);
|
||||
}
|
||||
105
node_modules/vega-scenegraph/src/path/curves.js
generated
vendored
Normal file
105
node_modules/vega-scenegraph/src/path/curves.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
import {hasOwnProperty} from 'vega-util';
|
||||
|
||||
import {
|
||||
curveBasis,
|
||||
curveBasisClosed,
|
||||
curveBasisOpen,
|
||||
curveBundle,
|
||||
curveCardinal,
|
||||
curveCardinalClosed,
|
||||
curveCardinalOpen,
|
||||
curveCatmullRom,
|
||||
curveCatmullRomClosed,
|
||||
curveCatmullRomOpen,
|
||||
curveLinear,
|
||||
curveLinearClosed,
|
||||
curveMonotoneX,
|
||||
curveMonotoneY,
|
||||
curveNatural,
|
||||
curveStep,
|
||||
curveStepAfter,
|
||||
curveStepBefore
|
||||
} from 'd3-shape';
|
||||
|
||||
var lookup = {
|
||||
'basis': {
|
||||
curve: curveBasis
|
||||
},
|
||||
'basis-closed': {
|
||||
curve: curveBasisClosed
|
||||
},
|
||||
'basis-open': {
|
||||
curve: curveBasisOpen
|
||||
},
|
||||
'bundle': {
|
||||
curve: curveBundle,
|
||||
tension: 'beta',
|
||||
value: 0.85
|
||||
},
|
||||
'cardinal': {
|
||||
curve: curveCardinal,
|
||||
tension: 'tension',
|
||||
value: 0
|
||||
},
|
||||
'cardinal-open': {
|
||||
curve: curveCardinalOpen,
|
||||
tension: 'tension',
|
||||
value: 0
|
||||
},
|
||||
'cardinal-closed': {
|
||||
curve: curveCardinalClosed,
|
||||
tension: 'tension',
|
||||
value: 0
|
||||
},
|
||||
'catmull-rom': {
|
||||
curve: curveCatmullRom,
|
||||
tension: 'alpha',
|
||||
value: 0.5
|
||||
},
|
||||
'catmull-rom-closed': {
|
||||
curve: curveCatmullRomClosed,
|
||||
tension: 'alpha',
|
||||
value: 0.5
|
||||
},
|
||||
'catmull-rom-open': {
|
||||
curve: curveCatmullRomOpen,
|
||||
tension: 'alpha',
|
||||
value: 0.5
|
||||
},
|
||||
'linear': {
|
||||
curve: curveLinear
|
||||
},
|
||||
'linear-closed': {
|
||||
curve: curveLinearClosed
|
||||
},
|
||||
'monotone': {
|
||||
horizontal: curveMonotoneY,
|
||||
vertical: curveMonotoneX
|
||||
},
|
||||
'natural': {
|
||||
curve: curveNatural
|
||||
},
|
||||
'step': {
|
||||
curve: curveStep
|
||||
},
|
||||
'step-after': {
|
||||
curve: curveStepAfter
|
||||
},
|
||||
'step-before': {
|
||||
curve: curveStepBefore
|
||||
}
|
||||
};
|
||||
|
||||
export default function curves(type, orientation, tension) {
|
||||
var entry = 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;
|
||||
}
|
||||
49
node_modules/vega-scenegraph/src/path/parse.js
generated
vendored
Normal file
49
node_modules/vega-scenegraph/src/path/parse.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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|,|###/];
|
||||
|
||||
export default function(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<n; ++i) {
|
||||
curr = path[i];
|
||||
chunks = curr
|
||||
.slice(1)
|
||||
.trim()
|
||||
.replace(regexp[2],'$1###$2')
|
||||
.split(regexp[3]);
|
||||
cmd = curr.charAt(0);
|
||||
|
||||
parsed = [cmd];
|
||||
for (j=0, m=chunks.length; j<m; ++j) {
|
||||
if ((param = +chunks[j]) === param) { // not NaN
|
||||
parsed.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
len = cmdlen[cmd.toLowerCase()];
|
||||
if (parsed.length-1 > len) {
|
||||
for (j=1, m=parsed.length; j<m; j+=len) {
|
||||
result.push([cmd].concat(parsed.slice(j, j+len)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.push(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
136
node_modules/vega-scenegraph/src/path/rectangle.js
generated
vendored
Normal file
136
node_modules/vega-scenegraph/src/path/rectangle.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
import {path} from 'd3-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(_) {
|
||||
return typeof _ === 'function' ? _ : () => +_;
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
}
|
||||
|
||||
export default function() {
|
||||
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 = 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;
|
||||
}
|
||||
323
node_modules/vega-scenegraph/src/path/render.js
generated
vendored
Normal file
323
node_modules/vega-scenegraph/src/path/render.js
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
import {bezier, segments} from './arc';
|
||||
|
||||
var temp = ['l', 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
function scale(current, sX, sY) {
|
||||
var 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];
|
||||
temp[5] = current[5];
|
||||
temp[6] = sX * current[6];
|
||||
temp[7] = sY * current[7];
|
||||
} else if (c === 'h' || c === 'H') {
|
||||
temp[1] = sX * current[1];
|
||||
} else if (c === 'v' || c === 'V') {
|
||||
temp[1] = sY * current[1];
|
||||
} else {
|
||||
for (var i=1, n=current.length; i<n; ++i) {
|
||||
temp[i] = (i % 2 == 1 ? sX : sY) * current[i];
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
export default function(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;
|
||||
|
||||
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(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];
|
||||
context.moveTo(x + l, y + t);
|
||||
break;
|
||||
|
||||
case 'M': // moveTo, absolute
|
||||
x = current[1];
|
||||
y = current[2];
|
||||
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
|
||||
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
|
||||
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
|
||||
// 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
|
||||
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
|
||||
// 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];
|
||||
|
||||
controlX = x + current[1];
|
||||
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;
|
||||
}
|
||||
else if (previous[0] === 't') {
|
||||
// calculate reflection of previous control points for t
|
||||
controlX = 2 * x - tempControlX;
|
||||
controlY = 2 * y - tempControlY;
|
||||
}
|
||||
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
|
||||
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':
|
||||
context.closePath();
|
||||
break;
|
||||
}
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
function drawArc(context, x, y, coords) {
|
||||
var 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 (var i=0; i<seg.length; ++i) {
|
||||
var bez = bezier(seg[i]);
|
||||
context.bezierCurveTo(bez[0], bez[1], bez[2], bez[3], bez[4], bez[5]);
|
||||
}
|
||||
}
|
||||
91
node_modules/vega-scenegraph/src/path/shapes.js
generated
vendored
Normal file
91
node_modules/vega-scenegraph/src/path/shapes.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
import curves from './curves';
|
||||
import symbols from './symbols';
|
||||
|
||||
import {default as vg_rect} from './rectangle';
|
||||
import {default as vg_trail} from './trail';
|
||||
|
||||
import {
|
||||
arc as d3_arc,
|
||||
area as d3_area,
|
||||
line as d3_line,
|
||||
symbol as d3_symbol
|
||||
} from 'd3-shape';
|
||||
|
||||
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 = d3_arc().startAngle(sa).endAngle(ea).padAngle(pa)
|
||||
.innerRadius(ir).outerRadius(or).cornerRadius(cr),
|
||||
areavShape = d3_area().x(x).y1(y).y0(yh).defined(def),
|
||||
areahShape = d3_area().y(y).x1(x).x0(xw).defined(def),
|
||||
lineShape = d3_line().x(x).y(y).defined(def),
|
||||
rectShape = vg_rect().x(x).y(y).width(w).height(h)
|
||||
.cornerRadius(tl, tr, br, bl),
|
||||
symbolShape = d3_symbol().type(type).size(sz),
|
||||
trailShape = vg_trail().x(x).y(y).defined(def).size(ts);
|
||||
|
||||
export function hasCornerRadius(item) {
|
||||
return item.cornerRadius
|
||||
|| item.cornerRadiusTopLeft
|
||||
|| item.cornerRadiusTopRight
|
||||
|| item.cornerRadiusBottomRight
|
||||
|| item.cornerRadiusBottomLeft;
|
||||
}
|
||||
|
||||
export function arc(context, item) {
|
||||
return arcShape.context(context)(item);
|
||||
}
|
||||
|
||||
export 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);
|
||||
}
|
||||
|
||||
export function line(context, items) {
|
||||
var item = items[0],
|
||||
interp = item.interpolate || 'linear';
|
||||
return lineShape.curve(curves(interp, item.orient, item.tension))
|
||||
.context(context)(items);
|
||||
}
|
||||
|
||||
export function rectangle(context, item, x, y) {
|
||||
return rectShape.context(context)(item, x, y);
|
||||
}
|
||||
|
||||
export function shape(context, item) {
|
||||
return (item.mark.shape || item.shape)
|
||||
.context(context)(item);
|
||||
}
|
||||
|
||||
export function symbol(context, item) {
|
||||
return symbolShape.context(context)(item);
|
||||
}
|
||||
|
||||
export function trail(context, items) {
|
||||
return trailShape.context(context)(items);
|
||||
}
|
||||
156
node_modules/vega-scenegraph/src/path/symbols.js
generated
vendored
Normal file
156
node_modules/vega-scenegraph/src/path/symbols.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
import pathParse from './parse';
|
||||
import pathRender from './render';
|
||||
import {HalfSqrt3, Tau} from '../util/constants';
|
||||
import {hasOwnProperty} from 'vega-util';
|
||||
|
||||
var Tan30 = 0.5773502691896257;
|
||||
|
||||
var builtins = {
|
||||
'circle': {
|
||||
draw: function(context, size) {
|
||||
var r = Math.sqrt(size) / 2;
|
||||
context.moveTo(r, 0);
|
||||
context.arc(0, 0, r, 0, Tau);
|
||||
}
|
||||
},
|
||||
'cross': {
|
||||
draw: function(context, size) {
|
||||
var r = Math.sqrt(size) / 2,
|
||||
s = r / 2.5;
|
||||
context.moveTo(-r, -s);
|
||||
context.lineTo(-r, s);
|
||||
context.lineTo(-s, s);
|
||||
context.lineTo(-s, r);
|
||||
context.lineTo(s, r);
|
||||
context.lineTo(s, s);
|
||||
context.lineTo(r, s);
|
||||
context.lineTo(r, -s);
|
||||
context.lineTo(s, -s);
|
||||
context.lineTo(s, -r);
|
||||
context.lineTo(-s, -r);
|
||||
context.lineTo(-s, -s);
|
||||
context.closePath();
|
||||
}
|
||||
},
|
||||
'diamond': {
|
||||
draw: function(context, size) {
|
||||
var r = Math.sqrt(size) / 2;
|
||||
context.moveTo(-r, 0);
|
||||
context.lineTo(0, -r);
|
||||
context.lineTo(r, 0);
|
||||
context.lineTo(0, r);
|
||||
context.closePath();
|
||||
}
|
||||
},
|
||||
'square': {
|
||||
draw: function(context, size) {
|
||||
var w = Math.sqrt(size),
|
||||
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;
|
||||
context.moveTo(-s, r);
|
||||
context.lineTo(s, r);
|
||||
context.lineTo(s, -v);
|
||||
context.lineTo(t, -v);
|
||||
context.lineTo(0, -r);
|
||||
context.lineTo(-t, -v);
|
||||
context.lineTo(-s, -v);
|
||||
context.closePath();
|
||||
}
|
||||
},
|
||||
'wedge': {
|
||||
draw: function(context, size) {
|
||||
var r = Math.sqrt(size) / 2,
|
||||
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);
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
context.moveTo(-h, 0);
|
||||
context.lineTo(h, -r);
|
||||
context.lineTo(h, r);
|
||||
context.closePath();
|
||||
}
|
||||
},
|
||||
'stroke': {
|
||||
draw: function(context, size) {
|
||||
var r = Math.sqrt(size) / 2;
|
||||
context.moveTo(-r, 0);
|
||||
context.lineTo(r, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default function symbols(_) {
|
||||
return hasOwnProperty(builtins, _) ? builtins[_] : customSymbol(_);
|
||||
}
|
||||
|
||||
var custom = {};
|
||||
|
||||
function customSymbol(path) {
|
||||
if (!hasOwnProperty(custom, path)) {
|
||||
var parsed = pathParse(path);
|
||||
custom[path] = {
|
||||
draw: function(context, size) {
|
||||
pathRender(context, parsed, 0, 0, Math.sqrt(size) / 2);
|
||||
}
|
||||
};
|
||||
}
|
||||
return custom[path];
|
||||
}
|
||||
116
node_modules/vega-scenegraph/src/path/trail.js
generated
vendored
Normal file
116
node_modules/vega-scenegraph/src/path/trail.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import {Tau} from '../util/constants';
|
||||
import {path} from 'd3-path';
|
||||
|
||||
export default function() {
|
||||
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 = 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;
|
||||
}
|
||||
167
node_modules/vega-scenegraph/src/util/aria.js
generated
vendored
Normal file
167
node_modules/vega-scenegraph/src/util/aria.js
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
import {domainCaption, isDiscrete} from 'vega-scale';
|
||||
import {array, peek, toSet} from 'vega-util';
|
||||
|
||||
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
|
||||
// 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'
|
||||
]);
|
||||
|
||||
// aria attribute generators for guide roles
|
||||
const AriaGuides = {
|
||||
'axis': {desc: 'axis', caption: axisCaption},
|
||||
'legend': {desc: 'legend', caption: legendCaption},
|
||||
'title-text': {
|
||||
desc: 'title',
|
||||
caption: item => `Title text '${titleCaption(item)}'`
|
||||
},
|
||||
'title-subtitle': {
|
||||
desc: 'subtitle',
|
||||
caption: item => `Subtitle text '${titleCaption(item)}'`
|
||||
}
|
||||
};
|
||||
|
||||
// aria properties generated for mark item encoding channels
|
||||
export const AriaEncode = {
|
||||
ariaRole: ARIA_ROLE,
|
||||
ariaRoleDescription: ARIA_ROLEDESCRIPTION,
|
||||
description: ARIA_LABEL
|
||||
};
|
||||
|
||||
export 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 || `${type} mark`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export 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,
|
||||
`${type} mark container`,
|
||||
mark.description
|
||||
);
|
||||
}
|
||||
|
||||
function ariaGuide(mark, opt) {
|
||||
try {
|
||||
const item = mark.items[0],
|
||||
caption = opt.caption || (() => '');
|
||||
return bundle(
|
||||
opt.role || GRAPHICS_SYMBOL,
|
||||
opt.desc,
|
||||
item.description || caption(item)
|
||||
);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function titleCaption(item) {
|
||||
return array(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 `${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 = `${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(peek(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(props);
|
||||
}
|
||||
|
||||
function capitalize(s) {
|
||||
return s.length ? s[0].toUpperCase() + s.slice(1) : s;
|
||||
}
|
||||
3
node_modules/vega-scenegraph/src/util/canvas/blend.js
generated
vendored
Normal file
3
node_modules/vega-scenegraph/src/util/canvas/blend.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function(context, item) {
|
||||
context.globalCompositeOperation = item.blend || 'source-over';
|
||||
}
|
||||
24
node_modules/vega-scenegraph/src/util/canvas/clip.js
generated
vendored
Normal file
24
node_modules/vega-scenegraph/src/util/canvas/clip.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import {hasCornerRadius, rectangle} from '../../path/shapes';
|
||||
import {isFunction} from 'vega-util';
|
||||
|
||||
export default function(context, scene) {
|
||||
var clip = scene.clip;
|
||||
|
||||
context.save();
|
||||
|
||||
if (isFunction(clip)) {
|
||||
context.beginPath();
|
||||
clip(context);
|
||||
context.clip();
|
||||
} else {
|
||||
clipGroup(context, scene.group);
|
||||
}
|
||||
}
|
||||
|
||||
export 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();
|
||||
}
|
||||
8
node_modules/vega-scenegraph/src/util/canvas/color.js
generated
vendored
Normal file
8
node_modules/vega-scenegraph/src/util/canvas/color.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import {isGradient} from '../../Gradient';
|
||||
import gradient from './gradient';
|
||||
|
||||
export default function(context, item, value) {
|
||||
return isGradient(value)
|
||||
? gradient(context, value, item.bounds)
|
||||
: value;
|
||||
}
|
||||
5
node_modules/vega-scenegraph/src/util/canvas/context.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/util/canvas/context.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import {canvas} from 'vega-canvas';
|
||||
|
||||
export var context = (context = canvas(1,1))
|
||||
? context.getContext('2d')
|
||||
: null;
|
||||
39
node_modules/vega-scenegraph/src/util/canvas/draw.js
generated
vendored
Normal file
39
node_modules/vega-scenegraph/src/util/canvas/draw.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import blend from './blend';
|
||||
import fill from './fill';
|
||||
import stroke from './stroke';
|
||||
import {visit} from '../visit';
|
||||
|
||||
export function drawAll(path) {
|
||||
return function(context, scene, bounds) {
|
||||
visit(scene, function(item) {
|
||||
if (!bounds || bounds.intersects(item.bounds)) {
|
||||
drawPath(path, context, item, item);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export 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();
|
||||
}
|
||||
}
|
||||
12
node_modules/vega-scenegraph/src/util/canvas/fill.js
generated
vendored
Normal file
12
node_modules/vega-scenegraph/src/util/canvas/fill.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import color from './color';
|
||||
|
||||
export default function(context, item, opacity) {
|
||||
opacity *= (item.fillOpacity==null ? 1 : item.fillOpacity);
|
||||
if (opacity > 0) {
|
||||
context.globalAlpha = opacity;
|
||||
context.fillStyle = color(context, item, item.fill);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
59
node_modules/vega-scenegraph/src/util/canvas/gradient.js
generated
vendored
Normal file
59
node_modules/vega-scenegraph/src/util/canvas/gradient.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
import value from '../value';
|
||||
import {canvas} from 'vega-canvas';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export default function(context, spec, bounds) {
|
||||
const w = bounds.width(),
|
||||
h = bounds.height();
|
||||
let gradient;
|
||||
|
||||
|
||||
if (spec.gradient === 'radial') {
|
||||
gradient = context.createRadialGradient(
|
||||
bounds.x1 + value(spec.x1, 0.5) * w,
|
||||
bounds.y1 + value(spec.y1, 0.5) * h,
|
||||
Math.max(w, h) * value(spec.r1, 0),
|
||||
bounds.x1 + value(spec.x2, 0.5) * w,
|
||||
bounds.y1 + value(spec.y2, 0.5) * h,
|
||||
Math.max(w, h) * value(spec.r2, 0.5)
|
||||
);
|
||||
} else { // linear gradient
|
||||
const x1 = value(spec.x1, 0),
|
||||
y1 = value(spec.y1, 0),
|
||||
x2 = value(spec.x2, 1),
|
||||
y2 = value(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 = canvas(Math.ceil(w), Math.ceil(h)),
|
||||
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);
|
||||
}
|
||||
4
node_modules/vega-scenegraph/src/util/canvas/image.js
generated
vendored
Normal file
4
node_modules/vega-scenegraph/src/util/canvas/image.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import {Canvas} from './canvas';
|
||||
|
||||
export default typeof Image !== 'undefined' ? Image
|
||||
: (Canvas && Canvas.Image || null);
|
||||
42
node_modules/vega-scenegraph/src/util/canvas/pick.js
generated
vendored
Normal file
42
node_modules/vega-scenegraph/src/util/canvas/pick.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
import {pickVisit} from '../visit';
|
||||
import {truthy} from 'vega-util';
|
||||
|
||||
export function pick(test) {
|
||||
test = test || 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;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export 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));
|
||||
};
|
||||
}
|
||||
|
||||
export function pickPath(path) {
|
||||
return pick(hitPath(path));
|
||||
}
|
||||
34
node_modules/vega-scenegraph/src/util/canvas/resize.js
generated
vendored
Normal file
34
node_modules/vega-scenegraph/src/util/canvas/resize.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
function devicePixelRatio() {
|
||||
return typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
|
||||
}
|
||||
|
||||
var pixelRatio = devicePixelRatio();
|
||||
|
||||
export default function(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;
|
||||
|
||||
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;
|
||||
}
|
||||
28
node_modules/vega-scenegraph/src/util/canvas/stroke.js
generated
vendored
Normal file
28
node_modules/vega-scenegraph/src/util/canvas/stroke.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import color from './color';
|
||||
|
||||
var Empty = [];
|
||||
|
||||
export default function(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;
|
||||
}
|
||||
}
|
||||
5
node_modules/vega-scenegraph/src/util/constants.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/util/constants.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const DegToRad = Math.PI / 180;
|
||||
export const Epsilon = 1e-14;
|
||||
export const HalfPi = Math.PI / 2;
|
||||
export const Tau = Math.PI * 2;
|
||||
export const HalfSqrt3 = Math.sqrt(3) / 2;
|
||||
45
node_modules/vega-scenegraph/src/util/dom.js
generated
vendored
Normal file
45
node_modules/vega-scenegraph/src/util/dom.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// create a new DOM element
|
||||
export 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
|
||||
export function domFind(el, tag) {
|
||||
tag = tag.toLowerCase();
|
||||
var nodes = el.childNodes, 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
|
||||
export function domChild(el, index, tag, ns) {
|
||||
var a = el.childNodes[index], 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
|
||||
export function domClear(el, index) {
|
||||
var nodes = el.childNodes,
|
||||
curr = nodes.length;
|
||||
while (curr > index) el.removeChild(nodes[--curr]);
|
||||
return el;
|
||||
}
|
||||
|
||||
// generate css class name for mark
|
||||
export function cssClass(mark) {
|
||||
return 'mark-' + mark.marktype
|
||||
+ (mark.role ? ' role-' + mark.role : '')
|
||||
+ (mark.name ? ' ' + mark.name : '');
|
||||
}
|
||||
40
node_modules/vega-scenegraph/src/util/equal.js
generated
vendored
Normal file
40
node_modules/vega-scenegraph/src/util/equal.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import pathParse from '../path/parse';
|
||||
import {isNumber, isObject} from 'vega-util';
|
||||
|
||||
var TOLERANCE = 1e-9;
|
||||
|
||||
export function sceneEqual(a, b, key) {
|
||||
return (a === b) ? true
|
||||
: (key === 'path') ? pathEqual(a, b)
|
||||
: (a instanceof Date && b instanceof Date) ? +a === +b
|
||||
: (isNumber(a) && isNumber(b)) ? Math.abs(a - b) <= TOLERANCE
|
||||
: (!a || !b || !isObject(a) && !isObject(b)) ? a == b
|
||||
: (a == null || b == null) ? false
|
||||
: objectEqual(a, b);
|
||||
}
|
||||
|
||||
export function pathEqual(a, b) {
|
||||
return sceneEqual(pathParse(a), pathParse(b));
|
||||
}
|
||||
|
||||
function objectEqual(a, b) {
|
||||
var ka = Object.keys(a),
|
||||
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;
|
||||
}
|
||||
45
node_modules/vega-scenegraph/src/util/events.js
generated
vendored
Normal file
45
node_modules/vega-scenegraph/src/util/events.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
export const KeyDownEvent = 'keydown';
|
||||
export const KeyPressEvent = 'keypress';
|
||||
export const KeyUpEvent = 'keyup';
|
||||
export const DragEnterEvent = 'dragenter';
|
||||
export const DragLeaveEvent = 'dragleave';
|
||||
export const DragOverEvent = 'dragover';
|
||||
export const MouseDownEvent = 'mousedown';
|
||||
export const MouseUpEvent = 'mouseup';
|
||||
export const MouseMoveEvent = 'mousemove';
|
||||
export const MouseOutEvent = 'mouseout';
|
||||
export const MouseOverEvent = 'mouseover';
|
||||
export const ClickEvent = 'click';
|
||||
export const DoubleClickEvent = 'dblclick';
|
||||
export const WheelEvent = 'wheel';
|
||||
export const MouseWheelEvent = 'mousewheel';
|
||||
export const TouchStartEvent = 'touchstart';
|
||||
export const TouchMoveEvent = 'touchmove';
|
||||
export const TouchEndEvent = 'touchend';
|
||||
|
||||
export const Events = [
|
||||
KeyDownEvent,
|
||||
KeyPressEvent,
|
||||
KeyUpEvent,
|
||||
DragEnterEvent,
|
||||
DragLeaveEvent,
|
||||
DragOverEvent,
|
||||
MouseDownEvent,
|
||||
MouseUpEvent,
|
||||
MouseMoveEvent,
|
||||
MouseOutEvent,
|
||||
MouseOverEvent,
|
||||
ClickEvent,
|
||||
DoubleClickEvent,
|
||||
WheelEvent,
|
||||
MouseWheelEvent,
|
||||
TouchStartEvent,
|
||||
TouchMoveEvent,
|
||||
TouchEndEvent
|
||||
];
|
||||
|
||||
export const TooltipShowEvent = MouseMoveEvent;
|
||||
|
||||
export const TooltipHideEvent = MouseOutEvent;
|
||||
|
||||
export const HrefEvent = ClickEvent;
|
||||
80
node_modules/vega-scenegraph/src/util/intersect.js
generated
vendored
Normal file
80
node_modules/vega-scenegraph/src/util/intersect.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
import {context} from './canvas/context';
|
||||
import Bounds from '../Bounds';
|
||||
|
||||
const b = new Bounds();
|
||||
|
||||
export function intersectPath(draw) {
|
||||
return function(item, brush) {
|
||||
// rely on (inaccurate) bounds intersection if no context
|
||||
if (!context) return true;
|
||||
|
||||
// add path to offscreen graphics context
|
||||
draw(context, 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
|
||||
for (let y = y1; y <= y2; ++y) {
|
||||
for (let x = x1; x <= x2; ++x) {
|
||||
if (context.isPointInPath(x, y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// false if no hits in intersection region
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export function intersectPoint(item, box) {
|
||||
return box.contains(item.x || 0, item.y || 0);
|
||||
}
|
||||
|
||||
export function intersectRect(item, box) {
|
||||
const x = item.x || 0,
|
||||
y = item.y || 0,
|
||||
w = item.width || 0,
|
||||
h = item.height || 0;
|
||||
return box.intersects(b.set(x, y, x + w, y + h));
|
||||
}
|
||||
|
||||
export 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;
|
||||
return intersectBoxLine(box, x, y, x2, y2);
|
||||
}
|
||||
|
||||
export function intersectBoxLine(box, x, y, u, v) {
|
||||
const {x1, y1, x2, y2} = box,
|
||||
dx = u - x,
|
||||
dy = v - y;
|
||||
|
||||
let t0 = 0, 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;
|
||||
}
|
||||
47
node_modules/vega-scenegraph/src/util/pickPath.js
generated
vendored
Normal file
47
node_modules/vega-scenegraph/src/util/pickPath.js
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
export 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;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
7
node_modules/vega-scenegraph/src/util/point.js
generated
vendored
Normal file
7
node_modules/vega-scenegraph/src/util/point.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function(event, el) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
return [
|
||||
event.clientX - rect.left - (el.clientLeft || 0),
|
||||
event.clientY - rect.top - (el.clientTop || 0)
|
||||
];
|
||||
}
|
||||
20
node_modules/vega-scenegraph/src/util/resolveItem.js
generated
vendored
Normal file
20
node_modules/vega-scenegraph/src/util/resolveItem.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import marks from '../marks/index';
|
||||
import point from './point';
|
||||
|
||||
export default function(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;
|
||||
}
|
||||
50
node_modules/vega-scenegraph/src/util/serialize.js
generated
vendored
Normal file
50
node_modules/vega-scenegraph/src/util/serialize.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import boundMark from '../bound/boundMark';
|
||||
|
||||
var keys = [
|
||||
'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
|
||||
];
|
||||
|
||||
export function sceneToJSON(scene, indent) {
|
||||
return JSON.stringify(scene, keys, indent);
|
||||
}
|
||||
|
||||
export function sceneFromJSON(json) {
|
||||
var scene = (typeof json === 'string' ? JSON.parse(json) : json);
|
||||
return initialize(scene);
|
||||
}
|
||||
|
||||
function initialize(scene) {
|
||||
var type = scene.marktype,
|
||||
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(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (type) boundMark(scene);
|
||||
return scene;
|
||||
}
|
||||
26
node_modules/vega-scenegraph/src/util/svg/clip.js
generated
vendored
Normal file
26
node_modules/vega-scenegraph/src/util/svg/clip.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import {hasCornerRadius, rectangle} from '../../path/shapes';
|
||||
import {isFunction} from 'vega-util';
|
||||
|
||||
var clip_id = 1;
|
||||
|
||||
export function resetSVGClipId() {
|
||||
clip_id = 1;
|
||||
}
|
||||
|
||||
export default function(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 (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 + ')';
|
||||
}
|
||||
5
node_modules/vega-scenegraph/src/util/svg/metadata.js
generated
vendored
Normal file
5
node_modules/vega-scenegraph/src/util/svg/metadata.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
'version': '1.1',
|
||||
'xmlns': 'http://www.w3.org/2000/svg',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink'
|
||||
};
|
||||
20
node_modules/vega-scenegraph/src/util/svg/styles.js
generated
vendored
Normal file
20
node_modules/vega-scenegraph/src/util/svg/styles.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export const 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',
|
||||
blend: 'mix-blend-mode'
|
||||
};
|
||||
|
||||
// ensure miter limit default is consistent with canvas (#2498)
|
||||
export const rootAttributes = {
|
||||
'fill': 'none',
|
||||
'stroke-miterlimit': 10
|
||||
};
|
||||
21
node_modules/vega-scenegraph/src/util/svg/transform.js
generated
vendored
Normal file
21
node_modules/vega-scenegraph/src/util/svg/transform.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
export function translate(x, y) {
|
||||
return 'translate(' + x + ',' + y + ')';
|
||||
}
|
||||
|
||||
export function rotate(a) {
|
||||
return 'rotate(' + a + ')';
|
||||
}
|
||||
|
||||
export function scale(scaleX, scaleY){
|
||||
return 'scale('+ scaleX + ',' + scaleY+')';
|
||||
}
|
||||
|
||||
export function translateItem(item) {
|
||||
return translate(item.x || 0, item.y || 0);
|
||||
}
|
||||
|
||||
export function transformItem(item) {
|
||||
return translate(item.x || 0, item.y || 0)
|
||||
+ (item.angle ? ' ' + rotate(item.angle) : '')
|
||||
+ (item.scaleX || item.scaleY ? ' ' + scale(item.scaleX || 1, item.scaleY || 1) : '');
|
||||
}
|
||||
31
node_modules/vega-scenegraph/src/util/tags.js
generated
vendored
Normal file
31
node_modules/vega-scenegraph/src/util/tags.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
const attrText = val => (val + '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"');
|
||||
|
||||
/**
|
||||
* Generate string for an opening xml tag.
|
||||
* @param tag the name of the xml tag
|
||||
* @param attr hash of attribute name-value pairs to include
|
||||
* @param raw additional raw string to include in tag markup
|
||||
*/
|
||||
export function openTag(tag, attr, raw) {
|
||||
var s = '<' + tag, key, val;
|
||||
if (attr) {
|
||||
for (key in attr) {
|
||||
val = attr[key];
|
||||
if (val != null) {
|
||||
s += ' ' + key + '="' + attrText(val) + '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (raw) s += ' ' + raw;
|
||||
return s + '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate string for closing xml tag.
|
||||
* @param tag the name of the xml tag
|
||||
*/
|
||||
export function closeTag(tag) {
|
||||
return '</' + tag + '>';
|
||||
}
|
||||
149
node_modules/vega-scenegraph/src/util/text.js
generated
vendored
Normal file
149
node_modules/vega-scenegraph/src/util/text.js
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
import {context} from './canvas/context';
|
||||
import {isArray, lruCache} from 'vega-util';
|
||||
|
||||
// memoize text width measurement
|
||||
const widthCache = lruCache();
|
||||
|
||||
export var textMetrics = {
|
||||
height: fontSize,
|
||||
measureWidth: measureWidth,
|
||||
estimateWidth: estimateWidth,
|
||||
width: estimateWidth,
|
||||
canvas: useCanvas
|
||||
};
|
||||
|
||||
useCanvas(true);
|
||||
|
||||
function useCanvas(use) {
|
||||
textMetrics.width = (use && context) ? measureWidth : estimateWidth;
|
||||
}
|
||||
|
||||
// make dumb, 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
|
||||
function measureWidth(item, text) {
|
||||
return fontSize(item) <= 0 || !(text = textValue(item, text)) ? 0
|
||||
: _measureWidth(text, font(item));
|
||||
}
|
||||
|
||||
function _measureWidth(text, currentFont) {
|
||||
const key = `(${currentFont}) ${text}`;
|
||||
let width = widthCache.get(key);
|
||||
if (width === undefined) {
|
||||
context.font = currentFont;
|
||||
width = context.measureText(text).width;
|
||||
widthCache.set(key, width);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
export function fontSize(item) {
|
||||
return item.fontSize != null ? (+item.fontSize || 0) : 11;
|
||||
}
|
||||
|
||||
export function lineHeight(item) {
|
||||
return item.lineHeight != null ? item.lineHeight : (fontSize(item) + 2);
|
||||
}
|
||||
|
||||
function lineArray(_) {
|
||||
return isArray(_) ? _.length > 1 ? _ : _[0] : _;
|
||||
}
|
||||
|
||||
export function textLines(item) {
|
||||
return lineArray(
|
||||
item.lineBreak && item.text && !isArray(item.text)
|
||||
? item.text.split(item.lineBreak)
|
||||
: item.text
|
||||
);
|
||||
}
|
||||
|
||||
export function multiLineOffset(item) {
|
||||
const tl = textLines(item);
|
||||
return (isArray(tl) ? (tl.length - 1) : 0) * lineHeight(item);
|
||||
}
|
||||
|
||||
export 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 {
|
||||
// we are relying on estimates
|
||||
const currentFontHeight = fontSize(item);
|
||||
return text => _estimateWidth(text, currentFontHeight);
|
||||
}
|
||||
}
|
||||
|
||||
function truncate(item, text) {
|
||||
var limit = +item.limit,
|
||||
width = widthGetter(item);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export function fontFamily(item, quote) {
|
||||
var font = item.font;
|
||||
return (quote && font
|
||||
? String(font).replace(/"/g, '\'')
|
||||
: font) || 'sans-serif';
|
||||
}
|
||||
|
||||
export function font(item, quote) {
|
||||
return '' +
|
||||
(item.fontStyle ? item.fontStyle + ' ' : '') +
|
||||
(item.fontVariant ? item.fontVariant + ' ' : '') +
|
||||
(item.fontWeight ? item.fontWeight + ' ' : '') +
|
||||
fontSize(item) + 'px ' +
|
||||
fontFamily(item, quote);
|
||||
}
|
||||
|
||||
export function offset(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);
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
3
node_modules/vega-scenegraph/src/util/value.js
generated
vendored
Normal file
3
node_modules/vega-scenegraph/src/util/value.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function(value, dflt) {
|
||||
return value == null ? dflt : value;
|
||||
}
|
||||
59
node_modules/vega-scenegraph/src/util/visit.js
generated
vendored
Normal file
59
node_modules/vega-scenegraph/src/util/visit.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
function compare(a, b) {
|
||||
return a.zindex - b.zindex || a.index - b.index;
|
||||
}
|
||||
|
||||
export function zorder(scene) {
|
||||
if (!scene.zdirty) return scene.zitems;
|
||||
|
||||
var items = scene.items,
|
||||
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);
|
||||
}
|
||||
|
||||
export function visit(scene, visitor) {
|
||||
var items = scene.items, i, n;
|
||||
if (!items || !items.length) return;
|
||||
|
||||
var 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]);
|
||||
}
|
||||
}
|
||||
|
||||
export function pickVisit(scene, visitor) {
|
||||
var items = scene.items, hit, i;
|
||||
if (!items || !items.length) return null;
|
||||
|
||||
var 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;
|
||||
}
|
||||
Reference in New Issue
Block a user