@kitware/vtk.js 33.0.0-beta.2 → 33.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/Common/Core/DataArray.d.ts +21 -0
  2. package/Common/Core/DataArray.js +39 -0
  3. package/Common/Core/Math/index.js +1 -1
  4. package/Common/Core/Math.js +1 -1
  5. package/Common/Core/URLExtract.js +2 -6
  6. package/Common/DataModel/Line.js +1 -0
  7. package/Common/DataModel/PolyLine.js +4 -0
  8. package/Filters/Core/ThresholdPoints.d.ts +72 -0
  9. package/Filters/Core/ThresholdPoints.js +219 -0
  10. package/Filters/General/ContourTriangulator/helper.js +1 -1
  11. package/IO/Core/DataAccessHelper/JSZipDataAccessHelper.js +1 -1
  12. package/IO/Geometry/DracoReader.d.ts +4 -4
  13. package/IO/Geometry/DracoReader.js +154 -105
  14. package/IO/Geometry/GLTFImporter/Animations.js +239 -0
  15. package/IO/Geometry/GLTFImporter/Constants.js +87 -0
  16. package/IO/Geometry/GLTFImporter/Decoder.js +69 -0
  17. package/IO/Geometry/GLTFImporter/Extensions.js +110 -0
  18. package/IO/Geometry/GLTFImporter/ORMTexture.worker.js +42 -0
  19. package/IO/Geometry/GLTFImporter/Parser.js +359 -0
  20. package/IO/Geometry/GLTFImporter/Reader.js +518 -0
  21. package/IO/Geometry/GLTFImporter/Utils.js +165 -0
  22. package/IO/Geometry/GLTFImporter.d.ts +266 -0
  23. package/IO/Geometry/GLTFImporter.js +245 -0
  24. package/IO/Geometry/IFCImporter.d.ts +163 -0
  25. package/IO/Geometry/IFCImporter.js +270 -0
  26. package/IO/Geometry/STLReader.d.ts +14 -0
  27. package/IO/Geometry/STLReader.js +57 -1
  28. package/IO/Geometry.js +5 -1
  29. package/IO/Image/HDRReader/Utils.js +1 -1
  30. package/IO/Image/HDRReader.js +1 -1
  31. package/IO/Image/TGAReader/Constants.js +28 -0
  32. package/IO/Image/TGAReader.d.ts +121 -0
  33. package/IO/Image/TGAReader.js +418 -0
  34. package/IO/Image/TIFFReader.d.ts +133 -0
  35. package/IO/Image/TIFFReader.js +144 -0
  36. package/IO/Image.js +5 -1
  37. package/IO/XML/XMLPolyDataWriter.js +1 -0
  38. package/Interaction/Manipulators/MouseCameraTrackballRollManipulator.js +1 -1
  39. package/Interaction/Style/InteractorStyleTrackballCamera.js +1 -1
  40. package/Rendering/Core/AbstractImageMapper.d.ts +81 -0
  41. package/Rendering/Core/AbstractImageMapper.js +5 -2
  42. package/Rendering/Core/AbstractPicker.d.ts +13 -13
  43. package/Rendering/Core/AbstractPicker.js +1 -1
  44. package/Rendering/Core/Actor2D.d.ts +22 -0
  45. package/Rendering/Core/Actor2D.js +1 -1
  46. package/Rendering/Core/CellPicker.js +4 -1
  47. package/Rendering/Core/Glyph3DMapper.d.ts +45 -29
  48. package/Rendering/Core/ImageCPRMapper.js +6 -5
  49. package/Rendering/Core/ImageProperty.d.ts +42 -1
  50. package/Rendering/Core/ImageProperty.js +7 -5
  51. package/Rendering/Core/ImageResliceMapper.d.ts +1 -2
  52. package/Rendering/Core/ImageResliceMapper.js +5 -4
  53. package/Rendering/Core/PointPicker.js +10 -1
  54. package/Rendering/Core/Prop3D.js +1 -1
  55. package/Rendering/Core/RenderWindowInteractor.d.ts +1 -1
  56. package/Rendering/Core/RenderWindowInteractor.js +1 -1
  57. package/Rendering/Core/Viewport.js +13 -3
  58. package/Rendering/Core/VolumeMapper.d.ts +70 -0
  59. package/Rendering/Core/VolumeMapper.js +10 -5
  60. package/Rendering/Core/VolumeProperty.d.ts +20 -1
  61. package/Rendering/Core/VolumeProperty.js +7 -5
  62. package/Rendering/Misc/CanvasView.js +4 -2
  63. package/Rendering/Misc/RemoteView.d.ts +9 -3
  64. package/Rendering/Misc/RemoteView.js +7 -3
  65. package/Rendering/Misc/SynchronizableRenderWindow/BehaviorManager/CameraSynchronizer.js +2 -2
  66. package/Rendering/Misc/SynchronizableRenderWindow/ObjectManager.d.ts +1 -1
  67. package/Rendering/OpenGL/ImageCPRMapper.js +18 -2
  68. package/Rendering/OpenGL/ImageMapper.js +42 -11
  69. package/Rendering/OpenGL/ImageResliceMapper.js +20 -4
  70. package/Rendering/OpenGL/Renderer.js +1 -1
  71. package/Rendering/OpenGL/Texture/supportsNorm16Linear.js +97 -0
  72. package/Rendering/OpenGL/Texture.d.ts +29 -8
  73. package/Rendering/OpenGL/Texture.js +172 -34
  74. package/Rendering/OpenGL/VolumeMapper.js +22 -4
  75. package/Rendering/SceneGraph/ViewNode.js +12 -2
  76. package/Rendering/WebXR/RenderWindowHelper.js +9 -0
  77. package/Widgets/Core/WidgetManager.d.ts +12 -1
  78. package/Widgets/Representations/WidgetRepresentation.d.ts +1 -7
  79. package/Widgets/Widgets3D/AngleWidget/behavior.js +2 -0
  80. package/Widgets/Widgets3D/InteractiveOrientationWidget.js +1 -1
  81. package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +17 -0
  82. package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +1 -0
  83. package/Widgets/Widgets3D/ResliceCursorWidget.d.ts +1 -8
  84. package/Widgets/Widgets3D/ShapeWidget/behavior.js +3 -0
  85. package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +296 -0
  86. package/index.d.ts +5 -0
  87. package/package.json +19 -17
