diff --git a/svganim/strokes.py b/svganim/strokes.py index c911aad..27d9147 100644 --- a/svganim/strokes.py +++ b/svganim/strokes.py @@ -23,7 +23,6 @@ class Annotation: def id(self) -> str: return f'{self.drawing.id}:{self.tag}:{self.t_in}:{self.t_out}' - def getAnimationSlice(self) -> AnimationSlice: return self.drawing.get_animation().getSlice(self.t_in, self.t_out) @@ -96,7 +95,7 @@ class Drawing: strokes.append( Stroke( event["color"], - [Point.fromTuple(tuple(p)).scaled(self.get_canvas_metadata()['dimensions']) for p in event["points"]], + [Point.fromTuple(tuple(p)) for p in event["points"]], ) ) return AnimationSlice(strokes, audioslice=self.get_audio() ) @@ -363,8 +362,9 @@ class Point: def fromTuple(cls, p: tuple[float, float, int, float]): return cls(p[0], p[1], bool(p[2]), p[3]) - def scaled(self, dimensions: dict[str, float]) -> Point: - return Point(self.x*dimensions['width'], self.y * dimensions['height'], self.last, self.t) + def scaledToFit(self, dimensions: dict[str, float]) -> Point: + # TODO: change so that it actually scales to FIT dimensions + return Point(self.x, self.y, self.last, self.t) Points = list[Point] diff --git a/webserver.py b/webserver.py index 1a1f4fd..52e60f5 100644 --- a/webserver.py +++ b/webserver.py @@ -47,6 +47,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): self.strokes = [] self.hasWritten = False self.prev_file = None + self.prev_file_duration = 0 self.dimensions = [None, None] # def check_origin(self, origin): @@ -126,15 +127,42 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): self.prev_file = prev_file - with open(self.prev_file, "r") as fp: + metadata = self.getFileMetadata(self.prev_file) + self.prev_file_duration = self.getLastTimestampInFile(self.prev_file) + logger.info( + "Previous file set. {self.prev_file} {metadata=} time: {self.prev_file_duration}") + + self.write_message(json.dumps( + {"preloaded_svg": f"/drawing/{file}", "dimensions": [metadata[1], metadata[2]], "time": self.prev_file_duration})) + + def getFileMetadata(self, filename): + with open(filename, "r") as fp: first_line = fp.readline().strip() if first_line.endswith(","): first_line = first_line[:-1] metadata = json.loads(first_line) - self.write_message(json.dumps( - {"preloaded_svg": f"/drawing/{file}", "dimensions": [metadata[1], metadata[2]]})) + return metadata + + def getLastTimestampInFile(self, filename): + with open(filename, "r") as fp: + for line in fp: + pass # loop until the end + last_line = line.strip() + if last_line.endswith(","): + last_line = last_line[:-1] + + data = json.loads(last_line) + if type(data) is list: + raise Exception("Oddly, the file ends with merely metadata") + + if data['event'] == 'stroke': + return data['points'][-1][3] + elif data['event'] == 'viewbox': + return data['viewboxes'][-1]['t'] + else: + raise Exception("Unknown last event") # the client sent the message def on_message(self, message): @@ -144,13 +172,20 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): msg = json.loads(message) if msg["event"] == "stroke": logger.info("stroke") + for i in range(len(msg['points'])): + msg['points'][i][3] += self.prev_file_duration self.appendEvent(msg) elif msg["event"] == "dimensions": self.dimensions = [int(msg["width"]), int(msg["height"])] logger.info(f"{self.dimensions=}") elif msg["event"] == "viewbox": logger.info("move or resize") - self.appendEvent(msg) + if len(msg['viewboxes']) == 0: + logger.warn("Empty viewbox array") + else: + for i in range(len(msg['viewboxes'])): + msg['viewboxes'][i]['t'] += self.prev_file_duration + self.appendEvent(msg) elif msg["event"] == "preload": self.preloadFile(msg["file"]) else: @@ -253,6 +288,9 @@ class AnimationHandler(tornado.web.RequestHandler): drawing["time"] = event[0] drawing["dimensions"] = [event[1], event[2]] else: + if type(event) is list: + # ignore double metadatas, which appear when continuaing an existing drawing + continue if event["event"] == "viewbox": pass if event["event"] == "stroke": diff --git a/www/annotate.js b/www/annotate.js index ca87bc5..8655385 100644 --- a/www/annotate.js +++ b/www/annotate.js @@ -56,7 +56,7 @@ class StrokeGroup { let cmd = ""; for (let stroke of strokes) { if (!last_stroke) { - d += `M${stroke[0] * this.player.dimensions[0]},${stroke[1] * this.player.dimensions[1]} `; + d += `M${stroke[0]},${stroke[1]} `; cmd = 'M'; } else { if (last_stroke[2] == 1) { @@ -67,7 +67,7 @@ class StrokeGroup { cmd = 'l'; } let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]]; - d += `${rel_stroke[0] * this.player.dimensions[0]},${rel_stroke[1] * this.player.dimensions[1]} `; + d += `${rel_stroke[0]},${rel_stroke[1]} `; } last_stroke = stroke; diff --git a/www/draw.js b/www/draw.js index 8302f7a..b7bd4cf 100644 --- a/www/draw.js +++ b/www/draw.js @@ -61,7 +61,7 @@ class Canvas { }) - this.colors = ["black", "red", "blue", "green"]; + this.colors = ["black", "#cc1414", "blue", "green"]; this.resize(); @@ -292,14 +292,15 @@ class Canvas { getCoordinates(e) { // convert event coordinates into relative positions on x & y axis let box = this.svgEl.getBoundingClientRect(); - let x = (e.x - box['left'] + this.viewbox.x) / box['width']; - let y = (e.y - box['top'] + this.viewbox.y) / box['height']; + let x = (e.x - box['left'] + this.viewbox.x); + let y = (e.y - box['top'] + this.viewbox.y); return { 'x': x, 'y': y }; } - isInsideBounds(pos) { - return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1); - } + // isInsideBounds(pos) { + // let box = this.svgEl.getBoundingClientRect(); + // return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > box['width'] || pos['y'] > box['height']); + // } draw(e) { @@ -377,7 +378,7 @@ class Canvas { let cmd = ""; for (let stroke of strokes) { if (!last_stroke) { - d += `M${stroke[0] * this.viewbox.width},${stroke[1] * this.viewbox.height} `; + d += `M${stroke[0]},${stroke[1]} `; cmd = 'M'; } else { if (last_stroke[2] == 1) { @@ -388,7 +389,7 @@ class Canvas { cmd = 'l'; } let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]]; - d += `${rel_stroke[0] * this.viewbox.width},${rel_stroke[1] * this.viewbox.height} `; + d += `${rel_stroke[0]},${rel_stroke[1]} `; } last_stroke = stroke; diff --git a/www/play.js b/www/play.js index af60efb..770a597 100644 --- a/www/play.js +++ b/www/play.js @@ -74,13 +74,13 @@ class Player { if ( this.currentPathI < this.inPointPosition[0] || this.currentPointI < this.inPointPosition[1]) { - this.drawStrokePosition( - // this.inPointPosition[0], - // this.inPointPosition[1], - // always draw at out position, as to see the whole shape of the range - this.outPointPosition[0], - this.outPointPosition[1], - ); + this.drawStrokePosition( + // this.inPointPosition[0], + // this.inPointPosition[1], + // always draw at out position, as to see the whole shape of the range + this.outPointPosition[0], + this.outPointPosition[1], + ); } } if (handle === 1) { @@ -122,7 +122,7 @@ class Player { // inactive is what comes before and after. // then, playing the video is just running pathRanghe(0, playhead) drawStrokePosition(path_i, point_i, show_all) { - if(typeof show_all === 'undefined') + if (typeof show_all === 'undefined') show_all = false; // check if anything is placed that is in the future from the current playhead @@ -194,7 +194,7 @@ class Player { } // when an outpoint is set, stop playing there - if(next_path > this.outPointPosition[0] || next_point > this.outPointPosition[1]){ + if (this.outPointPosition && (next_path > this.outPointPosition[0] || next_point > this.outPointPosition[1])) { return [null, null]; } @@ -202,12 +202,12 @@ class Player { } playStrokePosition(path_i, point_i, allow_interrupt) { - if(allow_interrupt) { - if(!this.isPlaying) { + if (allow_interrupt) { + if (!this.isPlaying) { console.log('not playing because of interrupt'); return; } - } else{ + } else { this.isPlaying = true; } this.drawStrokePosition(path_i, point_i); @@ -281,7 +281,7 @@ class Player { let cmd = ""; for (let stroke of strokes) { if (!last_stroke) { - d += `M${stroke[0] * this.dimensions[0]},${stroke[1] * this.dimensions[1]} `; + d += `M${stroke[0]},${stroke[1]} `; cmd = 'M'; } else { if (last_stroke[2] == 1) { @@ -292,7 +292,7 @@ class Player { cmd = 'l'; } let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]]; - d += `${rel_stroke[0] * this.dimensions[0]},${rel_stroke[1] * this.dimensions[1]} `; + d += `${rel_stroke[0]},${rel_stroke[1]} `; } last_stroke = stroke;