double transition;
} timeline_t;
+#define TIMELINE_ADVANCE_TIMER_PERIOD_MS 500
+#define TIMELINE_TRANSITION_FPS 20
+
timeline_t timeline[] = {
{ 0.5, ANIM_BLANK, 0.0},
{ 1.0, ANIM_HEAD, 0.0},
{ 2.0, ANIM_BODY, 0.0},
{ 7.0, ANIM_SPIN, 6.0},
{ 1.0, ANIM_SPIN_DONE, 0.0},
- { 1.0, ANIM_OUTLINE, 0.0},
- { 1.0, ANIM_FADE, 0.5},
+ { 1.0, ANIM_OUTLINE, 0.1},
+ { 3.0, ANIM_FADE, 1.0},
{ 0.0, ANIM_DONE }
};
GtkWidget *drawing_area;
double progress;
anim_stage_t anim_stage;
+ int stage_ticks_remaining;
+ /* frames are used during a transition */
+ int num_frames;
+ int frame;
+ double transition;
} state_t;
typedef struct _color {
}
static void
-draw_body (cairo_t *cr, double extra)
+draw_body (cairo_t *cr, double extra, double spin_transition)
{
+ cairo_save (cr);
+
cairo_move_to (cr,
XO_BODY_CENTER_X - XO_BODY_DELTA,
- XO_BODY_CENTER_Y - XO_BODY_DELTA);
- cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+ XO_BODY_CENTER_Y + XO_BODY_DELTA);
cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+ cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+
+ cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
+ cairo_rotate (cr, 2 * M_PI * spin_transition);
+ cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
+
cairo_move_to (cr,
XO_BODY_CENTER_X - XO_BODY_DELTA,
- XO_BODY_CENTER_Y + XO_BODY_DELTA);
- cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+ XO_BODY_CENTER_Y - XO_BODY_DELTA);
cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+ cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+
cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_stroke (cr);
+
+ cairo_restore (cr);
}
static void
-draw_dots (cairo_t *cr)
+draw_dots (cairo_t *cr, double num_dots)
{
int i;
set_color (cr, &dot_gray);
cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
- cairo_rotate (cr, - M_PI_2);
-
- for (i = 0; i < XO_DOTS_NUM_DOTS; i++) {
- cairo_arc (cr,
- XO_DOTS_POSITION_RADIUS, 0,
- XO_DOTS_DOT_RADIUS,
- 0, 2 * M_PI);
- cairo_fill (cr);
+ cairo_rotate (cr, - 1.5 * M_PI);
+
+ for (i = 0; i < num_dots; i++) {
+ if (i != 0) {
+ cairo_arc (cr,
+ XO_DOTS_POSITION_RADIUS, 0,
+ XO_DOTS_DOT_RADIUS,
+ 0, 2 * M_PI);
+ cairo_fill (cr);
+ }
cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
}
anim_stage_t anim_stage = state->anim_stage;
cairo_t *cr;
double shrink;
+ double spin_transition = 1.0;
cr = gdk_cairo_create (widget->window);
if (anim_stage < ANIM_FADE)
set_color (cr, &background[0]);
else
- set_color (cr, &background[1]);
+ cairo_set_source_rgb (cr, LERP_COLORS (background[0],
+ background[1],
+ state->transition));
cairo_paint (cr);
if (anim_stage == ANIM_BLANK)
goto DONE;
+ if (anim_stage == ANIM_SPIN)
+ spin_transition = state->transition;
+
shrink = 0.0;
if (anim_stage >= ANIM_OUTLINE) {
set_color (cr, &xo_black);
draw_head (cr, XO_OUTLINE_EXTRA);
- draw_body (cr, XO_OUTLINE_EXTRA);
+ draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
shrink = - XO_OUTLINE_SHRINK;
+ if (anim_stage == ANIM_OUTLINE)
+ shrink *= state->transition;
}
set_color (cr, &xo_green);
if (anim_stage == ANIM_HEAD)
goto DONE;
- draw_body (cr, shrink);
+ draw_body (cr, shrink, spin_transition);
if (anim_stage == ANIM_BODY)
goto DONE;
- if (anim_stage < ANIM_FADE)
- draw_dots (cr);
- else
+ if (anim_stage < ANIM_FADE) {
+ draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition);
+ } else {
draw_ring (cr);
+ }
DONE:
cairo_destroy (cr);
return drawing_area;
}
+static gint
+transition_advance (gpointer closure)
+{
+ state_t *state = closure;
+
+ if (state->frame >= state->num_frames - 1) {
+ state->frame = state->num_frames - 1;
+ return FALSE;
+ }
+
+ state->frame++;
+ state->transition = (double) state->frame / (state->num_frames - 1);
+
+ gtk_widget_queue_draw (state->drawing_area);
+
+ return TRUE;
+}
+
static gint
timeline_advance (gpointer closure)
{
state_t *state = closure;
+ timeline_t *tl;
if (state->anim_stage == ANIM_DONE)
return FALSE;
+ if (--state->stage_ticks_remaining)
+ return TRUE;
+
state->anim_stage++;
+ tl = &timeline[state->anim_stage];
+ state->stage_ticks_remaining = (tl->duration * 1000
+ / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
gtk_widget_queue_draw (state->drawing_area);
+ if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
+ state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
+ state->frame = 0;
+ state->transition = 0.0;
+ g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
+ } else {
+ state->num_frames = 1;
+ state->frame = 0;
+ state->transition = 1.0;
+ }
+
return TRUE;
}
state.drawing_area = create_window (&state);
state.progress = 0.0;
state.anim_stage = 0;
+ state.num_frames = 1;
+ state.frame = 0;
+ state.transition = 1.0;
+ state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
+ / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
- g_timeout_add (1000, timeline_advance, &state);
+ g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
+ timeline_advance, &state);
gtk_main ();