@@ -162,6 +162,76 @@ export interface vtkVolumeMapper extends vtkAbstractMapper3D {
162
162
  *
163
163
  */
164
164
  update(): void;
165
+
166
+ /**
167
+ * Set the opacity texture width.
168
+ *
169
+ * The default width (1024) should be fine in most instances.
170
+ * Only set this property if your opacity function range width is
171
+ * larger than 1024.
172
+ *
173
+ * @param {Number} width the texture width (defaults to 1024)
174
+ */
175
+ setOpacityTextureWidth(width: number): boolean;
176
+
177
+ /**
178
+ * Get the opacity texture width.
179
+ */
180
+ getOpacityTextureWidth(): number;
181
+
182
+ /**
183
+ * Set the color texture width.
184
+ *
185
+ * The default width (1024) should be fine in most instances.
186
+ * Only set this property if your color transfer function range width is
187
+ * larger than 1024.
188
+ *
189
+ * A reasonable max texture size would be either 2048 or 4096, as those
190
+ * widths are supported by the vast majority of devices. Any width larger
191
+ * than that will have issues with device support.
192
+ *
193
+ * Specifying a width that is less than or equal to 0 will use the largest
194
+ * possible texture width on the device. Use this with caution! The max texture
195
+ * width of one device may not be the same for another device.
196
+ *
197
+ * You can find more information about supported texture widths at the following link:
198
+ * https://web3dsurvey.com/webgl/parameters/MAX_TEXTURE_SIZE
199
+ *
200
+ * @param {Number} width the texture width (defaults to 1024)
201
+ */
202
+ setColorTextureWidth(width: number): boolean;
203
+
204
+ /**
205
+ * Get the color texture width.
206
+ */
207
+ getColorTextureWidth(): number;
208
+
209
+ /**
210
+ * Set the label outline texture width.
211
+ *
212
+ * The default width (1024) should be fine in most instances.
213
+ * Only set this property if you have more than 1024 labels
214
+ * that you want to render with thickness.
215
+ *
216
+ * A reasonable max texture size would be either 2048 or 4096, as those
217
+ * widths are supported by the vast majority of devices. Any width larger
218
+ * than that will have issues with device support.
219
+ *
220
+ * Specifying a width that is less than or equal to 0 will use the largest
221
+ * possible texture width on the device. Use this with caution! The max texture
222
+ * width of one device may not be the same for another device.
223
+ *
224
+ * You can find more information about supported texture widths at the following link:
225
+ * https://web3dsurvey.com/webgl/parameters/MAX_TEXTURE_SIZE
226
+ *
227
+ * @param {Number} width the texture width (defaults to 1024)
228
+ */
229
+ setLabelOutlineTextureWidth(width: number): boolean;
230
+
231
+ /**
232
+ * Get the label outline texture width.
233
+ */
234
+ getLabelOutlineTextureWidth(): number;
165
235
  }
166
236
 
