use macroquad::prelude::{Color, Vec2};
use crate::easing::Easing;
use crate::style;
use crate::timeline::{Clip, Prop, TargetValue, TextEvent, TrackSpec, Value};
#[derive(Debug, Clone)]
enum Verb {
MoveTo(String, Vec2),
MoveBy(String, Vec2),
FadeIn(String),
FadeOut(String),
ColorTo(String, Color),
Highlight(String, Color),
ScaleTo(String, f32),
Pulse(String),
Shake(String),
GrowTo(String, Vec2),
SetText(String, String),
}
impl Verb {
fn default_dur(&self) -> f32 {
match self {
Verb::FadeIn(_) | Verb::FadeOut(_) => 0.35,
Verb::Highlight(..) => 1.0,
Verb::Pulse(_) => 0.5,
Verb::Shake(_) => 0.45,
Verb::SetText(..) => 0.4,
_ => 0.5,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ActBuilder {
verb: Option<Verb>,
dur: Option<f32>,
easing: Easing,
}
pub fn act() -> ActBuilder {
ActBuilder::default()
}
pub fn wait(s: f32) -> Clip {
Clip::wait(s)
}
impl ActBuilder {
fn verb(mut self, v: Verb) -> Self {
self.verb = Some(v);
self
}
pub fn move_to(self, id: &str, to: Vec2) -> Self {
self.verb(Verb::MoveTo(id.into(), to))
}
pub fn move_by(self, id: &str, by: Vec2) -> Self {
self.verb(Verb::MoveBy(id.into(), by))
}
pub fn fade_in(self, id: &str) -> Self {
self.verb(Verb::FadeIn(id.into()))
}
pub fn fade_out(self, id: &str) -> Self {
self.verb(Verb::FadeOut(id.into()))
}
pub fn color_to(self, id: &str, c: Color) -> Self {
self.verb(Verb::ColorTo(id.into(), c))
}
pub fn highlight(self, id: &str, c: Color) -> Self {
self.verb(Verb::Highlight(id.into(), c))
}
pub fn scale_to(self, id: &str, s: f32) -> Self {
self.verb(Verb::ScaleTo(id.into(), s))
}
pub fn pulse(self, id: &str) -> Self {
self.verb(Verb::Pulse(id.into()))
}
pub fn shake(self, id: &str) -> Self {
self.verb(Verb::Shake(id.into()))
}
pub fn grow_to(self, id: &str, to: Vec2) -> Self {
self.verb(Verb::GrowTo(id.into(), to))
}
pub fn retarget(self, id: &str, to: Vec2) -> Self {
self.grow_to(id, to)
}
pub fn set_text(self, id: &str, text: &str) -> Self {
self.verb(Verb::SetText(id.into(), text.into()))
}
pub fn dur(mut self, s: f32) -> Self {
self.dur = Some(s);
self
}
pub fn ease(mut self, e: Easing) -> Self {
self.easing = e;
self
}
}
fn track(id: &str, prop: Prop, target: TargetValue, start: f32, dur: f32, easing: Easing) -> TrackSpec {
TrackSpec { id: id.into(), prop, target, start, dur, easing }
}
fn build_clip(b: ActBuilder) -> Clip {
let verb = b.verb.expect("ActBuilder used without a verb (e.g. .move_to(..))");
let d = b.dur.unwrap_or_else(|| verb.default_dur());
let e = b.easing;
let mut clip = Clip { dur: d, ..Default::default() };
match verb {
Verb::MoveTo(id, to) => {
clip.tracks.push(track(&id, Prop::Pos, TargetValue::Abs(Value::V(to)), 0.0, d, e));
}
Verb::MoveBy(id, by) => {
clip.tracks.push(track(&id, Prop::Pos, TargetValue::Rel(Value::V(by)), 0.0, d, e));
}
Verb::FadeIn(id) => {
clip.tracks.push(track(&id, Prop::Opacity, TargetValue::Abs(Value::F(1.0)), 0.0, d, e));
}
Verb::FadeOut(id) => {
clip.tracks.push(track(&id, Prop::Opacity, TargetValue::Abs(Value::F(0.0)), 0.0, d, e));
}
Verb::ColorTo(id, c) => {
clip.tracks.push(track(&id, Prop::Color, TargetValue::Abs(Value::C(c)), 0.0, d, e));
}
Verb::Highlight(id, c) => {
clip.tracks.push(track(&id, Prop::Color, TargetValue::Abs(Value::C(c)), 0.0, d * 0.25, Easing::OutQuad));
clip.tracks.push(track(&id, Prop::Color, TargetValue::Revert, d * 0.75, d * 0.25, Easing::InQuad));
}
Verb::ScaleTo(id, s) => {
clip.tracks.push(track(&id, Prop::Scale, TargetValue::Abs(Value::F(s)), 0.0, d, e));
}
Verb::Pulse(id) => {
clip.tracks.push(track(&id, Prop::Scale, TargetValue::Rel(Value::F(0.18)), 0.0, d * 0.4, Easing::OutQuad));
clip.tracks.push(track(&id, Prop::Scale, TargetValue::Revert, d * 0.4, d * 0.6, Easing::OutBack));
}
Verb::Shake(id) => {
let offs = [10.0, -16.0, 12.0, -8.0, 5.0, -3.0f32];
let seg = d / offs.len() as f32;
for (i, dx) in offs.iter().enumerate() {
clip.tracks.push(track(
&id,
Prop::Pos,
TargetValue::Rel(Value::V(Vec2::new(*dx, 0.0))),
seg * i as f32,
seg,
Easing::InOutQuad,
));
}
}
Verb::GrowTo(id, to) => {
clip.tracks.push(track(&id, Prop::To, TargetValue::Abs(Value::V(to)), 0.0, d, e));
}
Verb::SetText(id, text) => {
clip.tracks.push(track(&id, Prop::Opacity, TargetValue::Abs(Value::F(0.0)), 0.0, d * 0.4, Easing::InQuad));
clip.events.push(TextEvent { id: id.clone(), content: text, at: d * 0.5 });
clip.tracks.push(track(&id, Prop::Opacity, TargetValue::Abs(Value::F(1.0)), d * 0.6, d * 0.4, Easing::OutQuad));
}
}
clip
}
impl From<ActBuilder> for Clip {
fn from(b: ActBuilder) -> Clip {
build_clip(b)
}
}
pub fn flash(id: &str) -> ActBuilder {
act().highlight(id, style::ACCENT)
}
#[macro_export]
macro_rules! seq {
($($c:expr),* $(,)?) => {
$crate::timeline::Clip::seq(vec![$(::std::convert::Into::<$crate::timeline::Clip>::into($c)),*])
};
}
#[macro_export]
macro_rules! par {
($($c:expr),* $(,)?) => {
$crate::timeline::Clip::par(vec![$(::std::convert::Into::<$crate::timeline::Clip>::into($c)),*])
};
}