Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3de55e1
feat(LabelControls): create Add Label button
PaulHax Jun 13, 2023
e88bc77
chore(package): update typescript to 5.1.3
PaulHax Jun 14, 2023
ec680c2
feat(useLabels): key labels with IDs
PaulHax Jun 14, 2023
57c5947
feat(useLabels): label config file replaces all existing labels
PaulHax Jun 15, 2023
2d618d3
feat(LabelEditor): update label name and color
PaulHax Jun 15, 2023
c781f90
fix(useLabels): create new labels with defaults
PaulHax Jun 15, 2023
e7fe510
feat(labels): edit labels and update existing tools
PaulHax Jun 16, 2023
d0ef68f
feat(labels): add delete label button on LabelEditor
PaulHax Jun 16, 2023
629e84e
fix: useLabels uses useIdStore and move index.html to root
PaulHax Jun 16, 2023
5a549d2
fix(useLabels): fallback to existing label if deleted active label
PaulHax Jun 16, 2023
9a9d836
feat(LabelEditor): 2 column layout
PaulHax Jun 26, 2023
e736060
fix(LabelEditor): support named colors for color picker
PaulHax Jun 26, 2023
8c3c36b
feat(useAnnotationTool): serialize and deserialize labels
PaulHax Jun 26, 2023
39f5946
fix(useLabels): clear default labels if labels configured
PaulHax Jun 27, 2023
2930859
fix(LabelEditor): stop propagation of key events
PaulHax Jun 27, 2023
cf3473c
fix(useLabels): merge config and session labels by name
PaulHax Jun 27, 2023
7820a33
fix(useAnnotationTool): new set default label props for all props
PaulHax Jun 27, 2023
4ba9609
feat(config): more readable colors for default labels
PaulHax Jun 27, 2023
ab4e00b
refactor(useAnnotationTool): simplify typing of label updates
PaulHax Jun 27, 2023
7a81f10
refactor(useAnnotationTool): prefer Maybe utility type
PaulHax Jun 28, 2023
3201816
refactor(schema): prefer satisfies over type definition
PaulHax Jun 28, 2023
2b0117d
refactor(LabelEditor): remove unused attributes
PaulHax Jun 28, 2023
04ea90a
feat(LabelEditor): add close button, more whitespace
PaulHax Jun 30, 2023
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
534 changes: 15 additions & 519 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@
"kw-doc": "3.1.2",
"lint-staged": "10.5.4",
"patch-package": "^6.4.7",
"prettier": "2.6.2",
"prettier": "2.8.8",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.62.0",
"sinon": "14.0.0",
"sinon-chai": "3.7.0",
"typescript": "~4.6.4",
"typescript": "~5.1.3",
"vite": "^4.3.9",
"vite-plugin-html": "^3.2.0",
"vite-plugin-static-copy": "^0.16.0",
Expand Down
87 changes: 68 additions & 19 deletions src/components/LabelControls.vue
Original file line number Diff line number Diff line change
@@ -1,59 +1,106 @@
<script setup lang="ts">
import { computed } from 'vue';
import { Labels, SetActiveLabel, useLabels } from '@/src/store/tools/useLabels';
import { computed, ref, watchEffect } from 'vue';
import { useDisplay } from 'vuetify';
import { LabelsStore } from '@/src/store/tools/useLabels';
import { AnnotationTool } from '@/src/types/annotationTool';
import { Maybe } from '@/src/types';
import LabelEditor from './LabelEditor.vue';

const props = defineProps<{
labels: Labels<AnnotationTool>;
activeLabel: ReturnType<typeof useLabels>['activeLabel']['value'];
setActiveLabel: SetActiveLabel;
labelsStore: LabelsStore<AnnotationTool>;
}>();

const labels = computed(() => Object.entries(props.labels));
const labels = computed(() => Object.entries(props.labelsStore.labels));
// item groups need an index, not a value
const activeLabelIndex = computed(() => {
return labels.value.findIndex(([name]) => name === props.activeLabel);
return labels.value.findIndex(
([name]) => name === props.labelsStore.activeLabel
);
});

const editingLabel =
ref<Maybe<keyof typeof props.labelsStore.labels>>(undefined);

const createLabel = () => {
editingLabel.value = props.labelsStore.addLabel();
};

const editDialog = ref(false);
watchEffect(() => {
editDialog.value = !!editingLabel.value;
});

const display = useDisplay();
const mobile = computed(() => display.mobile.value);
</script>

<template>
<v-card>
<v-card-subtitle>Labels</v-card-subtitle>
<v-container>
<v-item-group
v-if="labels.length"
:model-value="activeLabelIndex"
selected-class="card-active"
mandatory
>
<v-row dense>
<v-col cols="6" v-for="[name, { color }] in labels" :key="name">
<v-col
cols="6"
v-for="[id, { labelName, color }] in labels"
:key="id"
>
<v-item v-slot="{ selectedClass, toggle }">
<v-chip
variant="tonal"
:class="['w-100', selectedClass]"
:class="['w-100 d-flex', selectedClass]"
@click="
() => {
toggle();
setActiveLabel(name);
labelsStore.setActiveLabel(id);
}
"
>
<div
class="color-dot mr-3"
:style="{ backgroundColor: color }"
<!-- dot container keeps overflowing name from squishing dot width -->
<div class="dot-container mr-3">
<div class="color-dot" :style="{ background: color }" />
</div>
<span class="overflow-hidden">{{ labelName }}</span>
<v-btn
icon="mdi-pencil"
density="compact"
class="ml-auto"
variant="plain"
@click.stop="
() => {
editingLabel = id;
editDialog = true;
}
"
/>
<span>{{ name }}</span>
</v-chip>
</v-item>
</v-col>

<!-- Add Label button -->
<v-col cols="6">
<v-chip variant="outlined" class="w-100" @click="createLabel">
<v-icon class="mr-2">mdi-plus</v-icon>
Add Label
</v-chip>
</v-col>
</v-row>
</v-item-group>
<div v-else class="text-caption text-center pa-2">
No labels configured
</div>
</v-container>
</v-card>

<v-dialog v-model="editDialog" :width="mobile ? '100%' : '50%'">
<LabelEditor
@close="editingLabel = undefined"
v-if="editingLabel"
:label="editingLabel"
:labelsStore="labelsStore"
/>
</v-dialog>
</template>

