Python script now correctly handles in and outpoint differently + formatting
This commit is contained in:
parent
8e6274601e
commit
50d4656ad7
1 changed files with 80 additions and 37 deletions
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue