Skip to content

Commit 6544a11

Browse files
committed
feat(crosshairs): add hold key for temp crosshairs tool
1 parent 5644bad commit 6544a11

8 files changed

Lines changed: 93 additions & 61 deletions

File tree

src/components/ControlsStripTools.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@
126126
</template>
127127

128128
<script lang="ts">
129-
import { computed, defineComponent, ref } from 'vue';
129+
import { computed, defineComponent, ref, watch } from 'vue';
130130
import { storeToRefs } from 'pinia';
131-
import { onKeyDown } from '@vueuse/core';
131+
import { onKeyDown, useMagicKeys } from '@vueuse/core';
132132
import { Tools } from '@/src/store/tools/types';
133133
import ControlButton from '@/src/components/ControlButton.vue';
134134
import ItemGroup from '@/src/components/ItemGroup.vue';
@@ -180,6 +180,15 @@ export default defineComponent({
180180
windowingMenu.value = false;
181181
});
182182
183+
const keys = useMagicKeys();
184+
const enableTempCrosshairs = computed(
185+
() => keys[actionToKey.value.temporaryCrosshairs].value
186+
);
187+
watch(enableTempCrosshairs, (enable) => {
188+
if (enable) toolStore.activateTemporaryCrosshairs();
189+
else toolStore.deactivateTemporaryCrosshairs();
190+
});
191+
183192
// Rename the computed property to map tool names to their keyboard shortcuts
184193
const nameToShortcut = computed(() => {
185194
const keyMap = actionToKey.value;

src/composables/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const ACTION_TO_FUNC = {
6969
paint: setTool(Tools.Paint),
7070
rectangle: setTool(Tools.Rectangle),
7171
crosshairs: setTool(Tools.Crosshairs),
72+
temporaryCrosshairs: NOOP, // behavior implemented elsewhere
7273
crop: setTool(Tools.Crop),
7374
polygon: setTool(Tools.Polygon),
7475
select: setTool(Tools.Select),

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ export const ACTION_TO_KEY = {
270270
paint: 'p',
271271
rectangle: 'r',
272272
crosshairs: 'c',
273+
temporaryCrosshairs: 'v',
273274
crop: 'b',
274275
polygon: 'g',
275276
mergeNewPolygon: 'Shift',

src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export const ACTIONS = {
4444
crosshairs: {
4545
readable: 'Activate Crosshairs tool',
4646
},
47+
temporaryCrosshairs: {
48+
readable: 'Temporarily activate crosshairs tool',
49+
},
4750
crop: {
4851
readable: 'Activate Crop tool',
4952
},

src/store/tools/crosshairs.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ export const useCrosshairsToolStore = defineStore('crosshairs', () => {
9999
});
100100

101101
function activateTool() {
102-
widgetState.setPlaced(false);
103102
active.value = true;
104103
return true;
105104
}
@@ -108,6 +107,10 @@ export const useCrosshairsToolStore = defineStore('crosshairs', () => {
108107
active.value = false;
109108
}
110109

110+
function startDragging() {
111+
widgetState.setDragging(true);
112+
}
113+
111114
function serialize(state: StateFile) {
112115
const { crosshairs } = state.manifest.tools;
113116
crosshairs.position = position.value;
@@ -125,6 +128,7 @@ export const useCrosshairsToolStore = defineStore('crosshairs', () => {
125128
imagePosition,
126129
activateTool,
127130
deactivateTool,
131+
startDragging,
128132
serialize,
129133
deserialize,
130134
};

src/store/tools/index.ts

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Manifest, StateFile } from '@/src/io/state-file/schema';
22
import { Maybe } from '@/src/types';
33
import type { AnnotationToolStore } from '@/src/store/tools/useAnnotationTool';
44
import { defineStore } from 'pinia';
5+
import { ref } from 'vue';
56
import { useCropStore } from './crop';
67
import { useCrosshairsToolStore } from './crosshairs';
78
import { usePaintToolStore } from './paint';
@@ -10,10 +11,6 @@ import { useRectangleStore } from './rectangles';
1011
import { AnnotationToolType, IToolStore, Tools } from './types';
1112
import { usePolygonStore } from './polygons';
1213

13-
interface State {
14-
currentTool: Tools;
15-
}
16-
1714
// TODO move these types out
1815
export const AnnotationToolStoreMap: Record<
1916
AnnotationToolType,
@@ -64,49 +61,68 @@ function teardownTool(tool: Tools) {
6461
}
6562
}
6663

67-
export const useToolStore = defineStore('tool', {
68-
state: (): State => ({
69-
currentTool: Tools.WindowLevel,
70-
}),
71-
actions: {
72-
setCurrentTool(tool: Tools) {
73-
if (!setupTool(tool)) {
74-
return;
75-
}
76-
teardownTool(this.currentTool);
77-
this.currentTool = tool;
78-
},
79-
serialize(state: StateFile) {
80-
const { tools } = state.manifest;
81-
82-
Object.values(ToolStoreMap)
83-
.map((useStore) => useStore?.())
84-
.filter((store): store is IToolStore => !!store)
85-
.forEach((store) => {
86-
store.serialize?.(state);
87-
});
88-
89-
tools.current = this.currentTool;
90-
},
91-
deserialize(
92-
manifest: Manifest,
93-
segmentGroupIDMap: Record<string, string>,
94-
dataIDMap: Record<string, string>
95-
) {
96-
const { tools } = manifest;
97-
98-
usePaintToolStore().deserialize(manifest, segmentGroupIDMap);
99-
100-
Object.values(ToolStoreMap)
101-
// paint store uses segmentGroupIDMap
102-
.filter((useStore) => useStore !== usePaintToolStore)
103-
.map((useStore) => useStore?.())
104-
.filter((store): store is IToolStore => !!store)
105-
.forEach((store) => {
106-
store.deserialize?.(manifest, dataIDMap);
107-
});
108-
109-
this.currentTool = tools.current;
110-
},
111-
},
64+
export const useToolStore = defineStore('tool', () => {
65+
const currentTool = ref(Tools.WindowLevel);
66+
const toolBeforeTemporaryCrosshairs = ref<Tools>(currentTool.value);
67+
68+
function setCurrentTool(tool: Tools) {
69+
if (!setupTool(tool)) {
70+
return;
71+
}
72+
teardownTool(currentTool.value);
73+
currentTool.value = tool;
74+
}
75+
76+
function activateTemporaryCrosshairs() {
77+
toolBeforeTemporaryCrosshairs.value = currentTool.value;
78+
setCurrentTool(Tools.Crosshairs);
79+
useCrosshairsToolStore().startDragging();
80+
}
81+
82+
function deactivateTemporaryCrosshairs() {
83+
setCurrentTool(toolBeforeTemporaryCrosshairs.value);
84+
}
85+
86+
function serialize(state: StateFile) {
87+
const { tools } = state.manifest;
88+
89+
Object.values(ToolStoreMap)
90+
.map((useStore) => useStore?.())
91+
.filter((store): store is IToolStore => !!store)
92+
.forEach((store) => {
93+
store.serialize?.(state);
94+
});
95+
96+
tools.current = currentTool.value;
97+
}
98+
99+
function deserialize(
100+
manifest: Manifest,
101+
segmentGroupIDMap: Record<string, string>,
102+
dataIDMap: Record<string, string>
103+
) {
104+
const { tools } = manifest;
105+
106+
usePaintToolStore().deserialize(manifest, segmentGroupIDMap);
107+
108+
Object.values(ToolStoreMap)
109+
// paint store uses segmentGroupIDMap
110+
.filter((useStore) => useStore !== usePaintToolStore)
111+
.map((useStore) => useStore?.())
112+
.filter((store): store is IToolStore => !!store)
113+
.forEach((store) => {
114+
store.deserialize?.(manifest, dataIDMap);
115+
});
116+
117+
currentTool.value = tools.current;
118+
}
119+
120+
return {
121+
currentTool,
122+
setCurrentTool,
123+
serialize,
124+
deserialize,
125+
activateTemporaryCrosshairs,
126+
deactivateTemporaryCrosshairs,
127+
};
112128
});

src/vtk/CrosshairsWidget/behavior.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ function clampPointToBounds(bounds: Bounds, point: vec3) {
1010

1111
export default function widgetBehavior(publicAPI: any, model: any) {
1212
model.classHierarchy.push('vtkCrosshairsWidgetProp');
13-
let isDragging = false;
1413

1514
// support setting per-view widget manipulators
1615
macro.setGet(publicAPI, model, ['manipulator']);
@@ -32,8 +31,7 @@ export default function widgetBehavior(publicAPI: any, model: any) {
3231
return macro.VOID;
3332
}
3433

35-
model.widgetState.setPlaced(true);
36-
isDragging = true;
34+
model.widgetState.setDragging(true);
3735
model._interactor.requestAnimation(publicAPI);
3836
model._apiSpecificRenderWindow.setCursor('crosshairs');
3937
publicAPI.invokeStartInteractionEvent();
@@ -52,7 +50,7 @@ export default function widgetBehavior(publicAPI: any, model: any) {
5250
// is actually being rendered.
5351

5452
if (
55-
(!model.widgetState.getPlaced() || isDragging) &&
53+
model.widgetState.getDragging() &&
5654
model.pickable &&
5755
model.manipulator &&
5856
!ignoreKey(callData)
@@ -90,12 +88,12 @@ export default function widgetBehavior(publicAPI: any, model: any) {
9088
// --------------------------------------------------------------------------
9189

9290
publicAPI.handleLeftButtonRelease = () => {
93-
if (isDragging && model.pickable) {
91+
if (model.widgetState.getDragging() && model.pickable) {
9492
model._interactor.cancelAnimation(publicAPI);
9593
model._apiSpecificRenderWindow.setCursor('default');
9694
publicAPI.invokeEndInteractionEvent();
9795
}
98-
isDragging = false;
96+
model.widgetState.setDragging(false);
9997
};
10098

10199
// --------------------------------------------------------------------------

src/vtk/CrosshairsWidget/state.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export interface CrosshairsHandleWidgetState extends vtkWidgetState {
1515
}
1616

1717
export interface CrosshairsWidgetState extends vtkWidgetState {
18-
setPlaced(placed: boolean): boolean;
19-
getPlaced(): boolean;
18+
setDragging(dragging: boolean): boolean;
19+
getDragging(): boolean;
2020
setIndexToWorld(indexToWorld: mat4): boolean;
2121
getIndexToWorld(): mat4;
2222
setWorldToIndex(worldToIndex: mat4): boolean;
@@ -28,7 +28,7 @@ export default function generateState() {
2828
return vtkStateBuilder
2929
.createBuilder()
3030
.addField({
31-
name: 'placed',
31+
name: 'dragging',
3232
initialValue: false,
3333
})
3434
.addField({

0 commit comments

Comments
 (0)