<style scoped>
Expand All @@ -65,7 +112,9 @@ const activeLabelIndex = computed(() => {
.color-dot {
width: 18px;
height: 18px;
background: yellow;
border-radius: 16px;
}
.dot-container {
width: 18px;
}
</style>
76 changes: 76 additions & 0 deletions src/components/LabelEditor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { LabelsStore } from '@/src/store/tools/useLabels';
import { AnnotationTool } from '../types/annotationTool';
import { standardizeColor } from '../utils';

const props = defineProps<{
label: string;
labelsStore: LabelsStore<AnnotationTool>;
}>();

const label = computed(() => props.labelsStore.labels[props.label]);

const labelName = ref(label.value.labelName);
watch(labelName, (name) => {
props.labelsStore.updateLabel(props.label, { labelName: name });
});

const colorLocal = ref(standardizeColor(label.value.color));
watch(colorLocal, (color) => {
props.labelsStore.updateLabel(props.label, { color });
});

const emit = defineEmits(['close']);
const deleteLabel = () => {
emit('close');
props.labelsStore.deleteLabel(props.label);
};
</script>

<template>
<v-card>
<v-card-title class="d-flex flex-row align-center">
Edit Label
<v-spacer />
<v-btn
variant="text"
density="compact"
icon="mdi-close"
@click="$emit('close')"
/>
</v-card-title>

<v-card-item>
<div class="d-flex flex-row">
<div class="flex-grow-1 d-flex flex-column mr-4">
<v-text-field
v-model="labelName"
@keydown.stop
label="Name"
class="flex-grow-0"
/>
<v-btn
prepend-icon="mdi-delete"
@click="deleteLabel"
class="delete-button"
>
Delete Label
</v-btn>
</div>
<v-color-picker v-model="colorLocal" label="Color" />
</div>
</v-card-item>

<v-card-actions>
<v-btn @click="$emit('close')">Close</v-btn>
</v-card-actions>
</v-card>
</template>

<style scoped>
.delete-button {
max-width: 200px;
align-self: center;
}
</style>
23 changes: 6 additions & 17 deletions src/components/MeasurementsRectangleList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,11 @@ export default defineComponent({
const rectStore = useRectangleStore();
const { currentImageID } = useCurrentImage();

const rects = computed(() => {
const imageID = currentImageID.value;
return rectStore.tools
.filter((rect) => rect.imageID === imageID && !rect.placing)
.map((rect) => ({
id: rect.id,
name: rect.name,
color: rect.color,
label: rect.label,
}));
});
const rects = computed(() =>
rectStore.tools.filter(
(rect) => !rect.placing && rect.imageID === currentImageID.value
)
);

function remove(id: RectangleID) {
rectStore.removeTool(id);
Expand All @@ -29,15 +23,10 @@ export default defineComponent({
rectStore.jumpToTool(id);
}

function updateColor(id: RectangleID, color: string) {
rectStore.updateTool(id, { color });
}

return {
rects,
remove,
jumpTo,
updateColor,
};
},
});
Expand All @@ -50,7 +39,7 @@ export default defineComponent({
<div class="color-dot mr-3" :style="{ backgroundColor: rect.color }" />
</template>
<v-list-item-title v-bind="$attrs">
{{ rect.label }}
{{ rect.labelName }}
</v-list-item-title>

<v-list-item-subtitle>
Expand Down
12 changes: 2 additions & 10 deletions src/components/MeasurementsRulerList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@ export default defineComponent({
return rulerStore.rulers
.filter((ruler) => ruler.imageID === imageID && !ruler.placing)
.map((ruler) => ({
id: ruler.id,
name: ruler.name,
...ruler,
length: lengthByID[ruler.id],
color: ruler.color,
label: ruler.label,
}));
});

Expand All @@ -30,15 +27,10 @@ export default defineComponent({
rulerStore.jumpToRuler(id);
}

function updateColor(id: string, color: string) {
rulerStore.updateRuler(id, { color });
}

return {
rulers,
remove,
jumpTo,
updateColor,
};
},
});
Expand All @@ -51,7 +43,7 @@ export default defineComponent({
<div class="color-dot mr-3" :style="{ backgroundColor: ruler.color }" />
</template>
<v-list-item-title v-bind="$attrs">
{{ ruler.label }}
{{ ruler.labelName }}
</v-list-item-title>
<v-list-item-subtitle>
<v-row>
Expand Down
6 changes: 1 addition & 5 deletions src/components/RectangleControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,5 @@ const activeToolStore = useRectangleStore();
</script>

<template>
<label-controls
:labels="activeToolStore.labels"
:set-active-label="activeToolStore.setActiveLabel"
:active-label="activeToolStore.activeLabel"
/>
<LabelControls :labels-store="activeToolStore" />
</template>
6 changes: 1 addition & 5 deletions src/components/RulerControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,5 @@ const activeToolStore = useRulerStore();
</script>

<template>
<label-controls
:labels="activeToolStore.labels"
:set-active-label="activeToolStore.setActiveLabel"
:active-label="activeToolStore.activeLabel"
/>
<LabelControls :labels-store="activeToolStore" />
</template>
6 changes: 3 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ export const TOOL_COLORS = [
export const RULER_LABEL_DEFAULTS = {
red: { color: 'red' },
green: { color: '#00ff00' },
gray: { color: '#888888' },
white: { color: '#ffffff' },
};

export const RECTANGLE_LABEL_DEFAULTS = {
lesion: { color: 'red', fillColor: 'transparent' },
innocuous: { color: '#00ff00', fillColor: '#00ff0020' },
artifact: { color: '#888888', fillColor: '#ffbf0040' },
innocuous: { color: '#00ff00', fillColor: '#00ff0020' },
lesion: { color: 'red', fillColor: 'transparent' },
};

export const DECREMENT_LABEL_KEY = 'q';
Expand Down
4 changes: 2 additions & 2 deletions src/io/import/processors/handleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ const applyConfig = (manifest: z.infer<typeof Config>) => {
if (toolLabels === undefined) return manifest.labels;
return toolLabels;
};
useRulerStore().setLabels(labelsIfUndefined(manifest.rulerLabels));
useRectangleStore().setLabels(labelsIfUndefined(manifest.rectangleLabels));
useRulerStore().mergeLabels(labelsIfUndefined(manifest.rulerLabels));
useRectangleStore().mergeLabels(labelsIfUndefined(manifest.rectangleLabels));
};

/**
Expand Down
4 changes: 1 addition & 3 deletions src/io/state-file/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { retypeFile } from '../io';
import { ARCHIVE_FILE_TYPES } from '../mimeTypes';

export const MANIFEST = 'manifest.json';
export const MANIFEST_VERSION = '1.1.0';
export const MANIFEST_VERSION = '2.1.0';

export async function serialize() {
const datasetStore = useDatasetStore();
Expand All @@ -28,8 +28,6 @@ export async function serialize() {
remoteFiles: {},
labelMaps: [],
tools: {
rulers: [],
rectangles: [],
crosshairs: {
position: [0, 0, 0],
},
Expand Down
Loading