From: Carl Worth <cworth@cworth.org>
Date: Tue, 28 Aug 2007 07:41:54 +0000 (-0700)
Subject: Add transition animations
X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=fbf45e3901075ef7e42f58cbfcf55947a8278e7e;p=xoboot

Add transition animations

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.
---

diff --git a/xoboot.c b/xoboot.c
index 14cfd07..4daa763 100644
--- a/xoboot.c
+++ b/xoboot.c
@@ -21,14 +21,17 @@ typedef struct _timeline {
     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 }
 };
 
@@ -36,6 +39,11 @@ typedef struct _state {
     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 {
@@ -98,25 +106,35 @@ draw_head (cairo_t *cr, double extra)
 }
 
 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;
 
@@ -125,14 +143,16 @@ draw_dots (cairo_t *cr)
     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);
     }
     
@@ -171,25 +191,33 @@ xoboot_expose_event (GtkWidget      *widget,
     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);
@@ -198,14 +226,15 @@ xoboot_expose_event (GtkWidget      *widget,
     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);
@@ -238,17 +267,53 @@ create_window (state_t *state)
     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;
 }
 
@@ -262,10 +327,16 @@ main (int argc, char *argv[])
     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 ();