Something went wrong. Try again.
Live STIB-MIVB departures on the Pebble Time 2. It covers the bus, tram, and metro network of Brussels
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254#include "anim.h"
#define LOADING_CYCLE_MS 900#define PULSE_CYCLE_MS 600#define PULSE_CYCLES 3#define PULSE_MAX_DELTA 3#define SLIDE_MS 220#define SLIDE_DROP 6#define DOT_RADIUS 3#define DOT_SPACING 14#define DOT_LIFT 6#define MARQUEE_HOLD_MS 800#define MARQUEE_PLAY_MS 60000#define MARQUEE_PLAY_PX 1800
typedef struct { Animation *anim; Layer *layer; int32_t value;} Effect;
static Effect s_loading;static Effect s_pulse;static Effect s_slide;static Effect s_marquee;static uint32_t s_marquee_id;
static void effect_mark(Effect *effect) { if (effect->layer) { layer_mark_dirty(effect->layer); }}
static int16_t effect_get_int16(void *subject) { return (int16_t)((Effect *)subject)->value;}
static void effect_set_int16(void *subject, int16_t value) { Effect *effect = subject; effect->value = value; effect_mark(effect);}static uint32_t effect_get_uint32(void *subject) { return (uint32_t)((Effect *)subject)->value;}
static void effect_set_uint32(void *subject, uint32_t value) { Effect *effect = subject; effect->value = (int32_t)value; effect_mark(effect);}
static const PropertyAnimationImplementation s_int16_impl = { .base = { .update = (AnimationUpdateImplementation)property_animation_update_int16, }, .accessors = { .setter = { .int16 = effect_set_int16 }, .getter = { .int16 = effect_get_int16 }, },};
static const PropertyAnimationImplementation s_uint32_impl = { .base = { .update = (AnimationUpdateImplementation)property_animation_update_uint32, }, .accessors = { .setter = { .uint32 = effect_set_uint32 }, .getter = { .uint32 = effect_get_uint32 }, },};static void effect_update_progress(Animation *anim, const AnimationProgress progress) { Effect *effect = animation_get_context(anim); effect->value = (int32_t)progress; effect_mark(effect);}
static const AnimationImplementation s_progress_impl = { .update = effect_update_progress };
static void effect_stopped(Animation *anim, bool finished, void *context) { Effect *effect = context; effect->anim = NULL; effect->value = 0; effect_mark(effect); effect->layer = NULL;}
static void effect_stop(Effect *effect) { effect->layer = NULL; effect->value = 0; if (effect->anim) { Animation *anim = effect->anim; effect->anim = NULL; animation_unschedule(anim); }}
static Animation *effect_int16_anim(Effect *effect, int16_t from, int16_t to, uint32_t duration) { PropertyAnimation *prop = property_animation_create(&s_int16_impl, effect, &from, &to); if (!prop) { return NULL; } Animation *anim = property_animation_get_animation(prop); animation_set_duration(anim, duration); animation_set_curve(anim, AnimationCurveLinear); return anim;}static Animation *effect_uint32_anim(Effect *effect, uint32_t from, uint32_t to, uint32_t duration) { PropertyAnimation *prop = property_animation_create(&s_uint32_impl, effect, &from, &to); if (!prop) { return NULL; } Animation *anim = property_animation_get_animation(prop); animation_set_duration(anim, duration); animation_set_curve(anim, AnimationCurveLinear); return anim;}
void anim_loading_start(Layer *layer) { if (s_loading.anim && s_loading.layer == layer) { return; } effect_stop(&s_loading); Animation *anim = effect_uint32_anim(&s_loading, 0, ANIMATION_NORMALIZED_MAX, LOADING_CYCLE_MS); if (!anim) { return; } s_loading.layer = layer; animation_set_play_count(anim, ANIMATION_PLAY_COUNT_INFINITE); animation_set_handlers(anim, (AnimationHandlers){ .stopped = effect_stopped }, &s_loading); s_loading.anim = anim; animation_schedule(anim);}
void anim_loading_stop(void) { effect_stop(&s_loading);}
void anim_draw_loading(GContext *ctx, GRect box, GColor color) { int16_t cx = box.origin.x + box.size.w / 2; int16_t cy = box.origin.y + box.size.h / 2; int32_t window = ANIMATION_NORMALIZED_MAX / 3; graphics_context_set_fill_color(ctx, color); for (int16_t i = 0; i < 3; i++) { int16_t dy = 0; if (s_loading.anim) { int32_t t = s_loading.value - i * window; if (t >= 0 && t < window) { int32_t tri = t < window / 2 ? t : window - t; dy = (int16_t)(tri * DOT_LIFT * 2 / window); } } graphics_fill_circle(ctx, GPoint(cx + (i - 1) * DOT_SPACING, cy - dy), DOT_RADIUS); }}
void anim_pulse_start(Layer *layer) { effect_stop(&s_pulse); s_pulse.layer = layer; Animation *up = effect_int16_anim(&s_pulse, 0, PULSE_MAX_DELTA, PULSE_CYCLE_MS / 2); if (!up) { effect_stop(&s_pulse); return; } Animation *down = effect_int16_anim(&s_pulse, PULSE_MAX_DELTA, 0, PULSE_CYCLE_MS / 2); if (!down) { animation_destroy(up); effect_stop(&s_pulse); return; } Animation *seq = animation_sequence_create(up, down, NULL); if (!seq) { animation_destroy(up); animation_destroy(down); effect_stop(&s_pulse); return; } animation_set_play_count(seq, PULSE_CYCLES); animation_set_handlers(seq, (AnimationHandlers){ .stopped = effect_stopped }, &s_pulse); s_pulse.anim = seq; animation_schedule(seq);}
int16_t anim_pulse_delta(void) { return (int16_t)s_pulse.value;}
void anim_slide_start(Layer *layer, GRect rest) { effect_stop(&s_slide); GRect from = rest; from.origin.y += SLIDE_DROP; PropertyAnimation *prop = property_animation_create_layer_frame(layer, &from, &rest); if (!prop) { return; } s_slide.layer = layer; Animation *anim = property_animation_get_animation(prop); animation_set_duration(anim, SLIDE_MS); animation_set_curve(anim, AnimationCurveEaseOut); animation_set_handlers(anim, (AnimationHandlers){ .stopped = effect_stopped }, &s_slide); s_slide.anim = anim; animation_schedule(anim);}
void anim_marquee_stop(void) { effect_stop(&s_marquee);}
void anim_marquee_start(Layer *layer, uint32_t id) { if (s_marquee.anim && s_marquee.layer == layer && s_marquee_id == id) { return; } effect_stop(&s_marquee); Animation *anim = animation_create(); if (!anim) { return; } s_marquee_id = id; s_marquee.layer = layer; s_marquee.value = 0; animation_set_implementation(anim, &s_progress_impl); animation_set_duration(anim, MARQUEE_PLAY_MS); animation_set_delay(anim, MARQUEE_HOLD_MS); animation_set_curve(anim, AnimationCurveLinear); animation_set_handlers(anim, (AnimationHandlers){ .stopped = effect_stopped }, &s_marquee); s_marquee.anim = anim; animation_schedule(anim);}
int16_t anim_marquee_offset(uint32_t id) { if (!s_marquee.anim || s_marquee_id != id) { return 0; } return (int16_t)(s_marquee.value * MARQUEE_PLAY_PX / ANIMATION_NORMALIZED_MAX);}
void anim_layer_gone(Layer *layer) { if (s_loading.layer == layer) { effect_stop(&s_loading); } if (s_pulse.layer == layer) { effect_stop(&s_pulse); } if (s_slide.layer == layer) { effect_stop(&s_slide); } if (s_marquee.layer == layer) { effect_stop(&s_marquee); }}