UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

804 lines (670 loc) 34.3 kB
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import * as d3 from 'd3-scale'; import { M as nearestPowerOfTwo } from '../../Common/Core/Math/index.js'; import macro from '../../macros.js'; import vtkActor from './Actor.js'; import vtkDataArray from '../../Common/Core/DataArray.js'; import vtkScalarsToColors from '../../Common/Core/ScalarsToColors.js'; import vtkMapper from './Mapper.js'; import vtkPolyData from '../../Common/DataModel/PolyData.js'; import vtkTexture from './Texture.js'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var VectorMode = vtkScalarsToColors.VectorMode; // ---------------------------------------------------------------------------- // vtkScalarBarActor // // Note log scales are currently not supported // // Developer note: This class is broken into the main class and a helper // class. The main class holds view independent properties (those properties // that do not change as the view's resolution/aspect ratio change). The // helper class is instantiated one per view and holds properties that can // depend on view specific values such as resolution. The helper class code // could have been left to the View specific implementation (such as // vtkWebGPUScalarBarActor) but is instead placed here to it can be shared by // multiple rendering backends. // // ---------------------------------------------------------------------------- function applyTextStyle(ctx, style) { ctx.strokeStyle = style.strokeColor; ctx.lineWidth = style.strokeSize; ctx.fillStyle = style.fontColor; ctx.font = "".concat(style.fontStyle, " ").concat(style.fontSize, "px ").concat(style.fontFamily); } // ---------------------------------------------------------------------------- // Default autoLayout function // ---------------------------------------------------------------------------- // compute good values to use based on window size etc a bunch of heuristics // here with hand tuned constants These values worked for me but really this // method could be redically changed. The basic gist is // 1) compute a resonable font size // 2) render the text atlas using those font sizes // 3) pick horizontal or vertical bsed on window size // 4) based on the size of the title and tick labels rendered // compute the box size and position such that // the text will all fit nicely and the bar will be a resonable size // 5) compute the bar segments based on the above settings // // Note that this function can and should read values from the // ScalarBarActor but should only write values to the view dependent helper // instance that is provided as those values are the ones that will be used // for rendering. // function defaultAutoLayout(publicAPI, model) { return function (helper) { // we don't do a linear scale, the proportions for // a 700 pixel window differ from a 1400 var lastSize = helper.getLastSize(); var xAxisAdjust = Math.pow(lastSize[0] / 700, 0.8); var yAxisAdjust = Math.pow(lastSize[1] / 700, 0.8); var minAdjust = Math.min(xAxisAdjust, yAxisAdjust); var axisTextStyle = helper.getAxisTextStyle(); var tickTextStyle = helper.getTickTextStyle(); Object.assign(axisTextStyle, model.axisTextStyle); Object.assign(tickTextStyle, model.tickTextStyle); // compute a reasonable font size first axisTextStyle.fontSize = Math.max(24 * minAdjust, 12); if (helper.getLastAspectRatio() > 1.0) { tickTextStyle.fontSize = Math.max(20 * minAdjust, 10); } else { tickTextStyle.fontSize = Math.max(16 * minAdjust, 10); } // rebuild the text atlas var textSizes = helper.updateTextureAtlas(); // now compute the boxSize and pixel offsets, different algorithm // for horizonal versus vertical helper.setTopTitle(false); var boxSize = helper.getBoxSizeByReference(); // if vertical if (helper.getLastAspectRatio() > 1.0) { helper.setTickLabelPixelOffset(0.3 * tickTextStyle.fontSize); // if the title will fit within the width of the bar then that looks // nicer to put it at the top (helper.topTitle), otherwise rotate it // and place it sideways if (textSizes.titleWidth <= textSizes.tickWidth + helper.getTickLabelPixelOffset() + 0.8 * tickTextStyle.fontSize) { helper.setTopTitle(true); helper.setAxisTitlePixelOffset(0.2 * tickTextStyle.fontSize); boxSize[0] = 2.0 * (textSizes.tickWidth + helper.getTickLabelPixelOffset() + 0.8 * tickTextStyle.fontSize) / lastSize[0]; helper.setBoxPosition([0.98 - boxSize[0], -0.92]); } else { helper.setAxisTitlePixelOffset(0.2 * tickTextStyle.fontSize); boxSize[0] = 2.0 * (textSizes.titleHeight + helper.getAxisTitlePixelOffset() + textSizes.tickWidth + helper.getTickLabelPixelOffset() + 0.8 * tickTextStyle.fontSize) / lastSize[0]; helper.setBoxPosition([0.99 - boxSize[0], -0.92]); } boxSize[1] = Math.max(1.2, Math.min(1.84 / yAxisAdjust, 1.84)); } else { // horizontal helper.setAxisTitlePixelOffset(1.2 * tickTextStyle.fontSize); helper.setTickLabelPixelOffset(0.1 * tickTextStyle.fontSize); var titleHeight = // total offset from top of bar (includes ticks) 2.0 * (0.8 * tickTextStyle.fontSize + textSizes.titleHeight + helper.getAxisTitlePixelOffset()) / lastSize[1]; var tickWidth = 2.0 * textSizes.tickWidth / lastSize[0]; boxSize[0] = Math.min(1.9, Math.max(1.4, 1.4 * tickWidth * (helper.getTicks().length + 3))); boxSize[1] = titleHeight; helper.setBoxPosition([-0.5 * boxSize[0], -0.97]); } // recomute bar segments based on positioning helper.recomputeBarSegments(textSizes); }; } // ---------------------------------------------------------------------------- // Default generateTicks function // ---------------------------------------------------------------------------- // This function returns the default function used to generate vtkScalarBarActor ticks. // The default function makes use of d3.scaleLinear() to generate 5 tick marks between // the minimum and maximum values of the scalar bar. Customize this behavior by passing // a function to vtkScalarBarActor.newInstance({ generateTicks: customGenerateTicks }) // or by calling scalarBarActor.setGenerateTicks(customGenerateTicks). function defaultGenerateTicks(publicApi, model) { return function (helper) { var lastTickBounds = helper.getLastTickBounds(); var scale = d3.scaleLinear().domain([lastTickBounds[0], lastTickBounds[1]]); var ticks = scale.ticks(5); var format = scale.tickFormat(5); helper.setTicks(ticks); helper.setTickStrings(ticks.map(format)); }; } // many properties of this actor depend on the API specific view The main // dependency being the resolution as that drives what font sizes to use. // Bacause of this we need to do some of the calculations in a API specific // subclass. But... we don't want a lot of duplicated code between WebGL and // WebGPU for example so we have this helper class, that is designed to be // fairly API independent so that API specific views can call this to do // most of the work. function vtkScalarBarActorHelper(publicAPI, model) { // Set our className model.classHierarchy.push('vtkScalarBarActorHelper'); publicAPI.setRenderable = function (renderable) { if (model.renderable === renderable) { return; } model.renderable = renderable; model.barActor.setProperty(renderable.getProperty()); model.barActor.setParentProp(renderable); model.barActor.setCoordinateSystemToDisplay(); model.tmActor.setProperty(renderable.getProperty()); model.tmActor.setParentProp(renderable); model.tmActor.setCoordinateSystemToDisplay(); model.generateTicks = renderable.generateTicks; model.axisTextStyle = _objectSpread({}, renderable.getAxisTextStyle()); model.tickTextStyle = _objectSpread({}, renderable.getTickTextStyle()); publicAPI.modified(); }; publicAPI.updateAPISpecificData = function (size, camera, renderWindow) { // has the size changed? if (model.lastSize[0] !== size[0] || model.lastSize[1] !== size[1]) { model.lastSize[0] = size[0]; model.lastSize[1] = size[1]; model.lastAspectRatio = size[0] / size[1]; model.forceUpdate = true; } var scalarsToColors = model.renderable.getScalarsToColors(); if (!scalarsToColors || !model.renderable.getVisibility()) { return; } // make sure the lut is assigned to our mapper model.barMapper.setLookupTable(scalarsToColors); // camera should be the same for all views model.camera = camera; model.renderWindow = renderWindow; // did something significant change? If so rebuild a lot of things if (model.forceUpdate || Math.max(scalarsToColors.getMTime(), publicAPI.getMTime(), model.renderable.getMTime()) > model.lastRebuildTime.getMTime()) { var range = scalarsToColors.getMappingRange(); model.lastTickBounds = _toConsumableArray(range); // compute tick marks for axes (update for log scale) model.renderable.getGenerateTicks()(publicAPI); if (model.renderable.getAutomated()) { model.renderable.getAutoLayout()(publicAPI); } else { // copy values from renderable model.axisTextStyle = _objectSpread({}, model.renderable.getAxisTextStyle()); model.tickTextStyle = _objectSpread({}, model.renderable.getTickTextStyle()); model.barPosition = _toConsumableArray(model.renderable.getBarPosition()); model.barSize = _toConsumableArray(model.renderable.getBarSize()); model.boxPosition = _toConsumableArray(model.renderable.getBoxPosition()); model.boxSize = _toConsumableArray(model.renderable.getBoxSize()); model.axisTitlePixelOffset = model.renderable.getAxisTitlePixelOffset(); model.tickLabelPixelOffset = model.renderable.getTickLabelPixelOffset(); // rebuild the texture only when force or changed bounds, face // visibility changes do to change the atlas var textSizes = publicAPI.updateTextureAtlas(); // recompute bar segments based on positioning publicAPI.recomputeBarSegments(textSizes); } publicAPI.updatePolyDataForLabels(); publicAPI.updatePolyDataForBarSegments(); model.lastRebuildTime.modified(); model.forceUpdate = false; } }; // create the texture map atlas that contains the rendering of // all the text strings. Only needs to be called when the text strings // have changed (labels and ticks) publicAPI.updateTextureAtlas = function () { // set the text properties model.tmContext.textBaseline = 'bottom'; model.tmContext.textAlign = 'left'; // return some factors about the text atlas var results = {}; // first the axislabel var newTmAtlas = new Map(); var maxWidth = 0; var totalHeight = 1; // start one pixel in so we have a border applyTextStyle(model.tmContext, model.axisTextStyle); var metrics = model.tmContext.measureText(model.renderable.getAxisLabel()); var entry = { height: metrics.actualBoundingBoxAscent + 2, startingHeight: totalHeight, width: metrics.width + 2, textStyle: model.axisTextStyle }; newTmAtlas.set(model.renderable.getAxisLabel(), entry); totalHeight += entry.height; maxWidth = entry.width; results.titleWidth = entry.width; results.titleHeight = entry.height; // and the ticks, NaN Below and Above results.tickWidth = 0; results.tickHeight = 0; applyTextStyle(model.tmContext, model.tickTextStyle); var strings = [].concat(_toConsumableArray(publicAPI.getTickStrings()), ['NaN', 'Below', 'Above']); for (var t = 0; t < strings.length; t++) { if (!newTmAtlas.has(strings[t])) { metrics = model.tmContext.measureText(strings[t]); entry = { height: metrics.actualBoundingBoxAscent + 2, startingHeight: totalHeight, width: metrics.width + 2, textStyle: model.tickTextStyle }; newTmAtlas.set(strings[t], entry); totalHeight += entry.height; if (maxWidth < entry.width) { maxWidth = entry.width; } if (results.tickWidth < entry.width) { results.tickWidth = entry.width; } if (results.tickHeight < entry.height) { results.tickHeight = entry.height; } } } // always use power of two to avoid interpolation // in cases where PO2 is required maxWidth = nearestPowerOfTwo(maxWidth); totalHeight = nearestPowerOfTwo(totalHeight); // set the tcoord values newTmAtlas.forEach(function (value) { value.tcoords = [0.0, (totalHeight - value.startingHeight - value.height) / totalHeight, value.width / maxWidth, (totalHeight - value.startingHeight - value.height) / totalHeight, value.width / maxWidth, (totalHeight - value.startingHeight) / totalHeight, 0.0, (totalHeight - value.startingHeight) / totalHeight]; }); // make sure we have power of two dimensions model.tmCanvas.width = maxWidth; model.tmCanvas.height = totalHeight; model.tmContext.textBaseline = 'bottom'; model.tmContext.textAlign = 'left'; model.tmContext.clearRect(0, 0, maxWidth, totalHeight); // draw the text onto the texture newTmAtlas.forEach(function (value, key) { applyTextStyle(model.tmContext, value.textStyle); model.tmContext.fillText(key, 1, value.startingHeight + value.height - 1); }); model.tmTexture.setCanvas(model.tmCanvas); // mark as modified since the canvas typically doesn't change model.tmTexture.modified(); model._tmAtlas = newTmAtlas; return results; }; publicAPI.computeBarSize = function (textSizes) { // compute orientation model.vertical = model.boxSize[1] > model.boxSize[0]; var tickHeight = 2.0 * textSizes.tickHeight / model.lastSize[1]; var segSize = [1, 1]; // horizontal and vertical have different astetics so adjust based on // orientation if (model.vertical) { var tickWidth = 2.0 * (textSizes.tickWidth + model.tickLabelPixelOffset) / model.lastSize[0]; if (model.topTitle) { var titleHeight = 2.0 * (textSizes.titleHeight + model.axisTitlePixelOffset) / model.lastSize[1]; model.barSize[0] = model.boxSize[0] - tickWidth; model.barSize[1] = model.boxSize[1] - titleHeight; } else { // rotated title so width is based off height var titleWidth = 2.0 * (textSizes.titleHeight + model.axisTitlePixelOffset) / model.lastSize[0]; model.barSize[0] = model.boxSize[0] - titleWidth - tickWidth; model.barSize[1] = model.boxSize[1]; } model.barPosition[0] = model.boxPosition[0] + tickWidth; model.barPosition[1] = model.boxPosition[1]; segSize[1] = tickHeight; } else { var _tickWidth = (2.0 * textSizes.tickWidth - 8) / model.lastSize[0]; var _titleHeight = 2.0 * (textSizes.titleHeight + model.axisTitlePixelOffset) / model.lastSize[1]; model.barSize[0] = model.boxSize[0]; model.barPosition[0] = model.boxPosition[0]; model.barSize[1] = model.boxSize[1] - _titleHeight; model.barPosition[1] = model.boxPosition[1]; segSize[0] = _tickWidth; } return segSize; }; // based on all the settins compute a barSegments array // containing the segments of the scalar bar // each segment contains // corners[4][2] // title - e.g. NaN, Above, ticks // scalars - the normalized scalars values to use for that segment // // Note that the bar consumes the space in the box that remains after // leaving room for the text labels publicAPI.recomputeBarSegments = function (textSizes) { var _model$renderable$get, _model$renderable$get2, _model$renderable$get3, _model$renderable$get4; // first compute the barSize/Position var segSize = publicAPI.computeBarSize(textSizes); model.barSegments = []; var startPos = [0.0, 0.0]; // horizontal and vertical have different astetics so adjust based on // orientation var barAxis = model.vertical ? 1 : 0; var segSpace = model.vertical ? 0.01 : 0.02; function pushSeg(title, scalars) { model.barSegments.push({ corners: [[].concat(startPos), [startPos[0] + segSize[0], startPos[1]], [startPos[0] + segSize[0], startPos[1] + segSize[1]], [startPos[0], startPos[1] + segSize[1]]], scalars: scalars, title: title }); startPos[barAxis] += segSize[barAxis] + segSpace; } if (model.renderable.getDrawNanAnnotation() && model.renderable.getScalarsToColors().getNanColor()) { pushSeg('NaN', [NaN, NaN, NaN, NaN]); } if (model.renderable.getDrawBelowRangeSwatch() && (_model$renderable$get = (_model$renderable$get2 = model.renderable.getScalarsToColors()).getUseBelowRangeColor) !== null && _model$renderable$get !== void 0 && _model$renderable$get.call(_model$renderable$get2)) { pushSeg('Below', [-0.1, -0.1, -0.1, -0.1]); } var haveAbove = (_model$renderable$get3 = (_model$renderable$get4 = model.renderable.getScalarsToColors()).getUseAboveRangeColor) === null || _model$renderable$get3 === void 0 ? void 0 : _model$renderable$get3.call(_model$renderable$get4); // extra space around the ticks section startPos[barAxis] += segSpace; var oldSegSize = segSize[barAxis]; segSize[barAxis] = haveAbove ? 1.0 - 2.0 * segSpace - segSize[barAxis] - startPos[barAxis] : 1.0 - segSpace - startPos[barAxis]; pushSeg('ticks', model.vertical ? [0, 0, 0.995, 0.995] : [0, 0.995, 0.995, 0]); if (model.renderable.getDrawAboveRangeSwatch() && haveAbove) { segSize[barAxis] = oldSegSize; startPos[barAxis] += segSpace; pushSeg('Above', [1.1, 1.1, 1.1, 1.1]); } }; // called by updatePolyDataForLabels // modifies class constants tmp2v3 var tmp2v3 = new Float64Array(3); // anchor point = pos // H alignment = left, middle, right // V alignment = bottom, middle, top // Text Orientation = horizontal, vertical // orientation publicAPI.createPolyDataForOneLabel = function (text, pos, alignment, orientation, offset, results) { var value = model._tmAtlas.get(text); if (!value) { return; } // have to find the four corners of the texture polygon for this label var ptIdx = results.ptIdx; var cellIdx = results.cellIdx; // get achor point in pixels tmp2v3[0] = (0.5 * pos[0] + 0.5) * model.lastSize[0]; tmp2v3[1] = (0.5 * pos[1] + 0.5) * model.lastSize[1]; tmp2v3[2] = pos[2]; tmp2v3[0] += offset[0]; tmp2v3[1] += offset[1]; // get text size in display pixels var textSize = []; var textAxes = orientation === 'vertical' ? [1, 0] : [0, 1]; if (orientation === 'vertical') { textSize[0] = value.width; textSize[1] = -value.height; // update anchor point based on alignment if (alignment[0] === 'middle') { tmp2v3[1] -= value.width / 2.0; } else if (alignment[0] === 'right') { tmp2v3[1] -= value.width; } if (alignment[1] === 'middle') { tmp2v3[0] += value.height / 2.0; } else if (alignment[1] === 'top') { tmp2v3[0] += value.height; } } else { textSize[0] = value.width; textSize[1] = value.height; // update anchor point based on alignment if (alignment[0] === 'middle') { tmp2v3[0] -= value.width / 2.0; } else if (alignment[0] === 'right') { tmp2v3[0] -= value.width; } if (alignment[1] === 'middle') { tmp2v3[1] -= value.height / 2.0; } else if (alignment[1] === 'top') { tmp2v3[1] -= value.height; } } results.points[ptIdx * 3] = tmp2v3[0]; results.points[ptIdx * 3 + 1] = tmp2v3[1]; results.points[ptIdx * 3 + 2] = tmp2v3[2]; results.tcoords[ptIdx * 2] = value.tcoords[0]; results.tcoords[ptIdx * 2 + 1] = value.tcoords[1]; ptIdx++; tmp2v3[textAxes[0]] += textSize[0]; results.points[ptIdx * 3] = tmp2v3[0]; results.points[ptIdx * 3 + 1] = tmp2v3[1]; results.points[ptIdx * 3 + 2] = tmp2v3[2]; results.tcoords[ptIdx * 2] = value.tcoords[2]; results.tcoords[ptIdx * 2 + 1] = value.tcoords[3]; ptIdx++; tmp2v3[textAxes[1]] += textSize[1]; results.points[ptIdx * 3] = tmp2v3[0]; results.points[ptIdx * 3 + 1] = tmp2v3[1]; results.points[ptIdx * 3 + 2] = tmp2v3[2]; results.tcoords[ptIdx * 2] = value.tcoords[4]; results.tcoords[ptIdx * 2 + 1] = value.tcoords[5]; ptIdx++; tmp2v3[textAxes[0]] -= textSize[0]; results.points[ptIdx * 3] = tmp2v3[0]; results.points[ptIdx * 3 + 1] = tmp2v3[1]; results.points[ptIdx * 3 + 2] = tmp2v3[2]; results.tcoords[ptIdx * 2] = value.tcoords[6]; results.tcoords[ptIdx * 2 + 1] = value.tcoords[7]; ptIdx++; // add the two triangles to represent the quad results.polys[cellIdx * 4] = 3; results.polys[cellIdx * 4 + 1] = ptIdx - 4; results.polys[cellIdx * 4 + 2] = ptIdx - 3; results.polys[cellIdx * 4 + 3] = ptIdx - 2; cellIdx++; results.polys[cellIdx * 4] = 3; results.polys[cellIdx * 4 + 1] = ptIdx - 4; results.polys[cellIdx * 4 + 2] = ptIdx - 2; results.polys[cellIdx * 4 + 3] = ptIdx - 1; results.ptIdx += 4; results.cellIdx += 2; }; // update the polydata associated with drawing the text labels // specifically the quads used for each label and their associated tcoords // etc. This changes every time the camera viewpoint changes var tmpv3 = new Float64Array(3); publicAPI.updatePolyDataForLabels = function () { // update the polydata var numLabels = publicAPI.getTickStrings().length + model.barSegments.length; var numPts = numLabels * 4; var numTris = numLabels * 2; var points = new Float64Array(numPts * 3); var polys = new Uint16Array(numTris * 4); var tcoords = new Float32Array(numPts * 2); var results = { ptIdx: 0, cellIdx: 0, polys: polys, points: points, tcoords: tcoords }; // compute the direction vector var offsetAxis = model.vertical ? 0 : 1; var spacedAxis = model.vertical ? 1 : 0; tmpv3[2] = -0.99; // near plane // draw the title var alignment = model.vertical ? ['right', 'middle'] : ['middle', 'bottom']; var dir = [0, 1]; var tickOffsets = [0, 0]; if (model.vertical) { tickOffsets[0] = -model.tickLabelPixelOffset; if (model.topTitle) { tmpv3[0] = model.boxPosition[0] + 0.5 * model.boxSize[0]; tmpv3[1] = model.barPosition[1] + model.barSize[1]; // write the axis label publicAPI.createPolyDataForOneLabel(model.renderable.getAxisLabel(), tmpv3, ['middle', 'bottom'], 'horizontal', [0, model.axisTitlePixelOffset], results); } else { tmpv3[0] = model.barPosition[0] + model.barSize[0]; tmpv3[1] = model.barPosition[1] + 0.5 * model.barSize[1]; // write the axis label publicAPI.createPolyDataForOneLabel(model.renderable.getAxisLabel(), tmpv3, ['middle', 'top'], 'vertical', [model.axisTitlePixelOffset, 0], results); } dir = [-1, 0]; } else { tickOffsets[1] = model.tickLabelPixelOffset; tmpv3[0] = model.barPosition[0] + 0.5 * model.barSize[0]; tmpv3[1] = model.barPosition[1] + model.barSize[1]; publicAPI.createPolyDataForOneLabel(model.renderable.getAxisLabel(), tmpv3, ['middle', 'bottom'], 'horizontal', [0, model.axisTitlePixelOffset], results); } tmpv3[offsetAxis] = model.barPosition[offsetAxis] + (0.5 * dir[offsetAxis] + 0.5) * model.barSize[offsetAxis]; tmpv3[spacedAxis] = model.barPosition[spacedAxis] + model.barSize[spacedAxis] * 0.5; // draw bar segment labels var tickSeg = null; for (var i = 0; i < model.barSegments.length; i++) { var seg = model.barSegments[i]; if (seg.title === 'ticks') { // handle ticks below tickSeg = seg; } else { tmpv3[spacedAxis] = model.barPosition[spacedAxis] + 0.5 * model.barSize[spacedAxis] * (seg.corners[2][spacedAxis] + seg.corners[0][spacedAxis]); publicAPI.createPolyDataForOneLabel(seg.title, tmpv3, alignment, 'horizontal', tickOffsets, results); } } // write the tick labels var tickSegmentStart = model.barPosition[spacedAxis] + model.barSize[spacedAxis] * tickSeg.corners[0][spacedAxis]; var tickSegmentSize = model.barSize[spacedAxis] * (tickSeg.corners[2][spacedAxis] - tickSeg.corners[0][spacedAxis]); var ticks = publicAPI.getTicks(); var tickStrings = publicAPI.getTickStrings(); for (var t = 0; t < ticks.length; t++) { var tickPos = (ticks[t] - model.lastTickBounds[0]) / (model.lastTickBounds[1] - model.lastTickBounds[0]); tmpv3[spacedAxis] = tickSegmentStart + tickSegmentSize * tickPos; publicAPI.createPolyDataForOneLabel(tickStrings[t], tmpv3, alignment, 'horizontal', tickOffsets, results); } var tcoordDA = vtkDataArray.newInstance({ numberOfComponents: 2, values: tcoords, name: 'TextureCoordinates' }); model.tmPolyData.getPointData().setTCoords(tcoordDA); model.tmPolyData.getPoints().setData(points, 3); model.tmPolyData.getPoints().modified(); model.tmPolyData.getPolys().setData(polys, 1); model.tmPolyData.getPolys().modified(); model.tmPolyData.modified(); }; publicAPI.updatePolyDataForBarSegments = function () { var _scalarsToColors$getU, _scalarsToColors$getU2; var scalarsToColors = model.renderable.getScalarsToColors(); var numberOfExtraColors = 0; if (model.renderable.getDrawNanAnnotation() && scalarsToColors.getNanColor()) { numberOfExtraColors += 1; } if (model.renderable.getDrawBelowRangeSwatch() && (_scalarsToColors$getU = scalarsToColors.getUseBelowRangeColor) !== null && _scalarsToColors$getU !== void 0 && _scalarsToColors$getU.call(scalarsToColors)) { numberOfExtraColors += 1; } if (model.renderable.getDrawAboveRangeSwatch() && (_scalarsToColors$getU2 = scalarsToColors.getUseAboveRangeColor) !== null && _scalarsToColors$getU2 !== void 0 && _scalarsToColors$getU2.call(scalarsToColors)) { numberOfExtraColors += 1; } var numPts = 4 * (1 + numberOfExtraColors); var numQuads = numPts; // handle vector component mode var numComps = 1; if (scalarsToColors.getVectorMode() === VectorMode.COMPONENT) { numComps = scalarsToColors.getVectorComponent() + 1; } // create the colored bars var points = new Float64Array(numPts * 3); var cells = new Uint16Array(numQuads * 5); var scalars = new Float32Array(numPts * numComps); var ptIdx = 0; var cellIdx = 0; for (var i = 0; i < model.barSegments.length; i++) { var seg = model.barSegments[i]; for (var e = 0; e < 4; e++) { tmpv3[0] = model.barPosition[0] + seg.corners[e][0] * model.barSize[0]; tmpv3[1] = model.barPosition[1] + seg.corners[e][1] * model.barSize[1]; points[ptIdx * 3] = (0.5 * tmpv3[0] + 0.5) * model.lastSize[0]; points[ptIdx * 3 + 1] = (0.5 * tmpv3[1] + 0.5) * model.lastSize[1]; points[ptIdx * 3 + 2] = tmpv3[2]; for (var nc = 0; nc < numComps; nc++) { scalars[ptIdx * numComps + nc] = model.lastTickBounds[0] + seg.scalars[e] * (model.lastTickBounds[1] - model.lastTickBounds[0]); } ptIdx++; } cells[cellIdx * 5] = 4; cells[cellIdx * 5 + 1] = ptIdx - 4; cells[cellIdx * 5 + 2] = ptIdx - 3; cells[cellIdx * 5 + 3] = ptIdx - 2; cells[cellIdx * 5 + 4] = ptIdx - 1; cellIdx++; } var scalarsDA = vtkDataArray.newInstance({ numberOfComponents: numComps, values: scalars, name: 'Scalars' }); model.polyData.getPointData().setScalars(scalarsDA); model.polyData.getPoints().setData(points, 3); model.polyData.getPoints().modified(); model.polyData.getPolys().setData(cells, 1); model.polyData.getPolys().modified(); model.polyData.modified(); }; } var newScalarBarActorHelper = macro.newInstance(function (publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { renderable: null }; Object.assign(model, {}, initialValues); // Inheritance macro.obj(publicAPI, model); macro.setGet(publicAPI, model, ['axisTitlePixelOffset', 'tickLabelPixelOffset', 'renderable', 'topTitle', 'ticks', 'tickStrings']); macro.get(publicAPI, model, ['lastSize', 'lastAspectRatio', 'lastTickBounds', 'axisTextStyle', 'tickTextStyle', 'barActor', 'tmActor']); macro.getArray(publicAPI, model, ['boxPosition', 'boxSize']); macro.setArray(publicAPI, model, ['boxPosition', 'boxSize'], 2); model.forceUpdate = false; model.lastRebuildTime = {}; macro.obj(model.lastRebuildTime, { mtime: 0 }); model.lastSize = [-1, -1]; model.tmCanvas = document.createElement('canvas'); model.tmContext = model.tmCanvas.getContext('2d'); model._tmAtlas = new Map(); model.barMapper = vtkMapper.newInstance(); model.barMapper.setInterpolateScalarsBeforeMapping(true); model.barMapper.setUseLookupTableScalarRange(true); model.polyData = vtkPolyData.newInstance(); model.barMapper.setInputData(model.polyData); model.barActor = vtkActor.newInstance(); model.barActor.setMapper(model.barMapper); // for texture atlas model.tmPolyData = vtkPolyData.newInstance(); model.tmMapper = vtkMapper.newInstance(); model.tmMapper.setInputData(model.tmPolyData); model.tmTexture = vtkTexture.newInstance({ resizable: true }); model.tmTexture.setInterpolate(false); model.tmActor = vtkActor.newInstance({ parentProp: publicAPI }); model.tmActor.setMapper(model.tmMapper); model.tmActor.addTexture(model.tmTexture); model.barPosition = [0, 0]; model.barSize = [0, 0]; model.boxPosition = [0.88, -0.92]; model.boxSize = [0.1, 1.1]; // internal variables model.lastTickBounds = []; vtkScalarBarActorHelper(publicAPI, model); }, 'vtkScalarBarActorHelper'); // // Now we define the public class that the application sets view independent // properties on. This class is fairly small as it mainly just holds // properties setter and getters leaving all calculations to the helper // class. // function vtkScalarBarActor(publicAPI, model) { // Set our className model.classHierarchy.push('vtkScalarBarActor'); publicAPI.setTickTextStyle = function (tickStyle) { model.tickTextStyle = _objectSpread(_objectSpread({}, model.tickTextStyle), tickStyle); publicAPI.modified(); }; publicAPI.setAxisTextStyle = function (axisStyle) { model.axisTextStyle = _objectSpread(_objectSpread({}, model.axisTextStyle), axisStyle); publicAPI.modified(); }; publicAPI.resetAutoLayoutToDefault = function () { publicAPI.setAutoLayout(defaultAutoLayout(publicAPI, model)); }; publicAPI.resetGenerateTicksToDefault = function () { publicAPI.setGenerateTicks(defaultGenerateTicks()); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- function defaultValues(initialValues) { return _objectSpread({ automated: true, autoLayout: null, axisLabel: 'Scalar Value', barPosition: [0, 0], barSize: [0, 0], boxPosition: [0.88, -0.92], boxSize: [0.1, 1.1], scalarToColors: null, axisTitlePixelOffset: 36.0, axisTextStyle: { fontColor: 'white', fontStyle: 'normal', fontSize: 18, fontFamily: 'serif' }, tickLabelPixelOffset: 14.0, tickTextStyle: { fontColor: 'white', fontStyle: 'normal', fontSize: 14, fontFamily: 'serif' }, generateTicks: null, drawNanAnnotation: true, drawBelowRangeSwatch: true, drawAboveRangeSwatch: true }, initialValues); } // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, defaultValues(initialValues)); if (!model.autoLayout) model.autoLayout = defaultAutoLayout(publicAPI, model); if (!model.generateTicks) model.generateTicks = defaultGenerateTicks(); // Inheritance vtkActor.extend(publicAPI, model, initialValues); publicAPI.getProperty().setDiffuse(0.0); publicAPI.getProperty().setAmbient(1.0); macro.setGet(publicAPI, model, ['automated', 'autoLayout', 'axisTitlePixelOffset', 'axisLabel', 'scalarsToColors', 'tickLabelPixelOffset', 'generateTicks', 'drawNanAnnotation', 'drawBelowRangeSwatch', 'drawAboveRangeSwatch']); macro.get(publicAPI, model, ['axisTextStyle', 'tickTextStyle']); macro.getArray(publicAPI, model, ['barPosition', 'barSize', 'boxPosition', 'boxSize']); macro.setArray(publicAPI, model, ['barPosition', 'barSize', 'boxPosition', 'boxSize'], 2); // Object methods vtkScalarBarActor(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkScalarBarActor'); // ---------------------------------------------------------------------------- var vtkScalarBarActor$1 = { newInstance: newInstance, extend: extend, newScalarBarActorHelper: newScalarBarActorHelper }; export { vtkScalarBarActor$1 as default, extend, newInstance };