At this point, things look _really_ close to the original Quicktime
animation. The most significant defect is that the circle of dots
and the rotating hands have different center points. Currently,
these rotate at basically the same angular speed, which means the
hands are not pointing nicely at the dots. We should be just a
touch more clever to make the dots appear exactly as they are
pointed to by the hands.
double transition;
} timeline_t;
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},
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},
GtkWidget *drawing_area;
double progress;
anim_stage_t anim_stage;
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 {
} state_t;
typedef struct _color {
-draw_body (cairo_t *cr, double extra)
+draw_body (cairo_t *cr, double extra, double spin_transition)
cairo_move_to (cr,
XO_BODY_CENTER_X - XO_BODY_DELTA,
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_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,
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_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_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_stroke (cr);
+draw_dots (cairo_t *cr, double num_dots)
set_color (cr, &dot_gray);
cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
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);
}
cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
}
anim_stage_t anim_stage = state->anim_stage;
cairo_t *cr;
double shrink;
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
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;
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);
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;
shrink = - XO_OUTLINE_SHRINK;
+ if (anim_stage == ANIM_OUTLINE)
+ shrink *= state->transition;
}
set_color (cr, &xo_green);
}
set_color (cr, &xo_green);
if (anim_stage == ANIM_HEAD)
goto DONE;
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_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 {
DONE:
cairo_destroy (cr);
DONE:
cairo_destroy (cr);
+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;
static gint
timeline_advance (gpointer closure)
{
state_t *state = closure;
if (state->anim_stage == ANIM_DONE)
return FALSE;
if (state->anim_stage == ANIM_DONE)
return FALSE;
+ if (--state->stage_ticks_remaining)
+ return TRUE;
+
+ tl = &timeline[state->anim_stage];
+ state->stage_ticks_remaining = (tl->duration * 1000
+ / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
gtk_widget_queue_draw (state->drawing_area);
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;
+ }
+
state.drawing_area = create_window (&state);
state.progress = 0.0;
state.anim_stage = 0;
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));
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);