Python script now correctly handles in and outpoint differently + formatting

This commit is contained in:
Ruben van de Ven 2022-03-15 13:41:12 +01:00
parent 8e6274601e
commit 50d4656ad7

View file

@ -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
@ -95,7 +96,8 @@ class Drawing:
strokes.append( strokes.append(
Stroke( Stroke(
event["color"], event["color"],
[Point.fromTuple(tuple(p)) 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())
@ -157,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)
@ -199,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)
@ -220,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 :-)
@ -250,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
@ -281,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
@ -304,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]
@ -313,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"]
@ -370,16 +411,17 @@ class Point:
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")
@ -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]} "
@ -456,7 +498,8 @@ def strokes2D(strokes):
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