167
237
  /**
@@ -81,7 +81,8 @@ function vtkVolumeMapper(publicAPI, model) {
81
81
  // Object factory
82
82
  // ----------------------------------------------------------------------------
83
83
 
84
- const DEFAULT_VALUES = {
84
+ // TODO: what values to use for averageIPScalarRange to get GLSL to use max / min values like [-Math.inf, Math.inf]?
85
+ const defaultValues = initialValues => ({
85
86
  bounds: [...vtkBoundingBox.INIT_BOUNDS],
86
87
  sampleDistance: 1.0,
87
88
  imageSampleDistance: 1.0,
@@ -90,16 +91,20 @@ const DEFAULT_VALUES = {
90
91
  initialInteractionScale: 1.0,
91
92
  interactionSampleDistanceFactor: 1.0,
92
93
  blendMode: BlendMode.COMPOSITE_BLEND,
93
- volumeShadowSamplingDistFactor: 5.0
94
- };
94
+ volumeShadowSamplingDistFactor: 5.0,
95
+ colorTextureWidth: 1024,
96
+ opacityTextureWidth: 1024,
97
+ labelOutlineTextureWidth: 1024,
98
+ ...initialValues
99
+ });
95
100
 
96
101
  // ----------------------------------------------------------------------------
97
102
 
98
103
  function extend(publicAPI, model) {
99
104
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
100
- Object.assign(model, DEFAULT_VALUES, initialValues);
105
+ Object.assign(model, defaultValues(initialValues));
101
106
  vtkAbstractMapper3D.extend(publicAPI, model, initialValues);
102
- macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'volumeShadowSamplingDistFactor']);
107
+ macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'volumeShadowSamplingDistFactor', 'colorTextureWidth', 'opacityTextureWidth', 'labelOutlineTextureWidth']);
103
108
  macro.event(publicAPI, model, 'lightingActivated');
104
109
 
105
110
  // Object methods
@@ -1,6 +1,6 @@
1
1
  import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction';
2
2
  import { vtkObject } from './../../interfaces';
3
- import { Nullable } from './../../types';
3
+ import { Extent, Nullable } from './../../types';
4
4
  import vtkColorTransferFunction from './ColorTransferFunction';
5
5
  import { ColorMixPreset, InterpolationType, OpacityMode } from './VolumeProperty/Constants';
6
6
 
@@ -486,6 +486,25 @@ export interface vtkVolumeProperty extends vtkObject {
486
486
  * @param LAOKernelRadius
487
487
  */
488
488
  setLAOKernelRadius(LAOKernelRadius: number): void;
489
+
490
+ /**
491
+ * Informs the mapper to only update the specified extents at the next render.
492
+ *
493
+ * If there are zero extents, the mapper updates the entire volume texture.
494
+ * Otherwise, the mapper will only update the texture by the specified extents
495
+ * during the next render call.
496
+ *
497
+ * This array is cleared after a successful render.
498
+ * @param extents
499
+ */
500
+ setUpdatedExtents(extents: Extent[]): boolean;
501
+
502
+ /**
503
+ * Retrieves the updated extents.
504
+ *
505
+ * This array is cleared after every successful render.
506
+ */
507
+ getUpdatedExtents(): Extent[];
489
508
  }
490
509
 
491
510
  /**
@@ -228,7 +228,7 @@ function vtkVolumeProperty(publicAPI, model) {
228
228
  // Object factory
229
229
  // ----------------------------------------------------------------------------
230
230
 
231
- const DEFAULT_VALUES = {
231
+ const defaultValues = initialValues => ({
232
232
  colorMixPreset: ColorMixPreset.DEFAULT,
233
233
  independentComponents: true,
234
234
  interpolationType: InterpolationType.FAST_LINEAR,
@@ -254,14 +254,16 @@ const DEFAULT_VALUES = {
254
254
  // local ambient occlusion
255
255
  localAmbientOcclusion: false,
256
256
  LAOKernelSize: 15,
257
- LAOKernelRadius: 7
258
- };
257
+ LAOKernelRadius: 7,
258
+ updatedExtents: [],
259
+ ...initialValues
260
+ });
259
261
 
260
262
  // ----------------------------------------------------------------------------
261
263
 
262
264
  function extend(publicAPI, model) {
263
265
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
264
- Object.assign(model, DEFAULT_VALUES, initialValues);
266
+ Object.assign(model, defaultValues(initialValues));
265
267
 
266
268
  // Build VTK API
267
269
  macro.obj(publicAPI, model);
@@ -287,7 +289,7 @@ function extend(publicAPI, model) {
287
289
  }
288
290
  macro.setGet(publicAPI, model, ['colorMixPreset', 'independentComponents', 'interpolationType', 'shade', 'ambient', 'diffuse', 'specular', 'specularPower', 'useLabelOutline', 'labelOutlineOpacity',
289
291
  // Properties moved from volume mapper
290
- 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius']);
292
+ 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius', 'updatedExtents']);
291
293
 
292
294
  // Property moved from volume mapper
293
295
  macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2);
@@ -134,8 +134,10 @@ function extend(publicAPI, model) {
134
134
  Object.assign(model, DEFAULT_VALUES, initialValues);
135
135
 
136
136
  // Create internal instances
137
- model.canvas = document.createElement('canvas');
138
- model.canvas.style.width = '100%';
137
+ if (!model.canvas) {
138
+ model.canvas = document.createElement('canvas');
139
+ model.canvas.style.width = '100%';
140
+ }
139
141
 
140
142
  // Create internal bgImage
141
143
  model.bgImage = new Image();
@@ -13,21 +13,27 @@ interface IRemoteViewInitialValues {
13
13
  rpcGestureEvent?: any;
14
14
  rpcWheelEvent?: any;
15
15
  viewStream?: vtkViewStream;
16
+ canvasElement?: HTMLCanvasElement;
16
17
  }
17
18
 
18
19
  export interface vtkRemoteView extends vtkObject {
19
20
  /**
20
- * Get container element
21
+ * Get container HTML element
21
22
  */
