diff --git a/src/components/tools/paint/PaintWidget2D.vue b/src/components/tools/paint/PaintWidget2D.vue index 4f7529b55..ffa5269c5 100644 --- a/src/components/tools/paint/PaintWidget2D.vue +++ b/src/components/tools/paint/PaintWidget2D.vue @@ -3,9 +3,6 @@ import vtkWidgetManager from '@kitware/vtk.js/Widgets/Core/WidgetManager'; import { computed, defineComponent, - onBeforeUnmount, - onMounted, - onUnmounted, PropType, ref, toRefs, @@ -15,14 +12,17 @@ import vtkPlaneManipulator from '@kitware/vtk.js/Widgets/Manipulators/PlaneManip import { getLPSAxisFromDir } from '@/src/utils/lps'; import { useCurrentImage } from '@/src/composables/useCurrentImage'; import { updatePlaneManipulatorFor2DView } from '@/src/utils/manipulators'; -import type { vtkSubscription } from '@kitware/vtk.js/interfaces'; import { usePaintToolStore } from '@/src/store/tools/paint'; import { vtkPaintViewWidget } from '@/src/vtk/PaintWidget'; import { useViewStore } from '@/src/store/views'; import { PaintWidgetState } from '@/src/vtk/PaintWidget/state'; import { vec3 } from 'gl-matrix'; -import { manageVTKSubscription } from '@/src/composables/manageVTKSubscription'; import { LPSAxisDir } from '@/src/types/lps'; +import { onVTKEvent } from '@/src/composables/onVTKEvent'; +import { + onViewProxyMounted, + onViewProxyUnmounted, +} from '@/src/composables/useViewProxy'; export default defineComponent({ name: 'PaintWidget2D', @@ -46,17 +46,14 @@ export default defineComponent({ }, setup(props) { const { - widgetManager: widgetManagerRef, + widgetManager, viewDirection, slice, viewId: viewID, } = toRefs(props); const paintStore = usePaintToolStore(); - const factory = paintStore.getWidgetFactory(); - - const viewStore = useViewStore(); - const viewProxyRef = computed(() => viewStore.getViewProxy(viewID.value)); + const widgetFactory = paintStore.getWidgetFactory(); const widgetRef = ref(); @@ -73,13 +70,26 @@ export default defineComponent({ return indexPoint; }; - onMounted(() => { - const widgetManager = widgetManagerRef.value; - widgetRef.value = widgetManager.addWidget(factory) as vtkPaintViewWidget; + const viewProxy = computed(() => useViewStore().getViewProxy(viewID.value)); + + onViewProxyMounted(viewProxy, () => { + widgetRef.value = widgetManager.value.addWidget( + widgetFactory + ) as vtkPaintViewWidget; if (!widgetRef.value) { throw new Error('[PaintWidget2D] failed to create view widget'); } + + widgetManager.value.renderWidgets(); + widgetManager.value.grabFocus(widgetRef.value); + }); + + onViewProxyUnmounted(viewProxy, () => { + if (!widgetRef.value) { + return; + } + widgetManager.value.removeWidget(widgetRef.value); }); // --- widget representation config --- // @@ -97,45 +107,31 @@ export default defineComponent({ // --- interaction --- // - const subs: vtkSubscription[] = []; - - onMounted(() => { - const widget = widgetRef.value!; + onVTKEvent(widgetRef, 'onStartInteractionEvent', () => { + const state = widgetRef.value!.getWidgetState() as PaintWidgetState; + // StartInteraction cannot occur if origin is null. + const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); + paintStore.startStroke(indexPoint, viewAxisIndex.value); + }); - subs.push( - widget.onStartInteractionEvent(() => { - // start stroke - const state = widget.getWidgetState() as PaintWidgetState; - // StartInteraction cannot occur if origin is null. - const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); - paintStore.startStroke(indexPoint, viewAxisIndex.value); - }), - widget.onInteractionEvent(() => { - // register stroke - const state = widget.getWidgetState() as PaintWidgetState; - const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); - paintStore.placeStrokePoint(indexPoint, viewAxisIndex.value); - }), - widget.onEndInteractionEvent(() => { - // end stroke - const state = widget.getWidgetState() as PaintWidgetState; - const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); - paintStore.endStroke(indexPoint, viewAxisIndex.value); - }) - ); + onVTKEvent(widgetRef, 'onInteractionEvent', () => { + const state = widgetRef.value!.getWidgetState() as PaintWidgetState; + const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); + paintStore.placeStrokePoint(indexPoint, viewAxisIndex.value); }); - onUnmounted(() => { - while (subs.length) { - subs.pop()!.unsubscribe(); - } + onVTKEvent(widgetRef, 'onEndInteractionEvent', () => { + // end stroke + const state = widgetRef.value!.getWidgetState() as PaintWidgetState; + const indexPoint = worldPointToIndex(state.getBrush().getOrigin()!); + paintStore.endStroke(indexPoint, viewAxisIndex.value); }); // --- manipulator --- // const manipulator = vtkPlaneManipulator.newInstance(); - onMounted(() => { + onViewProxyMounted(viewProxy, () => { widgetRef.value!.setManipulator(manipulator); }); @@ -150,40 +146,38 @@ export default defineComponent({ // --- visibility --- // - onMounted(() => { + let checkIfPointerInView = false; + + onViewProxyMounted(viewProxy, () => { widgetRef.value!.setVisibility(false); + checkIfPointerInView = true; }); - manageVTKSubscription( - viewProxyRef.value!.getInteractor().onMouseEnter(() => { - const widget = widgetRef.value; - if (widget) { - paintStore.setSliceAxis(viewAxisIndex.value); - widget.setVisibility(true); - } - }) - ); + const viewInteractor = computed(() => viewProxy.value!.getInteractor()); - manageVTKSubscription( - viewProxyRef.value!.getInteractor().onMouseLeave(() => { - const widget = widgetRef.value; - if (widget) { - widget.setVisibility(false); - } - }) - ); + // Turn on widget visibility if mouse starts within view + onVTKEvent(viewInteractor, 'onMouseMove', () => { + if (!checkIfPointerInView) { + return; + } + checkIfPointerInView = false; - // --- focus and rendering --- // + widgetRef.value!.setVisibility(true); + }); - onMounted(() => { - const widgetManager = widgetManagerRef.value; - widgetManager.renderWidgets(); - widgetManager.grabFocus(widgetRef.value!); + onVTKEvent(viewInteractor, 'onMouseEnter', () => { + const widget = widgetRef.value; + if (widget) { + paintStore.setSliceAxis(viewAxisIndex.value); + widget.setVisibility(true); + } }); - onBeforeUnmount(() => { - widgetManagerRef.value.removeWidget(widgetRef.value!); - widgetManagerRef.value.releaseFocus(); + onVTKEvent(viewInteractor, 'onMouseLeave', () => { + const widget = widgetRef.value; + if (widget) { + widget.setVisibility(false); + } }); return () => null; diff --git a/src/vtk/PaintWidget/behavior.ts b/src/vtk/PaintWidget/behavior.ts index b4254c26f..cfb2f44b9 100644 --- a/src/vtk/PaintWidget/behavior.ts +++ b/src/vtk/PaintWidget/behavior.ts @@ -59,8 +59,11 @@ export default function widgetBehavior(publicAPI: any, model: any) { if (isPainting) { publicAPI.invokeInteractionEvent(); + return macro.EVENT_ABORT; } - return macro.EVENT_ABORT; + + // Let interactor get event + return macro.VOID; }; /** diff --git a/src/vtk/PolygonWidget/behavior.ts b/src/vtk/PolygonWidget/behavior.ts index e68887968..8ed8ffe80 100644 --- a/src/vtk/PolygonWidget/behavior.ts +++ b/src/vtk/PolygonWidget/behavior.ts @@ -37,7 +37,7 @@ export default function widgetBehavior(publicAPI: any, model: any) { // overSegment guards against clicking anywhere in view const selections = model._widgetManager.getSelections(); const overSegment = - selections[0]?.getProperties().prop === + selections?.[0]?.getProperties().prop === model.representations[1].getActors()[0]; // line representation is second representation return overSegment && !overUnselectedHandle; }; @@ -428,6 +428,5 @@ export default function widgetBehavior(publicAPI: any, model: any) { model.activeState = null; model.hasFocus = false; model._widgetManager.enablePicking(); - model._interactor.render(); }; }