Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offscreen views #501

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
52 changes: 45 additions & 7 deletions src/components/VtkThreeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
class="vtk-view"
ref="vtkContainerRef"
data-testid="vtk-view vtk-three-view"
/>
>
<canvas ref="canvasRef" class="ccc" />
</div>
</div>
<div class="overlay-no-events tool-layer">
<crop-tool :view-id="viewID" />
Expand Down Expand Up @@ -77,6 +79,7 @@ import type { Vector3 } from '@kitware/vtk.js/types';

import { useProxyManager } from '@/src/composables/proxyManager';
import ViewOverlayGrid from '@/src/components/ViewOverlayGrid.vue';
import { onVTKEvent } from '@/src/composables/onVTKEvent';
import { useResizeObserver } from '../composables/useResizeObserver';
import { useCurrentImage } from '../composables/useCurrentImage';
import { useCameraOrientation } from '../composables/useCameraOrientation';
Expand Down Expand Up @@ -427,22 +430,51 @@ export default defineComponent({

// --- view proxy setup --- //

const { viewProxy, setContainer: setViewProxyContainer } =
useViewProxy<vtkLPSView3DProxy>(viewID, ViewProxyType.Volume);
const { viewProxy } = useViewProxy<vtkLPSView3DProxy>(
viewID,
ViewProxyType.Volume
);

const canvasRef = ref<HTMLCanvasElement | null>(null);

const interactor = computed(() => viewProxy.value.getInteractor());
onVTKEvent(interactor, 'onRenderEvent' as 'onModified', () => {
if (!canvasRef.value) return;
const ctx = canvasRef.value.getContext('2d');
const src = viewProxy.value.getOpenGLRenderWindow().getCanvas();
if (ctx && src) {
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
ctx.drawImage(src, 0, 0);
}
});

onMounted(() => {
viewProxy.value.setOrientationAxesVisibility(true);
viewProxy.value.setOrientationAxesType('cube');
viewProxy.value.setBackground([0, 0, 0, 0]);
setViewProxyContainer(vtkContainerRef.value);
viewProxy.value.setInteractionContainer(canvasRef.value);
// setViewProxyContainer(vtkContainerRef.value);
});

onBeforeUnmount(() => {
setViewProxyContainer(null);
viewProxy.value.setContainer(null);
// setViewProxyContainer(null);
});

useResizeObserver(vtkContainerRef, () => viewProxy.value.resize());
useResizeObserver(vtkContainerRef, (entry) => {
const bbox = entry.contentRect;
if (!bbox.width || !bbox.height) return;

canvasRef.value?.setAttribute(
'width',
String(bbox.width * window.devicePixelRatio)
);
canvasRef.value?.setAttribute(
'height',
String(bbox.height * window.devicePixelRatio)
);

viewProxy.value.setSize(bbox.width, bbox.height);
});

// --- scene setup --- //

Expand Down Expand Up @@ -606,6 +638,7 @@ export default defineComponent({

return {
vtkContainerRef,
canvasRef,
viewID,
active: false,
topLeftLabel: computed(
Expand All @@ -628,4 +661,9 @@ export default defineComponent({
background-color: black;
grid-template-columns: auto;
}

.ccc {
width: 100%;
height: 100%;
}
</style>
50 changes: 44 additions & 6 deletions src/components/VtkTwoView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
class="vtk-view"
ref="vtkContainerRef"
data-testid="vtk-view vtk-two-view"
/>
>
<canvas ref="canvasRef" class="ccc" />
</div>
</div>
<div class="overlay-no-events tool-layer" ref="toolContainer">
<svg class="overlay-no-events">
Expand Down Expand Up @@ -208,6 +210,7 @@ import { useToolSelectionStore } from '@/src/store/tools/toolSelection';
import { useAnnotationToolStore } from '@/src/store/tools';
import { doesToolFrameMatchViewAxis } from '@/src/composables/annotationTool';
import type { TypedArray } from '@kitware/vtk.js/types';
import { onVTKEvent } from '@/src/composables/onVTKEvent';
import { useResizeObserver } from '../composables/useResizeObserver';
import { useOrientationLabels } from '../composables/useOrientationLabels';
import { getLPSAxisFromDir } from '../utils/lps';
Expand Down Expand Up @@ -397,8 +400,23 @@ export default defineComponent({

// --- view proxy setup --- //

const { viewProxy, setContainer: setViewProxyContainer } =
useViewProxy<vtkLPSView2DProxy>(viewID, ViewProxyType.Slice);
const { viewProxy } = useViewProxy<vtkLPSView2DProxy>(
viewID,
ViewProxyType.Slice
);

const canvasRef = ref<HTMLCanvasElement | null>(null);

const interactor = computed(() => viewProxy.value.getInteractor());
onVTKEvent(interactor, 'onRenderEvent' as 'onModified', () => {
if (!canvasRef.value) return;
const ctx = canvasRef.value.getContext('2d');
const src = viewProxy.value.getOpenGLRenderWindow().getCanvas();
if (ctx && src) {
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);
ctx.drawImage(src, 0, 0);
}
});

onBeforeMount(() => {
// do this before mount, as the ManipulatorTools run onMounted
Expand All @@ -407,12 +425,13 @@ export default defineComponent({
});

onMounted(() => {
setViewProxyContainer(vtkContainerRef.value);
// setViewProxyContainer(vtkContainerRef.value);
viewProxy.value.setInteractionContainer(canvasRef.value);
viewProxy.value.setOrientationAxesVisibility(false);
});

onBeforeUnmount(() => {
setViewProxyContainer(null);
// setViewProxyContainer(null);
});

// --- Slicing setup --- //
Expand Down Expand Up @@ -457,7 +476,21 @@ export default defineComponent({

// --- resizing --- //

useResizeObserver(vtkContainerRef, () => viewProxy.value.resize());
useResizeObserver(vtkContainerRef, (entry) => {
const bbox = entry.contentRect;
if (!bbox.width || !bbox.height) return;

canvasRef.value?.setAttribute(
'width',
String(bbox.width * window.devicePixelRatio)
);
canvasRef.value?.setAttribute(
'height',
String(bbox.height * window.devicePixelRatio)
);

viewProxy.value.setSize(bbox.width, bbox.height);
});

// Used by SVG tool widgets for resizeCallback
const toolContainer = ref<HTMLElement>();
Expand Down Expand Up @@ -877,6 +910,7 @@ export default defineComponent({

return {
vtkContainerRef,
canvasRef,
toolContainer,
viewID,
viewProxy,
Expand Down Expand Up @@ -911,4 +945,8 @@ export default defineComponent({
height: 32px;
cursor: pointer;
}
.ccc {
width: 100%;
height: 100%;
}
</style>
21 changes: 8 additions & 13 deletions src/composables/useViewProxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import vtkViewProxy from '@kitware/vtk.js/Proxy/Core/ViewProxy';
import { computed, onUnmounted, ref, unref, watch, watchEffect } from 'vue';
import { MaybeRef, useElementSize } from '@vueuse/core';
import { computed, onUnmounted, ref, unref, watch } from 'vue';
import { MaybeRef } from '@vueuse/core';
import { onVTKEvent } from '@/src/composables/onVTKEvent';
import { Maybe } from '@/src/types';
import { ViewProxyType } from '../core/proxies';
Expand All @@ -23,7 +23,9 @@ export function useViewProxy<T extends vtkViewProxy = vtkViewProxy>(
);

watch(viewProxy, (curViewProxy, oldViewProxy) => {
oldViewProxy.setContainer(null);
if (oldViewProxy !== curViewProxy) {
oldViewProxy.setContainer(null);
}
curViewProxy.setContainer(container.value);
});

Expand All @@ -42,21 +44,14 @@ export function useViewProxy<T extends vtkViewProxy = vtkViewProxy>(
function useMountedViewProxy<T extends vtkViewProxy = vtkViewProxy>(
viewProxy: MaybeRef<Maybe<T>>
) {
const mounted = ref(false);

const container = ref<Maybe<HTMLElement>>(unref(viewProxy)?.getContainer());
onVTKEvent<vtkViewProxy, 'onModified'>(viewProxy, 'onModified', () => {
container.value = unref(viewProxy)?.getContainer();
});

const { width, height } = useElementSize(container);

const updateMounted = () => {
// view is considered mounted when the container has a non-zero size
mounted.value = !!(width.value && height.value);
};

watchEffect(() => updateMounted());
const mounted = computed(() => {
return !!container.value;
});

return mounted;
}
Expand Down
8 changes: 6 additions & 2 deletions src/vtk/LPSView2DProxy/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { vec3 } from 'gl-matrix';
import { vtkView2DProxy } from '@kitware/vtk.js/Proxy/Core/View2DProxy';
import { ViewProxyCustomizations } from '@/src/vtk/LPSView3DProxy';
import vtkLPSView3DProxy, {
ViewProxyCustomizations,
} from '@/src/vtk/LPSView3DProxy';

export interface vtkLPSView2DProxy extends vtkView2DProxy {
export interface vtkLPSView2DProxy
extends vtkView2DProxy,
ViewProxyCustomizations {
resizeToFit(lookAxis: Vector3, viewUpAxis: Vector3, worldDims: Vector3);
resetCamera(boundsToUse?: number[]);
/**
Expand Down
10 changes: 9 additions & 1 deletion src/vtk/LPSView3DProxy/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { vec3 } from 'gl-matrix';
import vtkViewProxy from '@kitware/vtk.js/Proxy/Core/ViewProxy';
import vtkInteractorStyleManipulator from '@kitware/vtk.js/Interaction/Style/InteractorStyleManipulator';
import { Maybe } from '@/src/types';

export interface vtkLPSView3DProxy extends vtkViewProxy {
export interface ViewProxyCustomizations {
removeAllRepresentations(): void;
updateCamera(directionOfProjection: vec3, viewUp: vec3, focalPoint: vec3);
getInteractorStyle2D(): vtkInteractorStyleManipulator;
getInteractorStyle3D(): vtkInteractorStyleManipulator;
setInteractionContainer(el: Maybe<HTMLElement>): boolean;
getInteractionContainer(): Maybe<HTMLElement>;
setSize(w: number, h: number): boolean;
}

export interface vtkLPSView3DProxy
extends vtkViewProxy,
ViewProxyCustomizations {}

export function commonViewCustomizations(publicAPI: any, model: any): void;

// TODO extend, newInstance...
Expand Down
46 changes: 46 additions & 0 deletions src/vtk/LPSView3DProxy/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { vec3 } from 'gl-matrix';
import macro from '@kitware/vtk.js/macro';
import vtkViewProxy from '@kitware/vtk.js/Proxy/Core/ViewProxy';
import vtkOffscreenRenderWindowInteractor from '@/src/vtk/vtkOffscreenRenderWindowInteractor';

export function replaceInteractor(publicAPI, model) {
// prep to remove the old interactor
const style = model.interactor.getInteractorStyle();
const orientationWidgetEnabled = model.orientationWidget.getEnabled();
model.orientationWidget.setEnabled(false);

// delete the old interactor in favor of the new one
model.interactor.delete();

model.interactor = vtkOffscreenRenderWindowInteractor.newInstance();
model.interactor.setView(model._openGLRenderWindow);
model.interactor.setInteractorStyle(style);
model.orientationWidget.setInteractor(model.interactor);
model.orientationWidget.setEnabled(orientationWidgetEnabled);
}

export function commonViewCustomizations(publicAPI, model) {
const delayedRender = macro.debounce(model.renderWindow.render, 5);

replaceInteractor(publicAPI, model);

// override resize to avoid flickering from rendering later
publicAPI.resize = () => {
if (model.container) {
Expand Down Expand Up @@ -68,6 +87,33 @@ export function commonViewCustomizations(publicAPI, model) {
resetCamera(args);
model.renderer.updateLightsGeometryToFollowCamera();
};

publicAPI.setSize = (width, height) => {
const container = publicAPI.getContainer();
if (!container) throw new Error('No container');
setTimeout(() => {
container.style.width = `${width}px`;
container.style.height = `${height}px`;
publicAPI.resize();
}, 0);
};

publicAPI.setInteractionContainer = (el) => {
return model.interactor.setInteractionContainer(el);
};

publicAPI.getInteractionContainer = () => {
return model.interactor.getInteractionContainer();
};

// initialize

const container = document.createElement('div');
container.style.display = 'block';
container.style.visibility = 'hidden';
container.style.position = 'absolute';
document.body.appendChild(container);
publicAPI.setContainer(container);
}

function vtkLPSView3DProxy(publicAPI, model) {
Expand Down
Loading
Loading