22
23
  getContainer(): HTMLElement;
23
24
 
24
25
  /**
25
- *
26
+ * Get vtkViewStream object
26
27
  */
27
28
  getViewStream(): vtkViewStream;
28
29
 
29
30
  /**
30
- *
31
+ * Get the canvas HTML element
32
+ */
33
+ getCanvasElement(): HTMLCanvasElement;
34
+
35
+ /**
36
+ * Get the vtkCanvasView object
31
37
  */
32
38
  getCanvasView(): vtkCanvasView;
33
39
 
@@ -17,7 +17,9 @@ function vtkRemoteView(publicAPI, model) {
17
17
  model.classHierarchy.push('vtkRemoteView');
18
18
 
19
19
  // Constructor
20
- model.canvasView = vtkCanvasView.newInstance();
20
+ model.canvasView = vtkCanvasView.newInstance({
21
+ canvas: model.canvasElement
22
+ });
21
23
  model.interactorStyle = vtkInteractorStyleRemoteMouse.newInstance();
22
24
  model.interactor = vtkRenderWindowInteractor.newInstance();
23
25
  model.interactor.setView(model.canvasView);
@@ -60,6 +62,7 @@ function vtkRemoteView(publicAPI, model) {
60
62
  model.viewStream.delete();
61
63
  }
62
64
  }, publicAPI.delete);
65
+ publicAPI.getCanvasElement = () => model.canvasView.getCanvas();
63
66
 
64
67
  // --------------------------------------------------------------------------
65
68
  // remote handing
@@ -194,7 +197,8 @@ const DEFAULT_VALUES = {
194
197
  stillRatio: 1,
195
198
  rpcMouseEvent: 'viewport.mouse.interaction',
196
199
  rpcGestureEvent: null,
197
- rpcWheelEvent: null
200
+ rpcWheelEvent: null,
201
+ canvasElement: null
198
202
  };
199
203
 
200
204
  // ----------------------------------------------------------------------------
@@ -203,7 +207,7 @@ function extend(publicAPI, model) {
203
207
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
204
208
  Object.assign(model, DEFAULT_VALUES, initialValues);
205
209
  macro.obj(publicAPI, model, initialValues);
206
- macro.get(publicAPI, model, ['container', 'viewStream', 'canvasView', 'interactor', 'interactorStyle', 'interactiveQuality', 'interactiveRatio', 'stillQuality', 'stillRatio']);
210
+ macro.get(publicAPI, model, ['container', 'viewStream', 'canvasView', 'interactor', 'interactorStyle', 'interactiveQuality', 'interactiveRatio', 'stillQuality', 'stillRatio', 'canvasElement']);
207
211
  macro.setGet(publicAPI, model, ['session', 'rpcMouseEvent', 'rpcGestureEvent', 'rpcWheelEvent']);
208
212
 
209
213
  // Object methods
@@ -33,8 +33,8 @@ function vtkCameraSynchronizer(publicAPI, model) {
33
33
  }
34
34
 
35
35
  // Update listeners automatically
36
- model._srcRendererChanged = updateListeners;
37
- model._dstRendererChanged = updateListeners;
36
+ model._onSrcRendererChanged = updateListeners;
37
+ model._onDstRendererChanged = updateListeners;
38
38
  function updatePreviousValues(position, focalPoint, viewUp) {
39
39
  if (cameraState[0] !== position[0] || cameraState[1] !== position[1] || cameraState[2] !== position[2] || cameraState[3] !== focalPoint[0] || cameraState[4] !== focalPoint[1] || cameraState[5] !== focalPoint[2] || cameraState[6] !== viewUp[0] || cameraState[7] !== viewUp[1] || cameraState[8] !== viewUp[2]) {
40
40
  cameraState[0] = position[0];
@@ -1,6 +1,6 @@
1
1
  import { vtkObject } from './../../../interfaces';
2
2
  import { Nullable } from './../../../types';
3
- import { ISynchronizerContext, IViewState } from '..';
3
+ import { ISynchronizerContext, IViewState } from '../SynchronizableRenderWindow';
4
4
 
5
5
  export type BuilderFunction = <T extends vtkObject>(
6
6
  type: string,
@@ -147,6 +147,7 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
147
147
  publicAPI.buildBufferObjects = (ren, actor) => {
148
148
  const image = model.currentImageDataInput;
149
149
  const centerline = model.currentCenterlineInput;
150
+ const property = actor.getProperty();
150
151
 
151
152
  // Rebuild the volumeTexture if the data has changed
152
153
  const scalars = image?.getPointData()?.getScalars();
@@ -156,6 +157,8 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
156
157
  const cachedScalarsEntry = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
157
158
  const volumeTextureHash = getImageDataHash(image, scalars);
158
159
  const reBuildTex = !cachedScalarsEntry?.oglObject?.getHandle() || cachedScalarsEntry?.hash !== volumeTextureHash;
160
+ const updatedExtents = property.getUpdatedExtents();
161
+ const hasUpdatedExtents = !!updatedExtents.length;
159
162
  if (reBuildTex) {
160
163
  model.volumeTexture = vtkOpenGLTexture.newInstance();
161
164
  model.volumeTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
@@ -174,6 +177,13 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
174
177
  } else {
175
178
  model.volumeTexture = cachedScalarsEntry.oglObject;
176
179
  }
180
+ if (hasUpdatedExtents) {
181
+ // If hasUpdatedExtents, then the texture is partially updated.
182
+ // clear the array to acknowledge the update.
183
+ property.setUpdatedExtents([]);
184
+ const dims = image.getDimensions();
185
+ model.volumeTexture.create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
186
+ }
177
187
 
178
188
  // Rebuild the color texture if needed
179
189
  const numComp = scalars.getNumberOfComponents();
@@ -190,7 +200,10 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
190
200
  const cachedColorEntry = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
191
201
  const reBuildColorTexture = !cachedColorEntry?.oglObject?.getHandle() || cachedColorEntry?.hash !== colorTextureHash;
192
202
  if (reBuildColorTexture) {
193
- const cWidth = 1024;
203
+ let cWidth = model.renderable.getColorTextureWidth();
204
+ if (cWidth <= 0) {
205
+ cWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
206
+ }
194
207
  const cSize = cWidth * textureHeight * 3;
195
208
  const cTable = new Uint8ClampedArray(cSize);
196
209
  model.colorTexture = vtkOpenGLTexture.newInstance();
@@ -247,7 +260,10 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
247
260
  const cachedPwfEntry = model._openGLRenderWindow.getGraphicsResourceForObject(firstPwFunc);
248
261
  const reBuildPwf = !cachedPwfEntry?.oglObject?.getHandle() || cachedPwfEntry?.hash !== pwfTextureHash;
249
262
  if (reBuildPwf) {
250
- const pwfWidth = 1024;
263
+ let pwfWidth = model.renderable.getOpacityTextureWidth();
264
+ if (pwfWidth <= 0) {
265
+ pwfWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
266
+ }
251
267
  const pwfSize = pwfWidth * textureHeight;
252
268
  const pwfTable = new Uint8ClampedArray(pwfSize);
253
269
  model.pwfTexture = vtkOpenGLTexture.newInstance();
@@ -167,9 +167,9 @@ function vtkOpenGLImageMapper(publicAPI, model) {
167
167
  // check for the outline thickness and opacity
168
168
  const vtkImageLabelOutline = actor.getProperty().getUseLabelOutline();
169
169
  if (vtkImageLabelOutline === true) {
170
- FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutline::Dec', ['uniform int outlineThickness;', 'uniform float vpWidth;', 'uniform float vpHeight;', 'uniform float vpOffsetX;', 'uniform float vpOffsetY;', 'uniform mat4 PCWCMatrix;', 'uniform mat4 vWCtoIDX;', 'uniform ivec3 imageDimensions;']).result;
170
+ FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutline::Dec', ['uniform int outlineThickness;', 'uniform float vpWidth;', 'uniform float vpHeight;', 'uniform float vpOffsetX;', 'uniform float vpOffsetY;', 'uniform mat4 PCWCMatrix;', 'uniform mat4 vWCtoIDX;', 'uniform ivec3 imageDimensions;', 'uniform int sliceAxis;']).result;
171
171
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ImageLabelOutlineOn', '#define vtkImageLabelOutlineOn').result;
172
- FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutlineHelperFunction', ['#ifdef vtkImageLabelOutlineOn', 'vec3 fragCoordToIndexSpace(vec4 fragCoord) {', ' vec4 pcPos = vec4(', ' (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,', ' (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,', ' (fragCoord.z - 0.5) * 2.0,', ' 1.0);', '', ' vec4 worldCoord = PCWCMatrix * pcPos;', ' vec4 vertex = (worldCoord/worldCoord.w);', '', ' vec3 index = (vWCtoIDX * vertex).xyz;', '', ' // half voxel fix for labelmapOutline', ' return (index + vec3(0.5)) / vec3(imageDimensions);', '}', '#endif']).result;
172
+ FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutlineHelperFunction', ['#ifdef vtkImageLabelOutlineOn', 'vec3 fragCoordToIndexSpace(vec4 fragCoord) {', ' vec4 pcPos = vec4(', ' (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,', ' (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,', ' (fragCoord.z - 0.5) * 2.0,', ' 1.0);', '', ' vec4 worldCoord = PCWCMatrix * pcPos;', ' vec4 vertex = (worldCoord/worldCoord.w);', '', ' vec3 index = (vWCtoIDX * vertex).xyz;', '', ' // half voxel fix for labelmapOutline', ' return (index + vec3(0.5)) / vec3(imageDimensions);', '}', 'vec2 getSliceCoords(vec3 coord, int axis) {', ' if (axis == 0) return coord.yz;', ' if (axis == 1) return coord.xz;', ' if (axis == 2) return coord.xy;', '}', '#endif']).result;
173
173
  }
174
174
  if (iComps) {
175
175
  const rgba = ['r', 'g', 'b', 'a'];
@@ -201,7 +201,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
201
201
  FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Impl', [...splitStringOnEnter(`
202
202
  #ifdef vtkImageLabelOutlineOn
203
203
  vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord);
204
- float centerValue = texture2D(texture1, centerPosIS.xy).r;
204
+ float centerValue = texture2D(texture1, getSliceCoords(centerPosIS, sliceAxis)).r;
205
205
  bool pixelOnBorder = false;
206
206
  vec3 tColor = texture2D(colorTexture1, vec2(centerValue * cscale0 + cshift0, 0.5)).rgb;
207
207
  float scalarOpacity = texture2D(pwfTexture1, vec2(centerValue * pwfscale0 + pwfshift0, 0.5)).r;
@@ -212,7 +212,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
212
212
  int actualThickness = int(textureValue * 255.0);
213
213
 
214
214
  if (segmentIndex == 0){
215
- gl_FragData[0] = vec4(0.0, 1.0, 1.0, 0.0);
215
+ gl_FragData[0] = vec4(0.0, 0.0, 0.0, 0.0);
216
216
  return;
217
217
  }
218
218
 
@@ -225,7 +225,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
225
225
  gl_FragCoord.y + float(j),
226
226
  gl_FragCoord.z, gl_FragCoord.w);
227
227
  vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);
228
- float value = texture2D(texture1, neighborPosIS.xy).r;
228
+ float value = texture2D(texture1, getSliceCoords(neighborPosIS, sliceAxis)).r;
229
229
  if (value != centerValue) {
230
230
  pixelOnBorder = true;
231
231
  break;
@@ -474,7 +474,14 @@ function vtkOpenGLImageMapper(publicAPI, model) {
474
474
  if (vtkImageLabelOutline === true) {
475
475
  const worldToIndex = image.getWorldToIndex();
476
476
  const imageDimensions = image.getDimensions();
477
- program.setUniform3i('imageDimensions', imageDimensions[0], imageDimensions[1], 1);
477
+ let sliceAxis = model.renderable.getClosestIJKAxis().ijkMode;
478
+
479
+ // SlicingMode.NONE equates to SlicingMode.K
480
+ if (sliceAxis === SlicingMode.NONE) {
481
+ sliceAxis = SlicingMode.K;
482
+ }
483
+ program.setUniform3i('imageDimensions', imageDimensions[0], imageDimensions[1], imageDimensions[2]);
484
+ program.setUniformi('sliceAxis', sliceAxis);
478
485
  program.setUniformMatrix('vWCtoIDX', worldToIndex);
479
486
  const labelOutlineKeyMats = model.openGLCamera.getKeyMatrices(ren);
480
487
 
@@ -583,7 +590,10 @@ function vtkOpenGLImageMapper(publicAPI, model) {
583
590
  resizable: true
584
591
  });
585
592
  model.colorTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
586
- const cWidth = 1024;
593
+ let cWidth = model.renderable.getColorTextureWidth();
594
+ if (cWidth <= 0) {
595
+ cWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
596
+ }
587
597
  const cSize = cWidth * textureHeight * 3;
588
598
  const cTable = new Uint8ClampedArray(cSize);
589
599
  // set interpolation on the texture based on property setting
@@ -646,7 +656,10 @@ function vtkOpenGLImageMapper(publicAPI, model) {
646
656
  // rebuild opacity tfun?
647
657
  const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== pwfunToString;
648
658
  if (reBuildPwf) {
649
- const pwfWidth = 1024;
659
+ let pwfWidth = model.renderable.getOpacityTextureWidth();
660
+ if (pwfWidth <= 0) {
661
+ pwfWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
662
+ }
650
663
  const pwfSize = pwfWidth * textureHeight;
651
664
  const pwfTable = new Uint8ClampedArray(pwfSize);
652
665
  model.pwfTexture = vtkOpenGLTexture.newInstance({
@@ -856,10 +869,25 @@ function vtkOpenGLImageMapper(publicAPI, model) {
856
869
  vtkErrorMacro('Reformat slicing not yet supported.');
857
870
  }
858
871
 
872
+ /**
873
+ *
874
+ * Fetch the ranges of the source volume, `imgScalars`, and use them when
875
+ * creating the texture. Whilst the pre-calculated ranges may not be
876
+ * strictly correct for the slice, it is guaranteed to be within the
877
+ * source volume's range.
878
+ *
879
+ * There is a significant performance improvement by pre-setting the range
880
+ * of the scalars array particularly when scrolling through the source
881
+ * volume as there is no need to calculate the range of the slice scalar.
882
+ *
883
+ * @type{ import("../../../interfaces").vtkRange[] }
884
+ */
885
+ const ranges = imgScalars.getRanges();
886
+
859
887
  // Don't share this resource as `scalars` is created in this function
860
888
  // so it is impossible to share
861
889
  model.openGLTexture.resetFormatAndType();
862
- model.openGLTexture.create2DFilterableFromRaw(dims[0], dims[1], numComp, imgScalars.getDataType(), scalars, model.renderable.getPreferSizeOverAccuracy?.());
890
+ model.openGLTexture.create2DFilterableFromRaw(dims[0], dims[1], numComp, imgScalars.getDataType(), scalars, model.renderable.getPreferSizeOverAccuracy?.(), ranges);
863
891
  model.openGLTexture.activate();
864
892
  model.openGLTexture.sendParameters();
865
893
  model.openGLTexture.deactivate();
@@ -896,7 +924,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
896
924
  }
897
925
  };
898
926
  publicAPI.updatelabelOutlineThicknessTexture = image => {
899
- const labelOutlineThicknessArray = image.getProperty().getLabelOutlineThickness();
927
+ const labelOutlineThicknessArray = image.getProperty().getLabelOutlineThicknessByReference();
900
928
  const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineThicknessArray);
901
929
 
902
930
  // compute the join of the labelOutlineThicknessArray so that
@@ -905,7 +933,10 @@ function vtkOpenGLImageMapper(publicAPI, model) {
905
933
  const toString = `${labelOutlineThicknessArray.join('-')}`;
906
934
  const reBuildL = !lTex?.oglObject?.getHandle() || lTex?.hash !== toString;
907
935
  if (reBuildL) {
908
- const lWidth = 1024;
936
+ let lWidth = model.renderable.getLabelOutlineTextureWidth();
937
+ if (lWidth <= 0) {
938
+ lWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
939
+ }
909
940
  const lHeight = 1;
910
941
  const lSize = lWidth * lHeight;
911
942
  const lTable = new Uint8Array(lSize);
@@ -245,6 +245,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
245
245
  return model.VBOBuildTime.getMTime() < imageData.getMTime();
246
246
  }) || model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime() || model.scalarTextures.length !== model.currentValidInputs.length || !model.scalarTextures.every(texture => !!texture?.getHandle()) || !model.colorTexture?.getHandle() || !model.pwfTexture?.getHandle();
247
247
  publicAPI.buildBufferObjects = (ren, actor) => {
248
+ const actorProperties = actor.getProperties();
248
249
  model.currentValidInputs.forEach((_ref3, component) => {
249
250
  let {
250
251
  imageData
@@ -254,7 +255,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
254
255
  const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
255
256
  const scalarsHash = getImageDataHash(imageData, scalars);
256
257
  const reBuildTex = !tex?.oglObject?.getHandle() || tex?.hash !== scalarsHash;
257
- if (reBuildTex) {
258
+ const actorProperty = actorProperties[component];
259
+ const updatedExtents = actorProperty.getUpdatedExtents();
260
+ const hasUpdatedExtents = !!updatedExtents.length;
261
+ if (reBuildTex && !hasUpdatedExtents) {
258
262
  const newScalarTexture = vtkOpenGLTexture.newInstance();
259
263
  newScalarTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
260
264
  // Build the textures
@@ -268,11 +272,17 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
268
272
  } else {
269
273
  model.scalarTextures[component] = tex.oglObject;
270
274
  }
275
+ if (hasUpdatedExtents) {
276
+ // If hasUpdatedExtents, then the texture is partially updated.
277
+ // clear the array to acknowledge the update.
278
+ actorProperty.setUpdatedExtents([]);
279
+ const dims = imageData.getDimensions();
280
+ model.scalarTextures[component].create3DFilterableFromDataArray(dims[0], dims[1], dims[2], scalars, false, updatedExtents);
281
+ }
271
282
  replaceGraphicsResource(model._openGLRenderWindow, model._scalarTexturesCore[component], scalars);
272
283
  model._scalarTexturesCore[component] = scalars;
273
284
  });
274
285
  const firstValidInput = model.currentValidInputs[0];
275
- const actorProperties = actor.getProperties();
276
286
  const firstActorProperty = actorProperties[firstValidInput.inputIndex];
277
287
  const iComps = firstActorProperty.getIndependentComponents();
278
288
  const numIComps = iComps ? model.numberOfComponents : 1;
@@ -286,7 +296,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
286
296
  const cTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstColorTransferFunc);
287
297
  const reBuildC = !cTex?.oglObject?.getHandle() || cTex?.hash !== colorFuncHash;
288
298
  if (reBuildC) {
289
- const cWidth = 1024;
299
+ let cWidth = model.renderable.getColorTextureWidth();
300
+ if (cWidth <= 0) {
301
+ cWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
302
+ }
290
303
  const cSize = cWidth * textureHeight * 3;
291
304
  const cTable = new Uint8ClampedArray(cSize);
292
305
  const newColorTexture = vtkOpenGLTexture.newInstance();
@@ -345,7 +358,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
345
358
  const pwfTex = model._openGLRenderWindow.getGraphicsResourceForObject(firstPwFunc);
346
359
  const reBuildPwf = !pwfTex?.oglObject?.getHandle() || pwfTex?.hash !== opacityFuncHash;
347
360
  if (reBuildPwf) {
348
- const pwfWidth = 1024;
361
+ let pwfWidth = model.renderable.getOpacityTextureWidth();
362
+ if (pwfWidth <= 0) {
363
+ pwfWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
364
+ }
349
365
  const pwfSize = pwfWidth * textureHeight;
350
366
  const pwfTable = new Uint8ClampedArray(pwfSize);
351
367
  const newOpacityTexture = vtkOpenGLTexture.newInstance();
@@ -24,7 +24,7 @@ function vtkOpenGLRenderer(publicAPI, model) {
24
24
  publicAPI.updateLights();
25
25
  publicAPI.prepareNodes();
26
26
  publicAPI.addMissingNode(model.renderable.getActiveCamera());
27
- publicAPI.addMissingNodes(model.renderable.getViewPropsWithNestedProps());
27
+ publicAPI.addMissingNodes(model.renderable.getViewPropsWithNestedProps(), true);
28
28
  publicAPI.removeUnusedNodes();
29
29
  }
30
30
  };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Even when the EXT_texture_norm16 extension is present, linear filtering
3
+ * might not be supported for normalized fixed point textures.
4
+ *
5
+ * This is a driver bug. See https://github.com/KhronosGroup/WebGL/issues/3706
6
+ * @return {boolean}
7
+ */
8
+ function supportsNorm16Linear() {
9
+ try {
10
+ const canvasSize = 4;
11
+ const texWidth = 2;
12
+ const texHeight = 1;
13
+ const texData = new Int16Array([0, 2 ** 15 - 1]);
14
+ const pixelToCheck = [1, 1];
15
+ const canvas = document.createElement('canvas');
16
+ canvas.width = canvasSize;
17
+ canvas.height = canvasSize;
18
+ const gl = canvas.getContext('webgl2');
19
+ if (!gl) {
20
+ return false;
21
+ }
22
+ const ext = gl.getExtension('EXT_texture_norm16');
23
+ if (!ext) {
24
+ return false;
25
+ }
26
+ const vs = `#version 300 es
27
+ void main() {
28
+ gl_PointSize = ${canvasSize.toFixed(1)};
29
+ gl_Position = vec4(0, 0, 0, 1);
30
+ }
31
+ `;
32
+ const fs = `#version 300 es
33
+ precision highp float;
34
+ precision highp int;
35
+ precision highp sampler2D;
36
+
37
+ uniform sampler2D u_image;
38
+
39
+ out vec4 color;
40
+
41
+ void main() {
42
+ vec4 intColor = texture(u_image, gl_PointCoord.xy);
43
+ color = vec4(vec3(intColor.rrr), 1);
44
+ }
45
+ `;
46
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
47
+ gl.shaderSource(vertexShader, vs);
48
+ gl.compileShader(vertexShader);
49
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
50
+ return false;
51
+ }
52
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
53
+ gl.shaderSource(fragmentShader, fs);
54
+ gl.compileShader(fragmentShader);
55
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
56
+ return false;
57
+ }
58
+ const program = gl.createProgram();
59
+ gl.attachShader(program, vertexShader);
60
+ gl.attachShader(program, fragmentShader);
61
+ gl.linkProgram(program);
62
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
63
+ return false;
64
+ }
65
+ const tex = gl.createTexture();
66
+ gl.bindTexture(gl.TEXTURE_2D, tex);
67
+ gl.texImage2D(gl.TEXTURE_2D, 0, ext.R16_SNORM_EXT, texWidth, texHeight, 0, gl.RED, gl.SHORT, texData);
68
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
69
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
70
+ gl.useProgram(program);
71
+ gl.drawArrays(gl.POINTS, 0, 1);
72
+ const pixel = new Uint8Array(4);
73
+ gl.readPixels(pixelToCheck[0], pixelToCheck[1], 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
74
+ const [r, g, b] = pixel;
75
+ const webglLoseContext = gl.getExtension('WEBGL_lose_context');
76
+ if (webglLoseContext) {
77
+ webglLoseContext.loseContext();
78
+ }
79
+ return r === g && g === b && r !== 0;
80
+ } catch (e) {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @type {boolean | undefined}
87
+ */
88
+ let supportsNorm16LinearCache;
89
+ function supportsNorm16LinearCached() {
90
+ // Only create a canvas+texture+shaders the first time
91
+ if (supportsNorm16LinearCache === undefined) {
92
+ supportsNorm16LinearCache = supportsNorm16Linear();
93
+ }
94
+ return supportsNorm16LinearCache;
95
+ }
96
+
97
+ export { supportsNorm16LinearCached as default };