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.
315 lines
8.1 KiB
315 lines
8.1 KiB
import * as utils from "../utils/index.js";
|
|
import InteractableMethods from "./InteractableMethods.js";
|
|
|
|
function install(scope) {
|
|
const {
|
|
interact,
|
|
defaults
|
|
} = scope;
|
|
scope.usePlugin(InteractableMethods);
|
|
defaults.base.actionChecker = null;
|
|
defaults.base.styleCursor = true;
|
|
utils.extend(defaults.perAction, {
|
|
manualStart: false,
|
|
max: Infinity,
|
|
maxPerElement: 1,
|
|
allowFrom: null,
|
|
ignoreFrom: null,
|
|
// only allow left button by default
|
|
// see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#Return_value
|
|
mouseButtons: 1
|
|
});
|
|
/**
|
|
* Returns or sets the maximum number of concurrent interactions allowed. By
|
|
* default only 1 interaction is allowed at a time (for backwards
|
|
* compatibility). To allow multiple interactions on the same Interactables and
|
|
* elements, you need to enable it in the draggable, resizable and gesturable
|
|
* `'max'` and `'maxPerElement'` options.
|
|
*
|
|
* @alias module:interact.maxInteractions
|
|
*
|
|
* @param {number} [newValue] Any number. newValue <= 0 means no interactions.
|
|
*/
|
|
|
|
interact.maxInteractions = newValue => maxInteractions(newValue, scope);
|
|
|
|
scope.autoStart = {
|
|
// Allow this many interactions to happen simultaneously
|
|
maxInteractions: Infinity,
|
|
withinInteractionLimit,
|
|
cursorElement: null
|
|
};
|
|
}
|
|
|
|
function prepareOnDown({
|
|
interaction,
|
|
pointer,
|
|
event,
|
|
eventTarget
|
|
}, scope) {
|
|
if (interaction.interacting()) {
|
|
return;
|
|
}
|
|
|
|
const actionInfo = getActionInfo(interaction, pointer, event, eventTarget, scope);
|
|
prepare(interaction, actionInfo, scope);
|
|
}
|
|
|
|
function prepareOnMove({
|
|
interaction,
|
|
pointer,
|
|
event,
|
|
eventTarget
|
|
}, scope) {
|
|
if (interaction.pointerType !== 'mouse' || interaction.pointerIsDown || interaction.interacting()) {
|
|
return;
|
|
}
|
|
|
|
const actionInfo = getActionInfo(interaction, pointer, event, eventTarget, scope);
|
|
prepare(interaction, actionInfo, scope);
|
|
}
|
|
|
|
function startOnMove(arg, scope) {
|
|
const {
|
|
interaction
|
|
} = arg;
|
|
|
|
if (!interaction.pointerIsDown || interaction.interacting() || !interaction.pointerWasMoved || !interaction.prepared.name) {
|
|
return;
|
|
}
|
|
|
|
scope.fire('autoStart:before-start', arg);
|
|
const {
|
|
interactable
|
|
} = interaction;
|
|
const actionName = interaction.prepared.name;
|
|
|
|
if (actionName && interactable) {
|
|
// check manualStart and interaction limit
|
|
if (interactable.options[actionName].manualStart || !withinInteractionLimit(interactable, interaction.element, interaction.prepared, scope)) {
|
|
interaction.stop();
|
|
} else {
|
|
interaction.start(interaction.prepared, interactable, interaction.element);
|
|
setInteractionCursor(interaction, scope);
|
|
}
|
|
}
|
|
}
|
|
|
|
function clearCursorOnStop({
|
|
interaction
|
|
}, scope) {
|
|
const {
|
|
interactable
|
|
} = interaction;
|
|
|
|
if (interactable && interactable.options.styleCursor) {
|
|
setCursor(interaction.element, '', scope);
|
|
}
|
|
} // Check if the current interactable supports the action.
|
|
// If so, return the validated action. Otherwise, return null
|
|
|
|
|
|
function validateAction(action, interactable, element, eventTarget, scope) {
|
|
if (interactable.testIgnoreAllow(interactable.options[action.name], element, eventTarget) && interactable.options[action.name].enabled && withinInteractionLimit(interactable, element, action, scope)) {
|
|
return action;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function validateMatches(interaction, pointer, event, matches, matchElements, eventTarget, scope) {
|
|
for (let i = 0, len = matches.length; i < len; i++) {
|
|
const match = matches[i];
|
|
const matchElement = matchElements[i];
|
|
const matchAction = match.getAction(pointer, event, interaction, matchElement);
|
|
|
|
if (!matchAction) {
|
|
continue;
|
|
}
|
|
|
|
const action = validateAction(matchAction, match, matchElement, eventTarget, scope);
|
|
|
|
if (action) {
|
|
return {
|
|
action,
|
|
interactable: match,
|
|
element: matchElement
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
action: null,
|
|
interactable: null,
|
|
element: null
|
|
};
|
|
}
|
|
|
|
function getActionInfo(interaction, pointer, event, eventTarget, scope) {
|
|
let matches = [];
|
|
let matchElements = [];
|
|
let element = eventTarget;
|
|
|
|
function pushMatches(interactable) {
|
|
matches.push(interactable);
|
|
matchElements.push(element);
|
|
}
|
|
|
|
while (utils.is.element(element)) {
|
|
matches = [];
|
|
matchElements = [];
|
|
scope.interactables.forEachMatch(element, pushMatches);
|
|
const actionInfo = validateMatches(interaction, pointer, event, matches, matchElements, eventTarget, scope);
|
|
|
|
if (actionInfo.action && !actionInfo.interactable.options[actionInfo.action.name].manualStart) {
|
|
return actionInfo;
|
|
}
|
|
|
|
element = utils.dom.parentNode(element);
|
|
}
|
|
|
|
return {
|
|
action: null,
|
|
interactable: null,
|
|
element: null
|
|
};
|
|
}
|
|
|
|
function prepare(interaction, {
|
|
action,
|
|
interactable,
|
|
element
|
|
}, scope) {
|
|
action = action || {
|
|
name: null
|
|
};
|
|
interaction.interactable = interactable;
|
|
interaction.element = element;
|
|
utils.copyAction(interaction.prepared, action);
|
|
interaction.rect = interactable && action.name ? interactable.getRect(element) : null;
|
|
setInteractionCursor(interaction, scope);
|
|
scope.fire('autoStart:prepared', {
|
|
interaction
|
|
});
|
|
}
|
|
|
|
function withinInteractionLimit(interactable, element, action, scope) {
|
|
const options = interactable.options;
|
|
const maxActions = options[action.name].max;
|
|
const maxPerElement = options[action.name].maxPerElement;
|
|
const autoStartMax = scope.autoStart.maxInteractions;
|
|
let activeInteractions = 0;
|
|
let interactableCount = 0;
|
|
let elementCount = 0; // no actions if any of these values == 0
|
|
|
|
if (!(maxActions && maxPerElement && autoStartMax)) {
|
|
return false;
|
|
}
|
|
|
|
for (const interaction of scope.interactions.list) {
|
|
const otherAction = interaction.prepared.name;
|
|
|
|
if (!interaction.interacting()) {
|
|
continue;
|
|
}
|
|
|
|
activeInteractions++;
|
|
|
|
if (activeInteractions >= autoStartMax) {
|
|
return false;
|
|
}
|
|
|
|
if (interaction.interactable !== interactable) {
|
|
continue;
|
|
}
|
|
|
|
interactableCount += otherAction === action.name ? 1 : 0;
|
|
|
|
if (interactableCount >= maxActions) {
|
|
return false;
|
|
}
|
|
|
|
if (interaction.element === element) {
|
|
elementCount++;
|
|
|
|
if (otherAction === action.name && elementCount >= maxPerElement) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return autoStartMax > 0;
|
|
}
|
|
|
|
function maxInteractions(newValue, scope) {
|
|
if (utils.is.number(newValue)) {
|
|
scope.autoStart.maxInteractions = newValue;
|
|
return this;
|
|
}
|
|
|
|
return scope.autoStart.maxInteractions;
|
|
}
|
|
|
|
function setCursor(element, cursor, scope) {
|
|
const {
|
|
cursorElement: prevCursorElement
|
|
} = scope.autoStart;
|
|
|
|
if (prevCursorElement && prevCursorElement !== element) {
|
|
prevCursorElement.style.cursor = '';
|
|
}
|
|
|
|
element.ownerDocument.documentElement.style.cursor = cursor;
|
|
element.style.cursor = cursor;
|
|
scope.autoStart.cursorElement = cursor ? element : null;
|
|
}
|
|
|
|
function setInteractionCursor(interaction, scope) {
|
|
const {
|
|
interactable,
|
|
element,
|
|
prepared
|
|
} = interaction;
|
|
|
|
if (!(interaction.pointerType === 'mouse' && interactable && interactable.options.styleCursor)) {
|
|
// clear previous target element cursor
|
|
if (scope.autoStart.cursorElement) {
|
|
setCursor(scope.autoStart.cursorElement, '', scope);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
let cursor = '';
|
|
|
|
if (prepared.name) {
|
|
const cursorChecker = interactable.options[prepared.name].cursorChecker;
|
|
|
|
if (utils.is.func(cursorChecker)) {
|
|
cursor = cursorChecker(prepared, interactable, element, interaction._interacting);
|
|
} else {
|
|
cursor = scope.actions[prepared.name].getCursor(prepared);
|
|
}
|
|
}
|
|
|
|
setCursor(interaction.element, cursor || '', scope);
|
|
}
|
|
|
|
const autoStart = {
|
|
id: 'auto-start/base',
|
|
before: ['actions', 'action/drag', 'actions/resize', 'actions/gesture'],
|
|
install,
|
|
listeners: {
|
|
'interactions:down': prepareOnDown,
|
|
'interactions:move': (arg, scope) => {
|
|
prepareOnMove(arg, scope);
|
|
startOnMove(arg, scope);
|
|
},
|
|
'interactions:stop': clearCursorOnStop
|
|
},
|
|
maxInteractions,
|
|
withinInteractionLimit,
|
|
validateAction
|
|
};
|
|
export default autoStart;
|
|
//# sourceMappingURL=base.js.map
|