Skip to content

Commit 5495580

Browse files
authored
Merge pull request #2602 from PaulHax/widget-dpr-fix
Fix widgets with scaleInPixels by adding RenderWindowViewNode.getComputedDevicePixelRatio
2 parents 438627c + 059a5d8 commit 5495580

File tree

6 files changed

+45
-8
lines changed

6 files changed

+45
-8
lines changed

Documentation/content/docs/concepts_widgets.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ In order to scale a representation such that it retains the same size in
113113
display space, a widget representation should use the `scaleInPixels` property
114114
and the `getPixelWorldHeightAtCoord(coord)` method. When `scaleInPixels` is set
115115
to true, a widget representation should multiply whatever scaling they perform
116-
by the output of `getDisplayScaleAtCoord(coord)`.
116+
by the output of `getPixelWorldHeightAtCoord(coord)`.
117117

118118
Look at the `SphereHandleRepresentation` as an example for how
119119
`getPixelWorldHeightAtCoord` is used.

Sources/Rendering/OpenGL/RenderWindow/index.d.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,18 @@ export interface vtkOpenGLRenderWindow extends vtkOpenGLRenderWindowBase {
327327
setViewStream(stream: vtkViewStream): boolean;
328328

329329
/**
330-
*
330+
* Sets the pixel width and height of the rendered image.
331+
*
332+
* WebGL and WebGPU render windows apply these values to
333+
* the width and height attribute of the canvas element.
334+
*
335+
* To match the device resolution in browser environments,
336+
* multiply the container size by `window.devicePixelRatio`
337+
* `apiSpecificRenderWindow.setSize(Math.floor(containerWidth * devicePixelRatio), Math.floor(containerHeight * devicePixelRatio));
338+
* See the VTK.js FullscreenRenderWindow class for an example.
339+
*
340+
* @see getComputedDevicePixelRatio()
341+
*
331342
* @param {Vector2} size
332343
*/
333344
setSize(size: Vector2): void;
@@ -361,6 +372,19 @@ export interface vtkOpenGLRenderWindow extends vtkOpenGLRenderWindowBase {
361372
*
362373
*/
363374
getVrResolution(): Vector2;
375+
376+
/**
377+
* Scales the size of a browser CSS pixel to a rendered canvas pixel.
378+
* `const renderedPixelWidth = cssPixelWidth * apiRenderWindow.getComputedDevicePixelRatio()`
379+
* Use to scale rendered objects to a consistent perceived size or DOM pixel position.
380+
*
381+
* Rather than using window.devicePixelRatio directly, the device pixel ratio is inferred
382+
* from the container CSS pixel size and rendered image pixel size. The user directly sets the rendered pixel size.
383+
*
384+
* @see setSize()
385+
* @see getContainerSize()
386+
*/
387+
getComputedDevicePixelRatio(): number;
364388
}
365389

366390
/**

Sources/Rendering/SceneGraph/RenderWindowViewNode/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,19 @@ function vtkRenderWindowViewNode(publicAPI, model) {
135135
return publicAPI.displayToNormalizedDisplay(x2, y2, z);
136136
};
137137

138+
publicAPI.getComputedDevicePixelRatio = () =>
139+
model.size[0] / publicAPI.getContainerSize()[0];
140+
141+
publicAPI.getContainerSize = () => {
142+
macro.vtkErrorMacro('not implemented');
143+
};
144+
138145
publicAPI.getPixelData = (x1, y1, x2, y2) => {
139146
macro.vtkErrorMacro('not implemented');
140-
return undefined;
141147
};
142148

143149
publicAPI.createSelector = () => {
144150
macro.vtkErrorMacro('not implemented');
145-
return undefined;
146151
};
147152
}
148153

Sources/Widgets/Core/WidgetManager/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function vtkWidgetManager(publicAPI, model) {
121121
const [cwidth, cheight] = model._apiSpecificRenderWindow.getViewportSize(
122122
model._renderer
123123
);
124-
const ratio = window.devicePixelRatio || 1;
124+
const ratio = model._apiSpecificRenderWindow.getComputedDevicePixelRatio();
125125
const bwidth = String(cwidth / ratio);
126126
const bheight = String(cheight / ratio);
127127
const viewBox = `0 0 ${cwidth} ${cheight}`;
@@ -224,7 +224,11 @@ function vtkWidgetManager(publicAPI, model) {
224224
if (_renderer && _apiSpecificRenderWindow && _camera) {
225225
const [rwW, rwH] = _apiSpecificRenderWindow.getSize();
226226
const [vxmin, vymin, vxmax, vymax] = _renderer.getViewport();
227-
const rendererPixelDims = [rwW * (vxmax - vxmin), rwH * (vymax - vymin)];
227+
const pixelRatio = _apiSpecificRenderWindow.getComputedDevicePixelRatio();
228+
const rendererPixelDims = [
229+
(rwW * (vxmax - vxmin)) / pixelRatio,
230+
(rwH * (vymax - vymin)) / pixelRatio,
231+
];
228232

229233
const cameraPosition = _camera.getPosition();
230234
const cameraDir = _camera.getDirectionOfProjection();

Sources/Widgets/Core/WidgetManager/test/testWidgetManager.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ test.onlyIfWebGL('Test getPixelWorldHeightAtCoord', (t) => {
3434

3535
const container = document.querySelector('body');
3636
const rwContainer = gc.registerDOMElement(document.createElement('div'));
37+
// maintain consistent container size across browsers
38+
rwContainer.style.width = '300px';
39+
rwContainer.style.height = '300px';
40+
3741
container.appendChild(rwContainer);
3842

3943
const grw = vtkGenericRenderWindow.newInstance({ listenWindowResize: false });

Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ const widget = vtkResliceCursorWidget.newInstance();
4848
const widgetState = widget.getWidgetState();
4949
widgetState.setKeepOrthogonality(true);
5050
widgetState.setOpacity(0.6);
51-
// Use devicePixelRatio in order to have the same display handle size on all devices
52-
widgetState.setSphereRadius(10 * window.devicePixelRatio);
51+
// Set size in CSS pixel space because scaleInPixels defaults to true
52+
widgetState.setSphereRadius(10);
5353
widgetState.setLineThickness(5);
5454

5555
const showDebugActors = true;

0 commit comments

Comments
 (0)