Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8abe79b
Fixed lawnmower center section first bug
yannik131 Jun 17, 2026
319d081
Added playPause widget
yannik131 Jun 17, 2026
f2ddc1f
Wiring of lawnmower states
yannik131 Jun 18, 2026
e179513
play/pause for lawnmower works
yannik131 Jun 18, 2026
5f732cb
Renamed play/pause states
yannik131 Jun 18, 2026
3bae58f
More concise naming
yannik131 Jun 18, 2026
7e8a51d
Fixed some naming issues
yannik131 Jun 18, 2026
e2a6fdc
Fixed renaming issues
yannik131 Jun 18, 2026
f67e736
Unified annotation mode buttons
yannik131 Jun 18, 2026
c9425ba
Wait for next tick to resume lawnmower
yannik131 Jun 18, 2026
7808884
Prefer @ over relative paths
yannik131 Jun 18, 2026
cfd6e25
Fixed wrong lawnmower position after resume
yannik131 Jun 18, 2026
c45cec0
Function name reflects signal emitted
yannik131 Jun 19, 2026
d85ca64
Restore last lawnmower section, remove unused code
yannik131 Jun 19, 2026
755071f
Removed unused code
yannik131 Jun 19, 2026
5e3d249
Refactored volare into composable
yannik131 Jun 19, 2026
1f6e235
Moved more code to volare component
yannik131 Jun 19, 2026
f866ab5
Implemented load/save for volare
yannik131 Jun 19, 2026
b27e065
TODO for removing watchers maybe
yannik131 Jun 19, 2026
befcb4b
Fixing volare pause/resume bugs
yannik131 Jun 20, 2026
dfc92ad
Use ID instead of index for resuming volare
yannik131 Jun 20, 2026
4dba4c3
Fixed linter errors
yannik131 Jun 20, 2026
628b63f
Merge branch 'master' into 547-pause-resume-for-lawnmower
yannik131 Jul 17, 2026
64f02e5
trying to pause lawnmower with viewport change
yannik131 Jul 17, 2026
95969d6
Use only a single mode state for paused
yannik131 Jul 22, 2026
4c45aa8
lawnmower pause/resume
yannik131 Jul 23, 2026
bf5985e
Pause/resume for lawnmower is now working
yannik131 Jul 23, 2026
6a4dac9
Muted text for lawnmower, playPause works for lawnmower
yannik131 Jul 27, 2026
1a001dc
Volare pause stated muted text
yannik131 Jul 27, 2026
424ce7d
linter fixes, don't overwrite saved state
yannik131 Jul 28, 2026
a12106b
Undid auto formatting
yannik131 Jul 28, 2026
1d65214
kebab-case for vue signals
yannik131 Aug 23, 2026
a95884a
fixed volare mode check
yannik131 Aug 23, 2026
f4cad92
Removed todo comment
yannik131 Aug 23, 2026
3bba13c
fixed timestamp typo
yannik131 Aug 23, 2026
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
158 changes: 70 additions & 88 deletions resources/assets/js/annotations/annotatorContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {CrossOriginTiffError} from './stores/images.js';
import {debounce} from '@/core/utils.js';
import {handleErrorResponse} from '@/core/messages/store.js';
import {urlParams as UrlParams} from '@/core/utils.js';
import {LawnmowerSaveState} from './components/annotationCanvas/lawnmower.vue';
import {useVolareMode} from './components/annotationCanvas/volare.js';

const asyncAnnotationCanvas = defineAsyncComponent({
loader: function () {
Expand Down Expand Up @@ -73,7 +75,6 @@ export default {
mapResolution: undefined,
selectedLabel: null,
annotationMode: 'default',
focussedAnnotationIndex: null,
// For lawnmower and sampling modes: When switching images, this determines
// if the first (0) or the last (Infinity) image section/sampling location
// should be shown.
Expand All @@ -89,13 +90,15 @@ export default {
preferredUnit: 'auto',
imagesArea: null,
openTab: null,
userUpdatedVolareResolution: false,
userId: null,
crossOriginError: false,
maybeCorsTiffError: false,
imageFilenames: {},
labelTrees: [],
projectIds: [],
lawnmowerSaveState: "",
volare: null,
pauseLawnmowerAutomatically: false,
};
},
provide() {
Expand Down Expand Up @@ -131,14 +134,8 @@ export default {
selectedAnnotations() {
return this.filteredAnnotations.filter((a) => a.selected);
},
focussedAnnotation() {
return this.filteredAnnotations[this.focussedAnnotationIndex];
},
isDefaultAnnotationMode() {
return this.annotationMode === 'default';
},
isVolareAnnotationMode() {
return this.annotationMode === 'volare';
return this.annotationMode === 'default' || this.annotationMode === 'volarePaused' || this.annotationMode === 'lawnmowerPaused';
},
isLawnmowerAnnotationMode() {
return this.annotationMode === 'lawnmower';
Expand Down Expand Up @@ -206,20 +203,8 @@ export default {
return;
}

if (this.isVolareAnnotationMode) {
if (this.focussedAnnotationIndex < (this.filteredAnnotations.length - 1)) {
this.focussedAnnotationIndex++;
return;
} else if (this.focussedAnnotationIndex === Infinity) {
// This may happen if the volume has only one image and we can't
// switch to the next image. Here, we want to go to the second
// annotation of the image.
this.focussedAnnotationIndex = Math.min(1, this.filteredAnnotations.length - 1);
} else {
// Show the first annotation of the next image in this case, so
// don't return.
this.focussedAnnotationIndex = -Infinity;
}
if (this.volare.handleNextAnnotation()) {
return;
} else if (this.isLawnmowerAnnotationMode) {
// This returns false if the image section can't be advanced (i.e.
// the last section is shown).
Expand All @@ -246,20 +231,8 @@ export default {
return;
}

if (this.isVolareAnnotationMode) {
if (this.focussedAnnotationIndex > 0) {
this.focussedAnnotationIndex--;
return;
} else if (this.focussedAnnotationIndex === -Infinity) {
// This may happen if the volume has only one image and we can't
// switch to the next image. Here, we want to go to the second to
// last annotation of the image.
this.focussedAnnotationIndex = Math.max(this.filteredAnnotations.length - 2, 0);
} else {
// Show the last annotation of the previous image in this case,
// so don't return.
this.focussedAnnotationIndex = Infinity;
}
if (this.volare.handlePreviousAnnotation()) {
return;
} else if (this.isLawnmowerAnnotationMode) {
// This returns false if the image section can't be reversed (i.e.
// the first section is shown).
Expand All @@ -282,26 +255,6 @@ export default {
// Show previous image.
this.imageIndex = this.getPreviousIndex(this.imageIndex);
},
maybeUpdateFocussedAnnotation() {
if (this.isVolareAnnotationMode) {
if (this.filteredAnnotations.length > 0) {
if (this.focussedAnnotationIndex === Infinity) {
// Show the last annotation if the previous image is shown.
this.focussedAnnotationIndex = this.filteredAnnotations.length - 1;
} else {
// Show the first annotation if the next image is shown or
// the annotation filter changed.
this.focussedAnnotationIndex = 0;
}
} else {
// Show the whole image if there are no annotations.
this.focussedAnnotationIndex = null;
this.$refs.canvas.fitImage();
}
} else {
this.focussedAnnotationIndex = null;
}
},
maybeUpdateShownImageSection() {
if (this.isLawnmowerAnnotationMode) {
if (this.annotationModeCarry === Infinity) {
Expand All @@ -325,7 +278,6 @@ export default {
}
},
maybeUpdateAnnotationMode(data) {
this.maybeUpdateFocussedAnnotation();
this.maybeUpdateShownImageSection();
this.maybeUpdateShownSampling(data);
},
Expand All @@ -338,6 +290,9 @@ export default {
y: Math.round(viewport.center[1]),
});
},
handleMapMovestart() {
this.pauseLawnmowerAfterViewportChanged();
},
// Handler for the select event fired by the global event bus.
handleSelectAnnotation(annotation, shift) {
// Handle the case where the second argument is an event object for
Expand Down Expand Up @@ -370,9 +325,6 @@ export default {
});
}
},
focusAnnotation(annotation, fast, keepResolution) {
this.$refs.canvas.focusAnnotation(annotation, fast, keepResolution);
},
handleDetachAnnotationLabel(annotation, annotationLabel) {
if (this.isEditor) {
if (annotation.labels.length > 1) {
Expand Down Expand Up @@ -411,13 +363,6 @@ export default {
.catch(handleErrorResponse);
}
},
selectAndFocusAnnotation(annotation, keepResolution) {
this.selectedAnnotations.forEach(function (a) {
a.selected = false;
});
annotation.selected = true;
this.focusAnnotation(annotation, true, keepResolution);
},
handleFilter(filter) {
this.annotationFilter = filter;
},
Expand Down Expand Up @@ -471,7 +416,7 @@ export default {
confidence: 1,
};

if (this.isVolareAnnotationMode) {
if (this.annotationMode === 'volare') {
this.$refs.canvas.blinkAnnotation(annotation);
}

Expand Down Expand Up @@ -596,6 +541,31 @@ export default {
break;
}
},
enableAutomaticLawnmowerPausing() {
this.pauseLawnmowerAutomatically = true;
},
disableAutomaticLawnmowerPausing() {
this.pauseLawnmowerAutomatically = false;
},
pauseLawnmowerAfterViewportChanged() {
if (!this.isLawnmowerAnnotationMode || !this.pauseLawnmowerAutomatically) {
return;
}

this.$refs.annotationModesTab.pauseLawnmower();
},
pauseLawnmower(timestamp) {
this.$refs.annotationModesTab.pauseLawnmower(timestamp);
},
pauseVolare(timestamp) {
this.$refs.annotationModesTab.pauseVolare(timestamp);
},
showImageWithId(imageId) {
const index = this.imagesIds.indexOf(imageId);
if (index !== -1) {
this.imageIndex = index;
}
},
handleAnnotationModeChange(mode, data) {
this.annotationMode = mode;
this.annotationModeCarry = null;
Expand Down Expand Up @@ -663,6 +633,9 @@ export default {
openSidebarLabels() {
this.openTab = 'labels';
},
focusAnnotation(annotation, fast, keepResolution) {
this.$refs.canvas.focusAnnotation(annotation, fast, keepResolution);
}
},
watch: {
async imageId(id) {
Expand All @@ -677,6 +650,11 @@ export default {
let [image, annotations] = await Promise.all(this.getImageAndAnnotationsPromises(id));
this.image = image;
this.annotations = annotations;

// Wait for the canvas to be updated, otherwise lawnmower mode
// may compute the current section using the old viewport
await this.$nextTick();

this.maybeUpdateAnnotationMode();
this.maybeShowTilingInProgressMessage();
} catch (e) {
Expand All @@ -702,14 +680,6 @@ export default {
// Twice the count because the next and previous images are cached.
ImagesStore.setMaxCacheSize(count * 2);
},
focussedAnnotation(annotation) {
if (annotation) {
this.selectAndFocusAnnotation(annotation, this.userUpdatedVolareResolution);
}
},
annotationFilter() {
this.maybeUpdateFocussedAnnotation();
},
showScaleLine(show) {
if (show) {
this.fetchImagesArea();
Expand All @@ -720,16 +690,6 @@ export default {
this.fetchImagesArea();
}
},
isVolareAnnotationMode(enabled) {
if (!enabled) {
this.userUpdatedVolareResolution = false;
}
},
mapResolution() {
if (this.isVolareAnnotationMode) {
this.userUpdatedVolareResolution = true;
}
},
image(image) {
this.crossOriginError = image?.crossOrigin;
},
Expand All @@ -738,6 +698,15 @@ export default {
this.openTab = '';
}
},
annotationMode(newMode, oldMode) {
if (newMode === 'lawnmowerPaused') {
this.lawnmowerSaveState = LawnmowerSaveState.SAVE;
} else if (oldMode === 'lawnmowerPaused' && newMode === 'lawnmower') {
this.lawnmowerSaveState = LawnmowerSaveState.LOAD;
} else {
this.lawnmowerSaveState = LawnmowerSaveState.DISCARD;
}
}
},
created() {
this.allImagesIds = biigle.$require('annotations.imagesIds');
Expand Down Expand Up @@ -789,7 +758,7 @@ export default {
// Use $nextTick so the annotationCanvas component has time to
// render the image.
this.$nextTick(
() => this.selectAndFocusAnnotation(annotations[i])
() => this.volare.selectAndFocusAnnotation(annotations[i])
);
return;
}
Expand All @@ -813,6 +782,19 @@ export default {
Keyboard.on('C', this.selectLastAnnotation, 0, this.listenerSet);

this.initLabelBot();

this.volare = useVolareMode({
filteredAnnotations: computed(() => this.filteredAnnotations),
selectedAnnotations: computed(() => this.selectedAnnotations),
focusAnnotationInCanvas: this.focusAnnotation,
fitImageInCanvas: (...args) => this.$refs.canvas.fitImage(...args),
annotationFilter: this.annotationFilter,
image: computed(() => this.image),
mapResolution: computed(() => this.mapResolution),
showImageWithId: this.showImageWithId,
annotationMode: computed(() => this.annotationMode),
restoreVolarePauseState: (timestamp) => this.$refs.annotationModesTab.pauseVolare(timestamp)
});
},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default {
template: '#annotation-canvas-template',
emits: [
'moveend',
'movestart',
'update',
'select',
'previous',
Expand Down Expand Up @@ -910,6 +911,7 @@ export default {

this.map.on('change:size', this.updateMapSize);
this.map.on('moveend', this.updateMapView);
this.map.on('movestart', () => this.$emit('movestart'));
this.map.on('pointermove', this.updateMousePosition);

this.selectInteraction.on('select', this.handleFeatureSelect);
Expand Down
Loading