// https://d3js.org/d3-brush/ v3.0.0 Copyright 2010-2021 Mike Bostock (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-dispatch'), require('d3-drag'), require('d3-interpolate'), require('d3-selection'), require('d3-transition')) : typeof define === 'function' && define.amd ? define(['exports', 'd3-dispatch', 'd3-drag', 'd3-interpolate', 'd3-selection', 'd3-transition'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3)); }(this, (function (exports, d3Dispatch, d3Drag, d3Interpolate, d3Selection, d3Transition) { 'use strict'; var constant = x => () => x; function BrushEvent(type, { sourceEvent, target, selection, mode, dispatch }) { Object.defineProperties(this, { type: {value: type, enumerable: true, configurable: true}, sourceEvent: {value: sourceEvent, enumerable: true, configurable: true}, target: {value: target, enumerable: true, configurable: true}, selection: {value: selection, enumerable: true, configurable: true}, mode: {value: mode, enumerable: true, configurable: true}, _: {value: dispatch} }); } function nopropagation(event) { event.stopImmediatePropagation(); } function noevent(event) { event.preventDefault(); event.stopImmediatePropagation(); } var MODE_DRAG = {name: "drag"}, MODE_SPACE = {name: "space"}, MODE_HANDLE = {name: "handle"}, MODE_CENTER = {name: "center"}; const {abs, max, min} = Math; function number1(e) { return [+e[0], +e[1]]; } function number2(e) { return [number1(e[0]), number1(e[1])]; } var X = { name: "x", handles: ["w", "e"].map(type), input: function(x, e) { return x == null ? null : [[+x[0], e[0][1]], [+x[1], e[1][1]]]; }, output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } }; var Y = { name: "y", handles: ["n", "s"].map(type), input: function(y, e) { return y == null ? null : [[e[0][0], +y[0]], [e[1][0], +y[1]]]; }, output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } }; var XY = { name: "xy", handles: ["n", "w", "e", "s", "nw", "ne", "sw", "se"].map(type), input: function(xy) { return xy == null ? null : number2(xy); }, output: function(xy) { return xy; } }; var cursors = { overlay: "crosshair", selection: "move", n: "ns-resize", e: "ew-resize", s: "ns-resize", w: "ew-resize", nw: "nwse-resize", ne: "nesw-resize", se: "nwse-resize", sw: "nesw-resize" }; var flipX = { e: "w", w: "e", nw: "ne", ne: "nw", se: "sw", sw: "se" }; var flipY = { n: "s", s: "n", nw: "sw", ne: "se", se: "ne", sw: "nw" }; var signsX = { overlay: +1, selection: +1, n: null, e: +1, s: null, w: -1, nw: -1, ne: +1, se: +1, sw: -1 }; var signsY = { overlay: +1, selection: +1, n: -1, e: null, s: +1, w: null, nw: -1, ne: -1, se: +1, sw: +1 }; function type(t) { return {type: t}; } // Ignore right-click, since that should open the context menu. function defaultFilter(event) { return !event.ctrlKey && !event.button; } function defaultExtent() { var svg = this.ownerSVGElement || this; if (svg.hasAttribute("viewBox")) { svg = svg.viewBox.baseVal; return [[svg.x, svg.y], [svg.x + svg.width, svg.y + svg.height]]; } return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; } function defaultTouchable() { return navigator.maxTouchPoints || ("ontouchstart" in this); } // Like d3.local, but with the name “__brush” rather than auto-generated. function local(node) { while (!node.__brush) if (!(node = node.parentNode)) return; return node.__brush; } function empty(extent) { return extent[0][0] === extent[1][0] || extent[0][1] === extent[1][1]; } function brushSelection(node) { var state = node.__brush; return state ? state.dim.output(state.selection) : null; } function brushX() { return brush$1(X); } function brushY() { return brush$1(Y); } function brush() { return brush$1(XY); } function brush$1(dim) { var extent = defaultExtent, filter = defaultFilter, touchable = defaultTouchable, keys = true, listeners = d3Dispatch.dispatch("start", "brush", "end"), handleSize = 6, touchending; function brush(group) { var overlay = group .property("__brush", initialize) .selectAll(".overlay") .data([type("overlay")]); overlay.enter().append("rect") .attr("class", "overlay") .attr("pointer-events", "all") .attr("cursor", cursors.overlay) .merge(overlay) .each(function() { var extent = local(this).extent; d3Selection.select(this) .attr("x", extent[0][0]) .attr("y", extent[0][1]) .attr("width", extent[1][0] - extent[0][0]) .attr("height", extent[1][1] - extent[0][1]); }); group.selectAll(".selection") .data([type("selection")]) .enter().append("rect") .attr("class", "selection") .attr("cursor", cursors.selection) .attr("fill", "#777") .attr("fill-opacity", 0.3) .attr("stroke", "#fff") .attr("shape-rendering", "crispEdges"); var handle = group.selectAll(".handle") .data(dim.handles, function(d) { return d.type; }); handle.exit().remove(); handle.enter().append("rect") .attr("class", function(d) { return "handle handle--" + d.type; }) .attr("cursor", function(d) { return cursors[d.type]; }); group .each(redraw) .attr("fill", "none") .attr("pointer-events", "all") .on("mousedown.brush", started) .filter(touchable) .on("touchstart.brush", started) .on("touchmove.brush", touchmoved) .on("touchend.brush touchcancel.brush", touchended) .style("touch-action", "none") .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); } brush.move = function(group, selection, event) { if (group.tween) { group .on("start.brush", function(event) { emitter(this, arguments).beforestart().start(event); }) .on("interrupt.brush end.brush", function(event) { emitter(this, arguments).end(event); }) .tween("brush", function() { var that = this, state = that.__brush, emit = emitter(that, arguments), selection0 = state.selection, selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent), i = d3Interpolate.interpolate(selection0, selection1); function tween(t) { state.selection = t === 1 && selection1 === null ? null : i(t); redraw.call(that); emit.brush(); } return selection0 !== null && selection1 !== null ? tween : tween(1); }); } else { group .each(function() { var that = this, args = arguments, state = that.__brush, selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent), emit = emitter(that, args).beforestart(); d3Transition.interrupt(that); state.selection = selection1 === null ? null : selection1; redraw.call(that); emit.start(event).brush(event).end(event); }); } }; brush.clear = function(group, event) { brush.move(group, null, event); }; function redraw() { var group = d3Selection.select(this), selection = local(this).selection; if (selection) { group.selectAll(".selection") .style("display", null) .attr("x", selection[0][0]) .attr("y", selection[0][1]) .attr("width", selection[1][0] - selection[0][0]) .attr("height", selection[1][1] - selection[0][1]); group.selectAll(".handle") .style("display", null) .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; }) .attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; }) .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; }) .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; }); } else { group.selectAll(".selection,.handle") .style("display", "none") .attr("x", null) .attr("y", null) .attr("width", null) .attr("height", null); } } function emitter(that, args, clean) { var emit = that.__brush.emitter; return emit && (!clean || !emit.clean) ? emit : new Emitter(that, args, clean); } function Emitter(that, args, clean) { this.that = that; this.args = args; this.state = that.__brush; this.active = 0; this.clean = clean; } Emitter.prototype = { beforestart: function() { if (++this.active === 1) this.state.emitter = this, this.starting = true; return this; }, start: function(event, mode) { if (this.starting) this.starting = false, this.emit("start", event, mode); else this.emit("brush", event); return this; }, brush: function(event, mode) { this.emit("brush", event, mode); return this; }, end: function(event, mode) { if (--this.active === 0) delete this.state.emitter, this.emit("end", event, mode); return this; }, emit: function(type, event, mode) { var d = d3Selection.select(this.that).datum(); listeners.call( type, this.that, new BrushEvent(type, { sourceEvent: event, target: brush, selection: dim.output(this.state.selection), mode, dispatch: listeners }), d ); } }; function started(event) { if (touchending && !event.touches) return; if (!filter.apply(this, arguments)) return; var that = this, type = event.target.__data__.type, mode = (keys && event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (keys && event.altKey ? MODE_CENTER : MODE_HANDLE), signX = dim === Y ? null : signsX[type], signY = dim === X ? null : signsY[type], state = local(that), extent = state.extent, selection = state.selection, W = extent[0][0], w0, w1, N = extent[0][1], n0, n1, E = extent[1][0], e0, e1, S = extent[1][1], s0, s1, dx = 0, dy = 0, moving, shifting = signX && signY && keys && event.shiftKey, lockX, lockY, points = Array.from(event.touches || [event], t => { const i = t.identifier; t = d3Selection.pointer(t, that); t.point0 = t.slice(); t.identifier = i; return t; }); d3Transition.interrupt(that); var emit = emitter(that, arguments, true).beforestart(); if (type === "overlay") { if (selection) moving = true; const pts = [points[0], points[1] || points[0]]; state.selection = selection = [[ w0 = dim === Y ? W : min(pts[0][0], pts[1][0]), n0 = dim === X ? N : min(pts[0][1], pts[1][1]) ], [ e0 = dim === Y ? E : max(pts[0][0], pts[1][0]), s0 = dim === X ? S : max(pts[0][1], pts[1][1]) ]]; if (points.length > 1) move(event); } else { w0 = selection[0][0]; n0 = selection[0][1]; e0 = selection[1][0]; s0 = selection[1][1]; } w1 = w0; n1 = n0; e1 = e0; s1 = s0; var group = d3Selection.select(that) .attr("pointer-events", "none"); var overlay = group.selectAll(".overlay") .attr("cursor", cursors[type]); if (event.touches) { emit.moved = moved; emit.ended = ended; } else { var view = d3Selection.select(event.view) .on("mousemove.brush", moved, true) .on("mouseup.brush", ended, true); if (keys) view .on("keydown.brush", keydowned, true) .on("keyup.brush", keyupped, true); d3Drag.dragDisable(event.view); } redraw.call(that); emit.start(event, mode.name); function moved(event) { for (const p of event.changedTouches || [event]) { for (const d of points) if (d.identifier === p.identifier) d.cur = d3Selection.pointer(p, that); } if (shifting && !lockX && !lockY && points.length === 1) { const point = points[0]; if (abs(point.cur[0] - point[0]) > abs(point.cur[1] - point[1])) lockY = true; else lockX = true; } for (const point of points) if (point.cur) point[0] = point.cur[0], point[1] = point.cur[1]; moving = true; noevent(event); move(event); } function move(event) { const point = points[0], point0 = point.point0; var t; dx = point[0] - point0[0]; dy = point[1] - point0[1]; switch (mode) { case MODE_SPACE: case MODE_DRAG: { if (signX) dx = max(W - w0, min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; if (signY) dy = max(N - n0, min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; break; } case MODE_HANDLE: { if (points[1]) { if (signX) w1 = max(W, min(E, points[0][0])), e1 = max(W, min(E, points[1][0])), signX = 1; if (signY) n1 = max(N, min(S, points[0][1])), s1 = max(N, min(S, points[1][1])), signY = 1; } else { if (signX < 0) dx = max(W - w0, min(E - w0, dx)), w1 = w0 + dx, e1 = e0; else if (signX > 0) dx = max(W - e0, min(E - e0, dx)), w1 = w0, e1 = e0 + dx; if (signY < 0) dy = max(N - n0, min(S - n0, dy)), n1 = n0 + dy, s1 = s0; else if (signY > 0) dy = max(N - s0, min(S - s0, dy)), n1 = n0, s1 = s0 + dy; } break; } case MODE_CENTER: { if (signX) w1 = max(W, min(E, w0 - dx * signX)), e1 = max(W, min(E, e0 + dx * signX)); if (signY) n1 = max(N, min(S, n0 - dy * signY)), s1 = max(N, min(S, s0 + dy * signY)); break; } } if (e1 < w1) { signX *= -1; t = w0, w0 = e0, e0 = t; t = w1, w1 = e1, e1 = t; if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); } if (s1 < n1) { signY *= -1; t = n0, n0 = s0, s0 = t; t = n1, n1 = s1, s1 = t; if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); } if (state.selection) selection = state.selection; // May be set by brush.move! if (lockX) w1 = selection[0][0], e1 = selection[1][0]; if (lockY) n1 = selection[0][1], s1 = selection[1][1]; if (selection[0][0] !== w1 || selection[0][1] !== n1 || selection[1][0] !== e1 || selection[1][1] !== s1) { state.selection = [[w1, n1], [e1, s1]]; redraw.call(that); emit.brush(event, mode.name); } } function ended(event) { nopropagation(event); if (event.touches) { if (event.touches.length) return; if (touchending) clearTimeout(touchending); touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! } else { d3Drag.dragEnable(event.view, moving); view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); } group.attr("pointer-events", "all"); overlay.attr("cursor", cursors.overlay); if (state.selection) selection = state.selection; // May be set by brush.move (on start)! if (empty(selection)) state.selection = null, redraw.call(that); emit.end(event, mode.name); } function keydowned(event) { switch (event.keyCode) { case 16: { // SHIFT shifting = signX && signY; break; } case 18: { // ALT if (mode === MODE_HANDLE) { if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; mode = MODE_CENTER; move(event); } break; } case 32: { // SPACE; takes priority over ALT if (mode === MODE_HANDLE || mode === MODE_CENTER) { if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; mode = MODE_SPACE; overlay.attr("cursor", cursors.selection); move(event); } break; } default: return; } noevent(event); } function keyupped(event) { switch (event.keyCode) { case 16: { // SHIFT if (shifting) { lockX = lockY = shifting = false; move(event); } break; } case 18: { // ALT if (mode === MODE_CENTER) { if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; mode = MODE_HANDLE; move(event); } break; } case 32: { // SPACE if (mode === MODE_SPACE) { if (event.altKey) { if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; mode = MODE_CENTER; } else { if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; mode = MODE_HANDLE; } overlay.attr("cursor", cursors[type]); move(event); } break; } default: return; } noevent(event); } } function touchmoved(event) { emitter(this, arguments).moved(event); } function touchended(event) { emitter(this, arguments).ended(event); } function initialize() { var state = this.__brush || {selection: null}; state.extent = number2(extent.apply(this, arguments)); state.dim = dim; return state; } brush.extent = function(_) { return arguments.length ? (extent = typeof _ === "function" ? _ : constant(number2(_)), brush) : extent; }; brush.filter = function(_) { return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter; }; brush.touchable = function(_) { return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), brush) : touchable; }; brush.handleSize = function(_) { return arguments.length ? (handleSize = +_, brush) : handleSize; }; brush.keyModifiers = function(_) { return arguments.length ? (keys = !!_, brush) : keys; }; brush.on = function() { var value = listeners.on.apply(listeners, arguments); return value === listeners ? brush : value; }; return brush; } exports.brush = brush; exports.brushSelection = brushSelection; exports.brushX = brushX; exports.brushY = brushY; Object.defineProperty(exports, '__esModule', { value: true }); })));