From f632c65c8bbc2fe51bdcd22d080d119784705b4d Mon Sep 17 00:00:00 2001 From: Laurent Chauvin Date: Fri, 22 Sep 2023 11:38:10 -0400 Subject: [PATCH 01/69] fix(build): Use import type instead of import --- src/components/tools/BoundingRectangle.vue | 2 +- src/components/tools/polygon/PolygonWidget2D.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/tools/BoundingRectangle.vue b/src/components/tools/BoundingRectangle.vue index 65d786a5e..7f93eb950 100644 --- a/src/components/tools/BoundingRectangle.vue +++ b/src/components/tools/BoundingRectangle.vue @@ -6,7 +6,7 @@ import { worldToSVG } from '@/src/utils/vtk-helpers'; import { nonNullable } from '@/src/utils/index'; import vtkLPSView2DProxy from '@/src/vtk/LPSView2DProxy'; import vtkBoundingBox from '@kitware/vtk.js/Common/DataModel/BoundingBox'; -import { Bounds, Vector3 } from '@kitware/vtk.js/types'; +import type { Bounds, Vector3 } from '@kitware/vtk.js/types'; import { onVTKEvent } from '@/src/composables/onVTKEvent'; const props = defineProps<{ diff --git a/src/components/tools/polygon/PolygonWidget2D.vue b/src/components/tools/polygon/PolygonWidget2D.vue index 73a2df77f..d915b8123 100644 --- a/src/components/tools/polygon/PolygonWidget2D.vue +++ b/src/components/tools/polygon/PolygonWidget2D.vue @@ -25,7 +25,7 @@ import vtkWidgetFactory, { vtkPolygonViewWidget as WidgetView, } from '@/src/vtk/PolygonWidget'; import { Maybe } from '@/src/types'; -import { Vector3 } from '@kitware/vtk.js/types'; +import type { Vector3 } from '@kitware/vtk.js/types'; import { useViewStore } from '@/src/store/views'; import { onViewProxyMounted, From 8320432f283c6c4f6733dfbe23323dadb2f22e4b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 22 Sep 2023 13:53:14 -0400 Subject: [PATCH 02/69] fix(polygon): fix shift clicking on polygon locks camera pan Fixes #429 --- src/vtk/PolygonWidget/behavior.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vtk/PolygonWidget/behavior.ts b/src/vtk/PolygonWidget/behavior.ts index 2e9c2d62f..e68887968 100644 --- a/src/vtk/PolygonWidget/behavior.ts +++ b/src/vtk/PolygonWidget/behavior.ts @@ -282,6 +282,12 @@ export default function widgetBehavior(publicAPI: any, model: any) { return macro.EVENT_ABORT; } + // If not placing, (and not dragging) don't consume event + // so camera control widgets can react. + if (!model.widgetState.getPlacing()) { + return macro.VOID; + } + // Double click? Then finish. const currentDisplayPos = [ event.position.x, From fd0f775e6b72ac9ec49df1ab70df6cd2f7efaa6b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Sun, 24 Sep 2023 15:14:39 -0400 Subject: [PATCH 03/69] feat(MeasurementsToolList): add selected annotation count --- src/components/MeasurementsToolList.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/MeasurementsToolList.vue b/src/components/MeasurementsToolList.vue index 6e5221186..5e7d28cca 100644 --- a/src/components/MeasurementsToolList.vue +++ b/src/components/MeasurementsToolList.vue @@ -103,9 +103,8 @@ function toggleGlobalHidden() { diff --git a/src/components/tools/ruler/RulerSVG2D.vue b/src/components/tools/ruler/RulerSVG2D.vue index 15033b9fa..2a0b2a41d 100644 --- a/src/components/tools/ruler/RulerSVG2D.vue +++ b/src/components/tools/ruler/RulerSVG2D.vue @@ -7,7 +7,7 @@ :x2="second.x" :y2="second.y" :stroke="color" - stroke-width="1" + :stroke-width="strokeWidth" /> @@ -24,7 +24,7 @@ :cx="second.x" :cy="second.y" :stroke="color" - stroke-width="1" + :stroke-width="strokeWidth" fill="transparent" :r="10 / devicePixelRatio" class="handle" @@ -76,6 +76,7 @@ export default defineComponent({ point1: Array as PropType>, point2: Array as PropType>, color: String, + strokeWidth: Number, length: Number, viewId: { type: String, diff --git a/src/components/tools/ruler/RulerWidget2D.vue b/src/components/tools/ruler/RulerWidget2D.vue index 3b46e64bf..8ba83f4ae 100644 --- a/src/components/tools/ruler/RulerWidget2D.vue +++ b/src/components/tools/ruler/RulerWidget2D.vue @@ -199,6 +199,7 @@ export default defineComponent({ :point1="firstPoint" :point2="secondPoint" :color="ruler.color" + :strokeWidth="ruler.strokeWidth" :length="length" /> diff --git a/src/config.ts b/src/config.ts index acca280b9..e05d5ad69 100644 --- a/src/config.ts +++ b/src/config.ts @@ -183,6 +183,8 @@ export const TOOL_COLORS = [ '#fea53b', ]; +export const STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT = 1; + export const RULER_LABEL_DEFAULTS = { red: { color: 'red' }, green: { color: '#00ff00' }, diff --git a/src/io/import/processors/handleConfig.ts b/src/io/import/processors/handleConfig.ts index 756bb6d7a..09217ee3a 100644 --- a/src/io/import/processors/handleConfig.ts +++ b/src/io/import/processors/handleConfig.ts @@ -28,6 +28,7 @@ const color = z.string(); const label = z.object({ color, + strokeWidth: z.number().optional(), }); const rulerLabel = label; diff --git a/src/io/state-file/schema.ts b/src/io/state-file/schema.ts index 05b540529..a2cf2bb1f 100644 --- a/src/io/state-file/schema.ts +++ b/src/io/state-file/schema.ts @@ -257,6 +257,7 @@ const annotationTool = z.object({ id: z.string() as unknown as z.ZodType, name: z.string(), color: z.string(), + strokeWidth: z.number(), label: z.string().optional(), labelName: z.string().optional(), }) satisfies z.ZodType; diff --git a/src/store/__tests__/rulers.spec.ts b/src/store/__tests__/rulers.spec.ts index 9625116d1..447a23e27 100644 --- a/src/store/__tests__/rulers.spec.ts +++ b/src/store/__tests__/rulers.spec.ts @@ -12,7 +12,7 @@ chai.use(chaiSubset); function createRuler(): RequiredWithPartial< Ruler, - 'id' | 'color' | 'label' | 'labelName' | 'hidden' + 'id' | 'color' | 'strokeWidth' | 'label' | 'labelName' | 'hidden' > { return { firstPoint: [1, 1, 1], diff --git a/src/store/tools/polygons.ts b/src/store/tools/polygons.ts index 9b3fab13c..9abbe1b20 100644 --- a/src/store/tools/polygons.ts +++ b/src/store/tools/polygons.ts @@ -4,7 +4,7 @@ import { POLYGON_LABEL_DEFAULTS } from '@/src/config'; import { Manifest, StateFile } from '@/src/io/state-file/schema'; import { ToolID } from '@/src/types/annotation-tool'; -import { useAnnotationTool } from './useAnnotationTool'; +import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; const toolDefaults = () => ({ points: [] as Array, @@ -12,10 +12,6 @@ const toolDefaults = () => ({ name: 'Polygon', }); -const newLabelDefault = { - color: '#ffffff', -}; - export const usePolygonStore = defineStore('polygon', () => { type _This = ReturnType; @@ -26,7 +22,7 @@ export const usePolygonStore = defineStore('polygon', () => { } = useAnnotationTool({ toolDefaults, initialLabels: POLYGON_LABEL_DEFAULTS, - newLabelDefault, + newLabelDefault: commonLabelDefaults, }); // --- serialization --- // diff --git a/src/store/tools/rectangles.ts b/src/store/tools/rectangles.ts index b41e5aed5..0bd387b53 100644 --- a/src/store/tools/rectangles.ts +++ b/src/store/tools/rectangles.ts @@ -4,7 +4,7 @@ import { Manifest, StateFile } from '@/src/io/state-file/schema'; import { RECTANGLE_LABEL_DEFAULTS } from '@/src/config'; import { ToolID } from '@/src/types/annotation-tool'; -import { useAnnotationTool } from './useAnnotationTool'; +import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; const rectangleDefaults = () => ({ firstPoint: [0, 0, 0] as Vector3, @@ -15,7 +15,7 @@ const rectangleDefaults = () => ({ }); const newLabelDefault = { - color: '#ffffff', + ...commonLabelDefaults, fillColor: 'transparent', }; diff --git a/src/store/tools/rulers.ts b/src/store/tools/rulers.ts index d817481d7..56efd73a5 100644 --- a/src/store/tools/rulers.ts +++ b/src/store/tools/rulers.ts @@ -6,7 +6,7 @@ import { distance2BetweenPoints } from '@kitware/vtk.js/Common/Core/Math'; import { RULER_LABEL_DEFAULTS } from '@/src/config'; import { Manifest, StateFile } from '@/src/io/state-file/schema'; -import { useAnnotationTool } from './useAnnotationTool'; +import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; const rulerDefaults = () => ({ firstPoint: [0, 0, 0] as Vector3, @@ -15,17 +15,13 @@ const rulerDefaults = () => ({ name: 'Ruler', }); -const newLabelDefault = { - color: '#ffffff', -}; - export const useRulerStore = defineStore('ruler', () => { type _This = ReturnType; const annotationTool = useAnnotationTool({ toolDefaults: rulerDefaults, initialLabels: RULER_LABEL_DEFAULTS, - newLabelDefault, + newLabelDefault: commonLabelDefaults, }); // prefix some props with ruler diff --git a/src/store/tools/useAnnotationTool.ts b/src/store/tools/useAnnotationTool.ts index b62afd7c7..cddb53798 100644 --- a/src/store/tools/useAnnotationTool.ts +++ b/src/store/tools/useAnnotationTool.ts @@ -2,7 +2,10 @@ import { Ref, UnwrapNestedRefs, computed, ref, watch } from 'vue'; import { Store, StoreActions, StoreGetters, StoreState } from 'pinia'; import { Maybe, PartialWithRequired } from '@/src/types'; -import { TOOL_COLORS } from '@/src/config'; +import { + STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT, + TOOL_COLORS, +} from '@/src/config'; import { removeFromArray } from '@/src/utils'; import { useCurrentImage } from '@/src/composables/useCurrentImage'; import { frameOfReferenceToImageSliceAndAxis } from '@/src/utils/frameOfReference'; @@ -15,6 +18,11 @@ import { useIdStore } from '@/src/store/id'; import useViewSliceStore from '../view-configs/slicing'; import { useLabels, Labels, Label } from './useLabels'; +export const commonLabelDefaults = Object.freeze({ + color: TOOL_COLORS[0], + strokeWidth: STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT, +}); + const makeAnnotationToolDefaults = () => ({ frameOfReference: { planeOrigin: [0, 0, 0], @@ -24,6 +32,7 @@ const makeAnnotationToolDefaults = () => ({ imageID: '', placing: false, color: TOOL_COLORS[0], + strokeWidth: STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT, name: 'baseAnnotationTool', }); diff --git a/src/types/annotation-tool.ts b/src/types/annotation-tool.ts index db15ca162..c905238cc 100644 --- a/src/types/annotation-tool.ts +++ b/src/types/annotation-tool.ts @@ -25,6 +25,8 @@ export type AnnotationTool = { labelName?: string; color: string; + strokeWidth: number; + name: string; hidden?: boolean; From 115bb94fa0bae956c7ee9d09f4a0d5580c0cfd55 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Mon, 25 Sep 2023 12:30:38 -0400 Subject: [PATCH 11/69] refactor(useAnnotationTool): simplify label prop typing --- src/store/tools/polygons.ts | 3 +-- src/store/tools/rectangles.ts | 3 +-- src/store/tools/rulers.ts | 3 +-- src/store/tools/useAnnotationTool.ts | 14 ++++++++------ src/store/tools/useLabels.ts | 17 +++++++++++------ 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/store/tools/polygons.ts b/src/store/tools/polygons.ts index 9abbe1b20..320618baa 100644 --- a/src/store/tools/polygons.ts +++ b/src/store/tools/polygons.ts @@ -4,7 +4,7 @@ import { POLYGON_LABEL_DEFAULTS } from '@/src/config'; import { Manifest, StateFile } from '@/src/io/state-file/schema'; import { ToolID } from '@/src/types/annotation-tool'; -import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; +import { useAnnotationTool } from './useAnnotationTool'; const toolDefaults = () => ({ points: [] as Array, @@ -22,7 +22,6 @@ export const usePolygonStore = defineStore('polygon', () => { } = useAnnotationTool({ toolDefaults, initialLabels: POLYGON_LABEL_DEFAULTS, - newLabelDefault: commonLabelDefaults, }); // --- serialization --- // diff --git a/src/store/tools/rectangles.ts b/src/store/tools/rectangles.ts index 0bd387b53..f043dbf8a 100644 --- a/src/store/tools/rectangles.ts +++ b/src/store/tools/rectangles.ts @@ -4,7 +4,7 @@ import { Manifest, StateFile } from '@/src/io/state-file/schema'; import { RECTANGLE_LABEL_DEFAULTS } from '@/src/config'; import { ToolID } from '@/src/types/annotation-tool'; -import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; +import { useAnnotationTool } from './useAnnotationTool'; const rectangleDefaults = () => ({ firstPoint: [0, 0, 0] as Vector3, @@ -15,7 +15,6 @@ const rectangleDefaults = () => ({ }); const newLabelDefault = { - ...commonLabelDefaults, fillColor: 'transparent', }; diff --git a/src/store/tools/rulers.ts b/src/store/tools/rulers.ts index 56efd73a5..7da2b78cd 100644 --- a/src/store/tools/rulers.ts +++ b/src/store/tools/rulers.ts @@ -6,7 +6,7 @@ import { distance2BetweenPoints } from '@kitware/vtk.js/Common/Core/Math'; import { RULER_LABEL_DEFAULTS } from '@/src/config'; import { Manifest, StateFile } from '@/src/io/state-file/schema'; -import { useAnnotationTool, commonLabelDefaults } from './useAnnotationTool'; +import { useAnnotationTool } from './useAnnotationTool'; const rulerDefaults = () => ({ firstPoint: [0, 0, 0] as Vector3, @@ -21,7 +21,6 @@ export const useRulerStore = defineStore('ruler', () => { const annotationTool = useAnnotationTool({ toolDefaults: rulerDefaults, initialLabels: RULER_LABEL_DEFAULTS, - newLabelDefault: commonLabelDefaults, }); // prefix some props with ruler diff --git a/src/store/tools/useAnnotationTool.ts b/src/store/tools/useAnnotationTool.ts index cddb53798..84b66eb5f 100644 --- a/src/store/tools/useAnnotationTool.ts +++ b/src/store/tools/useAnnotationTool.ts @@ -16,11 +16,10 @@ import { AnnotationTool, ToolID } from '@/src/types/annotation-tool'; import { findImageID, getDataID } from '@/src/store/datasets'; import { useIdStore } from '@/src/store/id'; import useViewSliceStore from '../view-configs/slicing'; -import { useLabels, Labels, Label } from './useLabels'; +import { useLabels, Labels } from './useLabels'; -export const commonLabelDefaults = Object.freeze({ - color: TOOL_COLORS[0], - strokeWidth: STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT, +const annotationToolLabelDefault = Object.freeze({ + strokeWidth: STROKE_WIDTH_ANNOTATION_TOOL_DEFAULT as number, }); const makeAnnotationToolDefaults = () => ({ @@ -47,7 +46,7 @@ export const useAnnotationTool = < }: { toolDefaults: MakeToolDefaults; initialLabels: Labels; - newLabelDefault: Label; + newLabelDefault?: LabelProps; }) => { type ToolDefaults = ReturnType; type Tool = ToolDefaults & AnnotationTool; @@ -67,7 +66,10 @@ export const useAnnotationTool = < tools.value.filter((tool) => !tool.placing) ); - const labels = useLabels(newLabelDefault); + const labels = useLabels({ + ...annotationToolLabelDefault, + ...newLabelDefault, + }); labels.mergeLabels(initialLabels, false); function makePropsFromLabel(label: string | undefined) { diff --git a/src/store/tools/useLabels.ts b/src/store/tools/useLabels.ts index 000b4f46a..955aa6aec 100644 --- a/src/store/tools/useLabels.ts +++ b/src/store/tools/useLabels.ts @@ -1,21 +1,26 @@ import { Maybe } from '@/src/types'; import { ref } from 'vue'; import { StoreActions, StoreState } from 'pinia'; +import { TOOL_COLORS } from '@/src/config'; import { useIdStore } from '../id'; -export type Label = Partial; +const labelDefault = Object.freeze({ + labelName: 'New Label' as string, + color: TOOL_COLORS[0] as string, +}); + +export type Label = Partial; export type Labels = Record>; type LabelID = string; -const labelDefault = { labelName: 'New Label' }; - // param newLabelDefault should contain all label controlled props // of the tool so placing tool does hold any last active label props. -export const useLabels = (newLabelDefault: Label) => { +export const useLabels = (newLabelDefault: Props) => { type ToolLabel = Label; + type ToolLabels = Labels; - const labels = ref>({}); + const labels = ref({}); const activeLabel = ref(); const setActiveLabel = (id: string) => { @@ -96,7 +101,7 @@ export const useLabels = (newLabelDefault: Label) => { * param clearDefault: if true, clear initial labels, do nothing if initial labels already cleared */ const mergeLabels = ( - newLabels: Maybe>, + newLabels: Maybe, clearDefault: boolean = true ) => { Object.entries(newLabels ?? {}).forEach(([labelName, props]) => From acd000b861666deb64736074579b67d74c69a15c Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Tue, 26 Sep 2023 12:02:42 -0400 Subject: [PATCH 12/69] fix(LabelEditor): react to label props changing while editing --- src/components/LabelEditor.vue | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/LabelEditor.vue b/src/components/LabelEditor.vue index 0f94b388f..7cbc3f805 100644 --- a/src/components/LabelEditor.vue +++ b/src/components/LabelEditor.vue @@ -1,5 +1,5 @@