Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 112 additions & 40 deletions packages/core/src/RenderingEngine/RenderingEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import StackViewport from './StackViewport';
import viewportTypeUsesCustomRenderingPipeline from './helpers/viewportTypeUsesCustomRenderingPipeline';
import getOrCreateCanvas from './helpers/getOrCreateCanvas';
import { getShouldUseCPURendering, isCornerstoneInitialized } from '../init';
import { calculateOptimalViewportOffsets } from './helpers/rectanglePacking';
import type IStackViewport from '../types/IStackViewport';
import type IVolumeViewport from '../types/IVolumeViewport';
import viewportTypeToViewportClass from './helpers/viewportTypeToViewportClass';
Expand Down Expand Up @@ -681,19 +682,39 @@ class RenderingEngine {

// 2.d Re-position previous viewports on the offScreen Canvas based on the new
// offScreen canvas size
const xOffset = this._resize(
this._resize(
viewportsDrivenByVtkJs as (IStackViewport | IVolumeViewport)[],
offScreenCanvasWidth,
offScreenCanvasHeight
);

const internalViewportEntry = { ...viewportInputEntry, canvas };

// Calculate the position for the new viewport using optimal packing
const allCanvases = [...canvasesDrivenByVtkJs];
const viewportInputs = allCanvases.map((c, index) => ({
id:
index === allCanvases.length - 1
? viewportInputEntry.viewportId
: `viewport-${index}`,
canvas: {
width: c.width,
height: c.height,
},
}));

const { viewportOffsets } = calculateOptimalViewportOffsets(viewportInputs);

const newViewportOffset = viewportOffsets.find(
(offset) => offset.id === viewportInputEntry.viewportId
) || { xOffset: 0, yOffset: 0 };

// 3 Add the requested viewport to rendering Engine
this.addVtkjsDrivenViewport(internalViewportEntry, {
offScreenCanvasWidth,
offScreenCanvasHeight,
xOffset,
xOffset: newViewportOffset.xOffset,
yOffset: newViewportOffset.yOffset,
});
}

Expand Down Expand Up @@ -734,6 +755,7 @@ class RenderingEngine {
offScreenCanvasWidth: number;
offScreenCanvasHeight: number;
xOffset: number;
yOffset?: number;
}
): void {
const { element, canvas, viewportId, type, defaultOptions } =
Expand All @@ -742,8 +764,12 @@ class RenderingEngine {
// Make the element not focusable, we use this for modifier keys to work
element.tabIndex = -1;

const { offScreenCanvasWidth, offScreenCanvasHeight, xOffset } =
offscreenCanvasProperties;
const {
offScreenCanvasWidth,
offScreenCanvasHeight,
xOffset,
yOffset = 0,
} = offscreenCanvasProperties;

// 1. Calculate the size of location of the viewport on the offScreen canvas
const {
Expand All @@ -759,7 +785,8 @@ class RenderingEngine {
viewportInputEntry,
offScreenCanvasWidth,
offScreenCanvasHeight,
xOffset
xOffset,
yOffset
);
// 2. Add a renderer to the offScreenMultiRenderWindow
this.offscreenMultiRenderWindow.addRenderer({
Expand Down Expand Up @@ -925,8 +952,27 @@ class RenderingEngine {
}*/

// 3. Adding the viewports based on the viewportInputEntry definition to the
// rendering engine.
let xOffset = 0;
// rendering engine using rectangle packing
const viewportInputs = vtkDrivenCanvases.map((canvas, index) => ({
id: viewportInputEntries[index].viewportId,
canvas: {
width: canvas.width,
height: canvas.height,
},
}));

const { viewportOffsets } =
calculateOptimalViewportOffsets(viewportInputs);

// Create a map for quick lookup
const offsetMap = new Map<string, { xOffset: number; yOffset: number }>();
viewportOffsets.forEach((offset) => {
offsetMap.set(offset.id, {
xOffset: offset.xOffset,
yOffset: offset.yOffset,
});
});

for (let i = 0; i < viewportInputEntries.length; i++) {
const vtkDrivenViewportInputEntry = viewportInputEntries[i];
const canvas = vtkDrivenCanvases[i];
Expand All @@ -935,15 +981,19 @@ class RenderingEngine {
canvas,
};

const offsets = offsetMap.get(
vtkDrivenViewportInputEntry.viewportId
) || {
xOffset: 0,
yOffset: 0,
};

this.addVtkjsDrivenViewport(internalViewportEntry, {
offScreenCanvasWidth,
offScreenCanvasHeight,
xOffset,
xOffset: offsets.xOffset,
yOffset: offsets.yOffset,
});

// Incrementing the xOffset which provides the horizontal location of each
// viewport on the offScreen canvas
xOffset += canvas.width;
}
}
}
Expand All @@ -959,28 +1009,33 @@ class RenderingEngine {
} {
const { offScreenCanvasContainer, offscreenMultiRenderWindow } = this;

// 1. Calculated the height of the offScreen canvas to be the maximum height
// between canvases
const offScreenCanvasHeight = Math.max(
...canvasesDrivenByVtkJs.map((canvas) => canvas.height)
);

// 2. Calculating the width of the offScreen canvas to be the sum of all
let offScreenCanvasWidth = 0;
// Calculate optimal dimensions based on viewport canvases
const viewportInputs = canvasesDrivenByVtkJs.map((canvas, index) => ({
id: `canvas-${index}`,
canvas: {
width: canvas.width,
height: canvas.height,
},
}));

canvasesDrivenByVtkJs.forEach((canvas) => {
offScreenCanvasWidth += canvas.width;
});
const { canvasWidth, canvasHeight } =
calculateOptimalViewportOffsets(viewportInputs);

// Update the offscreen canvas dimensions
// @ts-expect-error
offScreenCanvasContainer.width = offScreenCanvasWidth;
offScreenCanvasContainer.width = canvasWidth;
// @ts-expect-error
offScreenCanvasContainer.height = offScreenCanvasHeight;
offScreenCanvasContainer.height = canvasHeight;
offScreenCanvasContainer.style.width = `${canvasWidth}px`;
offScreenCanvasContainer.style.height = `${canvasHeight}px`;

// 3. Resize command
offscreenMultiRenderWindow.resize();

return { offScreenCanvasWidth, offScreenCanvasHeight };
return {
offScreenCanvasWidth: canvasWidth,
offScreenCanvasHeight: canvasHeight,
};
}

/**
Expand All @@ -996,12 +1051,32 @@ class RenderingEngine {
viewportsDrivenByVtkJs: (IStackViewport | IVolumeViewport)[],
offScreenCanvasWidth: number,
offScreenCanvasHeight: number
): number {
// Redefine viewport properties
let _xOffset = 0;
): void {
// Use rectangle packing to calculate viewport positions
const viewportInputs = viewportsDrivenByVtkJs.map((viewport) => ({
id: viewport.id,
canvas: {
width: viewport.canvas.width,
height: viewport.canvas.height,
},
}));

const { viewportOffsets } = calculateOptimalViewportOffsets(viewportInputs);

// Create a map for quick lookup
const offsetMap = new Map<string, { xOffset: number; yOffset: number }>();
viewportOffsets.forEach((offset) => {
offsetMap.set(offset.id, {
xOffset: offset.xOffset,
yOffset: offset.yOffset,
});
});

// Update viewport positions using packed offsets
for (let i = 0; i < viewportsDrivenByVtkJs.length; i++) {
const viewport = viewportsDrivenByVtkJs[i];
const offsets = offsetMap.get(viewport.id) || { xOffset: 0, yOffset: 0 };

const {
sxStartDisplayCoords,
syStartDisplayCoords,
Expand All @@ -1015,11 +1090,10 @@ class RenderingEngine {
viewport as IViewport,
offScreenCanvasWidth,
offScreenCanvasHeight,
_xOffset
offsets.xOffset,
offsets.yOffset
);

_xOffset += viewport.canvas.width;

viewport.sx = sx;
viewport.sy = sy;
viewport.sWidth = sWidth;
Expand All @@ -1034,9 +1108,6 @@ class RenderingEngine {
syEndDisplayCoords,
]);
}

// Returns the final xOffset
return _xOffset;
}

/**
Expand All @@ -1051,20 +1122,21 @@ class RenderingEngine {
viewport: InternalViewportInput | IViewport,
offScreenCanvasWidth: number,
offScreenCanvasHeight: number,
_xOffset: number
_xOffset: number,
_yOffset: number = 0
): ViewportDisplayCoords {
const { canvas } = viewport;
const { width: sWidth, height: sHeight } = canvas;

// Update the canvas drawImage offsets.
const sx = _xOffset;
const sy = 0;
const sy = _yOffset;

const sxStartDisplayCoords = sx / offScreenCanvasWidth;

// Need to offset y if it not max height
const syStartDisplayCoords =
sy + (offScreenCanvasHeight - sHeight) / offScreenCanvasHeight;
// VTK uses bottom-left origin, while canvas uses top-left
// We need to invert the Y coordinate for VTK
const syStartDisplayCoords = 1 - (sy + sHeight) / offScreenCanvasHeight;

const sWidthDisplayCoords = sWidth / offScreenCanvasWidth;
const sHeightDisplayCoords = sHeight / offScreenCanvasHeight;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/RenderingEngine/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import addVolumesToViewports from './addVolumesToViewports';
import volumeNewImageEventDispatcher from './volumeNewImageEventDispatcher';
import addImageSlicesToViewports from './addImageSlicesToViewports';

export * from './rectanglePacking';

export {
createVolumeActor,
createVolumeMapper,
Expand Down
Loading
Loading