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
1 changed files with 80 additions and 37 deletions

View File

@ -12,20 +12,21 @@ import logging
logger = logging.getLogger('svganim.strokes')
class Annotation:
def __init__(self, tag: str, drawing: Drawing, t_in: float, t_out: float) -> None:
self.tag = tag
self.t_in = t_in
self.t_out = t_out
self.drawing = drawing
@property
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)
def get_as_svg(self) -> str:
return self.getAnimationSlice().get_as_svg()
@ -68,8 +69,8 @@ class Drawing:
return None
if 'file' not in md['audio']:
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:
# with open(self.eventfile, "r") as fp:
@ -95,10 +96,11 @@ class Drawing:
strokes.append(
Stroke(
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())
def get_metadata(self):
canvas = self.get_canvas_metadata()
@ -157,12 +159,11 @@ class AnimationSlice:
return Viewbox(min_x, min_y, max_x - min_x, max_y - min_y)
def getSlice(self, t_in: float, t_out: float) -> AnimationSlice:
frame_in = self.getIndexForTime(t_in)
frame_out = self.getIndexForTime(t_out)
frame_in = self.getIndexForInPoint(t_in)
frame_out = self.getIndexForOutPoint(t_out)
strokes = self.getStrokeSlices(frame_in, frame_out)
audio = self.audio.getSlice(t_in, t_out) if self.audio else None
return AnimationSlice(strokes, t_in, t_out, audio)
def get_as_svg_dwg(self) -> svgwrite.Drawing:
box = self.get_bounding_box()
@ -177,7 +178,7 @@ class AnimationSlice:
fp = io.StringIO()
dwg.write(fp, pretty=True)
return fp.getvalue()
def add_to_dwg(self, dwg: SvgDrawing):
group = svgwrite.container.Group()
for stroke in self.strokes:
@ -199,11 +200,47 @@ class AnimationSlice:
break
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))
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:
"""Get the frame index (path, point) based on the given time
Equal to annotations.js findPositionForTime(ms)
@ -220,7 +257,7 @@ class AnimationSlice:
# we are getting close, find the right point_i
path_i = i
for pi, point in enumerate(stroke.points):
if point.t:
if point.t > ms:
break # too far
point_i = pi
break # done :-)
@ -233,12 +270,12 @@ class AnimationSlice:
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.t_in = t_in # in ms
self.t_out = t_out # in ms
self.offset = offset # in ms
self.offset = offset # in ms
def getSlice(self, t_in: float, t_out: float) -> AnimationSlice:
return AudioSlice(self.filename, t_in, t_out, self.offset)
@ -250,19 +287,22 @@ class AudioSlice:
end = self.t_out - self.offset
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:
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
else:
preroll = None
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
else:
postroll = None
extract = song[start: end]
if preroll:
extract = preroll + extract
@ -281,7 +321,8 @@ class AnnotationIndex:
self.drawing_dir = drawing_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):
# reset the index
@ -304,7 +345,8 @@ class AnnotationIndex:
if 'annotations' not in meta:
continue
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
if annotation.tag not in self.shelve['_tags']:
self.shelve['_tags'][annotation.tag] = [annotation]
@ -312,11 +354,10 @@ class AnnotationIndex:
self.shelve['_tags'][annotation.tag].append(
annotation
)
@property
def drawings(self) -> dict[str, Drawing]:
return self.shelve["_drawings"]
return self.shelve["_drawings"]
@property
def tags(self) -> dict[str, list[Annotation]]:
@ -354,14 +395,14 @@ class AnnotationIndex:
class Point:
def __init__(self, x: float, y: float, last: bool, t: float):
self.x = float(x)
self.y = float(y) # if y == 0 it can still be integer.... odd python
self.y = float(y) # if y == 0 it can still be integer.... odd python
self.last = last
self.t = t
@classmethod
def fromTuple(cls, p: tuple[float, float, int, float]):
return cls(p[0], p[1], bool(p[2]), p[3])
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)
@ -370,16 +411,17 @@ class Point:
Points = list[Point]
SvgDrawing = Union[svgwrite.container.SVG, svgwrite.container.Group]
class Stroke:
def __init__(self, color: str, points: Points) -> None:
self.color = color
self.points = points
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)
def get_bounding_box(self) -> Viewbox:
min_x, max_x = float("inf"), float("-inf")
min_y, max_y = float("inf"), float("-inf")
@ -432,7 +474,7 @@ class StrokeSlice(Stroke):
@property
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
def color(self) -> str:
@ -441,22 +483,23 @@ class StrokeSlice(Stroke):
def strokes2D(strokes):
# strokes to a d attribute for a path
d = "";
last_stroke = None;
cmd = "";
d = ""
last_stroke = None
cmd = ""
for stroke in strokes:
if not last_stroke:
d += f"M{stroke[0]},{stroke[1]} "
cmd = 'M'
else:
if last_stroke[2] == 1:
if last_stroke[2] == 1:
d += " m"
cmd = 'm'
elif cmd != 'l':
d+=' l '
d += ' 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]} "
last_stroke = stroke
return d
return d