Source code for fury.animation.interpolator

import numpy as np
from scipy.interpolate import splev, splprep
from scipy.spatial import transform

from fury.animation.helpers import (
    euclidean_distances,
    get_next_timestamp,
    get_previous_timestamp,
    get_time_tau,
    get_timestamps_from_keyframes,
    get_values_from_keyframes,
    lerp,
)
from fury.colormap import hsv2rgb, lab2rgb, rgb2hsv, rgb2lab, rgb2xyz, xyz2rgb


[docs] def spline_interpolator(keyframes, degree): """N-th degree spline interpolator for keyframes. This is a general n-th degree spline interpolator to be used for any shape of keyframes data. Parameters ---------- keyframes: dict Keyframe data containing timestamps and values to form the spline curve. Data should be on the following format: {1: {'value': np.array([...])}, 2: {'value': np.array([...])}} Returns ------- function The interpolation function that take time and return interpolated value at that time. """ if len(keyframes) < (degree + 1): raise ValueError( f"Minimum {degree + 1} " f"keyframes must be set in order to use " f"{degree}-degree spline" ) timestamps = get_timestamps_from_keyframes(keyframes) values = get_values_from_keyframes(keyframes) distances = euclidean_distances(values) distances_sum = sum(distances) cumulative_dist_sum = np.cumsum([0] + distances) tck = splprep(values.T, k=degree, full_output=1, s=0)[0][0] def interpolate(t): t0 = get_previous_timestamp(timestamps, t) t1 = get_next_timestamp(timestamps, t) mi_index = np.where(timestamps == t0)[0][0] dt = get_time_tau(t, t0, t1) section = cumulative_dist_sum[mi_index] ts = (section + dt * distances[mi_index]) / distances_sum return np.array(splev(ts, tck)) return interpolate
[docs] def cubic_spline_interpolator(keyframes): """Cubic spline interpolator for keyframes. This is a general cubic spline interpolator to be used for any shape of keyframes data. Parameters ---------- keyframes: dict Keyframe data containing timestamps and values to form the cubic spline curve. Returns ------- function The interpolation function that take time and return interpolated value at that time. See Also -------- spline_interpolator """ return spline_interpolator(keyframes, degree=3)
[docs] def step_interpolator(keyframes): """Step interpolator for keyframes. This is a simple step interpolator to be used for any shape of keyframes data. Parameters ---------- keyframes: dict Keyframe data containing timestamps and values to form the spline Returns ------- function The interpolation function that take time and return interpolated value at that time. """ timestamps = get_timestamps_from_keyframes(keyframes) def interpolate(t): previous_t = get_previous_timestamp(timestamps, t, include_last=True) return keyframes.get(previous_t).get("value") return interpolate
[docs] def linear_interpolator(keyframes): """Linear interpolator for keyframes. This is a general linear interpolator to be used for any shape of keyframes data. Parameters ---------- keyframes: dict Keyframe data to be linearly interpolated. Returns ------- function The interpolation function that take time and return interpolated value at that time. """ timestamps = get_timestamps_from_keyframes(keyframes) is_single = len(keyframes) == 1 def interpolate(t): if is_single: t = timestamps[0] return keyframes.get(t).get("value") t0 = get_previous_timestamp(timestamps, t) t1 = get_next_timestamp(timestamps, t) p0 = keyframes.get(t0).get("value") p1 = keyframes.get(t1).get("value") return lerp(p0, p1, t0, t1, t) return interpolate
[docs] def cubic_bezier_interpolator(keyframes): """Cubic Bézier interpolator for keyframes. This is a general cubic Bézier interpolator to be used for any shape of keyframes data. Parameters ---------- keyframes : dict Keyframes to be interpolated at any time. Returns ------- function The interpolation function that take time and return interpolated value at that time. Notes ----- If no control points are set in the keyframes, The cubic Bézier interpolator will almost behave as a linear interpolator. """ timestamps = get_timestamps_from_keyframes(keyframes) for ts in timestamps: # keyframe at timestamp kf_ts = keyframes.get(ts) if kf_ts.get("in_cp") is None: kf_ts["in_cp"] = kf_ts.get("value") if kf_ts.get("out_cp") is None: kf_ts["out_cp"] = kf_ts.get("value") def interpolate(t): t0 = get_previous_timestamp(timestamps, t) t1 = get_next_timestamp(timestamps, t) k0 = keyframes.get(t0) k1 = keyframes.get(t1) p0 = k0.get("value") p1 = k0.get("out_cp") p2 = k1.get("in_cp") p3 = k1.get("value") dt = get_time_tau(t, t0, t1) val = ( (1 - dt) ** 3 * p0 + 3 * (1 - dt) ** 2 * dt * p1 + 3 * (1 - dt) * dt**2 * p2 + dt**3 * p3 ) return val return interpolate
[docs] def slerp(keyframes): """Spherical based rotation keyframes interpolator. A rotation interpolator to be used for rotation keyframes. Parameters ---------- keyframes : dict Rotation keyframes to be interpolated at any time. Returns ------- function The interpolation function that take time and return interpolated value at that time. Notes ----- Rotation keyframes must be in the form of quaternions. """ timestamps = get_timestamps_from_keyframes(keyframes) quat_rots = [] for ts in timestamps: quat_rots.append(keyframes.get(ts).get("value")) rotations = transform.Rotation.from_quat(quat_rots) # if only one keyframe specified, linear interpolator is used. if len(timestamps) == 1: return linear_interpolator(keyframes) slerp_interp = transform.Slerp(timestamps, rotations) min_t = timestamps[0] max_t = timestamps[-1] def interpolate(t): t = min_t if t < min_t else max_t if t > max_t else t v = slerp_interp(t) q = v.as_quat() return q return interpolate
[docs] def color_interpolator(keyframes, rgb2space, space2rgb): """Custom-space color interpolator. Interpolate values linearly inside a custom color space. Parameters ---------- keyframes : dict Rotation keyframes to be interpolated at any time. rgb2space: function A functions that take color value in rgb and return that color converted to the targeted space. space2rgb: function A functions that take color value in the targeted space and returns that color in rgb space. Returns ------- function The interpolation function that take time and return interpolated value at that time. """ timestamps = get_timestamps_from_keyframes(keyframes) space_keyframes = {} is_single = len(keyframes) == 1 for ts, keyframe in keyframes.items(): space_keyframes[ts] = rgb2space(keyframe.get("value")) def interpolate(t): if is_single: t = timestamps[0] return keyframes.get(t).get("value") t0 = get_previous_timestamp(timestamps, t) t1 = get_next_timestamp(timestamps, t) c0 = space_keyframes.get(t0) c1 = space_keyframes.get(t1) space_color_val = lerp(c0, c1, t0, t1, t) return space2rgb(space_color_val) return interpolate
[docs] def hsv_color_interpolator(keyframes): """HSV interpolator for color keyframes See Also -------- color_interpolator """ return color_interpolator(keyframes, rgb2hsv, hsv2rgb)
[docs] def lab_color_interpolator(keyframes): """LAB interpolator for color keyframes See Also -------- color_interpolator """ return color_interpolator(keyframes, rgb2lab, lab2rgb)
[docs] def xyz_color_interpolator(keyframes): """XYZ interpolator for color keyframes See Also -------- color_interpolator """ return color_interpolator(keyframes, rgb2xyz, xyz2rgb)
[docs] def tan_cubic_spline_interpolator(keyframes): """Cubic spline interpolator for keyframes using tangents. glTF contains additional tangent information for the cubic spline interpolator. Parameters ---------- keyframes: dict Keyframe data containing timestamps and values to form the cubic spline curve. Returns ------- function The interpolation function that take time and return interpolated value at that time. """ timestamps = get_timestamps_from_keyframes(keyframes) for time in keyframes: data = keyframes.get(time) value = data.get("value") if data.get("in_tangent") is None: data["in_tangent"] = np.zeros_like(value) if data.get("in_tangent") is None: data["in_tangent"] = np.zeros_like(value) def interpolate(t): t0 = get_previous_timestamp(timestamps, t) t1 = get_next_timestamp(timestamps, t) dt = get_time_tau(t, t0, t1) time_delta = t1 - t0 p0 = keyframes.get(t0).get("value") tan_0 = keyframes.get(t0).get("out_tangent") * time_delta p1 = keyframes.get(t1).get("value") tan_1 = keyframes.get(t1).get("in_tangent") * time_delta # cubic spline equation using tangents t2 = dt * dt t3 = t2 * dt return ( (2 * t3 - 3 * t2 + 1) * p0 + (t3 - 2 * t2 + dt) * tan_0 + (-2 * t3 + 3 * t2) * p1 + (t3 - t2) * tan_1 ) return interpolate