viewbox drag now visible on playback annotator.
This commit is contained in:
parent
949a16b01f
commit
6d7a223a69
4 changed files with 82 additions and 14 deletions
|
@ -171,6 +171,7 @@ class AnimationSlice:
|
||||||
dwg = svgwrite.Drawing(fn, size=(box.width, box.height))
|
dwg = svgwrite.Drawing(fn, size=(box.width, box.height))
|
||||||
dwg.viewbox(box.x, box.y, box.width, box.height)
|
dwg.viewbox(box.x, box.y, box.width, box.height)
|
||||||
self.add_to_dwg(dwg)
|
self.add_to_dwg(dwg)
|
||||||
|
dwg.defs.add(dwg.style("path{stroke-width:1mm;stroke-linecap: round;}"))
|
||||||
return dwg
|
return dwg
|
||||||
|
|
||||||
def get_as_svg(self) -> str:
|
def get_as_svg(self) -> str:
|
||||||
|
@ -325,6 +326,7 @@ class AnnotationIndex:
|
||||||
self.shelve = {}
|
self.shelve = {}
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
logger.info("refreshing")
|
||||||
# reset the index
|
# reset the index
|
||||||
for key in list(self.shelve.keys()):
|
for key in list(self.shelve.keys()):
|
||||||
print(key)
|
print(key)
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</ul> -->
|
</ul> -->
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<a href="?refresh">Reload index</a>
|
<a href="?refresh=1">Reload index</a>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
let images = document.querySelectorAll('[data-audio]');
|
let images = document.querySelectorAll('[data-audio]');
|
||||||
|
|
|
@ -279,7 +279,7 @@ class AnimationHandler(tornado.web.RequestHandler):
|
||||||
self.config.storage, os.path.basename(
|
self.config.storage, os.path.basename(
|
||||||
filename) + ".json_appendable"
|
filename) + ".json_appendable"
|
||||||
)
|
)
|
||||||
drawing = {"file": filename, "shape": []}
|
drawing = {"file": filename, "shape": [], "viewboxes": []}
|
||||||
with open(path, "r") as fp:
|
with open(path, "r") as fp:
|
||||||
events = json.loads("[" + fp.read() + "]")
|
events = json.loads("[" + fp.read() + "]")
|
||||||
for i, event in enumerate(events):
|
for i, event in enumerate(events):
|
||||||
|
@ -292,7 +292,7 @@ class AnimationHandler(tornado.web.RequestHandler):
|
||||||
# ignore double metadatas, which appear when continuaing an existing drawing
|
# ignore double metadatas, which appear when continuaing an existing drawing
|
||||||
continue
|
continue
|
||||||
if event["event"] == "viewbox":
|
if event["event"] == "viewbox":
|
||||||
pass
|
drawing["viewboxes"].extend(event['viewboxes'])
|
||||||
if event["event"] == "stroke":
|
if event["event"] == "stroke":
|
||||||
# points = []
|
# points = []
|
||||||
# for i in range(int(len(stroke) / 4)):
|
# for i in range(int(len(stroke) / 4)):
|
||||||
|
@ -496,9 +496,9 @@ class IndexHandler(tornado.web.RequestHandler):
|
||||||
def get(self):
|
def get(self):
|
||||||
do_refresh = bool(self.get_query_argument('refresh', False))
|
do_refresh = bool(self.get_query_argument('refresh', False))
|
||||||
if do_refresh:
|
if do_refresh:
|
||||||
self.logger.info("Reloading Annotation Index")
|
logger.info("Reloading Annotation Index")
|
||||||
self.index.refresh()
|
self.index.refresh()
|
||||||
self.logger.info("\treloaded annotation index")
|
logger.info("\treloaded annotation index")
|
||||||
|
|
||||||
self.render("templates/index.html", index=self.index)
|
self.render("templates/index.html", index=self.index)
|
||||||
|
|
||||||
|
|
|
@ -476,23 +476,26 @@ class Annotator extends EventTarget {
|
||||||
}
|
}
|
||||||
this.filename = drawing.file;
|
this.filename = drawing.file;
|
||||||
this.strokes = drawing.shape.map(s => new Stroke(s['color'], s['points']));
|
this.strokes = drawing.shape.map(s => new Stroke(s['color'], s['points']));
|
||||||
|
this.viewboxes = drawing.viewboxes;
|
||||||
this.currentPathI = null;
|
this.currentPathI = null;
|
||||||
this.currentPointI = null;
|
this.currentPointI = null;
|
||||||
|
this.currentViewboxI = null;
|
||||||
this.dimensions = drawing.dimensions;
|
this.dimensions = drawing.dimensions;
|
||||||
this.svgEl.setAttribute('viewBox', `0 0 ${this.dimensions[0]} ${this.dimensions[1]}`)
|
this.svgEl.setAttribute('viewBox', `0 0 ${this.dimensions[0]} ${this.dimensions[1]}`)
|
||||||
|
|
||||||
let bgEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
// let bgEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
bgEl.setAttribute("x", 0);
|
// bgEl.setAttribute("x", 0);
|
||||||
bgEl.setAttribute("y", 0);
|
// bgEl.setAttribute("y", 0);
|
||||||
bgEl.setAttribute("width", this.dimensions[0]);
|
// bgEl.setAttribute("width", this.dimensions[0]);
|
||||||
bgEl.setAttribute("height", this.dimensions[1]);
|
// bgEl.setAttribute("height", this.dimensions[1]);
|
||||||
bgEl.classList.add('background');
|
// bgEl.classList.add('background');
|
||||||
this.svgEl.prepend(bgEl);
|
// this.svgEl.prepend(bgEl);
|
||||||
|
|
||||||
this.firstFrameTime = this.strokes[0].points[0][3];
|
this.firstFrameTime = this.strokes[0].points[0][3];
|
||||||
this.lastFrameTime = this.getFinalFrameTime();
|
this.lastFrameTime = this.getFinalFrameTime();
|
||||||
this.playheadEl.max = this.lastFrameTime;
|
this.playheadEl.max = this.lastFrameTime;
|
||||||
this.nextFrameTimeout = null;
|
this.nextFrameTimeout = null;
|
||||||
|
this.nextViewboxTimeout = null;
|
||||||
this._setPausedFlag(true);
|
this._setPausedFlag(true);
|
||||||
|
|
||||||
this.formatter = wNumb({
|
this.formatter = wNumb({
|
||||||
|
@ -740,6 +743,15 @@ class Annotator extends EventTarget {
|
||||||
// this.currentTime = path.points[point_i][3];
|
// this.currentTime = path.points[point_i][3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setViewboxPosition(box_i) {
|
||||||
|
if(this.currentViewboxI == box_i){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentViewboxI = box_i
|
||||||
|
const b = this.viewboxes[box_i];
|
||||||
|
this.svgEl.setAttribute('viewBox', `${b.x} ${b.y} ${this.dimensions[0]} ${this.dimensions[1]}`)
|
||||||
|
}
|
||||||
|
|
||||||
getNextPosition(path_i, point_i) {
|
getNextPosition(path_i, point_i) {
|
||||||
const path = this.strokes[path_i];
|
const path = this.strokes[path_i];
|
||||||
let next_path, next_point;
|
let next_path, next_point;
|
||||||
|
@ -790,6 +802,32 @@ class Annotator extends EventTarget {
|
||||||
this.nextFrameTimeout = setTimeout(() => this.playStrokePosition(next_path, next_point, true), dt);
|
this.nextFrameTimeout = setTimeout(() => this.playStrokePosition(next_path, next_point, true), dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
playViewboxPosition(box_i, allow_interrupt) {
|
||||||
|
if (allow_interrupt) {
|
||||||
|
if (!this.videoIsPlaying) {
|
||||||
|
console.log('not playing because of interrupt');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else {
|
||||||
|
// this.videoIsPlaying = true;
|
||||||
|
// }
|
||||||
|
this.setViewboxPosition(box_i);
|
||||||
|
|
||||||
|
const next_box_i = box_i+1;
|
||||||
|
if(this.viewboxes.length <= next_box_i){
|
||||||
|
console.debug('done playing viewbox');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = this.viewboxes[next_box_i].t;
|
||||||
|
|
||||||
|
// calculate interval based on playback start to avoid drifting of time
|
||||||
|
const dt = t - (window.performance.now() - this.startTimeMs);
|
||||||
|
this.nextViewboxTimeout = setTimeout(() => this.playViewboxPosition(next_box_i, true), dt);
|
||||||
|
}
|
||||||
|
|
||||||
scrubTo(ms) {
|
scrubTo(ms) {
|
||||||
// const [path_i, point_i] = this.findPositionForTime(ms);
|
// const [path_i, point_i] = this.findPositionForTime(ms);
|
||||||
// console.log(path_i, point_i);
|
// console.log(path_i, point_i);
|
||||||
|
@ -816,6 +854,7 @@ class Annotator extends EventTarget {
|
||||||
|
|
||||||
_interruptPlayback() {
|
_interruptPlayback() {
|
||||||
clearTimeout(this.nextFrameTimeout);
|
clearTimeout(this.nextFrameTimeout);
|
||||||
|
clearTimeout(this.nextViewboxTimeout);
|
||||||
clearTimeout(this.audioEndTimeout);
|
clearTimeout(this.audioEndTimeout);
|
||||||
clearTimeout(this.audioStartTimeout);
|
clearTimeout(this.audioStartTimeout);
|
||||||
clearTimeout(this.startVideoTimeout);
|
clearTimeout(this.startVideoTimeout);
|
||||||
|
@ -835,12 +874,18 @@ class Annotator extends EventTarget {
|
||||||
this._seekByTimeMs(this._currentTimeMs); // prevent playback issue for initial load
|
this._seekByTimeMs(this._currentTimeMs); // prevent playback issue for initial load
|
||||||
|
|
||||||
this.startTimeMs = window.performance.now() - this._currentTimeMs;
|
this.startTimeMs = window.performance.now() - this._currentTimeMs;
|
||||||
|
// strokes
|
||||||
if (this._currentTimeMs < 0) {
|
if (this._currentTimeMs < 0) {
|
||||||
this.startVideoTimeout = setTimeout((e) => this.playStrokePosition(this.currentPathI, this.currentPointI), this._currentTimeMs * -1);
|
this.startVideoTimeout = setTimeout((e) => this.playStrokePosition(this.currentPathI, this.currentPointI), this._currentTimeMs * -1);
|
||||||
} else {
|
} else {
|
||||||
this.playStrokePosition(this.currentPathI, this.currentPointI);
|
this.playStrokePosition(this.currentPathI, this.currentPointI);
|
||||||
} this.playAudioSegment(this._currentTimeMs, this.outPointTimeMs);
|
}
|
||||||
|
// viewboxes
|
||||||
|
// const nextViewboxI = Math.max(this.currentViewboxI++, this.viewboxes.length-1);
|
||||||
|
this.playViewboxPosition(this.currentViewboxI);
|
||||||
|
|
||||||
|
// audio
|
||||||
|
this.playAudioSegment(this._currentTimeMs, this.outPointTimeMs);
|
||||||
// this.playStrokePosition(this.currentPathI, this.currentPointI);
|
// this.playStrokePosition(this.currentPathI, this.currentPointI);
|
||||||
this._setPausedFlag(false);
|
this._setPausedFlag(false);
|
||||||
|
|
||||||
|
@ -927,6 +972,7 @@ class Annotator extends EventTarget {
|
||||||
this.dispatchEvent(new CustomEvent('seeking', { detail: time }));
|
this.dispatchEvent(new CustomEvent('seeking', { detail: time }));
|
||||||
this._currentTimeMs = Number.parseFloat(time) * 1000;
|
this._currentTimeMs = Number.parseFloat(time) * 1000;
|
||||||
[this.currentPathI, this.currentPointI] = this.findPositionForTime(this._currentTimeMs);
|
[this.currentPathI, this.currentPointI] = this.findPositionForTime(this._currentTimeMs);
|
||||||
|
|
||||||
this.playheadEl.value = this._currentTimeMs;
|
this.playheadEl.value = this._currentTimeMs;
|
||||||
this._updateFrame();
|
this._updateFrame();
|
||||||
this.dispatchEvent(new CustomEvent('seeked', { detail: this.currentTime }));
|
this.dispatchEvent(new CustomEvent('seeked', { detail: this.currentTime }));
|
||||||
|
@ -934,6 +980,7 @@ class Annotator extends EventTarget {
|
||||||
|
|
||||||
_updateFrame() {
|
_updateFrame() {
|
||||||
this.drawStrokePosition(this.inPointPosition, [this.currentPathI, this.currentPointI]);
|
this.drawStrokePosition(this.inPointPosition, [this.currentPathI, this.currentPointI]);
|
||||||
|
this.setViewboxPosition(this.findViewboxForTime(this._currentTimeMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -996,5 +1043,24 @@ class Annotator extends EventTarget {
|
||||||
return [path_i, point_i];
|
return [path_i, point_i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findViewboxForTime(ms){
|
||||||
|
ms = Math.min(Math.max(ms, 0), this.lastFrameTime);
|
||||||
|
// console.log('scrub to', ms)
|
||||||
|
let box_i = 0;
|
||||||
|
this.viewboxes.every((viewbox, index) => {
|
||||||
|
const startAt = viewbox.t;
|
||||||
|
|
||||||
|
if (startAt > ms) {
|
||||||
|
return false; // too far
|
||||||
|
} else {
|
||||||
|
// in case nothings comes after, we store the last best option thus far
|
||||||
|
box_i = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return box_i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue