UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

1,215 lines (961 loc) 40 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import macro from '../../macro.js'; import { h as hsv2rgb, i as isNan, E as isInf, F as rgb2hsv, G as rgb2lab, H as lab2rgb, I as floor } from '../../Common/Core/Math/index.js'; import vtkScalarsToColors from '../../Common/Core/ScalarsToColors.js'; import Constants from './ColorTransferFunction/Constants.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; } var ColorSpace = Constants.ColorSpace, Scale = Constants.Scale; var ScalarMappingTarget = vtkScalarsToColors.ScalarMappingTarget; var vtkDebugMacro = macro.vtkDebugMacro, vtkErrorMacro = macro.vtkErrorMacro, vtkWarningMacro = macro.vtkWarningMacro; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- /* eslint-disable no-continue */ // Convert to and from a special polar version of CIELAB (useful for creating // continuous diverging color maps). function vtkColorTransferFunctionLabToMsh(lab, msh) { var L = lab[0]; var a = lab[1]; var b = lab[2]; var M = Math.sqrt(L * L + a * a + b * b); var s = M > 0.001 ? Math.acos(L / M) : 0.0; var h = s > 0.001 ? Math.atan2(b, a) : 0.0; msh[0] = M; msh[1] = s; msh[2] = h; } function vtkColorTransferFunctionMshToLab(msh, lab) { var M = msh[0]; var s = msh[1]; var h = msh[2]; lab[0] = M * Math.cos(s); lab[1] = M * Math.sin(s) * Math.cos(h); lab[2] = M * Math.sin(s) * Math.sin(h); } // For the case when interpolating from a saturated color to an unsaturated // color, find a hue for the unsaturated color that makes sense. function vtkColorTransferFunctionAdjustHue(msh, unsatM) { if (msh[0] >= unsatM - 0.1) { // The best we can do is hold hue constant. return msh[2]; } // This equation is designed to make the perceptual change of the // interpolation to be close to constant. var hueSpin = msh[1] * Math.sqrt(unsatM * unsatM - msh[0] * msh[0]) / (msh[0] * Math.sin(msh[1])); // Spin hue away from 0 except in purple hues. if (msh[2] > -0.3 * Math.PI) { return msh[2] + hueSpin; } return msh[2] - hueSpin; } function vtkColorTransferFunctionAngleDiff(a1, a2) { var adiff = a1 - a2; if (adiff < 0.0) { adiff = -adiff; } while (adiff >= 2.0 * Math.PI) { adiff -= 2.0 * Math.PI; } if (adiff > Math.PI) { adiff = 2.0 * Math.PI - adiff; } return adiff; } // Interpolate a diverging color map. function vtkColorTransferFunctionInterpolateDiverging(s, rgb1, rgb2, result) { var lab1 = []; var lab2 = []; rgb2lab(rgb1, lab1); rgb2lab(rgb2, lab2); var msh1 = []; var msh2 = []; vtkColorTransferFunctionLabToMsh(lab1, msh1); vtkColorTransferFunctionLabToMsh(lab2, msh2); // If the endpoints are distinct saturated colors, then place white in between // them. var localS = s; if (msh1[1] > 0.05 && msh2[1] > 0.05 && vtkColorTransferFunctionAngleDiff(msh1[2], msh2[2]) > 0.33 * Math.PI) { // Insert the white midpoint by setting one end to white and adjusting the // scalar value. var Mmid = Math.max(msh1[0], msh2[0]); Mmid = Math.max(88.0, Mmid); if (s < 0.5) { msh2[0] = Mmid; msh2[1] = 0.0; msh2[2] = 0.0; localS *= 2.0; } else { msh1[0] = Mmid; msh1[1] = 0.0; msh1[2] = 0.0; localS = 2.0 * localS - 1.0; } } // If one color has no saturation, then its hue value is invalid. In this // case, we want to set it to something logical so that the interpolation of // hue makes sense. if (msh1[1] < 0.05 && msh2[1] > 0.05) { msh1[2] = vtkColorTransferFunctionAdjustHue(msh2, msh1[0]); } else if (msh2[1] < 0.05 && msh1[1] > 0.05) { msh2[2] = vtkColorTransferFunctionAdjustHue(msh1, msh2[0]); } var mshTmp = []; mshTmp[0] = (1 - localS) * msh1[0] + localS * msh2[0]; mshTmp[1] = (1 - localS) * msh1[1] + localS * msh2[1]; mshTmp[2] = (1 - localS) * msh1[2] + localS * msh2[2]; // Now convert back to RGB var labTmp = []; vtkColorTransferFunctionMshToLab(mshTmp, labTmp); lab2rgb(labTmp, result); } // ---------------------------------------------------------------------------- // vtkColorTransferFunction methods // ---------------------------------------------------------------------------- function vtkColorTransferFunction(publicAPI, model) { // Set our className model.classHierarchy.push('vtkColorTransferFunction'); // Return the number of points which specify this function publicAPI.getSize = function () { return model.nodes.length; }; //---------------------------------------------------------------------------- // Add a point defined in RGB publicAPI.addRGBPoint = function (x, r, g, b) { return publicAPI.addRGBPointLong(x, r, g, b, 0.5, 0.0); }; //---------------------------------------------------------------------------- // Add a point defined in RGB publicAPI.addRGBPointLong = function (x, r, g, b) { var midpoint = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0.5; var sharpness = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0.0; // Error check if (midpoint < 0.0 || midpoint > 1.0) { vtkErrorMacro('Midpoint outside range [0.0, 1.0]'); return -1; } if (sharpness < 0.0 || sharpness > 1.0) { vtkErrorMacro('Sharpness outside range [0.0, 1.0]'); return -1; } // remove any node already at this X location if (!model.allowDuplicateScalars) { publicAPI.removePoint(x); } // Create the new node var node = { x: x, r: r, g: g, b: b, midpoint: midpoint, sharpness: sharpness }; // Add it, then sort to get everything in order model.nodes.push(node); publicAPI.sortAndUpdateRange(); // We need to find the index of the node we just added in order // to return this value var i = 0; for (; i < model.nodes.length; i++) { if (model.nodes[i].x === x) { break; } } // If we didn't find it, something went horribly wrong so // return -1 if (i < model.nodes.length) { return i; } return -1; }; //---------------------------------------------------------------------------- // Add a point defined in HSV publicAPI.addHSVPoint = function (x, h, s, v) { return publicAPI.addHSVPointLong(x, h, s, v, 0.5, 0.0); }; //---------------------------------------------------------------------------- // Add a point defined in HSV publicAPI.addHSVPointLong = function (x, h, s, v) { var midpoint = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0.5; var sharpness = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0.0; var rgb = []; var hsv = [h, s, v]; hsv2rgb(hsv, rgb); return publicAPI.addRGBPoint(x, rgb[0], rgb[1], rgb[2], midpoint, sharpness); }; //---------------------------------------------------------------------------- // Set nodes directly publicAPI.setNodes = function (nodes) { if (model.nodes !== nodes) { model.nodes = nodes; publicAPI.sortAndUpdateRange(); } }; //---------------------------------------------------------------------------- // Sort the vector in increasing order, then fill in // the Range publicAPI.sortAndUpdateRange = function () { model.nodes.sort(function (a, b) { return a.x - b.x; }); var modifiedInvoked = publicAPI.updateRange(); // If range is updated, Modified() has been called, don't call it again. if (!modifiedInvoked) { publicAPI.modified(); } }; //---------------------------------------------------------------------------- publicAPI.updateRange = function () { var oldRange = [2]; oldRange[0] = model.mappingRange[0]; oldRange[1] = model.mappingRange[1]; var size = model.nodes.length; if (size) { model.mappingRange[0] = model.nodes[0].x; model.mappingRange[1] = model.nodes[size - 1].x; } else { model.mappingRange[0] = 0; model.mappingRange[1] = 0; } // If the range is the same, then no need to call Modified() if (oldRange[0] === model.mappingRange[0] && oldRange[1] === model.mappingRange[1]) { return false; } publicAPI.modified(); return true; }; //---------------------------------------------------------------------------- // Remove a point publicAPI.removePoint = function (x) { // First find the node since we need to know its // index as our return value var i = 0; for (; i < model.nodes.length; i++) { if (model.nodes[i].x === x) { break; } } var retVal = i; // If the node doesn't exist, we return -1 if (i >= model.nodes.length) { return -1; } // If the first or last point has been removed, then we update the range // No need to sort here as the order of points hasn't changed. var modifiedInvoked = false; model.nodes.splice(i, 1); if (i === 0 || i === model.nodes.length) { modifiedInvoked = publicAPI.updateRange(); } if (!modifiedInvoked) { publicAPI.modified(); } return retVal; }; //---------------------------------------------------------------------------- publicAPI.movePoint = function (oldX, newX) { if (oldX === newX) { // Nothing to do. return; } publicAPI.removePoint(newX); for (var i = 0; i < model.nodes.length; i++) { if (model.nodes[i].x === oldX) { model.nodes[i].x = newX; publicAPI.sortAndUpdateRange(); break; } } }; //---------------------------------------------------------------------------- // Remove all points publicAPI.removeAllPoints = function () { model.nodes = []; publicAPI.sortAndUpdateRange(); }; //---------------------------------------------------------------------------- // Add a line defined in RGB publicAPI.addRGBSegment = function (x1, r1, g1, b1, x2, r2, g2, b2) { // First, find all points in this range and remove them publicAPI.sortAndUpdateRange(); for (var i = 0; i < model.nodes.length;) { if (model.nodes[i].x >= x1 && model.nodes[i].x <= x2) { model.nodes.splice(i, 1); } else { i++; } } // Now add the points publicAPI.addRGBPointLong(x1, r1, g1, b1, 0.5, 0.0); publicAPI.addRGBPointLong(x2, r2, g2, b2, 0.5, 0.0); publicAPI.modified(); }; //---------------------------------------------------------------------------- // Add a line defined in HSV publicAPI.addHSVSegment = function (x1, h1, s1, v1, x2, h2, s2, v2) { var hsv1 = [h1, s1, v1]; var hsv2 = [h2, s2, v2]; var rgb1 = []; var rgb2 = []; hsv2rgb(hsv1, rgb1); hsv2rgb(hsv2, rgb2); publicAPI.addRGBSegment(x1, rgb1[0], rgb1[1], rgb1[2], x2, rgb2[0], rgb2[1], rgb2[2]); }; //---------------------------------------------------------------------------- // Returns the RGBA color evaluated at the specified location publicAPI.mapValue = function (x) { var rgb = []; publicAPI.getColor(x, rgb); return [Math.floor(255.0 * rgb[0] + 0.5), Math.floor(255.0 * rgb[1] + 0.5), Math.floor(255.0 * rgb[2] + 0.5), 255]; }; //---------------------------------------------------------------------------- // Returns the RGB color evaluated at the specified location publicAPI.getColor = function (x, rgb) { if (model.indexedLookup) { var numNodes = publicAPI.getSize(); // todo var idx = publicAPI.getAnnotatedValueIndexInternal(x); if (idx < 0 || numNodes === 0) { publicAPI.getNanColor(rgb); } else { var nodeVal = []; publicAPI.getNodeValue(idx % numNodes, nodeVal); rgb[0] = nodeVal.r; rgb[1] = nodeVal.g; rgb[2] = nodeVal.b; } return; } publicAPI.getTable(x, x, 1, rgb); }; //---------------------------------------------------------------------------- // Returns the red color evaluated at the specified location publicAPI.getRedValue = function (x) { var rgb = []; publicAPI.getColor(x, rgb); return rgb[0]; }; //---------------------------------------------------------------------------- // Returns the green color evaluated at the specified location publicAPI.getGreenValue = function (x) { var rgb = []; publicAPI.getColor(x, rgb); return rgb[1]; }; //---------------------------------------------------------------------------- // Returns the blue color evaluated at the specified location publicAPI.getBlueValue = function (x) { var rgb = []; publicAPI.getColor(x, rgb); return rgb[2]; }; //---------------------------------------------------------------------------- // Returns a table of RGB colors at regular intervals along the function publicAPI.getTable = function (xStart, xEnd, size, table) { // Special case: If either the start or end is a NaN, then all any // interpolation done on them is also a NaN. Therefore, fill the table with // the NaN color. if (isNan(xStart) || isNan(xEnd)) { for (var i = 0; i < size; i++) { table[i * 3 + 0] = model.nanColor[0]; table[i * 3 + 1] = model.nanColor[1]; table[i * 3 + 2] = model.nanColor[2]; } return; } var idx = 0; var numNodes = model.nodes.length; // Need to keep track of the last value so that // we can fill in table locations past this with // this value if Clamping is On. var lastR = 0.0; var lastG = 0.0; var lastB = 0.0; if (numNodes !== 0) { lastR = model.nodes[numNodes - 1].r; lastG = model.nodes[numNodes - 1].g; lastB = model.nodes[numNodes - 1].b; } var x = 0.0; var x1 = 0.0; var x2 = 0.0; var rgb1 = [0.0, 0.0, 0.0]; var rgb2 = [0.0, 0.0, 0.0]; var midpoint = 0.0; var sharpness = 0.0; var tmpVec = []; // If the scale is logarithmic, make sure the range is valid. var usingLogScale = model.scale === Scale.LOG10; if (usingLogScale) { // Note: This requires range[0] <= range[1]. usingLogScale = model.mappingRange[0] > 0.0; } var logStart = 0.0; var logEnd = 0.0; var logX = 0.0; if (usingLogScale) { logStart = Math.log10(xStart); logEnd = Math.log10(xEnd); } // For each table entry for (var _i = 0; _i < size; _i++) { // Find our location in the table var tidx = 3 * _i; // Find our X location. If we are taking only 1 sample, make // it halfway between start and end (usually start and end will // be the same in this case) if (size > 1) { if (usingLogScale) { logX = logStart + _i / (size - 1.0) * (logEnd - logStart); x = Math.pow(10.0, logX); } else { x = xStart + _i / (size - 1.0) * (xEnd - xStart); } } else if (usingLogScale) { logX = 0.5 * (logStart + logEnd); x = Math.pow(10.0, logX); } else { x = 0.5 * (xStart + xEnd); } // Linearly map x from mappingRange to [0, numberOfValues-1], // discretize (round down to the closest integer), // then map back to mappingRange if (model.discretize) { var range = model.mappingRange; if (x >= range[0] && x <= range[1]) { var numberOfValues = model.numberOfValues; var deltaRange = range[1] - range[0]; if (numberOfValues <= 1) { x = range[0] + deltaRange / 2.0; } else { // normalize x var xn = (x - range[0]) / deltaRange; // discretize var discretizeIndex = floor(numberOfValues * xn); // get discretized x x = range[0] + discretizeIndex / (numberOfValues - 1) * deltaRange; } } } // Do we need to move to the next node? while (idx < numNodes && x > model.nodes[idx].x) { idx++; // If we are at a valid point index, fill in // the value at this node, and the one before (the // two that surround our current sample location) // idx cannot be 0 since we just incremented it. if (idx < numNodes) { x1 = model.nodes[idx - 1].x; x2 = model.nodes[idx].x; if (usingLogScale) { x1 = Math.log10(x1); x2 = Math.log10(x2); } rgb1[0] = model.nodes[idx - 1].r; rgb2[0] = model.nodes[idx].r; rgb1[1] = model.nodes[idx - 1].g; rgb2[1] = model.nodes[idx].g; rgb1[2] = model.nodes[idx - 1].b; rgb2[2] = model.nodes[idx].b; // We only need the previous midpoint and sharpness // since these control this region midpoint = model.nodes[idx - 1].midpoint; sharpness = model.nodes[idx - 1].sharpness; // Move midpoint away from extreme ends of range to avoid // degenerate math if (midpoint < 0.00001) { midpoint = 0.00001; } if (midpoint > 0.99999) { midpoint = 0.99999; } } } // Are we at or past the end? If so, just use the last value if (x > model.mappingRange[1]) { table[tidx] = 0.0; table[tidx + 1] = 0.0; table[tidx + 2] = 0.0; if (model.clamping) { if (publicAPI.getUseAboveRangeColor()) { table[tidx] = model.aboveRangeColor[0]; table[tidx + 1] = model.aboveRangeColor[1]; table[tidx + 2] = model.aboveRangeColor[2]; } else { table[tidx] = lastR; table[tidx + 1] = lastG; table[tidx + 2] = lastB; } } } else if (x < model.mappingRange[0] || isInf(x) && x < 0) { // we are before the first node? If so, duplicate this node's values. // We have to deal with -inf here table[tidx] = 0.0; table[tidx + 1] = 0.0; table[tidx + 2] = 0.0; if (model.clamping) { if (publicAPI.getUseBelowRangeColor()) { table[tidx] = model.belowRangeColor[0]; table[tidx + 1] = model.belowRangeColor[1]; table[tidx + 2] = model.belowRangeColor[2]; } else if (numNodes > 0) { table[tidx] = model.nodes[0].r; table[tidx + 1] = model.nodes[0].g; table[tidx + 2] = model.nodes[0].b; } } } else if (idx === 0 && (Math.abs(x - xStart) < 1e-6 || model.discretize)) { if (numNodes > 0) { table[tidx] = model.nodes[0].r; table[tidx + 1] = model.nodes[0].g; table[tidx + 2] = model.nodes[0].b; } else { table[tidx] = 0.0; table[tidx + 1] = 0.0; table[tidx + 2] = 0.0; } } else { // OK, we are between two nodes - interpolate // Our first attempt at a normalized location [0,1] - // we will be modifying this based on midpoint and // sharpness to get the curve shape we want and to have // it pass through (y1+y2)/2 at the midpoint. var s = 0.0; if (usingLogScale) { s = (logX - x1) / (x2 - x1); } else { s = (x - x1) / (x2 - x1); } // Readjust based on the midpoint - linear adjustment if (s < midpoint) { s = 0.5 * s / midpoint; } else { s = 0.5 + 0.5 * (s - midpoint) / (1.0 - midpoint); } // override for sharpness > 0.99 // In this case we just want piecewise constant if (sharpness > 0.99) { // Use the first value since we are below the midpoint if (s < 0.5) { table[tidx] = rgb1[0]; table[tidx + 1] = rgb1[1]; table[tidx + 2] = rgb1[2]; continue; } else { // Use the second value at or above the midpoint table[tidx] = rgb2[0]; table[tidx + 1] = rgb2[1]; table[tidx + 2] = rgb2[2]; continue; } } // Override for sharpness < 0.01 // In this case we want piecewise linear if (sharpness < 0.01) { // Simple linear interpolation if (model.colorSpace === ColorSpace.RGB) { table[tidx] = (1 - s) * rgb1[0] + s * rgb2[0]; table[tidx + 1] = (1 - s) * rgb1[1] + s * rgb2[1]; table[tidx + 2] = (1 - s) * rgb1[2] + s * rgb2[2]; } else if (model.colorSpace === ColorSpace.HSV) { var hsv1 = []; var hsv2 = []; rgb2hsv(rgb1, hsv1); rgb2hsv(rgb2, hsv2); if (model.hSVWrap && (hsv1[0] - hsv2[0] > 0.5 || hsv2[0] - hsv1[0] > 0.5)) { if (hsv1[0] > hsv2[0]) { hsv1[0] -= 1.0; } else { hsv2[0] -= 1.0; } } var hsvTmp = []; hsvTmp[0] = (1.0 - s) * hsv1[0] + s * hsv2[0]; if (hsvTmp[0] < 0.0) { hsvTmp[0] += 1.0; } hsvTmp[1] = (1.0 - s) * hsv1[1] + s * hsv2[1]; hsvTmp[2] = (1.0 - s) * hsv1[2] + s * hsv2[2]; // Now convert this back to RGB hsv2rgb(hsvTmp, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else if (model.colorSpace === ColorSpace.LAB) { var lab1 = []; var lab2 = []; rgb2lab(rgb1, lab1); rgb2lab(rgb2, lab2); var labTmp = []; labTmp[0] = (1 - s) * lab1[0] + s * lab2[0]; labTmp[1] = (1 - s) * lab1[1] + s * lab2[1]; labTmp[2] = (1 - s) * lab1[2] + s * lab2[2]; // Now convert back to RGB lab2rgb(labTmp, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else if (model.colorSpace === ColorSpace.DIVERGING) { vtkColorTransferFunctionInterpolateDiverging(s, rgb1, rgb2, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else { vtkErrorMacro('ColorSpace set to invalid value.', model.colorSpace); } continue; } // We have a sharpness between [0.01, 0.99] - we will // used a modified hermite curve interpolation where we // derive the slope based on the sharpness, and we compress // the curve non-linearly based on the sharpness // First, we will adjust our position based on sharpness in // order to make the curve sharper (closer to piecewise constant) if (s < 0.5) { s = 0.5 * Math.pow(s * 2.0, 1.0 + 10.0 * sharpness); } else if (s > 0.5) { s = 1.0 - 0.5 * Math.pow((1.0 - s) * 2, 1 + 10.0 * sharpness); } // Compute some coefficients we will need for the hermite curve var ss = s * s; var sss = ss * s; var h1 = 2.0 * sss - 3 * ss + 1; var h2 = -2 * sss + 3 * ss; var h3 = sss - 2 * ss + s; var h4 = sss - ss; var slope = void 0; var t = void 0; if (model.colorSpace === ColorSpace.RGB) { for (var j = 0; j < 3; j++) { // Use one slope for both end points slope = rgb2[j] - rgb1[j]; t = (1.0 - sharpness) * slope; // Compute the value table[tidx + j] = h1 * rgb1[j] + h2 * rgb2[j] + h3 * t + h4 * t; } } else if (model.colorSpace === ColorSpace.HSV) { var _hsv = []; var _hsv2 = []; rgb2hsv(rgb1, _hsv); rgb2hsv(rgb2, _hsv2); if (model.hSVWrap && (_hsv[0] - _hsv2[0] > 0.5 || _hsv2[0] - _hsv[0] > 0.5)) { if (_hsv[0] > _hsv2[0]) { _hsv[0] -= 1.0; } else { _hsv2[0] -= 1.0; } } var _hsvTmp = []; for (var _j = 0; _j < 3; _j++) { // Use one slope for both end points slope = _hsv2[_j] - _hsv[_j]; t = (1.0 - sharpness) * slope; // Compute the value _hsvTmp[_j] = h1 * _hsv[_j] + h2 * _hsv2[_j] + h3 * t + h4 * t; if (_j === 0 && _hsvTmp[_j] < 0.0) { _hsvTmp[_j] += 1.0; } } // Now convert this back to RGB hsv2rgb(_hsvTmp, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else if (model.colorSpace === ColorSpace.LAB) { var _lab = []; var _lab2 = []; rgb2lab(rgb1, _lab); rgb2lab(rgb2, _lab2); var _labTmp = []; for (var _j2 = 0; _j2 < 3; _j2++) { // Use one slope for both end points slope = _lab2[_j2] - _lab[_j2]; t = (1.0 - sharpness) * slope; // Compute the value _labTmp[_j2] = h1 * _lab[_j2] + h2 * _lab2[_j2] + h3 * t + h4 * t; } // Now convert this back to RGB lab2rgb(_labTmp, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else if (model.colorSpace === ColorSpace.DIVERGING) { // I have not implemented proper interpolation by a hermite curve for // the diverging color map, but I cannot think of a good use case for // that anyway. vtkColorTransferFunctionInterpolateDiverging(s, rgb1, rgb2, tmpVec); table[tidx] = tmpVec[0]; table[tidx + 1] = tmpVec[1]; table[tidx + 2] = tmpVec[2]; } else { vtkErrorMacro('ColorSpace set to invalid value.'); } // Final error check to make sure we don't go outside [0,1] for (var _j3 = 0; _j3 < 3; _j3++) { table[tidx + _j3] = table[tidx + _j3] < 0.0 ? 0.0 : table[tidx + _j3]; table[tidx + _j3] = table[tidx + _j3] > 1.0 ? 1.0 : table[tidx + _j3]; } } } }; //---------------------------------------------------------------------------- publicAPI.getUint8Table = function (xStart, xEnd, size) { var withAlpha = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; if (publicAPI.getMTime() <= model.buildTime && model.tableSize === size && model.tableWithAlpha !== withAlpha) { return model.table; } if (model.nodes.length === 0) { vtkErrorMacro('Attempting to lookup a value with no points in the function'); return model.table; } var nbChannels = withAlpha ? 4 : 3; if (model.tableSize !== size || model.tableWithAlpha !== withAlpha) { model.table = new Uint8Array(size * nbChannels); model.tableSize = size; model.tableWithAlpha = withAlpha; } var tmpTable = []; publicAPI.getTable(xStart, xEnd, size, tmpTable); for (var i = 0; i < size; i++) { model.table[i * nbChannels + 0] = Math.floor(tmpTable[i * 3 + 0] * 255.0 + 0.5); model.table[i * nbChannels + 1] = Math.floor(tmpTable[i * 3 + 1] * 255.0 + 0.5); model.table[i * nbChannels + 2] = Math.floor(tmpTable[i * 3 + 2] * 255.0 + 0.5); if (withAlpha) { model.table[i * nbChannels + 3] = 255; } } model.buildTime.modified(); return model.table; }; //---------------------------------------------------------------------------- publicAPI.buildFunctionFromTable = function (xStart, xEnd, size, table) { var inc = 0.0; publicAPI.removeAllPoints(); if (size > 1) { inc = (xEnd - xStart) / (size - 1.0); } for (var i = 0; i < size; i++) { var node = { x: xStart + inc * i, r: table[i * 3], g: table[i * 3 + 1], b: table[i * 3 + 2], sharpness: 0.0, midpoint: 0.5 }; model.nodes.push(node); } publicAPI.sortAndUpdateRange(); }; //---------------------------------------------------------------------------- // For a specified index value, get the node parameters publicAPI.getNodeValue = function (index, val) { if (index < 0 || index >= model.nodes.length) { vtkErrorMacro('Index out of range!'); return -1; } val[0] = model.nodes[index].x; val[1] = model.nodes[index].r; val[2] = model.nodes[index].g; val[3] = model.nodes[index].b; val[4] = model.nodes[index].midpoint; val[5] = model.nodes[index].sharpness; return 1; }; //---------------------------------------------------------------------------- // For a specified index value, get the node parameters publicAPI.setNodeValue = function (index, val) { if (index < 0 || index >= model.nodes.length) { vtkErrorMacro('Index out of range!'); return -1; } var oldX = model.nodes[index].x; model.nodes[index].x = val[0]; model.nodes[index].r = val[1]; model.nodes[index].g = val[2]; model.nodes[index].b = val[3]; model.nodes[index].midpoint = val[4]; model.nodes[index].sharpness = val[5]; if (oldX !== val[0]) { // The point has been moved, the order of points or the range might have // been modified. publicAPI.sortAndUpdateRange(); // No need to call Modified() here because SortAndUpdateRange() has done it // already. } else { publicAPI.modified(); } return 1; }; //---------------------------------------------------------------------------- publicAPI.getNumberOfAvailableColors = function () { if (model.indexedLookup && publicAPI.getSize()) { return publicAPI.getSize(); } if (model.tableSize) { // Not sure if this is correct since it is only set if // "const unsigned char *::GetTable(double xStart, double xEnd,int size)" // has been called. return model.tableSize; } return 16777216; // 2^24 }; //---------------------------------------------------------------------------- publicAPI.getIndexedColor = function (idx, rgba) { var n = publicAPI.getSize(); if (n > 0 && idx >= 0) { var nodeValue = []; publicAPI.getNodeValue(idx % n, nodeValue); for (var j = 0; j < 3; ++j) { rgba[j] = nodeValue[j + 1]; } rgba[3] = 1.0; // NodeColor is RGB-only. return; } publicAPI.getNanColor(rgba); rgba[3] = 1.0; // NanColor is RGB-only. }; //---------------------------------------------------------------------------- publicAPI.fillFromDataPointer = function (nb, ptr) { if (nb <= 0 || !ptr) { return; } publicAPI.removeAllPoints(); for (var i = 0; i < nb; i++) { publicAPI.addRGBPoint(ptr[i * 4], ptr[i * 4 + 1], ptr[i * 4 + 2], ptr[i * 4 + 3]); } }; //---------------------------------------------------------------------------- publicAPI.setMappingRange = function (min, max) { var range = [min, max]; var originalRange = publicAPI.getRange(); if (originalRange[1] === range[1] && originalRange[0] === range[0]) { return; } if (range[1] === range[0]) { vtkErrorMacro('attempt to set zero width color range'); return; } var scale = (range[1] - range[0]) / (originalRange[1] - originalRange[0]); var shift = range[0] - originalRange[0] * scale; for (var i = 0; i < model.nodes.length; ++i) { model.nodes[i].x = model.nodes[i].x * scale + shift; } model.mappingRange[0] = range[0]; model.mappingRange[1] = range[1]; publicAPI.modified(); }; //---------------------------------------------------------------------------- publicAPI.adjustRange = function (range) { var functionRange = publicAPI.getRange(); // Make sure we have points at each end of the range var rgb = []; if (functionRange[0] < range[0]) { publicAPI.getColor(range[0], rgb); publicAPI.addRGBPoint(range[0], rgb[0], rgb[1], rgb[2]); } else { publicAPI.getColor(functionRange[0], rgb); publicAPI.addRGBPoint(range[0], rgb[0], rgb[1], rgb[2]); } if (functionRange[1] > range[1]) { publicAPI.getColor(range[1], rgb); publicAPI.addRGBPoint(range[1], rgb[0], rgb[1], rgb[2]); } else { publicAPI.getColor(functionRange[1], rgb); publicAPI.addRGBPoint(range[1], rgb[0], rgb[1], rgb[2]); } // Remove all points out-of-range publicAPI.sortAndUpdateRange(); for (var i = 0; i < model.nodes.length;) { if (model.nodes[i].x >= range[0] && model.nodes[i].x <= range[1]) { model.nodes.splice(i, 1); } else { ++i; } } return 1; }; //-------------------------------------------------------------------------- publicAPI.estimateMinNumberOfSamples = function (x1, x2) { var d = publicAPI.findMinimumXDistance(); return Math.ceil((x2 - x1) / d); }; //---------------------------------------------------------------------------- publicAPI.findMinimumXDistance = function () { if (model.nodes.length < 2) { return -1.0; } var distance = Number.MAX_VALUE; for (var i = 0; i < model.nodes.length - 1; i++) { var currentDist = model.nodes[i + 1].x - model.nodes[i].x; if (currentDist < distance) { distance = currentDist; } } return distance; }; publicAPI.mapScalarsThroughTable = function (input, output, outFormat, inputOffset) { if (publicAPI.getSize() === 0) { vtkDebugMacro('Transfer Function Has No Points!'); return; } if (model.indexedLookup) { publicAPI.mapDataIndexed(input, output, outFormat, inputOffset); } else { publicAPI.mapData(input, output, outFormat, inputOffset); } }; //---------------------------------------------------------------------------- publicAPI.mapData = function (input, output, outFormat, inputOffset) { if (publicAPI.getSize() === 0) { vtkWarningMacro('Transfer Function Has No Points!'); return; } var alpha = Math.floor(publicAPI.getAlpha() * 255.0 + 0.5); var length = input.getNumberOfTuples(); var inIncr = input.getNumberOfComponents(); var outputV = output.getData(); var inputV = input.getData(); var rgb = []; if (outFormat === ScalarMappingTarget.RGBA) { for (var i = 0; i < length; i++) { var x = inputV[i * inIncr + inputOffset]; publicAPI.getColor(x, rgb); outputV[i * 4] = Math.floor(rgb[0] * 255.0 + 0.5); outputV[i * 4 + 1] = Math.floor(rgb[1] * 255.0 + 0.5); outputV[i * 4 + 2] = Math.floor(rgb[2] * 255.0 + 0.5); outputV[i * 4 + 3] = alpha; } } if (outFormat === ScalarMappingTarget.RGB) { for (var _i2 = 0; _i2 < length; _i2++) { var _x = inputV[_i2 * inIncr + inputOffset]; publicAPI.getColor(_x, rgb); outputV[_i2 * 3] = Math.floor(rgb[0] * 255.0 + 0.5); outputV[_i2 * 3 + 1] = Math.floor(rgb[1] * 255.0 + 0.5); outputV[_i2 * 3 + 2] = Math.floor(rgb[2] * 255.0 + 0.5); } } if (outFormat === ScalarMappingTarget.LUMINANCE) { for (var _i3 = 0; _i3 < length; _i3++) { var _x2 = inputV[_i3 * inIncr + inputOffset]; publicAPI.getColor(_x2, rgb); outputV[_i3] = Math.floor(rgb[0] * 76.5 + rgb[1] * 150.45 + rgb[2] * 28.05 + 0.5); } } if (outFormat === ScalarMappingTarget.LUMINANCE_ALPHA) { for (var _i4 = 0; _i4 < length; _i4++) { var _x3 = inputV[_i4 * inIncr + inputOffset]; publicAPI.getColor(_x3, rgb); outputV[_i4 * 2] = Math.floor(rgb[0] * 76.5 + rgb[1] * 150.45 + rgb[2] * 28.05 + 0.5); outputV[_i4 * 2 + 1] = alpha; } } }; //---------------------------------------------------------------------------- publicAPI.applyColorMap = function (colorMap) { if (colorMap.ColorSpace) { model.colorSpace = ColorSpace[colorMap.ColorSpace.toUpperCase()]; if (model.colorSpace === undefined) { vtkErrorMacro("ColorSpace ".concat(colorMap.ColorSpace, " not supported, using RGB instead")); model.colorSpace = ColorSpace.RGB; } } if (colorMap.NanColor) { model.nanColor = [].concat(colorMap.NanColor); while (model.nanColor.length < 4) { model.nanColor.push(1.0); } } if (colorMap.RGBPoints) { var size = colorMap.RGBPoints.length; model.nodes = []; var midpoint = 0.5; var sharpness = 0.0; for (var i = 0; i < size; i += 4) { model.nodes.push({ x: colorMap.RGBPoints[i], r: colorMap.RGBPoints[i + 1], g: colorMap.RGBPoints[i + 2], b: colorMap.RGBPoints[i + 3], midpoint: midpoint, sharpness: sharpness }); } } // FIXME: not supported ? // if (colorMap.IndexedColors) { // } // if (colorMap.Annotations) { // } publicAPI.sortAndUpdateRange(); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { clamping: true, colorSpace: ColorSpace.RGB, hSVWrap: true, scale: Scale.LINEAR, nanColor: null, belowRangeColor: null, aboveRangeColor: null, useAboveRangeColor: false, useBelowRangeColor: false, allowDuplicateScalars: false, table: null, tableSize: 0, buildTime: null, nodes: null, discretize: false, numberOfValues: 256 }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance vtkScalarsToColors.extend(publicAPI, model, initialValues); // Internal objects initialization model.table = []; model.nodes = []; model.nanColor = [0.5, 0.0, 0.0, 1.0]; model.belowRangeColor = [0.0, 0.0, 0.0, 1.0]; model.aboveRangeColor = [1.0, 1.0, 1.0, 1.0]; model.buildTime = {}; macro.obj(model.buildTime); // Create get-only macros macro.get(publicAPI, model, ['buildTime', 'mappingRange']); // Create get-set macros macro.setGet(publicAPI, model, ['useAboveRangeColor', 'useBelowRangeColor', 'colorSpace', 'discretize', 'numberOfValues']); macro.setArray(publicAPI, model, ['nanColor', 'belowRangeColor', 'aboveRangeColor'], 4); // Create get macros for array macro.getArray(publicAPI, model, ['nanColor', 'belowRangeColor', 'aboveRangeColor']); // For more macro methods, see "Sources/macro.js" // Object specific methods vtkColorTransferFunction(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkColorTransferFunction'); // ---------------------------------------------------------------------------- var vtkColorTransferFunction$1 = _objectSpread({ newInstance: newInstance, extend: extend }, Constants); export default vtkColorTransferFunction$1; export { extend, newInstance };