Compare commits
2 Commits
d7319a0c6d
...
50d4656ad7
Author | SHA1 | Date |
---|---|---|
Ruben van de Ven | 50d4656ad7 | |
Ruben van de Ven | 8e6274601e |
|
@ -12,6 +12,7 @@ import logging
|
||||||
|
|
||||||
logger = logging.getLogger('svganim.strokes')
|
logger = logging.getLogger('svganim.strokes')
|
||||||
|
|
||||||
|
|
||||||
class Annotation:
|
class Annotation:
|
||||||
def __init__(self, tag: str, drawing: Drawing, t_in: float, t_out: float) -> None:
|
def __init__(self, tag: str, drawing: Drawing, t_in: float, t_out: float) -> None:
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
|
@ -23,7 +24,6 @@ class Annotation:
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
return f'{self.drawing.id}:{self.tag}:{self.t_in}:{self.t_out}'
|
return f'{self.drawing.id}:{self.tag}:{self.t_in}:{self.t_out}'
|
||||||
|
|
||||||
|
|
||||||
def getAnimationSlice(self) -> AnimationSlice:
|
def getAnimationSlice(self) -> AnimationSlice:
|
||||||
return self.drawing.get_animation().getSlice(self.t_in, self.t_out)
|
return self.drawing.get_animation().getSlice(self.t_in, self.t_out)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class Drawing:
|
||||||
if 'file' not in md['audio']:
|
if 'file' not in md['audio']:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return AudioSlice(filename=os.path.join(self.basedir,md['audio']['file'][1:]), offset=md['audio']['offset']*1000)
|
return AudioSlice(filename=os.path.join(self.basedir, md['audio']['file'][1:]), offset=md['audio']['offset']*1000)
|
||||||
|
|
||||||
def get_animation(self) -> AnimationSlice:
|
def get_animation(self) -> AnimationSlice:
|
||||||
# with open(self.eventfile, "r") as fp:
|
# with open(self.eventfile, "r") as fp:
|
||||||
|
@ -96,10 +96,11 @@ class Drawing:
|
||||||
strokes.append(
|
strokes.append(
|
||||||
Stroke(
|
Stroke(
|
||||||
event["color"],
|
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() )
|
return AnimationSlice(strokes, audioslice=self.get_audio())
|
||||||
|
|
||||||
def get_metadata(self):
|
def get_metadata(self):
|
||||||
canvas = self.get_canvas_metadata()
|
canvas = self.get_canvas_metadata()
|
||||||
|
@ -158,13 +159,12 @@ class AnimationSlice:
|
||||||
return Viewbox(min_x, min_y, max_x - min_x, max_y - min_y)
|
return Viewbox(min_x, min_y, max_x - min_x, max_y - min_y)
|
||||||
|
|
||||||
def getSlice(self, t_in: float, t_out: float) -> AnimationSlice:
|
def getSlice(self, t_in: float, t_out: float) -> AnimationSlice:
|
||||||
frame_in = self.getIndexForTime(t_in)
|
frame_in = self.getIndexForInPoint(t_in)
|
||||||
frame_out = self.getIndexForTime(t_out)
|
frame_out = self.getIndexForOutPoint(t_out)
|
||||||
strokes = self.getStrokeSlices(frame_in, frame_out)
|
strokes = self.getStrokeSlices(frame_in, frame_out)
|
||||||
audio = self.audio.getSlice(t_in, t_out) if self.audio else None
|
audio = self.audio.getSlice(t_in, t_out) if self.audio else None
|
||||||
return AnimationSlice(strokes, t_in, t_out, audio)
|
return AnimationSlice(strokes, t_in, t_out, audio)
|
||||||
|
|
||||||
|
|
||||||
def get_as_svg_dwg(self) -> svgwrite.Drawing:
|
def get_as_svg_dwg(self) -> svgwrite.Drawing:
|
||||||
box = self.get_bounding_box()
|
box = self.get_bounding_box()
|
||||||
(_, fn) = tempfile.mkstemp(suffix='.svg', text=True)
|
(_, fn) = tempfile.mkstemp(suffix='.svg', text=True)
|
||||||
|
@ -200,11 +200,47 @@ class AnimationSlice:
|
||||||
break
|
break
|
||||||
|
|
||||||
in_i = index_in[1] if index_in[0] == i else 0
|
in_i = index_in[1] if index_in[0] == i else 0
|
||||||
out_i = index_out[1] if index_out[0] == i else len(stroke.points) - 1
|
out_i = index_out[1] if index_out[0] == i else len(
|
||||||
|
stroke.points) - 1
|
||||||
|
|
||||||
slices.append(StrokeSlice(stroke, in_i, out_i))
|
slices.append(StrokeSlice(stroke, in_i, out_i))
|
||||||
return slices
|
return slices
|
||||||
|
|
||||||
|
def getIndexForInPoint(self, ms) -> FrameIndex:
|
||||||
|
"""Get the frame index (path, point) based on the given time
|
||||||
|
The In point version (so the first index after ms)
|
||||||
|
Equal to annotations.js findPositionForTime(ms)
|
||||||
|
"""
|
||||||
|
path_i = 0
|
||||||
|
point_i = 0
|
||||||
|
for i, stroke in enumerate(self.strokes):
|
||||||
|
start_at = stroke.points[0].t
|
||||||
|
end_at = stroke.points[-1].t
|
||||||
|
if end_at < ms:
|
||||||
|
# certainly not the right point yet
|
||||||
|
continue
|
||||||
|
if start_at > ms:
|
||||||
|
path_i = i
|
||||||
|
point_i = 0
|
||||||
|
break # too far, so this is the first point after in point
|
||||||
|
else:
|
||||||
|
# our in-point is inbetween first and last of the stroke
|
||||||
|
# we are getting close, find the right point_i
|
||||||
|
path_i = i
|
||||||
|
for pi, point in enumerate(stroke.points):
|
||||||
|
point_i = pi
|
||||||
|
if point.t > ms:
|
||||||
|
break # stop when finding the next point after in point
|
||||||
|
break # done :-)
|
||||||
|
return (path_i, point_i)
|
||||||
|
|
||||||
|
def getIndexForOutPoint(self, ms) -> FrameIndex:
|
||||||
|
"""Get the frame index (path, point) based on the given time
|
||||||
|
The Out point version (so the last index before ms)
|
||||||
|
Equal to annotations.js findPositionForTime(ms)
|
||||||
|
"""
|
||||||
|
return self.getIndexForTime( ms)
|
||||||
|
|
||||||
def getIndexForTime(self, ms) -> FrameIndex:
|
def getIndexForTime(self, ms) -> FrameIndex:
|
||||||
"""Get the frame index (path, point) based on the given time
|
"""Get the frame index (path, point) based on the given time
|
||||||
Equal to annotations.js findPositionForTime(ms)
|
Equal to annotations.js findPositionForTime(ms)
|
||||||
|
@ -221,7 +257,7 @@ class AnimationSlice:
|
||||||
# we are getting close, find the right point_i
|
# we are getting close, find the right point_i
|
||||||
path_i = i
|
path_i = i
|
||||||
for pi, point in enumerate(stroke.points):
|
for pi, point in enumerate(stroke.points):
|
||||||
if point.t:
|
if point.t > ms:
|
||||||
break # too far
|
break # too far
|
||||||
point_i = pi
|
point_i = pi
|
||||||
break # done :-)
|
break # done :-)
|
||||||
|
@ -234,7 +270,7 @@ class AnimationSlice:
|
||||||
|
|
||||||
|
|
||||||
class AudioSlice:
|
class AudioSlice:
|
||||||
def __init__(self, filename: Filename, t_in: float=None, t_out: float=None, offset:float = None):
|
def __init__(self, filename: Filename, t_in: float = None, t_out: float = None, offset: float = None):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.t_in = t_in # in ms
|
self.t_in = t_in # in ms
|
||||||
self.t_out = t_out # in ms
|
self.t_out = t_out # in ms
|
||||||
|
@ -251,15 +287,18 @@ class AudioSlice:
|
||||||
end = self.t_out - self.offset
|
end = self.t_out - self.offset
|
||||||
|
|
||||||
if start < 0 and end < 0:
|
if start < 0 and end < 0:
|
||||||
extract = AudioSegment.silent(duration=end-start, frame_rate=song.frame_rate)
|
extract = AudioSegment.silent(
|
||||||
|
duration=end-start, frame_rate=song.frame_rate)
|
||||||
else:
|
else:
|
||||||
if start < 0:
|
if start < 0:
|
||||||
preroll = AudioSegment.silent(duration=start * -1, frame_rate=song.frame_rate)
|
preroll = AudioSegment.silent(
|
||||||
|
duration=start * -1, frame_rate=song.frame_rate)
|
||||||
start = 0
|
start = 0
|
||||||
else:
|
else:
|
||||||
preroll = None
|
preroll = None
|
||||||
if end > len(song):
|
if end > len(song):
|
||||||
postroll = AudioSegment.silent(duration=end - len(song), frame_rate=song.frame_rate)
|
postroll = AudioSegment.silent(
|
||||||
|
duration=end - len(song), frame_rate=song.frame_rate)
|
||||||
end = len(song) - 1
|
end = len(song) - 1
|
||||||
else:
|
else:
|
||||||
postroll = None
|
postroll = None
|
||||||
|
@ -282,7 +321,8 @@ class AnnotationIndex:
|
||||||
self.drawing_dir = drawing_dir
|
self.drawing_dir = drawing_dir
|
||||||
self.metadata_dir = metadata_dir
|
self.metadata_dir = metadata_dir
|
||||||
|
|
||||||
self.shelve = {} #disable disk cache because of glitches shelve.open(filename, writeback=True)
|
# disable disk cache because of glitches shelve.open(filename, writeback=True)
|
||||||
|
self.shelve = {}
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
# reset the index
|
# reset the index
|
||||||
|
@ -305,7 +345,8 @@ class AnnotationIndex:
|
||||||
if 'annotations' not in meta:
|
if 'annotations' not in meta:
|
||||||
continue
|
continue
|
||||||
for ann in meta['annotations']:
|
for ann in meta['annotations']:
|
||||||
annotation = Annotation(ann['tag'], drawing, ann['t_in'], ann['t_out'])
|
annotation = Annotation(
|
||||||
|
ann['tag'], drawing, ann['t_in'], ann['t_out'])
|
||||||
self.shelve['_annotations'][annotation.id] = annotation
|
self.shelve['_annotations'][annotation.id] = annotation
|
||||||
if annotation.tag not in self.shelve['_tags']:
|
if annotation.tag not in self.shelve['_tags']:
|
||||||
self.shelve['_tags'][annotation.tag] = [annotation]
|
self.shelve['_tags'][annotation.tag] = [annotation]
|
||||||
|
@ -314,7 +355,6 @@ class AnnotationIndex:
|
||||||
annotation
|
annotation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def drawings(self) -> dict[str, Drawing]:
|
def drawings(self) -> dict[str, Drawing]:
|
||||||
return self.shelve["_drawings"]
|
return self.shelve["_drawings"]
|
||||||
|
@ -363,23 +403,25 @@ class Point:
|
||||||
def fromTuple(cls, p: tuple[float, float, int, float]):
|
def fromTuple(cls, p: tuple[float, float, int, float]):
|
||||||
return cls(p[0], p[1], bool(p[2]), p[3])
|
return cls(p[0], p[1], bool(p[2]), p[3])
|
||||||
|
|
||||||
def scaled(self, dimensions: dict[str, float]) -> Point:
|
def scaledToFit(self, dimensions: dict[str, float]) -> Point:
|
||||||
return Point(self.x*dimensions['width'], self.y * dimensions['height'], self.last, self.t)
|
# TODO: change so that it actually scales to FIT dimensions
|
||||||
|
return Point(self.x, self.y, self.last, self.t)
|
||||||
|
|
||||||
|
|
||||||
Points = list[Point]
|
Points = list[Point]
|
||||||
SvgDrawing = Union[svgwrite.container.SVG, svgwrite.container.Group]
|
SvgDrawing = Union[svgwrite.container.SVG, svgwrite.container.Group]
|
||||||
|
|
||||||
|
|
||||||
class Stroke:
|
class Stroke:
|
||||||
def __init__(self, color: str, points: Points) -> None:
|
def __init__(self, color: str, points: Points) -> None:
|
||||||
self.color = color
|
self.color = color
|
||||||
self.points = points
|
self.points = points
|
||||||
|
|
||||||
def add_to_dwg(self, dwg: SvgDrawing):
|
def add_to_dwg(self, dwg: SvgDrawing):
|
||||||
path = svgwrite.path.Path(d=self.get_as_d()).stroke(self.color,1).fill("none")
|
path = svgwrite.path.Path(d=self.get_as_d()).stroke(
|
||||||
|
self.color, 1).fill("none")
|
||||||
dwg.add(path)
|
dwg.add(path)
|
||||||
|
|
||||||
|
|
||||||
def get_bounding_box(self) -> Viewbox:
|
def get_bounding_box(self) -> Viewbox:
|
||||||
min_x, max_x = float("inf"), float("-inf")
|
min_x, max_x = float("inf"), float("-inf")
|
||||||
min_y, max_y = float("inf"), float("-inf")
|
min_y, max_y = float("inf"), float("-inf")
|
||||||
|
@ -432,7 +474,7 @@ class StrokeSlice(Stroke):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def points(self) -> Points:
|
def points(self) -> Points:
|
||||||
return self.stroke.points[self.i_in : self.i_out + 1]
|
return self.stroke.points[self.i_in: self.i_out + 1]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self) -> str:
|
def color(self) -> str:
|
||||||
|
@ -441,9 +483,9 @@ class StrokeSlice(Stroke):
|
||||||
|
|
||||||
def strokes2D(strokes):
|
def strokes2D(strokes):
|
||||||
# strokes to a d attribute for a path
|
# strokes to a d attribute for a path
|
||||||
d = "";
|
d = ""
|
||||||
last_stroke = None;
|
last_stroke = None
|
||||||
cmd = "";
|
cmd = ""
|
||||||
for stroke in strokes:
|
for stroke in strokes:
|
||||||
if not last_stroke:
|
if not last_stroke:
|
||||||
d += f"M{stroke[0]},{stroke[1]} "
|
d += f"M{stroke[0]},{stroke[1]} "
|
||||||
|
@ -453,10 +495,11 @@ def strokes2D(strokes):
|
||||||
d += " m"
|
d += " m"
|
||||||
cmd = 'm'
|
cmd = 'm'
|
||||||
elif cmd != 'l':
|
elif cmd != 'l':
|
||||||
d+=' l '
|
d += ' l '
|
||||||
cmd = 'l'
|
cmd = 'l'
|
||||||
|
|
||||||
rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
rel_stroke = [stroke[0] - last_stroke[0],
|
||||||
|
stroke[1] - last_stroke[1]]
|
||||||
d += f"{rel_stroke[0]},{rel_stroke[1]} "
|
d += f"{rel_stroke[0]},{rel_stroke[1]} "
|
||||||
last_stroke = stroke
|
last_stroke = stroke
|
||||||
return d
|
return d
|
44
webserver.py
44
webserver.py
|
@ -47,6 +47,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
self.strokes = []
|
self.strokes = []
|
||||||
self.hasWritten = False
|
self.hasWritten = False
|
||||||
self.prev_file = None
|
self.prev_file = None
|
||||||
|
self.prev_file_duration = 0
|
||||||
self.dimensions = [None, None]
|
self.dimensions = [None, None]
|
||||||
|
|
||||||
# def check_origin(self, origin):
|
# def check_origin(self, origin):
|
||||||
|
@ -126,15 +127,42 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
|
|
||||||
self.prev_file = prev_file
|
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()
|
first_line = fp.readline().strip()
|
||||||
if first_line.endswith(","):
|
if first_line.endswith(","):
|
||||||
first_line = first_line[:-1]
|
first_line = first_line[:-1]
|
||||||
|
|
||||||
metadata = json.loads(first_line)
|
metadata = json.loads(first_line)
|
||||||
|
|
||||||
self.write_message(json.dumps(
|
return metadata
|
||||||
{"preloaded_svg": f"/drawing/{file}", "dimensions": [metadata[1], metadata[2]]}))
|
|
||||||
|
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
|
# the client sent the message
|
||||||
def on_message(self, message):
|
def on_message(self, message):
|
||||||
|
@ -144,12 +172,19 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
msg = json.loads(message)
|
msg = json.loads(message)
|
||||||
if msg["event"] == "stroke":
|
if msg["event"] == "stroke":
|
||||||
logger.info("stroke")
|
logger.info("stroke")
|
||||||
|
for i in range(len(msg['points'])):
|
||||||
|
msg['points'][i][3] += self.prev_file_duration
|
||||||
self.appendEvent(msg)
|
self.appendEvent(msg)
|
||||||
elif msg["event"] == "dimensions":
|
elif msg["event"] == "dimensions":
|
||||||
self.dimensions = [int(msg["width"]), int(msg["height"])]
|
self.dimensions = [int(msg["width"]), int(msg["height"])]
|
||||||
logger.info(f"{self.dimensions=}")
|
logger.info(f"{self.dimensions=}")
|
||||||
elif msg["event"] == "viewbox":
|
elif msg["event"] == "viewbox":
|
||||||
logger.info("move or resize")
|
logger.info("move or resize")
|
||||||
|
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)
|
self.appendEvent(msg)
|
||||||
elif msg["event"] == "preload":
|
elif msg["event"] == "preload":
|
||||||
self.preloadFile(msg["file"])
|
self.preloadFile(msg["file"])
|
||||||
|
@ -253,6 +288,9 @@ class AnimationHandler(tornado.web.RequestHandler):
|
||||||
drawing["time"] = event[0]
|
drawing["time"] = event[0]
|
||||||
drawing["dimensions"] = [event[1], event[2]]
|
drawing["dimensions"] = [event[1], event[2]]
|
||||||
else:
|
else:
|
||||||
|
if type(event) is list:
|
||||||
|
# ignore double metadatas, which appear when continuaing an existing drawing
|
||||||
|
continue
|
||||||
if event["event"] == "viewbox":
|
if event["event"] == "viewbox":
|
||||||
pass
|
pass
|
||||||
if event["event"] == "stroke":
|
if event["event"] == "stroke":
|
||||||
|
|
|
@ -56,7 +56,7 @@ class StrokeGroup {
|
||||||
let cmd = "";
|
let cmd = "";
|
||||||
for (let stroke of strokes) {
|
for (let stroke of strokes) {
|
||||||
if (!last_stroke) {
|
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';
|
cmd = 'M';
|
||||||
} else {
|
} else {
|
||||||
if (last_stroke[2] == 1) {
|
if (last_stroke[2] == 1) {
|
||||||
|
@ -67,7 +67,7 @@ class StrokeGroup {
|
||||||
cmd = 'l';
|
cmd = 'l';
|
||||||
}
|
}
|
||||||
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
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;
|
last_stroke = stroke;
|
||||||
|
|
||||||
|
|
17
www/draw.js
17
www/draw.js
|
@ -61,7 +61,7 @@ class Canvas {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
this.colors = ["black", "red", "blue", "green"];
|
this.colors = ["black", "#cc1414", "blue", "green"];
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
|
@ -292,14 +292,15 @@ class Canvas {
|
||||||
getCoordinates(e) {
|
getCoordinates(e) {
|
||||||
// convert event coordinates into relative positions on x & y axis
|
// convert event coordinates into relative positions on x & y axis
|
||||||
let box = this.svgEl.getBoundingClientRect();
|
let box = this.svgEl.getBoundingClientRect();
|
||||||
let x = (e.x - box['left'] + this.viewbox.x) / box['width'];
|
let x = (e.x - box['left'] + this.viewbox.x);
|
||||||
let y = (e.y - box['top'] + this.viewbox.y) / box['height'];
|
let y = (e.y - box['top'] + this.viewbox.y);
|
||||||
return { 'x': x, 'y': y };
|
return { 'x': x, 'y': y };
|
||||||
}
|
}
|
||||||
|
|
||||||
isInsideBounds(pos) {
|
// isInsideBounds(pos) {
|
||||||
return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1);
|
// let box = this.svgEl.getBoundingClientRect();
|
||||||
}
|
// return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > box['width'] || pos['y'] > box['height']);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
draw(e) {
|
draw(e) {
|
||||||
|
@ -377,7 +378,7 @@ class Canvas {
|
||||||
let cmd = "";
|
let cmd = "";
|
||||||
for (let stroke of strokes) {
|
for (let stroke of strokes) {
|
||||||
if (!last_stroke) {
|
if (!last_stroke) {
|
||||||
d += `M${stroke[0] * this.viewbox.width},${stroke[1] * this.viewbox.height} `;
|
d += `M${stroke[0]},${stroke[1]} `;
|
||||||
cmd = 'M';
|
cmd = 'M';
|
||||||
} else {
|
} else {
|
||||||
if (last_stroke[2] == 1) {
|
if (last_stroke[2] == 1) {
|
||||||
|
@ -388,7 +389,7 @@ class Canvas {
|
||||||
cmd = 'l';
|
cmd = 'l';
|
||||||
}
|
}
|
||||||
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
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;
|
last_stroke = stroke;
|
||||||
|
|
||||||
|
|
14
www/play.js
14
www/play.js
|
@ -122,7 +122,7 @@ class Player {
|
||||||
// inactive is what comes before and after.
|
// inactive is what comes before and after.
|
||||||
// then, playing the video is just running pathRanghe(0, playhead)
|
// then, playing the video is just running pathRanghe(0, playhead)
|
||||||
drawStrokePosition(path_i, point_i, show_all) {
|
drawStrokePosition(path_i, point_i, show_all) {
|
||||||
if(typeof show_all === 'undefined')
|
if (typeof show_all === 'undefined')
|
||||||
show_all = false;
|
show_all = false;
|
||||||
|
|
||||||
// check if anything is placed that is in the future from the current playhead
|
// 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
|
// 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];
|
return [null, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,12 +202,12 @@ class Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
playStrokePosition(path_i, point_i, allow_interrupt) {
|
playStrokePosition(path_i, point_i, allow_interrupt) {
|
||||||
if(allow_interrupt) {
|
if (allow_interrupt) {
|
||||||
if(!this.isPlaying) {
|
if (!this.isPlaying) {
|
||||||
console.log('not playing because of interrupt');
|
console.log('not playing because of interrupt');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else{
|
} else {
|
||||||
this.isPlaying = true;
|
this.isPlaying = true;
|
||||||
}
|
}
|
||||||
this.drawStrokePosition(path_i, point_i);
|
this.drawStrokePosition(path_i, point_i);
|
||||||
|
@ -281,7 +281,7 @@ class Player {
|
||||||
let cmd = "";
|
let cmd = "";
|
||||||
for (let stroke of strokes) {
|
for (let stroke of strokes) {
|
||||||
if (!last_stroke) {
|
if (!last_stroke) {
|
||||||
d += `M${stroke[0] * this.dimensions[0]},${stroke[1] * this.dimensions[1]} `;
|
d += `M${stroke[0]},${stroke[1]} `;
|
||||||
cmd = 'M';
|
cmd = 'M';
|
||||||
} else {
|
} else {
|
||||||
if (last_stroke[2] == 1) {
|
if (last_stroke[2] == 1) {
|
||||||
|
@ -292,7 +292,7 @@ class Player {
|
||||||
cmd = 'l';
|
cmd = 'l';
|
||||||
}
|
}
|
||||||
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
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;
|
last_stroke = stroke;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue