From ae483660a84703972d2fee9fa730e1e4d0ba5370 Mon Sep 17 00:00:00 2001 From: Ken Martin Date: Mon, 7 Feb 2022 10:53:18 -0500 Subject: [PATCH] fix(ScalarBarActor): Fix missing text updates This MR fixes the missing text updates for the scalar bar actor on some browsers. It also seperates the View dependent data and functions from the non-view dependent through a new helper class. One helper should be created per view. Unfortunately this results in a huge code change and one breaking API change. When users provided their own function to compute values, those functions will need to be reworked to deal with the new helper class. The default autolayout function has been modified and can be used as an example of how to update your own function. --- .../Rendering/Core/ScalarBarActor/index.js | 365 +++++++++++------- Sources/Rendering/OpenGL/Profiles/All.js | 1 + Sources/Rendering/OpenGL/Profiles/Geometry.js | 1 + .../Rendering/OpenGL/ScalarBarActor/index.js | 77 ++++ Sources/Rendering/WebGPU/Profiles/All.js | 1 + Sources/Rendering/WebGPU/Profiles/Geometry.js | 1 + .../Rendering/WebGPU/ScalarBarActor/index.js | 77 ++++ 7 files changed, 385 insertions(+), 138 deletions(-) create mode 100644 Sources/Rendering/OpenGL/ScalarBarActor/index.js create mode 100644 Sources/Rendering/WebGPU/ScalarBarActor/index.js diff --git a/Sources/Rendering/Core/ScalarBarActor/index.js b/Sources/Rendering/Core/ScalarBarActor/index.js index da0aab6d028..1145eef98b2 100644 --- a/Sources/Rendering/Core/ScalarBarActor/index.js +++ b/Sources/Rendering/Core/ScalarBarActor/index.js @@ -6,7 +6,6 @@ import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkScalarsToColors from 'vtk.js/Sources/Common/Core/ScalarsToColors'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; -import vtkPixelSpaceCallbackMapper from 'vtk.js/Sources/Rendering/Core/PixelSpaceCallbackMapper'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; import vtkTexture from 'vtk.js/Sources/Rendering/Core/Texture'; @@ -16,6 +15,16 @@ const { VectorMode } = vtkScalarsToColors; // 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. +// // ---------------------------------------------------------------------------- // some shared temp variables to reduce heap allocs @@ -38,10 +47,9 @@ function applyTextStyle(ctx, style) { // 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 +// 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 @@ -49,100 +57,150 @@ function applyTextStyle(ctx, style) { // 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 () => { + return (helper) => { // we don't do a linear scale, the proportions for // a 700 pixel window differ from a 1400 - const xAxisAdjust = (model.lastSize[0] / 700) ** 0.8; - const yAxisAdjust = (model.lastSize[1] / 700) ** 0.8; + const lastSize = helper.getLastSize(); + const xAxisAdjust = (lastSize[0] / 700) ** 0.8; + const yAxisAdjust = (lastSize[1] / 700) ** 0.8; const minAdjust = Math.min(xAxisAdjust, yAxisAdjust); + const axisTextStyle = helper.getAxisTextStyle(); + const tickTextStyle = helper.getTickTextStyle(); + Object.assign(axisTextStyle, model.axisTextStyle); + Object.assign(tickTextStyle, model.tickTextStyle); + // compute a reasonable font size first - model.axisTextStyle.fontSize = Math.max(24 * minAdjust, 12); - if (model.lastAspectRatio > 1.0) { - model.tickTextStyle.fontSize = Math.max(20 * minAdjust, 10); + axisTextStyle.fontSize = Math.max(24 * minAdjust, 12); + if (helper.getLastAspectRatio() > 1.0) { + tickTextStyle.fontSize = Math.max(20 * minAdjust, 10); } else { - model.tickTextStyle.fontSize = Math.max(16 * minAdjust, 10); + tickTextStyle.fontSize = Math.max(16 * minAdjust, 10); } // rebuild the text atlas - const textSizes = publicAPI.updateTextureAtlas(); + const textSizes = helper.updateTextureAtlas(); // now compute the boxSize and pixel offsets, different algorithm // for horizonal versus vertical - model.topTitle = false; + helper.setTopTitle(false); + + const boxSize = helper.getBoxSizeByReference(); + // if vertical - if (model.lastAspectRatio > 1.0) { - model.tickLabelPixelOffset = 0.4 * model.tickTextStyle.fontSize; + if (helper.getLastAspectRatio() > 1.0) { + helper.setTickLabelPixelOffset(0.4 * tickTextStyle.fontSize); const tickWidth = - (2.0 * (textSizes.tickWidth + model.tickLabelPixelOffset)) / - model.lastSize[0]; - model.axisTitlePixelOffset = 0.8 * model.axisTextStyle.fontSize; + (2.0 * (textSizes.tickWidth + helper.getTickLabelPixelOffset())) / + lastSize[0]; + helper.setAxisTitlePixelOffset(0.8 * axisTextStyle.fontSize); // width required if the title is vertical const titleWidth = - (2.0 * (textSizes.titleHeight + model.axisTitlePixelOffset)) / - model.lastSize[0]; + (2.0 * (textSizes.titleHeight + helper.getAxisTitlePixelOffset())) / + lastSize[0]; // if the title will fit within the width of the bar then that looks - // nicer to put it at the top (model.topTitle), otherwise rotate it + // nicer to put it at the top (helper.topTitle), otherwise rotate it // and place it sideways if ( tickWidth + 0.4 * titleWidth > - (2.0 * textSizes.titleWidth) / model.lastSize[0] + (2.0 * textSizes.titleWidth) / lastSize[0] ) { - model.topTitle = true; - model.boxSize[0] = tickWidth + 0.4 * titleWidth; - model.boxPosition = [0.98 - model.boxSize[0], -0.92]; + helper.setTopTitle(true); + boxSize[0] = tickWidth + 0.4 * titleWidth; + helper.setBoxPosition([0.98 - boxSize[0], -0.92]); } else { - model.boxSize[0] = tickWidth + 1.4 * titleWidth; - model.boxPosition = [0.99 - model.boxSize[0], -0.92]; + boxSize[0] = tickWidth + 1.4 * titleWidth; + helper.setBoxPosition([0.99 - boxSize[0], -0.92]); } - model.boxSize[1] = Math.max(1.2, Math.min(1.84 / yAxisAdjust, 1.84)); + boxSize[1] = Math.max(1.2, Math.min(1.84 / yAxisAdjust, 1.84)); } else { // horizontal - model.axisTitlePixelOffset = 2.0 * model.tickTextStyle.fontSize; - model.tickLabelPixelOffset = 0.5 * model.tickTextStyle.fontSize; + helper.setAxisTitlePixelOffset(2.0 * tickTextStyle.fontSize); + helper.setTickLabelPixelOffset(0.5 * tickTextStyle.fontSize); const tickHeight = - (2.0 * (textSizes.tickHeight + model.tickLabelPixelOffset)) / - model.lastSize[1]; + (2.0 * (textSizes.tickHeight + helper.getTickLabelPixelOffset())) / + lastSize[1]; const titleHeight = - (2.0 * (textSizes.titleHeight + model.axisTitlePixelOffset)) / - model.lastSize[1]; - const tickWidth = (2.0 * textSizes.tickWidth) / model.lastSize[0]; - model.boxSize[0] = Math.min( + (2.0 * (textSizes.titleHeight + helper.getAxisTitlePixelOffset())) / + lastSize[1]; + const tickWidth = (2.0 * textSizes.tickWidth) / lastSize[0]; + boxSize[0] = Math.min( 1.9, - Math.max(1.4, 1.4 * tickWidth * (model.ticks.length + 3)) + Math.max(1.4, 1.4 * tickWidth * (helper.getTicks().length + 3)) ); - model.boxSize[1] = tickHeight + titleHeight; - model.boxPosition = [-0.5 * model.boxSize[0], -0.97]; + boxSize[1] = tickHeight + titleHeight; + helper.setBoxPosition([-0.5 * boxSize[0], -0.97]); } // recomute bar segments based on positioning - publicAPI.recomputeBarSegments(textSizes); + helper.recomputeBarSegments(textSizes); }; } -function vtkScalarBarActor(publicAPI, model) { +// 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('vtkScalarBarActor'); + model.classHierarchy.push('vtkScalarBarActorHelper'); + + publicAPI.setRenderable = (renderable) => { + if (model.renderable === renderable) { + return; + } + model.renderable = renderable; + model.barActor.setProperty(renderable.getProperty()); + model.barActor.setParentProp(renderable); + model.tmActor.setProperty(renderable.getProperty()); + model.tmActor.setParentProp(renderable); + + model.axisTextStyle = { ...renderable.getAxisTextStyle() }; + model.tickTextStyle = { ...renderable.getTickTextStyle() }; + + publicAPI.modified(); + }; + + publicAPI.updateAPISpecificData = (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; + } - // main method to rebuild the scalarBar when something has changed - // tracks modified times - publicAPI.update = () => { - if (!model.scalarsToColors || !model.visibility) { + const scalarsToColors = model.renderable.getScalarsToColors(); + if (!scalarsToColors || !model.renderable.getVisibility()) { return; } // make sure the lut is assigned to our mapper - model.barMapper.setLookupTable(model.scalarsToColors); + 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(model.scalarsToColors.getMTime(), publicAPI.getMTime()) > + Math.max(scalarsToColors.getMTime(), publicAPI.getMTime()) > model.lastRebuildTime.getMTime() ) { - const range = model.scalarsToColors.getMappingRange(); + const range = scalarsToColors.getMappingRange(); model.lastTickBounds = [...range]; model.barMapper.setScalarRange(model.lastTickBounds); @@ -152,11 +210,21 @@ function vtkScalarBarActor(publicAPI, model) { .domain([model.lastTickBounds[0], model.lastTickBounds[1]]); model.ticks = scale.ticks(5); const format = scale.tickFormat(5); - model.tickStrings = model.ticks.map(format); + model.tickstrings = model.ticks.map(format); - if (model.automated) { - model.autoLayout(); + if (model.renderable.getAutomated()) { + model.renderable.getAutoLayout()(publicAPI); } else { + // copy values from renderable + model.axisTextStyle = { ...model.renderable.getAxisTextStyle() }; + model.tickTextStyle = { ...model.renderable.getTickTextStyle() }; + model.barPosition = [...model.renderable.getBarPosition()]; + model.barSize = [...model.renderable.getBarSize()]; + model.boxPosition = [...model.renderable.getBoxPosition()]; + model.boxSize = [...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 const textSizes = publicAPI.updateTextureAtlas(); @@ -193,7 +261,7 @@ function vtkScalarBarActor(publicAPI, model) { model._nextAtlas = null; if (doUpdate) { model.forceViewUpdate = true; - publicAPI.update(); + model.renderWindow.render(); } } }; @@ -214,14 +282,14 @@ function vtkScalarBarActor(publicAPI, model) { let maxWidth = 0; let totalHeight = 1; // start one pixel in so we have a border applyTextStyle(model.tmContext, model.axisTextStyle); - let metrics = model.tmContext.measureText(model.axisLabel); + let metrics = model.tmContext.measureText(model.renderable.getAxisLabel()); let entry = { height: metrics.actualBoundingBoxAscent + 2, startingHeight: totalHeight, width: metrics.width + 2, textStyle: model.axisTextStyle, }; - newTmAtlas.set(model.axisLabel, entry); + newTmAtlas.set(model.renderable.getAxisLabel(), entry); totalHeight += entry.height; maxWidth = entry.width; results.titleWidth = entry.width; @@ -231,7 +299,7 @@ function vtkScalarBarActor(publicAPI, model) { results.tickWidth = 0; results.tickHeight = 0; applyTextStyle(model.tmContext, model.tickTextStyle); - const strings = [...model.tickStrings, 'NaN', 'Below', 'Above']; + const strings = [...model.tickstrings, 'NaN', 'Below', 'Above']; for (let t = 0; t < strings.length; t++) { if (!newTmAtlas.has(strings[t])) { metrics = model.tmContext.measureText(strings[t]); @@ -382,18 +450,23 @@ function vtkScalarBarActor(publicAPI, model) { }); startPos[barAxis] += segSize[barAxis] + segSpace; } - if (model.drawNanAnnotation && model.scalarsToColors.getNanColor) { + if ( + model.renderable.getDrawNanAnnotation() && + model.renderable.getScalarsToColors().getNanColor() + ) { pushSeg('NaN', [NaN, NaN, NaN, NaN]); } if ( - model.drawBelowRangeSwatch && - model.scalarsToColors.getUseBelowRangeColor?.() + model.renderable.getDrawBelowRangeSwatch() && + model.renderable.getScalarsToColors().getUseBelowRangeColor?.() ) { pushSeg('Below', [-0.1, -0.1, -0.1, -0.1]); } - const haveAbove = model.scalarsToColors.getUseAboveRangeColor?.(); + const haveAbove = model.renderable + .getScalarsToColors() + .getUseAboveRangeColor?.(); // extra space around the ticks section startPos[barAxis] += segSpace; @@ -408,7 +481,7 @@ function vtkScalarBarActor(publicAPI, model) { model.vertical ? [0, 0, 0.995, 0.995] : [0, 0.995, 0.995, 0] ); - if (model.drawAboveRangeSwatch && haveAbove) { + if (model.renderable.getDrawAboveRangeSwatch() && haveAbove) { segSize[barAxis] = oldSegSize; startPos[barAxis] += segSpace; pushSeg('Above', [1.1, 1.1, 1.1, 1.1]); @@ -529,7 +602,7 @@ function vtkScalarBarActor(publicAPI, model) { } // update the polydata - const numLabels = model.tickStrings.length + model.barSegments.length; + const numLabels = model.tickstrings.length + model.barSegments.length; const numPts = numLabels * 4; const numTris = numLabels * 2; const points = new Float64Array(numPts * 3); @@ -558,7 +631,7 @@ function vtkScalarBarActor(publicAPI, model) { // write the axis label publicAPI.createPolyDataForOneLabel( - model.axisLabel, + model.renderable.getAxisLabel(), ptv3, xDir, yDir, @@ -574,7 +647,7 @@ function vtkScalarBarActor(publicAPI, model) { // write the axis label vec3.scale(xDir, xDir, -1); publicAPI.createPolyDataForOneLabel( - model.axisLabel, + model.renderable.getAxisLabel(), ptv3, yDir, xDir, @@ -590,7 +663,7 @@ function vtkScalarBarActor(publicAPI, model) { tmpv3[1] = model.barPosition[1] + model.barSize[1]; vec3.transformMat4(ptv3, tmpv3, invmat); publicAPI.createPolyDataForOneLabel( - model.axisLabel, + model.renderable.getAxisLabel(), ptv3, xDir, yDir, @@ -647,7 +720,7 @@ function vtkScalarBarActor(publicAPI, model) { tmp2v3[spacedAxis] = tickSegmentStart + tickSegmentSize * tickPos; vec3.transformMat4(ptv3, tmp2v3, invmat); publicAPI.createPolyDataForOneLabel( - model.tickStrings[t], + model.tickstrings[t], ptv3, xDir, yDir, @@ -679,19 +752,23 @@ function vtkScalarBarActor(publicAPI, model) { mat4.transpose(cmat, cmat); mat4.invert(invmat, cmat); + const scalarsToColors = model.renderable.getScalarsToColors(); let numberOfExtraColors = 0; - if (model.drawNanAnnotation && model.scalarsToColors.getNanColor) { + if ( + model.renderable.getDrawNanAnnotation() && + scalarsToColors.getNanColor() + ) { numberOfExtraColors += 1; } if ( - model.drawBelowRangeSwatch && - model.scalarsToColors.getUseBelowRangeColor?.() + model.renderable.getDrawBelowRangeSwatch() && + scalarsToColors.getUseBelowRangeColor?.() ) { numberOfExtraColors += 1; } if ( - model.drawAboveRangeSwatch && - model.scalarsToColors.getUseAboveRangeColor?.() + model.renderable.getDrawAboveRangeSwatch() && + scalarsToColors.getUseAboveRangeColor?.() ) { numberOfExtraColors += 1; } @@ -701,8 +778,8 @@ function vtkScalarBarActor(publicAPI, model) { // handle vector component mode let numComps = 1; - if (model.scalarsToColors.getVectorMode() === VectorMode.COMPONENT) { - numComps = model.scalarsToColors.getVectorComponent() + 1; + if (scalarsToColors.getVectorMode() === VectorMode.COMPONENT) { + numComps = scalarsToColors.getVectorComponent() + 1; } // create the colored bars @@ -752,10 +829,83 @@ function vtkScalarBarActor(publicAPI, model) { model.polyData.getPolys().modified(); model.polyData.modified(); }; +} - publicAPI.getActors = () => [model.barActor, model.tmActor]; +const newScalarBarActorHelper = macro.newInstance( + (publicAPI, model, initialValues = { renderable: null }) => { + Object.assign(model, {}, initialValues); + + // Inheritance + macro.obj(publicAPI, model); + + macro.setGet(publicAPI, model, [ + 'axisTitlePixelOffset', + 'tickLabelPixelOffset', + 'renderable', + 'topTitle', + ]); + macro.get(publicAPI, model, [ + 'lastSize', + 'lastAspectRatio', + 'axisTextStyle', + 'tickTextStyle', + 'barActor', + 'tmActor', + 'ticks', + ]); + macro.getArray(publicAPI, model, ['boxPosition', 'boxSize']); + macro.setArray(publicAPI, model, ['boxPosition', 'boxSize'], 2); + + model.forceUpdate = false; + model.lastRedrawTime = {}; + macro.obj(model.lastRedrawTime, { mtime: 0 }); + 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.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(); + 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' +); - publicAPI.getNestedProps = () => publicAPI.getActors(); +// +// 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 = (tickStyle) => { model.tickTextStyle = { ...model.tickTextStyle, ...tickStyle }; @@ -767,13 +917,6 @@ function vtkScalarBarActor(publicAPI, model) { publicAPI.modified(); }; - const setVisibility = macro.chain( - publicAPI.setVisibility, - model.barActor.setVisibility, - model.tmActor.setVisibility - ); - publicAPI.setVisibility = (...args) => setVisibility(...args).some(Boolean); - publicAPI.resetAutoLayoutToDefault = () => { model.autoLayout = defaultAutoLayout(publicAPI, model); }; @@ -827,60 +970,6 @@ export function extend(publicAPI, model, initialValues = {}) { publicAPI.getProperty().setDiffuse(0.0); publicAPI.getProperty().setAmbient(1.0); - model._tmAtlas = new Map(); - - // internal variables - model.lastSize = [800, 800]; - model.lastAspectRatio = 1.0; - model.textValues = []; - model.lastTickBounds = []; - model.barMapper = vtkMapper.newInstance(); - model.barMapper.setInterpolateScalarsBeforeMapping(true); - model.polyData = vtkPolyData.newInstance(); - model.barMapper.setInputData(model.polyData); - model.barActor = vtkActor.newInstance({ parentProp: publicAPI }); - model.barActor.setMapper(model.barMapper); - model.barActor.setProperty(publicAPI.getProperty()); - - model.lastRedrawTime = {}; - macro.obj(model.lastRedrawTime, { mtime: 0 }); - model.lastRebuildTime = {}; - macro.obj(model.lastRebuildTime, { mtime: 0 }); - - model.textPolyData = vtkPolyData.newInstance(); - - // for texture atlas - model.tmPolyData = vtkPolyData.newInstance(); - model.tmMapper = vtkMapper.newInstance(); - model.tmMapper.setInputData(model.tmPolyData); - model.tmTexture = vtkTexture.newInstance(); - model.tmTexture.setInterpolate(false); - model.tmActor = vtkActor.newInstance({ parentProp: publicAPI }); - model.tmActor.setMapper(model.tmMapper); - model.tmActor.addTexture(model.tmTexture); - model.tmActor.setProperty(publicAPI.getProperty()); - model.tmCanvas = document.createElement('canvas'); - model.tmContext = model.tmCanvas.getContext('2d'); - - // PixelSpaceCallbackMapper - we do need an empty polydata - // really just used to get the window size which we need to do - // proper text positioning and scaling. - model.mapper = vtkPixelSpaceCallbackMapper.newInstance(); - model.pixelMapperPolyData = vtkPolyData.newInstance(); - model.mapper.setInputData(model.pixelMapperPolyData); - model.mapper.setCallback((coords, camera, aspect, depthValues, size) => { - model.camera = camera; - 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]; - // we could use modified, but really the public state is not - // modified - model.forceUpdate = true; - } - publicAPI.update(); - }); - macro.setGet(publicAPI, model, [ 'automated', 'autoLayout', @@ -906,4 +995,4 @@ export const newInstance = macro.newInstance(extend, 'vtkScalarBarActor'); // ---------------------------------------------------------------------------- -export default { newInstance, extend }; +export default { newInstance, extend, newScalarBarActorHelper }; diff --git a/Sources/Rendering/OpenGL/Profiles/All.js b/Sources/Rendering/OpenGL/Profiles/All.js index 71ec7d86fb3..93d5ec7e07f 100644 --- a/Sources/Rendering/OpenGL/Profiles/All.js +++ b/Sources/Rendering/OpenGL/Profiles/All.js @@ -7,6 +7,7 @@ import 'vtk.js/Sources/Rendering/OpenGL/Actor'; import 'vtk.js/Sources/Rendering/OpenGL/Actor2D'; import 'vtk.js/Sources/Rendering/OpenGL/PolyDataMapper'; import 'vtk.js/Sources/Rendering/OpenGL/PolyDataMapper2D'; +import 'vtk.js/Sources/Rendering/OpenGL/ScalarBarActor'; import 'vtk.js/Sources/Rendering/OpenGL/Skybox'; import 'vtk.js/Sources/Rendering/OpenGL/Texture'; diff --git a/Sources/Rendering/OpenGL/Profiles/Geometry.js b/Sources/Rendering/OpenGL/Profiles/Geometry.js index 0d330756a68..b5ac3d8579a 100644 --- a/Sources/Rendering/OpenGL/Profiles/Geometry.js +++ b/Sources/Rendering/OpenGL/Profiles/Geometry.js @@ -7,6 +7,7 @@ import 'vtk.js/Sources/Rendering/OpenGL/Actor'; import 'vtk.js/Sources/Rendering/OpenGL/Actor2D'; import 'vtk.js/Sources/Rendering/OpenGL/PolyDataMapper'; import 'vtk.js/Sources/Rendering/OpenGL/PolyDataMapper2D'; +import 'vtk.js/Sources/Rendering/OpenGL/ScalarBarActor'; import 'vtk.js/Sources/Rendering/OpenGL/Skybox'; import 'vtk.js/Sources/Rendering/OpenGL/Texture'; diff --git a/Sources/Rendering/OpenGL/ScalarBarActor/index.js b/Sources/Rendering/OpenGL/ScalarBarActor/index.js new file mode 100644 index 00000000000..7ac143811d6 --- /dev/null +++ b/Sources/Rendering/OpenGL/ScalarBarActor/index.js @@ -0,0 +1,77 @@ +import * as macro from 'vtk.js/Sources/macros'; +import vtkScalarBarActor from 'vtk.js/Sources/Rendering/Core/ScalarBarActor'; +import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode'; + +import { registerOverride } from 'vtk.js/Sources/Rendering/OpenGL/ViewNodeFactory'; + +// ---------------------------------------------------------------------------- +// vtkOpenGLScalarBarActor methods +// ---------------------------------------------------------------------------- + +function vtkOpenGLScalarBarActor(publicAPI, model) { + model.classHierarchy.push('vtkOpenGLScalarBarActor'); + + // Builds myself. + publicAPI.buildPass = (prepass) => { + if (prepass) { + model.openGLRenderer = + publicAPI.getFirstAncestorOfType('vtkOpenGLRenderer'); + model.openGLRenderWindow = model.openGLRenderer.getParent(); + + if (!model.scalarBarActorHelper.getRenderable()) { + model.scalarBarActorHelper.setRenderable(model.renderable); + } + + publicAPI.prepareNodes(); + publicAPI.addMissingNode(model.scalarBarActorHelper.getBarActor()); + publicAPI.addMissingNode(model.scalarBarActorHelper.getTmActor()); + publicAPI.removeUnusedNodes(); + } + }; + + publicAPI.opaquePass = (prepass, renderPass) => { + if (prepass) { + const camera = model.openGLRenderer + ? model.openGLRenderer.getRenderable().getActiveCamera() + : null; + const tsize = model.openGLRenderer.getTiledSizeAndOrigin(); + + model.scalarBarActorHelper.updateAPISpecificData( + [tsize.usize, tsize.vsize], + camera, + model.openGLRenderWindow.getRenderable() + ); + } + }; +} + +// ---------------------------------------------------------------------------- +// Object factory +// ---------------------------------------------------------------------------- + +const DEFAULT_VALUES = {}; + +// ---------------------------------------------------------------------------- + +export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, DEFAULT_VALUES, initialValues); + + // Inheritance + vtkViewNode.extend(publicAPI, model, initialValues); + + model.scalarBarActorHelper = vtkScalarBarActor.newScalarBarActorHelper(); + + // Object methods + vtkOpenGLScalarBarActor(publicAPI, model); +} + +// ---------------------------------------------------------------------------- + +export const newInstance = macro.newInstance(extend, 'vtkOpenGLScalarBarActor'); + +// ---------------------------------------------------------------------------- + +export default { newInstance, extend }; + +// Register ourself to OpenGL backend if imported +registerOverride('vtkScalarBarActor', newInstance); diff --git a/Sources/Rendering/WebGPU/Profiles/All.js b/Sources/Rendering/WebGPU/Profiles/All.js index e6de54f9d61..2b66bcde791 100644 --- a/Sources/Rendering/WebGPU/Profiles/All.js +++ b/Sources/Rendering/WebGPU/Profiles/All.js @@ -7,6 +7,7 @@ import 'vtk.js/Sources/Rendering/WebGPU/Actor'; // import 'vtk.js/Sources/Rendering/WebGPU/Actor2D'; import 'vtk.js/Sources/Rendering/WebGPU/PolyDataMapper'; // import 'vtk.js/Sources/Rendering/WebGPU/Skybox'; +import 'vtk.js/Sources/Rendering/WebGPU/ScalarBarActor'; import 'vtk.js/Sources/Rendering/WebGPU/Texture'; // Geometry advanced diff --git a/Sources/Rendering/WebGPU/Profiles/Geometry.js b/Sources/Rendering/WebGPU/Profiles/Geometry.js index 4b3816bebbc..0de18fe8d1a 100644 --- a/Sources/Rendering/WebGPU/Profiles/Geometry.js +++ b/Sources/Rendering/WebGPU/Profiles/Geometry.js @@ -7,6 +7,7 @@ import 'vtk.js/Sources/Rendering/WebGPU/Actor'; // import 'vtk.js/Sources/Rendering/WebGPU/Actor2D'; import 'vtk.js/Sources/Rendering/WebGPU/PolyDataMapper'; // import 'vtk.js/Sources/Rendering/WebGPU/Skybox'; +import 'vtk.js/Sources/Rendering/WebGPU/ScalarBarActor'; import 'vtk.js/Sources/Rendering/WebGPU/Texture'; // Pixel mapping diff --git a/Sources/Rendering/WebGPU/ScalarBarActor/index.js b/Sources/Rendering/WebGPU/ScalarBarActor/index.js new file mode 100644 index 00000000000..e97720926e5 --- /dev/null +++ b/Sources/Rendering/WebGPU/ScalarBarActor/index.js @@ -0,0 +1,77 @@ +import * as macro from 'vtk.js/Sources/macros'; +import vtkScalarBarActor from 'vtk.js/Sources/Rendering/Core/ScalarBarActor'; +import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode'; + +import { registerOverride } from 'vtk.js/Sources/Rendering/WebGPU/ViewNodeFactory'; + +// ---------------------------------------------------------------------------- +// vtkWebGPUScalarBarActor methods +// ---------------------------------------------------------------------------- + +function vtkWebGPUScalarBarActor(publicAPI, model) { + model.classHierarchy.push('vtkWebGPUScalarBarActor'); + + // Builds myself. + publicAPI.buildPass = (prepass) => { + if (prepass) { + model.WebGPURenderer = + publicAPI.getFirstAncestorOfType('vtkWebGPURenderer'); + model.WebGPURenderWindow = model.WebGPURenderer.getParent(); + + if (!model.scalarBarActorHelper.getRenderable()) { + model.scalarBarActorHelper.setRenderable(model.renderable); + } + + publicAPI.prepareNodes(); + publicAPI.addMissingNode(model.scalarBarActorHelper.getBarActor()); + publicAPI.addMissingNode(model.scalarBarActorHelper.getTmActor()); + publicAPI.removeUnusedNodes(); + } + }; + + publicAPI.opaquePass = (prepass, renderPass) => { + if (prepass) { + const camera = model.WebGPURenderer + ? model.WebGPURenderer.getRenderable().getActiveCamera() + : null; + const tsize = model.WebGPURenderer.getTiledSizeAndOrigin(); + + model.scalarBarActorHelper.updateAPISpecificData( + [tsize.usize, tsize.vsize], + camera, + model.WebGPURenderWindow.getRenderable() + ); + } + }; +} + +// ---------------------------------------------------------------------------- +// Object factory +// ---------------------------------------------------------------------------- + +const DEFAULT_VALUES = {}; + +// ---------------------------------------------------------------------------- + +export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, DEFAULT_VALUES, initialValues); + + // Inheritance + vtkViewNode.extend(publicAPI, model, initialValues); + + model.scalarBarActorHelper = vtkScalarBarActor.newScalarBarActorHelper(); + + // Object methods + vtkWebGPUScalarBarActor(publicAPI, model); +} + +// ---------------------------------------------------------------------------- + +export const newInstance = macro.newInstance(extend, 'vtkWebGPUScalarBarActor'); + +// ---------------------------------------------------------------------------- + +export default { newInstance, extend }; + +// Register ourself to WebGPU backend if imported +registerOverride('vtkScalarBarActor', newInstance);