Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 53 additions & 33 deletions client/src/components/annotators/ImageAnnotator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,10 @@ export default Vue.extend({
this.quadFeatureLayer = this.geoViewer.createLayer('feature', {
features: ['quad'],
});
this.quadFeature = this.quadFeatureLayer
.createFeature('quad')
.data([
{
ul: { x: 0, y: 0 },
lr: { x: this.width, y: this.height },
image: this.imgs[this.frame],
},
])
.draw();
this.quadFeature = this.quadFeatureLayer.createFeature('quad');
this.drawImage(this.imgs[this.frame]);
this.ready = true;
},

async play() {
try {
this.playing = true;
Expand All @@ -75,24 +66,43 @@ export default Vue.extend({
console.error(ex);
}
},

async seek(frame) {
this.lastFrame = this.frame;
this.frame = frame;
this.syncedFrame = frame;
this.emitFrame();
this.cacheImages();
drawImage(img) {
if ((img.naturalWidth !== this.width) || (img.naturalHeight !== this.height)) {
this.width = img.naturalWidth;
this.height = img.naturalHeight;
this.resetMapDimensions();
}
this.quadFeature
.data([
{
ul: { x: 0, y: 0 },
lr: { x: this.width, y: this.height },
image: this.imgs[frame],
image: img,
},
])
.draw();
},

async seek(newFrame) {
this.lastFrame = this.frame;
this.frame = newFrame;
this.syncedFrame = newFrame;
this.emitFrame();
const imgsPromise = this.cacheImages();
const img = this.imgs[newFrame];
if (img.cached) {
// if image is already loaded, draw it now
this.drawImage(img);
} else {
// else wait for it to load
const imgs = await imgsPromise;
const loadedImg = await imgs[newFrame];
if (loadedImg !== undefined && loadedImg.frame === this.frame) {
// if the image exists ()
// and the seek hasn't changed since the image completed loading, draw it.
this.drawImage(loadedImg);
}
}
},
pause() {
this.playing = false;
this.loadingVideo = false;
Expand Down Expand Up @@ -153,7 +163,7 @@ export default Vue.extend({
await this.loadFrame(frame);
}
// Cache a new range of images based on current frame
this.cacheNewRange(min, max);
return this.cacheNewRange(min, max);
},
/**
* Wraps loading of a single frame in a promise, used to gurantee frame loads
Expand All @@ -169,6 +179,7 @@ export default Vue.extend({
img.onload = () => {
img.onload = null;
img.cached = true;
img.frame = frame;
resolve(frame);
};
this.loadImageFunc(this.imageData[frame], img);
Expand All @@ -181,40 +192,49 @@ export default Vue.extend({
* @param {int} max upper bound frame number for caching
*/
cacheNewRange(min, max) {
const frameImages = [];
for (let i = this.frame; i <= max; i += 1) {
this.cacheFrame(i);
frameImages[i] = this.cacheFrame(i);
const minusFrame = this.frame - (i - this.frame);
if (minusFrame >= min) {
this.cacheFrame(minusFrame);
}
}
return frameImages;
},
/**
* Adds a single frame to the pendingImgs array for loading and assigns it to the master
* imgs list. Once the image is loaded it is removed from the pendingImgs
* @param {int} i the image to cache if it isn't already assigned
* @returns {Array<Promise<Array<image>>>} loaded images
*/
cacheFrame(i) {
let p = Promise.resolve(this.imgs[i]);
if (!this.imgs[i]) {
const img = new Image();
img.crossOrigin = 'Anonymous';
this.imgs[i] = img;
const imageAndFrame = [img, i];
this.pendingImgs.add(imageAndFrame);
img.onload = () => {
this.pendingImgs.delete(imageAndFrame);
img.onload = null;
img.cached = true;
// If we are trying to play and waiting for loaded frames we check the cache again
if (this.playing && this.loadingVideo) {
if (this.checkCached(this.playCache)) {
this.loadingVideo = false;
this.syncWithVideo();
p = new Promise((resolve) => {
img.onload = () => {
this.pendingImgs.delete(imageAndFrame);
img.onload = null;
img.cached = true;
img.frame = i;
// If we are trying to play and waiting for loaded frames we check the cache again
if (this.playing && this.loadingVideo) {
if (this.checkCached(this.playCache)) {
this.loadingVideo = false;
this.syncWithVideo();
}
}
}
};
resolve(img);
};
});
this.loadImageFunc(this.imageData[i], img);
}
return p;
},
/**
* Checks to see if there is enough cached images to play for X seconds
Expand Down
26 changes: 26 additions & 0 deletions client/src/components/annotators/annotator.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ export default {
setImageCursor(newCursor) {
this.imageCursor = `${newCursor}`;
},
resetMapDimensions() {
this.geoViewer.bounds({
left: 0,
top: 0,
Comment thread
BryonLewis marked this conversation as resolved.
bottom: this.height,
right: this.width,
});
const params = geo.util.pixelCoordinateParams(
this.$refs.container,
this.width,
this.height,
this.width,
this.height,
);
this.geoViewer.maxBounds({
left: 0,
top: 0,
right: params.map.maxBounds.right,
bottom: params.map.maxBounds.bottom,
});
this.geoViewer.zoomRange(params.map);
Comment thread
BryonLewis marked this conversation as resolved.
this.geoViewer.zoom(this.geoViewer.zoomRange().origMin);
this.geoViewer.center({ x: 0, y: 0 });
},
baseInit() {
const params = geo.util.pixelCoordinateParams(
this.$refs.container,
Expand All @@ -81,6 +105,7 @@ export default {
this.height,
);
this.geoViewer = geo.map(params.map);
this.resetMapDimensions();
this.geoViewer.zoomRange({
min: this.geoViewer.zoomRange().origMin,
max: this.geoViewer.zoomRange().max + 3,
Expand Down Expand Up @@ -117,6 +142,7 @@ export default {
interactorOpts.wheelScaleY = 0.2;
this.geoViewer.interactor().options(interactorOpts);
},

prevFrame() {
const targetFrame = this.frame - 1;
if (targetFrame >= 0) {
Expand Down