UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

1,174 lines (964 loc) 40.9 kB
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import macro from '../../macro.js'; import { C as arrayRange } from '../../Common/Core/Math/index.js'; import { l as lib } from '../../vendor/webworker-promise/lib/index.js'; import { W as WorkerFactory } from '../../_virtual/rollup-plugin-worker-loader__module_Sources/Interaction/Widgets/PiecewiseGaussianWidget/ComputeHistogram.worker.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } /* eslint-disable no-continue */ // ---------------------------------------------------------------------------- // Global structures // ---------------------------------------------------------------------------- var MIN_GAUSSIAN_WIDTH = 0.001; var ACTION_TO_CURSOR = { adjustPosition: '-webkit-grab', adjustHeight: 'row-resize', adjustBias: 'crosshair', adjustWidth: 'col-resize', adjustZoom: 'col-resize' }; var TOUCH_CLICK = []; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- var ACTIONS = { adjustPosition: function adjustPosition(x, y, _ref) { var originalXY = _ref.originalXY, gaussian = _ref.gaussian, originalGaussian = _ref.originalGaussian; var xOffset = originalGaussian.position - originalXY[0]; gaussian.position = x + xOffset; return true; }, adjustHeight: function adjustHeight(x, y, _ref2) { var model = _ref2.model, gaussian = _ref2.gaussian; gaussian.height = 1 - y; gaussian.height = Math.min(1, Math.max(model.gaussianMinimumHeight, gaussian.height)); return true; }, adjustBias: function adjustBias(x, y, _ref3) { var originalXY = _ref3.originalXY, gaussian = _ref3.gaussian, originalGaussian = _ref3.originalGaussian; gaussian.xBias = originalGaussian.xBias - (originalXY[0] - x) / gaussian.height; gaussian.yBias = originalGaussian.yBias + 4 * (originalXY[1] - y) / gaussian.height; // Clamps gaussian.xBias = Math.max(-1, Math.min(1, gaussian.xBias)); gaussian.yBias = Math.max(0, Math.min(2, gaussian.yBias)); return true; }, adjustWidth: function adjustWidth(x, y, _ref4) { var originalXY = _ref4.originalXY, gaussian = _ref4.gaussian, originalGaussian = _ref4.originalGaussian, gaussianSide = _ref4.gaussianSide; gaussian.width = gaussianSide < 0 ? originalGaussian.width - (originalXY[0] - x) : originalGaussian.width + (originalXY[0] - x); if (gaussian.width < MIN_GAUSSIAN_WIDTH) { gaussian.width = MIN_GAUSSIAN_WIDTH; } return true; }, adjustZoom: function adjustZoom(x, y, _ref5) { var rangeZoom = _ref5.rangeZoom, publicAPI = _ref5.publicAPI; var delta = rangeZoom[1] - rangeZoom[0]; var absNormX = (x - rangeZoom[0]) / delta; var minDelta = Math.abs(absNormX - rangeZoom[0]); var maxDelta = Math.abs(absNormX - rangeZoom[1]); var meanDelta = Math.abs(absNormX - 0.5 * (rangeZoom[0] + rangeZoom[1])); if (meanDelta < Math.min(minDelta, maxDelta)) { var halfDelta = delta * 0.5; rangeZoom[0] = Math.min(Math.max(absNormX - halfDelta, 0), rangeZoom[1] - 0.1); rangeZoom[1] = Math.max(Math.min(absNormX + halfDelta, 1), rangeZoom[0] + 0.1); } else if (minDelta < maxDelta) { rangeZoom[0] = Math.min(Math.max(absNormX, 0), rangeZoom[1] - 0.1); } else { rangeZoom[1] = Math.max(Math.min(absNormX, 1), rangeZoom[0] + 0.1); } publicAPI.invokeZoomChange(rangeZoom); // The opacity did not changed return false; } }; // ---------------------------------------------------------------------------- function computeOpacities(gaussians) { var sampling = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 256; var opacities = []; while (opacities.length < sampling) { opacities.push(0); } var count = gaussians.length; while (count--) { var _gaussians$count = gaussians[count], position = _gaussians$count.position, height = _gaussians$count.height, width = _gaussians$count.width, xBias = _gaussians$count.xBias, yBias = _gaussians$count.yBias; for (var i = 0; i < sampling; i++) { var x = i / (sampling - 1); // clamp non-zero values to pos +/- width if (x > position + width || x < position - width) { if (opacities[i] < 0.0) { opacities[i] = 0.0; } continue; } // non-zero width var correctedWidth = width < MIN_GAUSSIAN_WIDTH ? MIN_GAUSSIAN_WIDTH : width; // translate the original x to a new x based on the xbias var x0 = 0; if (xBias === 0 || x === position + xBias) { x0 = x; } else if (x > position + xBias) { if (correctedWidth === xBias) { x0 = position; } else { x0 = position + (x - position - xBias) * (correctedWidth / (correctedWidth - xBias)); } } else if (-correctedWidth === xBias) { // (x < pos+xBias) x0 = position; } else { x0 = position - (x - position - xBias) * (correctedWidth / (correctedWidth + xBias)); } // center around 0 and normalize to -1,1 var x1 = (x0 - position) / correctedWidth; // do a linear interpolation between: // a gaussian and a parabola if 0 < yBias <1 // a parabola and a step function if 1 < yBias <2 var h0a = Math.exp(-(4 * x1 * x1)); var h0b = 1.0 - x1 * x1; var h0c = 1.0; var h1 = void 0; if (yBias < 1) { h1 = yBias * h0b + (1 - yBias) * h0a; } else { h1 = (2 - yBias) * h0b + (yBias - 1) * h0c; } var h2 = height * h1; // perform the MAX over different gaussians, not the sum if (h2 > opacities[i]) { opacities[i] = h2; } } } return opacities; } // ---------------------------------------------------------------------------- function applyGaussianToPiecewiseFunction(gaussians, sampling, rangeToUse, piecewiseFunction) { var opacities = computeOpacities(gaussians, sampling); var nodes = []; var delta = (rangeToUse[1] - rangeToUse[0]) / (opacities.length - 1); var midpoint = 0.5; var sharpness = 0; for (var index = 0; index < opacities.length; index++) { var x = rangeToUse[0] + delta * index; var y = opacities[index]; nodes.push({ x: x, y: y, midpoint: midpoint, sharpness: sharpness }); } piecewiseFunction.setNodes(nodes); } // ---------------------------------------------------------------------------- function drawChart(ctx, area, values) { var style = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : { lineWidth: 1, strokeStyle: '#000' }; var verticalScale = area[3]; var horizontalScale = area[2] / (values.length - 1); var fill = !!style.fillStyle; var offset = verticalScale + area[1]; ctx.lineWidth = style.lineWidth; ctx.strokeStyle = style.strokeStyle; ctx.beginPath(); ctx.moveTo(area[0], area[1] + area[3]); for (var index = 0; index < values.length; index++) { ctx.lineTo(area[0] + index * horizontalScale, Math.max(area[1], offset - values[index] * verticalScale)); } if (fill) { ctx.fillStyle = style.fillStyle; ctx.lineTo(area[0] + area[2], area[1] + area[3]); if (style.clip) { ctx.clip(); return; } ctx.fill(); } ctx.stroke(); } // ---------------------------------------------------------------------------- function updateColorCanvas(colorTransferFunction, width, rangeToUse, canvas) { var workCanvas = canvas || document.createElement('canvas'); workCanvas.setAttribute('width', width); workCanvas.setAttribute('height', 256); var ctx = workCanvas.getContext('2d'); var rgba = colorTransferFunction.getUint8Table(rangeToUse[0], rangeToUse[1], width, 4); var pixelsArea = ctx.getImageData(0, 0, width, 256); for (var lineIdx = 0; lineIdx < 256; lineIdx++) { pixelsArea.data.set(rgba, lineIdx * 4 * width); } var nbValues = 256 * width * 4; var lineSize = width * 4; for (var i = 3; i < nbValues; i += 4) { pixelsArea.data[i] = 255 - Math.floor(i / lineSize); } ctx.putImageData(pixelsArea, 0, 0); return workCanvas; } // ---------------------------------------------------------------------------- function updateColorCanvasFromImage(img, width, canvas) { var workCanvas = canvas || document.createElement('canvas'); workCanvas.setAttribute('width', width); workCanvas.setAttribute('height', 256); var ctx = workCanvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, 256); return workCanvas; } // ---------------------------------------------------------------------------- function normalizeCoordinates(x, y, subRectangeArea) { var zoomRange = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [0, 1]; return [zoomRange[0] + (x - subRectangeArea[0]) / subRectangeArea[2] * (zoomRange[1] - zoomRange[0]), (y - subRectangeArea[1]) / subRectangeArea[3]]; } // ---------------------------------------------------------------------------- function findGaussian(x, gaussians) { var distances = gaussians.map(function (g) { return Math.abs(g.position - x); }); var min = Math.min.apply(Math, _toConsumableArray(distances)); return distances.indexOf(min); } // ---------------------------------------------------------------------------- function createListener(callback) { var preventDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; return function (e) { var offsetX = e.offsetX, offsetY = e.offsetY; if (preventDefault) { e.preventDefault(); } callback(offsetX, offsetY); }; } // ---------------------------------------------------------------------------- function createTouchClickListener() { var id = TOUCH_CLICK.length; for (var _len = arguments.length, callbacks = new Array(_len), _key = 0; _key < _len; _key++) { callbacks[_key] = arguments[_key]; } TOUCH_CLICK.push({ callbacks: callbacks, timeout: 0, deltaT: 200, count: 0, ready: false }); return id; } // ---------------------------------------------------------------------------- function processTouchClicks() { TOUCH_CLICK.filter(function (t) { return t.ready; }).forEach(function (touchHandle) { touchHandle.callbacks.forEach(function (callback) { if (callback.touches === touchHandle.touches && callback.clicks === touchHandle.count) { callback.action.apply(callback, _toConsumableArray(touchHandle.singleTouche)); } }); // Clear state touchHandle.ts = 0; touchHandle.count = 0; touchHandle.touches = 0; touchHandle.ready = false; }); } // ---------------------------------------------------------------------------- function createTouchListener(id, callback) { var nbTouches = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var preventDefault = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; return function (e) { var targetBounds = e.target.getBoundingClientRect(); var relativeTouches = Array.prototype.map.call(e.touches, function (t) { return [t.pageX - targetBounds.left, t.pageY - targetBounds.top]; }); var singleTouche = relativeTouches.reduce(function (a, b) { return [a[0] + b[0], a[1] + b[1]]; }, [0, 0]).map(function (v) { return v / e.touches.length; }); if (e.type === 'touchstart') { clearTimeout(TOUCH_CLICK[id].timeout); TOUCH_CLICK[id].ts = e.timeStamp; TOUCH_CLICK[id].singleTouche = singleTouche; TOUCH_CLICK[id].touches = e.touches.length; } else if (e.type === 'touchmove') { TOUCH_CLICK[id].ts = 0; TOUCH_CLICK[id].count = 0; TOUCH_CLICK[id].ready = false; } else if (e.type === 'touchend') { if (e.timeStamp - TOUCH_CLICK[id].ts < TOUCH_CLICK[id].deltaT) { TOUCH_CLICK[id].count += 1; TOUCH_CLICK[id].ready = true; if (preventDefault) { e.preventDefault(); } TOUCH_CLICK[id].timeout = setTimeout(processTouchClicks, TOUCH_CLICK[id].deltaT); } else { TOUCH_CLICK[id].ready = false; } } if (e.touches.length === nbTouches) { callback.apply(void 0, _toConsumableArray(singleTouche)); if (preventDefault) { e.preventDefault(); } } }; } // ---------------------------------------------------------------------------- function listenerSelector(condition, ok, ko) { return function (e) { return condition() ? ok(e) : ko(e); }; } // ---------------------------------------------------------------------------- function rescaleArray(array, focusArea) { if (!focusArea) { return array; } var maxIdx = array.length - 1; var idxRange = focusArea.map(function (v) { return Math.round(v * maxIdx); }); return array.slice(idxRange[0], idxRange[1] + 1); } // ---------------------------------------------------------------------------- function rescaleValue(value, focusArea) { if (!focusArea) { return value; } return (value - focusArea[0]) / (focusArea[1] - focusArea[0]); } // ---------------------------------------------------------------------------- // Static API // ---------------------------------------------------------------------------- var STATIC = { applyGaussianToPiecewiseFunction: applyGaussianToPiecewiseFunction, computeOpacities: computeOpacities, createListener: createListener, drawChart: drawChart, findGaussian: findGaussian, listenerSelector: listenerSelector, normalizeCoordinates: normalizeCoordinates }; // ---------------------------------------------------------------------------- // vtkPiecewiseGaussianWidget methods // ---------------------------------------------------------------------------- function vtkPiecewiseGaussianWidget(publicAPI, model) { // Set our className model.classHierarchy.push('vtkPiecewiseGaussianWidget'); if (!model.canvas) { model.canvas = document.createElement('canvas'); } publicAPI.setContainer = function (el) { if (model.container && model.container !== el) { model.container.removeChild(model.canvas); } if (model.container !== el) { model.container = el; if (model.container) { model.container.appendChild(model.canvas); } publicAPI.modified(); } }; publicAPI.setGaussians = function (gaussians) { if (model.gaussians === gaussians) { return; } model.gaussians = gaussians; model.opacities = computeOpacities(model.gaussians, model.piecewiseSize); publicAPI.invokeOpacityChange(publicAPI); publicAPI.modified(); }; publicAPI.addGaussian = function (position, height, width, xBias, yBias) { var nextIndex = model.gaussians.length; model.gaussians.push({ position: position, height: height, width: width, xBias: xBias, yBias: yBias }); model.opacities = computeOpacities(model.gaussians, model.piecewiseSize); publicAPI.invokeOpacityChange(publicAPI); publicAPI.modified(); return nextIndex; }; publicAPI.removeGaussian = function (index) { model.gaussians.splice(index, 1); model.opacities = computeOpacities(model.gaussians, model.piecewiseSize); publicAPI.invokeOpacityChange(publicAPI); publicAPI.modified(); }; publicAPI.setSize = function (width, height) { model.canvas.setAttribute('width', width); model.canvas.setAttribute('height', height); if (model.size[0] !== width || model.size[1] !== height) { model.size = [width, height]; model.colorCanvasMTime = 0; publicAPI.modified(); } }; publicAPI.updateStyle = function (style) { model.style = _objectSpread(_objectSpread({}, model.style), style); publicAPI.modified(); }; // Method used to compute and show data distribution in the background. // When an array with many components is used, you can provide additional // information to choose which component you want to extract the histogram // from. publicAPI.setDataArray = function (array) { var _ref6 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref6$numberOfBinToCo = _ref6.numberOfBinToConsiders, numberOfBinToConsiders = _ref6$numberOfBinToCo === void 0 ? 1 : _ref6$numberOfBinToCo, _ref6$numberOfBinsToS = _ref6.numberOfBinsToSkip, numberOfBinsToSkip = _ref6$numberOfBinsToS === void 0 ? 1 : _ref6$numberOfBinsToS, _ref6$numberOfCompone = _ref6.numberOfComponents, numberOfComponents = _ref6$numberOfCompone === void 0 ? 1 : _ref6$numberOfCompone, _ref6$component = _ref6.component, component = _ref6$component === void 0 ? 0 : _ref6$component; model.histogram = null; model.histogramArray = array; model.dataRange = arrayRange(array, component, numberOfComponents); var _model$dataRange = _slicedToArray(model.dataRange, 2), min = _model$dataRange[0], max = _model$dataRange[1]; var maxNumberOfWorkers = 4; var arrayStride = Math.floor(array.length / maxNumberOfWorkers) || 1; arrayStride += arrayStride % numberOfComponents; var arrayIndex = 0; var workerChunks = []; var workers = []; while (arrayIndex < array.length) { var worker = new WorkerFactory(); workers.push(worker); var workerPromise = new lib(worker); var arrayStart = arrayIndex; var arrayEnd = Math.min(arrayIndex + arrayStride, array.length - 1); var subArray = new array.constructor(array.slice(arrayStart, arrayEnd + 1)); workerChunks.push(workerPromise.postMessage({ array: subArray, component: component, numberOfComponents: numberOfComponents, min: min, max: max, numberOfBins: model.numberOfBins }, [subArray.buffer])); arrayIndex += arrayStride; } Promise.all(workerChunks).then(function (subHistograms) { workers.forEach(function (worker) { return worker.terminate(); }); model.histogram = new Float32Array(model.numberOfBins); model.histogram.fill(0); subHistograms.forEach(function (subHistogram) { for (var i = 0, len = subHistogram.length; i < len; ++i) { model.histogram[i] += subHistogram[i]; } }); // Smart Rescale Histogram var sampleSize = Math.min(numberOfBinToConsiders, model.histogram.length - numberOfBinsToSkip); var sortedArray = Array.from(model.histogram); sortedArray.sort(function (a, b) { return Number(a) - Number(b); }); for (var i = 0; i < numberOfBinsToSkip; i++) { sortedArray.pop(); } while (sortedArray.length > sampleSize) { sortedArray.shift(); } var mean = sortedArray.reduce(function (a, b) { return a + b; }, 0) / sampleSize; for (var _i = 0, len = model.histogram.length; _i < len; ++_i) { model.histogram[_i] /= mean; } publicAPI.modified(); setTimeout(publicAPI.render, 0); }); publicAPI.modified(); }; publicAPI.onClick = function (x, y) { var _normalizeCoordinates = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null), _normalizeCoordinates2 = _slicedToArray(_normalizeCoordinates, 2), xNormalized = _normalizeCoordinates2[0], yNormalized = _normalizeCoordinates2[1]; if (xNormalized < 0 && model.style.iconSize > 1) { // Control buttons var delta = model.style.iconSize + model.style.padding; var offset = delta; var buttonIdx = 0; while (y > offset) { buttonIdx += 1; offset += delta; } switch (buttonIdx) { case 0: { var gaussianIdx = publicAPI.addGaussian(0, 1, 0.1, 0, 0); var gaussian = model.gaussians[gaussianIdx]; var originalGaussian = _objectSpread({}, gaussian); var action = ACTIONS.adjustPosition; model.activeGaussian = gaussianIdx; model.selectedGaussian = gaussianIdx; // Fake active action macro.setImmediate(function () { publicAPI.onDown(x, y); model.dragAction = { originalXY: [0, 0], action: action, gaussian: gaussian, originalGaussian: originalGaussian }; }); break; } case 1: { if (model.selectedGaussian !== -1) { publicAPI.removeGaussian(model.selectedGaussian); } break; } default: { model.selectedGaussian = -1; model.dragAction = null; } } } else if (xNormalized < 0 || xNormalized > 1 || yNormalized < 0 || yNormalized > 1) { model.selectedGaussian = -1; model.dragAction = null; } else { var newSelected = findGaussian(xNormalized, model.gaussians); if (newSelected !== model.selectedGaussian) { model.selectedGaussian = newSelected; publicAPI.modified(); } } return true; }; publicAPI.onHover = function (x, y) { // Determines the interaction region size for adjusting the Gaussian's // height. var tolerance = 20 / model.canvas.height; var _normalizeCoordinates3 = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null), _normalizeCoordinates4 = _slicedToArray(_normalizeCoordinates3, 2), xNormalized = _normalizeCoordinates4[0], yNormalized = _normalizeCoordinates4[1]; var _normalizeCoordinates5 = normalizeCoordinates(x, y, model.graphArea), _normalizeCoordinates6 = _slicedToArray(_normalizeCoordinates5, 1), xNormalizedAbs = _normalizeCoordinates6[0]; var newActive = xNormalized < 0 ? model.selectedGaussian : findGaussian(xNormalized, model.gaussians); model.canvas.style.cursor = 'default'; var gaussian = model.gaussians[newActive]; if (model.enableRangeZoom && xNormalizedAbs >= 0 && y < model.graphArea[1] - 6 // circle radius ) { var thirdDelta = (model.rangeZoom[1] - model.rangeZoom[0]) / 3; if (xNormalizedAbs < model.rangeZoom[0] + thirdDelta || xNormalizedAbs > model.rangeZoom[1] - thirdDelta) { model.canvas.style.cursor = ACTION_TO_CURSOR.adjustZoom; } else { model.canvas.style.cursor = ACTION_TO_CURSOR.adjustPosition; } model.dragAction = { rangeZoom: model.rangeZoom, action: ACTIONS.adjustZoom }; } else if (gaussian && xNormalizedAbs >= 0) { var invY = 1 - yNormalized; var actionName = null; if (invY > gaussian.height + tolerance) { actionName = 'adjustPosition'; } else if (invY > gaussian.height - tolerance) { if (Math.abs(xNormalized - gaussian.position) < tolerance) { actionName = 'adjustHeight'; } else { actionName = 'adjustPosition'; } } else if (invY > gaussian.height * 0.5 + tolerance) { actionName = 'adjustPosition'; } else if (invY > gaussian.height * 0.5 - tolerance) { if (Math.abs(xNormalized - gaussian.position) < tolerance) { actionName = 'adjustBias'; } else { actionName = 'adjustPosition'; } } else if (invY > tolerance) { actionName = 'adjustPosition'; } else { actionName = 'adjustWidth'; } model.canvas.style.cursor = ACTION_TO_CURSOR[actionName]; var action = ACTIONS[actionName]; var originalGaussian = _objectSpread({}, gaussian); model.dragAction = { originalXY: [xNormalized, yNormalized], action: action, gaussian: gaussian, originalGaussian: originalGaussian }; } if (newActive !== model.activeGaussian) { model.activeGaussian = newActive; publicAPI.modified(); } return true; }; publicAPI.onDown = function (x, y) { if (!model.mouseIsDown) { publicAPI.invokeAnimation(true); } model.mouseIsDown = true; var xNormalized = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null)[0]; var newSelected = findGaussian(xNormalized, model.gaussians); model.gaussianSide = 0; var gaussian = model.gaussians[newSelected]; if (gaussian) { model.gaussianSide = gaussian.position - xNormalized; } if (newSelected !== model.selectedGaussian && xNormalized > 0) { model.selectedGaussian = newSelected; publicAPI.modified(); } return true; }; publicAPI.onDrag = function (x, y) { if (model.dragAction) { var _normalizeCoordinates7 = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null), _normalizeCoordinates8 = _slicedToArray(_normalizeCoordinates7, 2), xNormalized = _normalizeCoordinates8[0], yNormalized = _normalizeCoordinates8[1]; var action = model.dragAction.action; if (action(xNormalized, yNormalized, _objectSpread({ gaussianSide: model.gaussianSide, model: model, publicAPI: publicAPI }, model.dragAction))) { model.opacities = computeOpacities(model.gaussians, model.piecewiseSize); publicAPI.invokeOpacityChange(publicAPI, true); } publicAPI.modified(); } return true; }; publicAPI.onUp = function (x, y) { if (model.mouseIsDown) { publicAPI.invokeAnimation(false); } model.mouseIsDown = false; return true; }; publicAPI.onLeave = function (x, y) { publicAPI.onUp(x, y); model.canvas.style.cursor = 'default'; model.activeGaussian = -1; publicAPI.modified(); return true; }; publicAPI.onAddGaussian = function (x, y) { var _normalizeCoordinates9 = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null), _normalizeCoordinates10 = _slicedToArray(_normalizeCoordinates9, 2), xNormalized = _normalizeCoordinates10[0], yNormalized = _normalizeCoordinates10[1]; if (xNormalized >= 0) { publicAPI.addGaussian(xNormalized, 1 - yNormalized, 0.1, 0, 0); } return true; }; publicAPI.onRemoveGaussian = function (x, y) { var xNormalized = normalizeCoordinates(x, y, model.graphArea, model.enableRangeZoom ? model.rangeZoom : null)[0]; var newSelected = findGaussian(xNormalized, model.gaussians); if (xNormalized >= 0 && newSelected !== -1) { publicAPI.removeGaussian(newSelected); } return true; }; publicAPI.bindMouseListeners = function () { if (!model.listeners) { var isDown = function isDown() { return !!model.mouseIsDown; }; var touchId = createTouchClickListener({ clicks: 1, touches: 1, action: publicAPI.onClick }, { clicks: 2, touches: 1, action: publicAPI.onAddGaussian }, { clicks: 2, touches: 2, action: publicAPI.onRemoveGaussian }); model.listeners = { mousemove: listenerSelector(isDown, createListener(publicAPI.onDrag), createListener(publicAPI.onHover)), dblclick: createListener(publicAPI.onAddGaussian), contextmenu: createListener(publicAPI.onRemoveGaussian), click: createListener(publicAPI.onClick), mouseup: createListener(publicAPI.onUp), mousedown: createListener(publicAPI.onDown), mouseout: createListener(publicAPI.onLeave), touchstart: createTouchListener(touchId, macro.chain(publicAPI.onHover, publicAPI.onDown)), touchmove: listenerSelector(isDown, createTouchListener(touchId, publicAPI.onDrag), createTouchListener(touchId, publicAPI.onHover)), touchend: createTouchListener(touchId, publicAPI.onUp, 0) // touchend have 0 touch event... }; Object.keys(model.listeners).forEach(function (eventType) { model.canvas.addEventListener(eventType, model.listeners[eventType], false); }); } }; publicAPI.unbindMouseListeners = function () { if (model.listeners) { Object.keys(model.listeners).forEach(function (eventType) { model.canvas.removeEventListener(eventType, model.listeners[eventType]); }); delete model.listeners; } }; publicAPI.render = function () { var ctx = model.canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; var _model$size = _slicedToArray(model.size, 2), width = _model$size[0], height = _model$size[1]; var offset = model.style.padding; var graphArea = [Math.floor(model.style.iconSize + offset), Math.floor(offset), Math.ceil(width - 2 * offset - model.style.iconSize), Math.ceil(height - 2 * offset)]; var zoomControlHeight = model.style.zoomControlHeight; if (model.enableRangeZoom) { graphArea[1] += Math.floor(zoomControlHeight); graphArea[3] -= Math.floor(zoomControlHeight); } model.graphArea = graphArea; // Clear canvas ctx.clearRect(0, 0, width, height); ctx.lineJoin = 'round'; ctx.fillStyle = model.style.backgroundColor; ctx.fillRect.apply(ctx, graphArea); if (model.style.iconSize > 1) { // Draw icons // + var halfSize = Math.round(model.style.iconSize / 2 - model.style.strokeWidth); var center = Math.round(halfSize + offset + model.style.strokeWidth); ctx.beginPath(); ctx.lineWidth = model.style.buttonStrokeWidth; ctx.strokeStyle = model.style.buttonStrokeColor; ctx.arc(center - offset / 2, center, halfSize, 0, 2 * Math.PI, false); ctx.fillStyle = model.style.buttonFillColor; ctx.fill(); ctx.stroke(); ctx.moveTo(center - halfSize + model.style.strokeWidth + 2 - offset / 2, center); ctx.lineTo(center + halfSize - model.style.strokeWidth - 2 - offset / 2, center); ctx.stroke(); ctx.moveTo(center - offset / 2, center - halfSize + model.style.strokeWidth + 2); ctx.lineTo(center - offset / 2, center + halfSize - model.style.strokeWidth - 2); ctx.stroke(); // - if (model.selectedGaussian === -1) { ctx.fillStyle = model.style.buttonDisableFillColor; ctx.lineWidth = model.style.buttonDisableStrokeWidth; ctx.strokeStyle = model.style.buttonDisableStrokeColor; } else { ctx.fillStyle = model.style.buttonFillColor; ctx.lineWidth = model.style.buttonStrokeWidth; ctx.strokeStyle = model.style.buttonStrokeColor; } ctx.beginPath(); ctx.arc(center - offset / 2, center + offset / 2 + model.style.iconSize, halfSize, 0, 2 * Math.PI, false); ctx.fill(); ctx.stroke(); ctx.moveTo(center - halfSize + model.style.strokeWidth + 2 - offset / 2, center + offset / 2 + model.style.iconSize); ctx.lineTo(center + halfSize - model.style.strokeWidth - 2 - offset / 2, center + offset / 2 + model.style.iconSize); ctx.stroke(); } // Draw histogram if (model.histogram) { drawChart(ctx, graphArea, rescaleArray(model.histogram, model.rangeZoom), { lineWidth: 1, strokeStyle: model.style.histogramColor, fillStyle: model.style.histogramColor }); } // Draw gaussians drawChart(ctx, graphArea, rescaleArray(model.opacities, model.enableRangeZoom && model.rangeZoom), { lineWidth: model.style.strokeWidth, strokeStyle: model.style.strokeColor }); // Draw color function if any if (model.colorTransferFunction && model.colorTransferFunction.getSize()) { var rangeToUse = model.dataRange || model.colorTransferFunction.getMappingRange(); if (!model.colorCanvas || model.colorCanvasMTime !== model.colorTransferFunction.getMTime()) { model.colorCanvasMTime = model.colorTransferFunction.getMTime(); model.colorCanvas = updateColorCanvas(model.colorTransferFunction, graphArea[2], rangeToUse, model.colorCanvas); } ctx.save(); drawChart(ctx, graphArea, rescaleArray(model.opacities, model.enableRangeZoom && model.rangeZoom), { lineWidth: 1, strokeStyle: 'rgba(0,0,0,0)', fillStyle: 'rgba(0,0,0,1)', clip: true }); // Draw the correct portion of the color BG image if (model.enableRangeZoom) { ctx.drawImage(model.colorCanvas, model.rangeZoom[0] * graphArea[2], 0, graphArea[2], graphArea[3], graphArea[0], graphArea[1], graphArea[2] / (model.rangeZoom[1] - model.rangeZoom[0]), graphArea[3]); } else { ctx.drawImage(model.colorCanvas, graphArea[0], graphArea[1]); } ctx.restore(); } else if (model.backgroundImage) { model.colorCanvas = updateColorCanvasFromImage(model.backgroundImage, graphArea[2], model.colorCanvas); ctx.save(); drawChart(ctx, graphArea, rescaleArray(model.opacities, model.enableRangeZoom && model.rangeZoom), { lineWidth: 1, strokeStyle: 'rgba(0,0,0,0)', fillStyle: 'rgba(0,0,0,1)', clip: true }); ctx.drawImage(model.colorCanvas, graphArea[0], graphArea[1]); ctx.restore(); } // Draw zoomed area if (model.enableRangeZoom) { ctx.fillStyle = model.style.zoomControlColor; ctx.beginPath(); ctx.rect(graphArea[0] + model.rangeZoom[0] * graphArea[2], 0, (model.rangeZoom[1] - model.rangeZoom[0]) * graphArea[2], zoomControlHeight); ctx.fill(); } // Draw active gaussian var activeGaussian = model.gaussians[model.activeGaussian] || model.gaussians[model.selectedGaussian]; if (activeGaussian) { var activeOpacities = computeOpacities([activeGaussian], graphArea[2]); drawChart(ctx, graphArea, rescaleArray(activeOpacities, model.enableRangeZoom && model.rangeZoom), { lineWidth: model.style.activeStrokeWidth, strokeStyle: model.style.activeColor }); // Draw controls var xCenter = graphArea[0] + rescaleValue(activeGaussian.position, model.enableRangeZoom && model.rangeZoom) * graphArea[2]; var yTop = graphArea[1] + (1 - activeGaussian.height) * graphArea[3]; var yMiddle = graphArea[1] + (1 - 0.5 * activeGaussian.height) * graphArea[3]; var yBottom = graphArea[1] + graphArea[3]; var widthInPixel = activeGaussian.width * graphArea[2]; if (model.enableRangeZoom) { widthInPixel /= model.rangeZoom[1] - model.rangeZoom[0]; } ctx.lineWidth = model.style.handleWidth; ctx.strokeStyle = model.style.handleColor; ctx.fillStyle = model.style.backgroundColor; ctx.beginPath(); ctx.moveTo(xCenter, graphArea[1] + (1 - activeGaussian.height) * graphArea[3]); ctx.lineTo(xCenter, graphArea[1] + graphArea[3]); ctx.stroke(); // Height ctx.beginPath(); ctx.arc(xCenter, yTop, 6, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); // Bias var radius = Math.min(widthInPixel * 0.1, activeGaussian.height * graphArea[3] * 0.2); ctx.beginPath(); ctx.rect(xCenter - radius, yMiddle - radius, radius * 2, radius * 2); ctx.fill(); ctx.stroke(); ctx.beginPath(); // Width var sliderWidth = widthInPixel * 0.8; ctx.rect(xCenter - sliderWidth, yBottom - 5, 2 * sliderWidth, 10); ctx.fill(); ctx.stroke(); } }; publicAPI.getOpacityNodes = function (dataRange) { var rangeToUse = dataRange || model.dataRange; var delta = (rangeToUse[1] - rangeToUse[0]) / (model.opacities.length - 1); var nodes = []; var midpoint = 0.5; var sharpness = 0; for (var index = 0; index < model.opacities.length; index++) { var x = rangeToUse[0] + delta * index; var y = model.opacities[index]; nodes.push({ x: x, y: y, midpoint: midpoint, sharpness: sharpness }); } return nodes; }; publicAPI.applyOpacity = function (piecewiseFunction, dataRange) { var nodes = publicAPI.getOpacityNodes(dataRange); piecewiseFunction.setNodes(nodes); }; publicAPI.getOpacityRange = function (dataRange) { var rangeToUse = dataRange || model.dataRange; var delta = (rangeToUse[1] - rangeToUse[0]) / (model.opacities.length - 1); var minIndex = model.opacities.length - 1; var maxIndex = 0; for (var index = 0; index < model.opacities.length; index++) { if (model.opacities[index] > 0) { minIndex = Math.min(minIndex, index); } if (model.opacities[index] > 0) { maxIndex = Math.max(maxIndex, index); } } return [rangeToUse[0] + minIndex * delta, rangeToUse[0] + maxIndex * delta]; }; var enableZoom = publicAPI.setEnableRangeZoom; publicAPI.setEnableRangeZoom = function (v) { var change = enableZoom(v); if (change) { model.colorCanvasMTime = 0; model.rangeZoom = [0, 1]; } return change; }; var rangeZoom = publicAPI.setRangeZoom; publicAPI.setRangeZoom = function () { var change = rangeZoom.apply(void 0, arguments); if (change) { model.colorCanvasMTime = 0; } return change; }; // Trigger rendering for any modified event publicAPI.onModified(function () { return publicAPI.render(); }); publicAPI.setSize.apply(publicAPI, _toConsumableArray(model.size)); } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { histogram: [], numberOfBins: 256, histogramArray: null, dataRange: [0, 1], gaussians: [], opacities: [], size: [600, 300], piecewiseSize: 256, colorCanvasMTime: 0, gaussianMinimumHeight: 0.05, style: { backgroundColor: 'rgba(255, 255, 255, 1)', histogramColor: 'rgba(200, 200, 200, 0.5)', strokeColor: 'rgb(0, 0, 0)', activeColor: 'rgb(0, 0, 150)', buttonDisableFillColor: 'rgba(255, 255, 255, 0.5)', buttonDisableStrokeColor: 'rgba(0, 0, 0, 0.5)', buttonStrokeColor: 'rgba(0, 0, 0, 1)', buttonFillColor: 'rgba(255, 255, 255, 1)', handleColor: 'rgb(0, 150, 0)', strokeWidth: 2, activeStrokeWidth: 3, buttonStrokeWidth: 1.5, handleWidth: 3, iconSize: 20, padding: 10, zoomControlHeight: 10, zoomControlColor: '#999' }, activeGaussian: -1, selectedGaussian: -1, enableRangeZoom: true, rangeZoom: [0, 1] // normalized value }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Object methods macro.obj(publicAPI, model); macro.setGet(publicAPI, model, ['piecewiseSize', 'numberOfBins', 'colorTransferFunction', 'backgroundImage', 'enableRangeZoom', 'gaussianMinimumHeight']); macro.setGetArray(publicAPI, model, ['rangeZoom'], 2); macro.get(publicAPI, model, ['size', 'canvas', 'gaussians']); macro.event(publicAPI, model, 'opacityChange'); macro.event(publicAPI, model, 'animation'); macro.event(publicAPI, model, 'zoomChange'); // Object specific methods vtkPiecewiseGaussianWidget(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkPiecewiseGaussianWidget'); // ---------------------------------------------------------------------------- var vtkPiecewiseGaussianWidget$1 = _objectSpread({ newInstance: newInstance, extend: extend }, STATIC); export default vtkPiecewiseGaussianWidget$1; export { STATIC, extend, newInstance };