···
from collections import defaultdict
+
from numbers import Number
from .. import elements, types
from . import playback_control_ui, playback_control_js
···
repeat_count: Union[int, str] = 'indefinite'
+
freeze_frame_at: Optional[float] = None
show_playback_progress: bool = False
···
self, controls_width=width, controls_x=x, controls_center_y=y,
+
def override_args(self, args, lcontext):
+
if (self.freeze_frame_at is not None
+
and hasattr(lcontext.element, 'animation_data')):
+
data = lcontext.element.animation_data
+
args.update(data.interpolate_at_time(self.freeze_frame_at))
class AnimatedAttributeTimeline:
···
raise ValueError('out-of-order key frame times')
self.values.extend(values)
+
def interpolate_at_time(self, at_time):
+
return linear_interpolate_value(self.times, self.values, at_time)
def as_animate_element(self, config: Optional[SyncedAnimationConfig]=None):
···
self.attr_timelines[attr] = timeline
timeline.extend(times, values)
+
def interpolate_at_time(self, at_time):
+
name: timeline.interpolate_at_time(at_time)
+
for name, timeline in self.attr_timelines.items()
def _timelines_adjusted_for_context(self, lcontext=None):
all_timelines = dict(self.attr_timelines)
if lcontext is not None and lcontext.context.invert_y:
···
yvalues = [lcontext.element.args.get('y', 0)]
if y_timeline is not None or height_timeline is not None:
ytimes, yvalues = _merge_timeline_inverted_y_values(
+
ytimes, yvalues, htimes, hvalues,
+
linear_interpolate_value, linear_interpolate_value)
y_timeline = AnimatedAttributeTimeline(
'y', y_attrs, ytimes, yvalues)
···
def children_with_context(self, lcontext=None):
+
if (lcontext is not None
+
and lcontext.context.animation_config is not None
+
and lcontext.context.animation_config.freeze_frame_at
+
return [] # Don't animate if frame is frozen
all_timelines = self._timelines_adjusted_for_context(lcontext)
timeline.as_animate_element(lcontext.context.animation_config)
···
+
def linear_interpolate_value(times, values, at_time):
+
print(times, values, at_time)
+
idx = sum(t <= at_time for t in times)
+
elif at_time == times[idx-1]:
+
elif isinstance(values[idx], Number) and isinstance(values[idx-1], Number):
+
fraction = (at_time-times[idx-1]) / (times[idx]-times[idx-1])
+
return values[idx-1] * (1-fraction) + (values[idx] * fraction)
+
def _merge_timeline_inverted_y_values(ytimes, yvalues, htimes, hvalues,
+
yinterpolate, hinterpolate):
return htimes, [-yvalues[0]-h for h in hvalues]
···
return ytimes, [-y-h for y, h in zip(yvalues, hvalues)]
# Offset y-value by height if invert_y
# Merge key_times for y and height animations
···
yt = ytimes[0] if len(ytimes) else inf
while ht < inf and yt < inf:
+
h_val = hinterpolate(htimes, hvalues, yt)
new_values.append(-yvalues[yi] - h_val)
+
y_val = yinterpolate(ytimes, yvalues, ht)
new_values.append(-y_val - hvalues[hi])