StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
12 KiB
447 lines
12 KiB
import { Interaction } from "../core/Interaction.js";
|
|
import { ActionName } from "../core/scope.js";
|
|
import * as arr from "../utils/arr.js";
|
|
import * as dom from "../utils/domUtils.js";
|
|
import extend from "../utils/extend.js";
|
|
import * as is from "../utils/is.js";
|
|
ActionName.Resize = 'resize';
|
|
|
|
function install(scope) {
|
|
const {
|
|
actions,
|
|
browser,
|
|
|
|
/** @lends Interactable */
|
|
Interactable,
|
|
// tslint:disable-line no-shadowed-variable
|
|
defaults
|
|
} = scope; // Less Precision with touch input
|
|
|
|
resize.cursors = initCursors(browser);
|
|
resize.defaultMargin = browser.supportsTouch || browser.supportsPointerEvent ? 20 : 10;
|
|
/**
|
|
* ```js
|
|
* interact(element).resizable({
|
|
* onstart: function (event) {},
|
|
* onmove : function (event) {},
|
|
* onend : function (event) {},
|
|
*
|
|
* edges: {
|
|
* top : true, // Use pointer coords to check for resize.
|
|
* left : false, // Disable resizing from left edge.
|
|
* bottom: '.resize-s',// Resize if pointer target matches selector
|
|
* right : handleEl // Resize if pointer target is the given Element
|
|
* },
|
|
*
|
|
* // Width and height can be adjusted independently. When `true`, width and
|
|
* // height are adjusted at a 1:1 ratio.
|
|
* square: false,
|
|
*
|
|
* // Width and height can be adjusted independently. When `true`, width and
|
|
* // height maintain the aspect ratio they had when resizing started.
|
|
* preserveAspectRatio: false,
|
|
*
|
|
* // a value of 'none' will limit the resize rect to a minimum of 0x0
|
|
* // 'negate' will allow the rect to have negative width/height
|
|
* // 'reposition' will keep the width/height positive by swapping
|
|
* // the top and bottom edges and/or swapping the left and right edges
|
|
* invert: 'none' || 'negate' || 'reposition'
|
|
*
|
|
* // limit multiple resizes.
|
|
* // See the explanation in the {@link Interactable.draggable} example
|
|
* max: Infinity,
|
|
* maxPerElement: 1,
|
|
* })
|
|
*
|
|
* var isResizeable = interact(element).resizable()
|
|
* ```
|
|
*
|
|
* Gets or sets whether resize actions can be performed on the target
|
|
*
|
|
* @param {boolean | object} [options] true/false or An object with event
|
|
* listeners to be fired on resize events (object makes the Interactable
|
|
* resizable)
|
|
* @return {boolean | Interactable} A boolean indicating if this can be the
|
|
* target of resize elements, or this Interactable
|
|
*/
|
|
|
|
Interactable.prototype.resizable = function (options) {
|
|
return resizable(this, options, scope);
|
|
};
|
|
|
|
actions[ActionName.Resize] = resize;
|
|
actions.names.push(ActionName.Resize);
|
|
arr.merge(actions.eventTypes, ['resizestart', 'resizemove', 'resizeinertiastart', 'resizeresume', 'resizeend']);
|
|
actions.methodDict.resize = 'resizable';
|
|
defaults.actions.resize = resize.defaults;
|
|
}
|
|
|
|
function resizeChecker(arg) {
|
|
const {
|
|
interaction,
|
|
interactable,
|
|
element,
|
|
rect,
|
|
buttons
|
|
} = arg;
|
|
|
|
if (!rect) {
|
|
return undefined;
|
|
}
|
|
|
|
const page = extend({}, interaction.coords.cur.page);
|
|
const resizeOptions = interactable.options.resize;
|
|
|
|
if (!(resizeOptions && resizeOptions.enabled) || // check mouseButton setting if the pointer is down
|
|
interaction.pointerIsDown && /mouse|pointer/.test(interaction.pointerType) && (buttons & resizeOptions.mouseButtons) === 0) {
|
|
return undefined;
|
|
} // if using resize.edges
|
|
|
|
|
|
if (is.object(resizeOptions.edges)) {
|
|
const resizeEdges = {
|
|
left: false,
|
|
right: false,
|
|
top: false,
|
|
bottom: false
|
|
};
|
|
|
|
for (const edge in resizeEdges) {
|
|
resizeEdges[edge] = checkResizeEdge(edge, resizeOptions.edges[edge], page, interaction._latestPointer.eventTarget, element, rect, resizeOptions.margin || resize.defaultMargin);
|
|
}
|
|
|
|
resizeEdges.left = resizeEdges.left && !resizeEdges.right;
|
|
resizeEdges.top = resizeEdges.top && !resizeEdges.bottom;
|
|
|
|
if (resizeEdges.left || resizeEdges.right || resizeEdges.top || resizeEdges.bottom) {
|
|
arg.action = {
|
|
name: ActionName.Resize,
|
|
edges: resizeEdges
|
|
};
|
|
}
|
|
} else {
|
|
const right = resizeOptions.axis !== 'y' && page.x > rect.right - resize.defaultMargin;
|
|
const bottom = resizeOptions.axis !== 'x' && page.y > rect.bottom - resize.defaultMargin;
|
|
|
|
if (right || bottom) {
|
|
arg.action = {
|
|
name: 'resize',
|
|
axes: (right ? 'x' : '') + (bottom ? 'y' : '')
|
|
};
|
|
}
|
|
}
|
|
|
|
return arg.action ? false : undefined;
|
|
}
|
|
|
|
function resizable(interactable, options, scope) {
|
|
if (is.object(options)) {
|
|
interactable.options.resize.enabled = options.enabled !== false;
|
|
interactable.setPerAction(ActionName.Resize, options);
|
|
interactable.setOnEvents(ActionName.Resize, options);
|
|
|
|
if (is.string(options.axis) && /^x$|^y$|^xy$/.test(options.axis)) {
|
|
interactable.options.resize.axis = options.axis;
|
|
} else if (options.axis === null) {
|
|
interactable.options.resize.axis = scope.defaults.actions.resize.axis;
|
|
}
|
|
|
|
if (is.bool(options.preserveAspectRatio)) {
|
|
interactable.options.resize.preserveAspectRatio = options.preserveAspectRatio;
|
|
} else if (is.bool(options.square)) {
|
|
interactable.options.resize.square = options.square;
|
|
}
|
|
|
|
return interactable;
|
|
}
|
|
|
|
if (is.bool(options)) {
|
|
interactable.options.resize.enabled = options;
|
|
return interactable;
|
|
}
|
|
|
|
return interactable.options.resize;
|
|
}
|
|
|
|
function checkResizeEdge(name, value, page, element, interactableElement, rect, margin) {
|
|
// false, '', undefined, null
|
|
if (!value) {
|
|
return false;
|
|
} // true value, use pointer coords and element rect
|
|
|
|
|
|
if (value === true) {
|
|
// if dimensions are negative, "switch" edges
|
|
const width = is.number(rect.width) ? rect.width : rect.right - rect.left;
|
|
const height = is.number(rect.height) ? rect.height : rect.bottom - rect.top; // don't use margin greater than half the relevent dimension
|
|
|
|
margin = Math.min(margin, (name === 'left' || name === 'right' ? width : height) / 2);
|
|
|
|
if (width < 0) {
|
|
if (name === 'left') {
|
|
name = 'right';
|
|
} else if (name === 'right') {
|
|
name = 'left';
|
|
}
|
|
}
|
|
|
|
if (height < 0) {
|
|
if (name === 'top') {
|
|
name = 'bottom';
|
|
} else if (name === 'bottom') {
|
|
name = 'top';
|
|
}
|
|
}
|
|
|
|
if (name === 'left') {
|
|
return page.x < (width >= 0 ? rect.left : rect.right) + margin;
|
|
}
|
|
|
|
if (name === 'top') {
|
|
return page.y < (height >= 0 ? rect.top : rect.bottom) + margin;
|
|
}
|
|
|
|
if (name === 'right') {
|
|
return page.x > (width >= 0 ? rect.right : rect.left) - margin;
|
|
}
|
|
|
|
if (name === 'bottom') {
|
|
return page.y > (height >= 0 ? rect.bottom : rect.top) - margin;
|
|
}
|
|
} // the remaining checks require an element
|
|
|
|
|
|
if (!is.element(element)) {
|
|
return false;
|
|
}
|
|
|
|
return is.element(value) // the value is an element to use as a resize handle
|
|
? value === element // otherwise check if element matches value as selector
|
|
: dom.matchesUpTo(element, value, interactableElement);
|
|
}
|
|
|
|
function initCursors(browser) {
|
|
return browser.isIe9 ? {
|
|
x: 'e-resize',
|
|
y: 's-resize',
|
|
xy: 'se-resize',
|
|
top: 'n-resize',
|
|
left: 'w-resize',
|
|
bottom: 's-resize',
|
|
right: 'e-resize',
|
|
topleft: 'se-resize',
|
|
bottomright: 'se-resize',
|
|
topright: 'ne-resize',
|
|
bottomleft: 'ne-resize'
|
|
} : {
|
|
x: 'ew-resize',
|
|
y: 'ns-resize',
|
|
xy: 'nwse-resize',
|
|
top: 'ns-resize',
|
|
left: 'ew-resize',
|
|
bottom: 'ns-resize',
|
|
right: 'ew-resize',
|
|
topleft: 'nwse-resize',
|
|
bottomright: 'nwse-resize',
|
|
topright: 'nesw-resize',
|
|
bottomleft: 'nesw-resize'
|
|
};
|
|
}
|
|
|
|
function start({
|
|
iEvent,
|
|
interaction
|
|
}) {
|
|
if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) {
|
|
return;
|
|
}
|
|
|
|
const rect = interaction.rect;
|
|
interaction._rects = {
|
|
start: extend({}, rect),
|
|
corrected: extend({}, rect),
|
|
previous: extend({}, rect),
|
|
delta: {
|
|
left: 0,
|
|
right: 0,
|
|
width: 0,
|
|
top: 0,
|
|
bottom: 0,
|
|
height: 0
|
|
}
|
|
};
|
|
iEvent.edges = interaction.prepared.edges;
|
|
iEvent.rect = interaction._rects.corrected;
|
|
iEvent.deltaRect = interaction._rects.delta;
|
|
}
|
|
|
|
function move({
|
|
iEvent,
|
|
interaction
|
|
}) {
|
|
if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) {
|
|
return;
|
|
}
|
|
|
|
const resizeOptions = interaction.interactable.options.resize;
|
|
const invert = resizeOptions.invert;
|
|
const invertible = invert === 'reposition' || invert === 'negate'; // eslint-disable-next-line no-shadow
|
|
|
|
const current = interaction.rect;
|
|
const {
|
|
start: startRect,
|
|
corrected,
|
|
delta: deltaRect,
|
|
previous
|
|
} = interaction._rects;
|
|
extend(previous, corrected);
|
|
|
|
if (invertible) {
|
|
// if invertible, copy the current rect
|
|
extend(corrected, current);
|
|
|
|
if (invert === 'reposition') {
|
|
// swap edge values if necessary to keep width/height positive
|
|
if (corrected.top > corrected.bottom) {
|
|
const swap = corrected.top;
|
|
corrected.top = corrected.bottom;
|
|
corrected.bottom = swap;
|
|
}
|
|
|
|
if (corrected.left > corrected.right) {
|
|
const swap = corrected.left;
|
|
corrected.left = corrected.right;
|
|
corrected.right = swap;
|
|
}
|
|
}
|
|
} else {
|
|
// if not invertible, restrict to minimum of 0x0 rect
|
|
corrected.top = Math.min(current.top, startRect.bottom);
|
|
corrected.bottom = Math.max(current.bottom, startRect.top);
|
|
corrected.left = Math.min(current.left, startRect.right);
|
|
corrected.right = Math.max(current.right, startRect.left);
|
|
}
|
|
|
|
corrected.width = corrected.right - corrected.left;
|
|
corrected.height = corrected.bottom - corrected.top;
|
|
|
|
for (const edge in corrected) {
|
|
deltaRect[edge] = corrected[edge] - previous[edge];
|
|
}
|
|
|
|
iEvent.edges = interaction.prepared.edges;
|
|
iEvent.rect = corrected;
|
|
iEvent.deltaRect = deltaRect;
|
|
}
|
|
|
|
function end({
|
|
iEvent,
|
|
interaction
|
|
}) {
|
|
if (interaction.prepared.name !== 'resize' || !interaction.prepared.edges) {
|
|
return;
|
|
}
|
|
|
|
iEvent.edges = interaction.prepared.edges;
|
|
iEvent.rect = interaction._rects.corrected;
|
|
iEvent.deltaRect = interaction._rects.delta;
|
|
}
|
|
|
|
function updateEventAxes({
|
|
iEvent,
|
|
interaction
|
|
}) {
|
|
if (interaction.prepared.name !== ActionName.Resize || !interaction.resizeAxes) {
|
|
return;
|
|
}
|
|
|
|
const options = interaction.interactable.options;
|
|
|
|
if (options.resize.square) {
|
|
if (interaction.resizeAxes === 'y') {
|
|
iEvent.delta.x = iEvent.delta.y;
|
|
} else {
|
|
iEvent.delta.y = iEvent.delta.x;
|
|
}
|
|
|
|
iEvent.axes = 'xy';
|
|
} else {
|
|
iEvent.axes = interaction.resizeAxes;
|
|
|
|
if (interaction.resizeAxes === 'x') {
|
|
iEvent.delta.y = 0;
|
|
} else if (interaction.resizeAxes === 'y') {
|
|
iEvent.delta.x = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
const resize = {
|
|
id: 'actions/resize',
|
|
before: ['actions/drag'],
|
|
install,
|
|
listeners: {
|
|
'interactions:new': ({
|
|
interaction
|
|
}) => {
|
|
interaction.resizeAxes = 'xy';
|
|
},
|
|
'interactions:action-start': arg => {
|
|
start(arg);
|
|
updateEventAxes(arg);
|
|
},
|
|
'interactions:action-move': arg => {
|
|
move(arg);
|
|
updateEventAxes(arg);
|
|
},
|
|
'interactions:action-end': end,
|
|
'auto-start:check': resizeChecker
|
|
},
|
|
defaults: {
|
|
square: false,
|
|
preserveAspectRatio: false,
|
|
axis: 'xy',
|
|
// use default margin
|
|
margin: NaN,
|
|
// object with props left, right, top, bottom which are
|
|
// true/false values to resize when the pointer is over that edge,
|
|
// CSS selectors to match the handles for each direction
|
|
// or the Elements for each handle
|
|
edges: null,
|
|
// a value of 'none' will limit the resize rect to a minimum of 0x0
|
|
// 'negate' will alow the rect to have negative width/height
|
|
// 'reposition' will keep the width/height positive by swapping
|
|
// the top and bottom edges and/or swapping the left and right edges
|
|
invert: 'none'
|
|
},
|
|
cursors: null,
|
|
|
|
getCursor({
|
|
edges,
|
|
axis,
|
|
name
|
|
}) {
|
|
const cursors = resize.cursors;
|
|
let result = null;
|
|
|
|
if (axis) {
|
|
result = cursors[name + axis];
|
|
} else if (edges) {
|
|
let cursorKey = '';
|
|
|
|
for (const edge of ['top', 'bottom', 'left', 'right']) {
|
|
if (edges[edge]) {
|
|
cursorKey += edge;
|
|
}
|
|
}
|
|
|
|
result = cursors[cursorKey];
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
defaultMargin: null
|
|
};
|
|
export default resize;
|
|
//# sourceMappingURL=resize.js.map
|