]> git.cworth.org Git - akamaru/blob - akamaru.c
Generalize offset constraint to work on a number of points.
[akamaru] / akamaru.c
1 /*                                           -*- mode: c; c-basic-offset: 2 -*-
2  * To compile:
3  *
4  *     gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) \
5  *             akamaru.c -o akamaru
6  *
7  * See:
8  *
9  *     http://en.wikipedia.org/wiki/Verlet_integration
10  *     http://www.teknikus.dk/tj/gdc2001.htm
11  *
12  * TODO:
13  *
14  * - Add code to add boxes
15  * - Add circle object
16  * - Try out this idea: make constraint solver take mean of all
17  *   corrections at the end instead of meaning as it goes.
18  */
19
20 #include <gtk/gtk.h>
21 #include <cairo.h>
22 #include <cairo-xlib.h>
23 #include <gdk/gdkx.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
27 #include <math.h>
28
29 const double ground_level = 500;
30 const double elasticity = 0.7;
31
32 typedef struct _xy_pair Point;
33 typedef struct _xy_pair Vector;
34 struct _xy_pair {
35   double x, y;
36 };
37
38 typedef struct _Object Object;
39 typedef struct _Stick Stick;
40 typedef struct _String String;
41 typedef struct _Polygon Polygon;
42 typedef struct _Offset Offset;
43 typedef struct _Model Model;
44
45 struct _Object {
46   Vector force;
47
48   Point position;
49   Point previous_position;
50   Vector velocity;
51
52   double mass;
53   double theta;
54 };
55
56 struct _Stick {
57   Object *a, *b;
58   int length;
59 };
60
61 struct _String {
62   Object *a, *b;
63   int length;
64 };
65
66 struct _Offset {
67   int num_objects;
68   Object **objects;
69   int dx, dy;
70 };
71
72 struct _Polygon {
73   int num_points;
74   Point *points;
75   Vector *normals;
76   int edge;
77 };
78
79 struct _Model {
80   int num_objects;
81   Object *objects;
82   int num_sticks;
83   Stick *sticks;
84   int num_strings;
85   String *strings;
86   int num_offsets;
87   Offset *offsets;
88   int num_polygons;
89   Polygon *polygons;
90   double k;
91   double friction;
92
93   Object *anchor_object;
94   Vector anchor_position;
95
96   double theta;
97 };
98
99 static void
100 polygon_init (Polygon *p, int num_points, ...)
101 {
102   double dx, dy, length;
103   int i, j;
104   va_list ap;
105
106   /* Polygons are defined counter-clock-wise in a coordinate system
107    * with the y-axis pointing down. */
108
109   va_start (ap, num_points);
110   p->num_points = num_points;
111   p->points = g_new (Point, num_points);
112
113   for (i = 0; i < num_points; i++) {
114     p->points[i].x = va_arg (ap, double);
115     p->points[i].y = va_arg (ap, double);
116   }
117   va_end (ap);
118   
119   p->normals = g_new (Vector, p->num_points);
120   /* Compute outward pointing normals.  p->normals[i] is the normal
121    * for the edged between p->points[i] and p->points[i + 1]. */
122  for (i = 0; i < p->num_points; i++) {
123     j = (i + 1) % p->num_points;
124     dx = p->points[j].x - p->points[i].x;
125     dy = p->points[j].y - p->points[i].y;
126     length = sqrt (dx * dx + dy * dy);
127     p->normals[i].x = -dy / length;
128     p->normals[i].y = dx / length;
129   }
130 }
131
132 static void
133 polygon_init_diamond (Polygon *polygon, double x, double y)
134 {
135   return polygon_init (polygon, 5, 
136                        x, y, 
137                        x + 10, y + 40,
138                        x + 90, y + 40,
139                        x + 100, y,
140                        x + 50, y - 20);
141 }
142
143 static void
144 polygon_init_rectangle (Polygon *polygon, double x0, double y0,
145                         double x1, double y1)
146 {
147   return polygon_init (polygon, 4, x0, y0, x0, y1, x1, y1, x1, y0);
148 }
149
150 static void
151 model_init_polygons (Model *model)
152 {
153   const int num_polygons = 5;
154
155   model->polygons = g_new (Polygon, num_polygons);
156   polygon_init_diamond (&model->polygons[0], 250, 300);
157   polygon_init_diamond (&model->polygons[1], 400, 150);
158   polygon_init_rectangle (&model->polygons[2], -100, 200, 200, 250);
159   polygon_init_rectangle (&model->polygons[3], -200, ground_level,
160                           1200, ground_level + 400);
161
162   polygon_init_rectangle (&model->polygons[4], 300, 320, 400, 350);
163
164
165   model->num_polygons = num_polygons;
166 }
167
168 static void
169 model_init_snake (Model *model)
170 {
171   const int num_objects = 20;
172   const int num_sticks = num_objects * 2 - 3;
173   int i;
174
175   memset (model, 0, sizeof *model);
176   model->objects = g_new (Object, num_objects);
177   model->num_objects = num_objects;
178   model->sticks = g_new (Stick, num_sticks);
179   model->num_sticks = num_sticks;
180   model_init_polygons (model);
181
182   for (i = 0; i < num_objects; i++) {
183     model->objects[i].position.x = random() % 200 + 20;
184     model->objects[i].position.y = random() % 200 + 20;
185     model->objects[i].previous_position.x = random() % 200 + 20;
186     model->objects[i].previous_position.y = random() % 200 + 20;
187
188     if (i + 1 < num_objects) {
189       model->sticks[i * 2].a = &model->objects[i];
190       model->sticks[i * 2].b = &model->objects[i + 1];
191       model->sticks[i * 2].length = random() % 20 + 20;
192     }
193     if (i + 2 < num_objects) {
194       model->sticks[i * 2 + 1].a = &model->objects[i];
195       model->sticks[i * 2 + 1].b = &model->objects[i + 2];
196       model->sticks[i * 2 + 1].length = random() % 20 + 20;
197     }
198   }
199
200   model->anchor_object = NULL;
201 }
202
203 static void
204 model_init_rope (Model *model)
205 {
206   const int num_objects = 20;
207   const int num_sticks = num_objects - 1;
208   const int stick_length = 10;
209   int i;
210
211   memset (model, 0, sizeof *model);
212   model->objects = g_new (Object, num_objects);
213   model->num_objects = num_objects;
214   model->sticks = g_new (Stick, num_sticks);
215   model->num_sticks = num_sticks;
216   model_init_polygons (model);
217
218   for (i = 0; i < num_objects; i++) {
219     model->objects[i].position.x = 200;
220     model->objects[i].position.y = 40 + i * stick_length;
221     model->objects[i].previous_position.x = 200;
222     model->objects[i].previous_position.y = 40 + i * stick_length;
223
224     if (i + 1 < num_objects) {
225       model->sticks[i].a = &model->objects[i];
226       model->sticks[i].b = &model->objects[i + 1];
227       model->sticks[i].length = stick_length;
228     }
229   }
230
231   model->anchor_object = NULL;
232 }
233
234 static void
235 model_init_curtain (Model *model)
236 {
237   const int num_ropes = 5;
238   const int num_rope_objects = 15;
239   const int num_objects = num_ropes * num_rope_objects;
240   const int num_sticks = num_ropes * (num_rope_objects - 1);
241   const int stick_length = 10;
242   const int rope_offset = 30;
243   double x, y;
244   int i, j, index, stick_index;
245
246   memset (model, 0, sizeof *model);
247   model->objects = g_new (Object, num_objects);
248   model->num_objects = num_objects;
249   model->sticks = g_new (Stick, num_sticks);
250   model->num_sticks = num_sticks;
251   model->offsets = g_new (Offset, 1);
252   model->num_offsets = 1;
253   model_init_polygons (model);
254
255   model->offsets[0].num_objects = num_ropes;
256   model->offsets[0].objects = g_new (Object *, num_ropes);
257   model->offsets[0].dx = rope_offset;
258   model->offsets[0].dy = 0;
259
260   for (i = 0; i < num_ropes; i++) {
261     for (j = 0; j < num_rope_objects; j++) {
262       x = 200 + i * rope_offset;
263       y = 40 + j * stick_length;
264       index = i * num_rope_objects + j;
265       model->objects[index].position.x = x;
266       model->objects[index].position.y = y;
267       model->objects[index].previous_position.x = x;
268       model->objects[index].previous_position.y = y;
269
270       if (j + 1 < num_rope_objects) {
271         stick_index = i * (num_rope_objects - 1) + j;
272         model->sticks[stick_index].a = &model->objects[index];
273         model->sticks[stick_index].b = &model->objects[index + 1];
274         model->sticks[stick_index].length = stick_length;
275       }
276     }
277
278     model->offsets[0].objects[i] = &model->objects[i * num_rope_objects];
279   }
280
281   model->anchor_object = NULL;
282 }
283
284 static void
285 model_init_grid (Model *model)
286 {
287   const int num_ropes = 10;
288   const int num_rope_objects = 10;
289   const int num_objects = num_ropes * num_rope_objects;
290   const int num_strings = num_ropes * (num_rope_objects - 1) +
291     (num_ropes - 1) * num_rope_objects;
292   const int string_length = 10;
293   const int rope_offset = 10;
294   double x, y;
295   int i, j, index, string_index;
296
297   memset (model, 0, sizeof *model);
298   model->objects = g_new (Object, num_objects);
299   model->num_objects = num_objects;
300   model->strings = g_new (String, num_strings);
301   model->num_strings = num_strings;
302   model->offsets = g_new (Offset, 1);
303   model->num_offsets = 1;
304   model_init_polygons (model);
305
306   model->offsets[0].num_objects = num_ropes;
307   model->offsets[0].objects = g_new (Object *, num_ropes);
308   model->offsets[0].dx = rope_offset;
309   model->offsets[0].dy = 0;
310
311   for (i = 0; i < num_ropes; i++) {
312     for (j = 0; j < num_rope_objects; j++) {
313       x = 200 + i * rope_offset;
314       y = 40 + j * string_length;
315       index = i * num_rope_objects + j;
316       model->objects[index].position.x = x;
317       model->objects[index].position.y = y;
318       model->objects[index].previous_position.x = x;
319       model->objects[index].previous_position.y = y;
320
321       if (i + 1 < num_ropes) {
322         string_index = i * num_rope_objects + j;
323         model->strings[string_index].a = &model->objects[index];
324         model->strings[string_index].b = &model->objects[index + num_rope_objects];
325         model->strings[string_index].length = string_length;
326       }
327
328       if (j + 1 < num_rope_objects) {
329         string_index =
330           (num_ropes - 1) * num_rope_objects + i * (num_rope_objects - 1) + j;
331         model->strings[string_index].a = &model->objects[index];
332         model->strings[string_index].b = &model->objects[index + 1];
333         model->strings[string_index].length = string_length;
334       }
335     }
336
337     model->offsets[0].objects[i] = &model->objects[i * num_rope_objects];
338   }
339
340   model->anchor_object = NULL;
341 }
342
343 static void
344 model_fini (Model *model)
345 {
346   g_free (model->objects);
347   g_free (model->sticks);
348   g_free (model->strings);
349   g_free (model->offsets);
350   memset (model, 0, sizeof *model);
351 }
352
353 static void
354 model_accumulate_forces (Model *model)
355 {
356   int i;
357
358   for (i = 0; i < model->num_objects; i++) {
359     model->objects[i].force.x = 0;
360     model->objects[i].force.y = 3;
361   }
362 }
363
364 static void
365 model_integrate (Model *model, double step)
366 {
367   double x, y;
368   Object *o;
369   int i;
370
371   for (i = 0; i < model->num_objects; i++) {
372     o = &model->objects[i];
373     x = o->position.x;
374     y = o->position.y;
375     
376     o->position.x =
377       x + 0.9 * (x - o->previous_position.x) + o->force.x * step * step;
378     o->position.y =
379       y + 0.9 * (y - o->previous_position.y) + o->force.y * step * step;
380
381     o->previous_position.x = x;
382     o->previous_position.y = y;
383   }
384 }
385
386 /* The square root in the distance computation for the string and
387  * stick constraints can be aproximated using Newton:
388  *
389  *    distance = 
390  *      (model->sticks[i].length +
391  *       (dx * dx + dy * dy) / model->sticks[i].length) / 2;
392  *
393  * This works really well, since the constraints aren't typically
394  * violated much.  Thus, the distance is really close to the stick
395  * length, which then makes a good initial guess.  However, the
396  * approximation seems to be slower that just calling sqrt()...
397  */
398
399 static inline double
400 estimate_distance (double dx, double dy, double r)
401 {
402 #ifdef APPROXIMATE_SQUARE_ROOTS
403   return (r + (dx * dx + dy * dy) / r) / 2;
404 #else
405   return sqrt (dx * dx + dy * dy);
406 #endif
407 }
408
409 static int
410 polygon_contains_point (Polygon *polygon, Point *point)
411 {
412   int i;
413   double dx, dy;
414
415   for (i = 0; i < polygon->num_points; i++) {
416     dx = point->x - polygon->points[i].x;
417     dy = point->y - polygon->points[i].y;
418
419     if (polygon->normals[i].x * dx + polygon->normals[i].y * dy >= 0)
420       return FALSE;
421   }
422
423   return TRUE;
424 }
425
426 static void
427 polygon_reflect_object (Polygon *polygon, Object *object)
428 {
429   int i, edge;
430   double d, distance;
431   Vector *n;
432
433   distance = -1000;
434   for (i = 0; i < polygon->num_points; i++) {
435     d = polygon->normals[i].x * (object->position.x - polygon->points[i].x) +
436       polygon->normals[i].y * (object->position.y - polygon->points[i].y);
437
438     if (d > distance) {
439       distance = d;
440       edge = i;
441       polygon->edge = i;
442       n = &polygon->normals[i];
443     }
444   }
445
446   object->position.x -= (1 + elasticity) * distance * n->x;
447   object->position.y -= (1 + elasticity) * distance * n->y;
448
449   distance =
450     n->x * (object->previous_position.x - polygon->points[edge].x) +
451     n->y * (object->previous_position.y - polygon->points[edge].y);
452
453   object->previous_position.x -= (1 + elasticity) * distance * n->x;
454   object->previous_position.y -= (1 + elasticity) * distance * n->y;
455 }
456
457 static void
458 model_constrain_polygon (Model *model, Polygon *polygon)
459 {
460   int i;
461
462   for (i = 0; i < model->num_objects; i++) {
463     if (polygon_contains_point (polygon, &model->objects[i].position))
464       polygon_reflect_object (polygon, &model->objects[i]);
465   }
466 }
467
468 static void
469 model_constrain_offset (Model *model, Offset *offset)
470 {
471   double x, y;
472   int i;
473
474   x = 0;
475   y = 0;
476   for (i = 0; i < offset->num_objects; i++) {
477     x += offset->objects[i]->position.x;
478     y += offset->objects[i]->position.y;
479   }
480
481   x = x / offset->num_objects - offset->dx * (offset->num_objects - 1) / 2;
482   y = y / offset->num_objects - offset->dy * (offset->num_objects - 1) / 2;
483     
484   for (i = 0; i < offset->num_objects; i++) {
485     offset->objects[i]->position.x = x + offset->dx * i;
486     offset->objects[i]->position.y = y + offset->dy * i;
487   }
488 }
489
490 static void
491 model_constrain (Model *model)
492 {
493   double dx, dy, x, y, distance, fraction;
494   int i;
495
496   /* Anchor object constraint. */
497   if (model->anchor_object != NULL) {
498     model->anchor_object->position.x = model->anchor_position.x;
499     model->anchor_object->position.y = model->anchor_position.y;
500     model->anchor_object->previous_position.x = model->anchor_position.x;
501     model->anchor_object->previous_position.y = model->anchor_position.y;
502   }
503
504   /* String constraints. */
505   for (i = 0; i < model->num_strings; i++) {
506     x = model->strings[i].a->position.x;
507     y = model->strings[i].a->position.y;
508     dx = model->strings[i].b->position.x - x;
509     dy = model->strings[i].b->position.y - y;
510     distance = estimate_distance (dx, dy, model->strings[i].length);
511     if (distance < model->strings[i].length)
512       continue;
513     fraction = (distance - model->strings[i].length) / distance / 2;
514     model->strings[i].a->position.x = x + dx * fraction;
515     model->strings[i].a->position.y = y + dy * fraction;
516     model->strings[i].b->position.x = x + dx * (1 - fraction);
517     model->strings[i].b->position.y = y + dy * (1 - fraction);
518   }
519
520   /* Stick constraints. */
521   for (i = 0; i < model->num_sticks; i++) {
522     x = model->sticks[i].a->position.x;
523     y = model->sticks[i].a->position.y;
524     dx = model->sticks[i].b->position.x - x;
525     dy = model->sticks[i].b->position.y - y;
526     distance = estimate_distance (dx, dy, model->sticks[i].length);
527     fraction = (distance - model->sticks[i].length) / distance / 2;
528     model->sticks[i].a->position.x = x + dx * fraction;
529     model->sticks[i].a->position.y = y + dy * fraction;
530     model->sticks[i].b->position.x = x + dx * (1 - fraction);
531     model->sticks[i].b->position.y = y + dy * (1 - fraction);
532   }
533
534   /* Offset constraints. */
535   for (i = 0; i < model->num_offsets; i++)
536     model_constrain_offset (model, &model->offsets[i]);
537
538   /* Polygon constraints. */
539   for (i = 0; i < model->num_polygons; i++)
540     model_constrain_polygon (model, &model->polygons[i]);
541 }
542
543 static void
544 model_step (Model *model, double delta_t)
545 {
546   int i;
547
548   model_accumulate_forces (model);
549   model_integrate (model, delta_t);
550
551   for (i = 0; i < 500; i++)
552     model_constrain (model);
553
554   model->theta += delta_t;
555 }
556
557 static double
558 object_distance (Object *object, double x, double y)
559 {
560   double dx, dy;
561
562   dx = object->position.x - x;
563   dy = object->position.y - y;
564
565   return sqrt (dx*dx + dy*dy);
566 }
567
568 static Object *
569 model_find_nearest (Model *model, double x, double y)
570 {
571   Object *object;
572   double distance, min_distance;
573   int i;
574
575   for (i = 0; i < model->num_objects; i++) {
576     distance = object_distance (&model->objects[i], x, y);
577     if (i == 0 || distance < min_distance) {
578       min_distance = distance;
579       object = &model->objects[i];
580     }
581   }
582
583   return object;
584 }
585
586 typedef struct _Color Color;
587 struct _Color {
588   double red, green, blue;
589 };
590
591 static void
592 draw_sticks (cairo_t *cr,
593              Model   *model,
594              Color   *color)
595 {
596   int i;
597
598   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 1);
599   cairo_new_path (cr);
600   cairo_set_line_width (cr, 2);
601   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
602   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
603
604   for (i = 0; i < model->num_sticks; i++) {
605     cairo_move_to (cr,
606                    model->sticks[i].a->position.x,
607                    model->sticks[i].a->position.y);
608     cairo_line_to (cr,
609                    model->sticks[i].b->position.x,
610                    model->sticks[i].b->position.y);
611   }
612
613   cairo_stroke (cr);
614 }
615
616 static void
617 draw_strings (cairo_t *cr,
618               Model   *model,
619               Color   *color)
620 {
621   int i;
622
623   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 1);
624   cairo_new_path (cr);
625   cairo_set_line_width (cr, 1);
626   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
627   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
628
629   for (i = 0; i < model->num_strings; i++) {
630     cairo_move_to (cr,
631                    model->strings[i].a->position.x,
632                    model->strings[i].a->position.y);
633     cairo_line_to (cr,
634                    model->strings[i].b->position.x,
635                    model->strings[i].b->position.y);
636   }
637
638   cairo_stroke (cr);
639 }
640
641 static void
642 draw_offsets (cairo_t *cr,
643               Model   *model,
644               Color   *color)
645 {
646   int i, j;
647
648   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.5);
649   cairo_new_path (cr);
650   cairo_set_line_width (cr, 4);
651   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
652   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
653
654   for (i = 0; i < model->num_offsets; i++) {
655     for (j = 0; j < model->offsets[i].num_objects; j++) {
656       cairo_line_to (cr,
657                      model->offsets[i].objects[j]->position.x,
658                      model->offsets[i].objects[j]->position.y);
659     }
660     cairo_stroke (cr);
661   }
662
663 }
664
665 static void
666 draw_polygons (cairo_t *cr, Model *model, Color *color)
667 {
668   Polygon *p;
669   int i, j;
670
671   for (i = 0; i < model->num_polygons; i++) {
672     p = &model->polygons[i];
673     cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.4);
674
675     
676     for (j = 0; j < p->num_points; j++)
677       cairo_line_to (cr, p->points[j].x, p->points[j].y);
678     cairo_close_path (cr);
679   }
680   cairo_fill (cr);
681
682 }
683
684 static void
685 draw_objects (cairo_t *cr, Model *model, Color *color)
686 {
687   int i;
688
689   for (i = 0; i < model->num_objects; i++) {
690   }
691 }
692
693 static Color blue = { 0, 0, 1 };
694 static Color green = { 0, 1, 0 };
695 static Color red = { 1, 0, 0 };
696 static Color black = { 0, 0, 0 };
697 static Color white = { 1, 1, 1 };
698
699 typedef struct _Closure Closure;
700 struct _Closure {
701   GtkWidget *drawing_area;
702   GtkWidget *fps_label;
703   Model *model;
704   int frame_count;
705   int i;
706   struct timeval start;
707 };
708
709 static void
710 draw_model (GtkWidget *widget, Model *model)
711 {
712   cairo_t *cr;
713
714   cr = gdk_cairo_create (widget->window);
715
716   cairo_set_source_rgb (cr, 1, 1, 1);
717   cairo_paint (cr);
718
719   draw_polygons (cr, model, &blue);
720   draw_sticks (cr, model, &black);
721   draw_strings (cr, model, &green);
722   draw_offsets (cr, model, &blue);
723   draw_objects (cr, model, &white);
724
725   cairo_destroy (cr);
726 }
727
728 static gboolean
729 expose_event (GtkWidget      *widget,
730               GdkEventExpose *event,
731               gpointer        data)
732 {
733   Closure *closure = data;
734
735   draw_model (widget, closure->model);
736
737   return TRUE;
738 }
739
740 static gboolean
741 button_press_event (GtkWidget      *widget,
742                     GdkEventButton *event,
743                     gpointer        data)
744 {
745   Closure *closure = data;
746
747   if (event->button != 1)
748     return TRUE;
749
750   closure->model->anchor_position.x = event->x;
751   closure->model->anchor_position.y = event->y;
752   closure->model->anchor_object = model_find_nearest (closure->model,
753                                                       event->x, event->y);
754
755   return TRUE;
756 }
757
758 static gboolean
759 button_release_event (GtkWidget      *widget,
760                       GdkEventButton *event,
761                       gpointer        data)
762 {
763   Closure *closure = data;
764
765   if ((event->state & GDK_BUTTON1_MASK) == 0)
766     return TRUE;
767
768   closure->model->anchor_object = NULL;
769
770   return TRUE;
771 }
772
773 static gboolean
774 motion_notify_event (GtkWidget      *widget,
775                      GdkEventMotion *event,
776                      gpointer        data)
777 {
778   Closure *closure = data;
779   int x, y;
780   GdkModifierType state;
781
782   gdk_window_get_pointer (event->window, &x, &y, &state);
783   
784   closure->model->anchor_position.x = x + 0.5;
785   closure->model->anchor_position.y = y + 0.5;
786
787   return TRUE;
788 }
789
790 typedef void (*ModelInitFunc) (Model *model);
791
792 static void
793 model_changed (GtkComboBox *combo, gpointer user_data)
794 {
795   Closure *closure = user_data;
796   GtkTreeIter iter;
797   GtkTreeModel *tree_model;
798   ModelInitFunc init;
799   char *name;
800
801   tree_model = gtk_combo_box_get_model (combo);
802   if (!gtk_combo_box_get_active_iter (combo, &iter))
803     return;
804
805   gtk_tree_model_get (tree_model, &iter, 0, &name, 1, &init, -1);
806
807   model_fini (closure->model);
808   (*init) (closure->model);
809 }
810
811 static GtkTreeModel *
812 create_model_store (void)
813 {
814   static struct {
815     const char *name;
816     ModelInitFunc init;
817   } models[] = {
818     { "Rope", model_init_rope },
819     { "Snake", model_init_snake },
820     { "Curtain", model_init_curtain },
821     { "Grid", model_init_grid }
822   };
823
824   GtkTreeIter iter;
825   GtkTreeStore *store;
826   gint i;
827
828   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
829
830   for (i = 0; i < G_N_ELEMENTS(models); i++) {
831     gtk_tree_store_append (store, &iter, NULL);
832     gtk_tree_store_set (store, &iter,
833                         0, models[i].name, 1, models[i].init, -1);
834  }
835   
836   return GTK_TREE_MODEL (store);
837
838 }
839
840 static GtkWidget *
841 create_model_combo (Closure *closure)
842 {
843   GtkWidget *hbox;
844   GtkWidget *combo, *label;
845   GtkTreeModel *store;
846   GtkCellRenderer *renderer;
847
848   hbox = gtk_hbox_new (FALSE, 8);
849
850   label = gtk_label_new_with_mnemonic ("_Model:");
851   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
852
853   store = create_model_store ();
854   combo = gtk_combo_box_new_with_model (store);
855   gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
856   g_object_unref (store);
857
858   renderer = gtk_cell_renderer_text_new ();
859   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
860   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
861                                   "text", 0,
862                                   NULL);
863
864   gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
865   gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
866   g_signal_connect (combo, "changed",
867                     G_CALLBACK (model_changed), closure);
868
869   label = gtk_label_new ("Frames per second: 0");
870   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
871
872   closure->fps_label = label;
873
874   return hbox;
875 }
876
877 static void
878 create_window (Closure *closure)
879 {
880   GtkWidget *window;
881   GtkWidget *frame;
882   GtkWidget *vbox;
883   GtkWidget *da;
884   GtkWidget *model_combo;
885
886   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
887   gtk_window_set_title (GTK_WINDOW (window), "Akamaru");
888
889   g_signal_connect (window, "destroy",
890                     G_CALLBACK (gtk_main_quit), &window);
891
892   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
893
894   vbox = gtk_vbox_new (FALSE, 8);
895   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
896   gtk_container_add (GTK_CONTAINER (window), vbox);
897
898   /*
899    * Create the drawing area
900    */
901       
902   frame = gtk_frame_new (NULL);
903   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
904   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
905       
906   da = gtk_drawing_area_new ();
907   /* set a minimum size */
908   gtk_widget_set_size_request (da, 200, 200);
909
910   gtk_container_add (GTK_CONTAINER (frame), da);
911
912   /* Signals used to handle backing pixmap */
913       
914   g_signal_connect (da, "expose_event",
915                     G_CALLBACK (expose_event), closure);
916       
917   /* Event signals */
918       
919   g_signal_connect (da, "motion_notify_event",
920                     G_CALLBACK (motion_notify_event), closure);
921   g_signal_connect (da, "button_press_event",
922                     G_CALLBACK (button_press_event), closure);
923   g_signal_connect (da, "button_release_event",
924                     G_CALLBACK (button_release_event), closure);
925
926   /* Ask to receive events the drawing area doesn't normally
927    * subscribe to
928    */
929   gtk_widget_set_events (da, gtk_widget_get_events (da)
930                          | GDK_LEAVE_NOTIFY_MASK
931                          | GDK_BUTTON_PRESS_MASK
932                          | GDK_BUTTON_RELEASE_MASK
933                          | GDK_POINTER_MOTION_MASK
934                          | GDK_POINTER_MOTION_HINT_MASK);
935
936   model_combo = create_model_combo (closure);
937   gtk_box_pack_start (GTK_BOX (vbox), model_combo, FALSE, FALSE, 0);
938
939   closure->drawing_area = da;
940 }
941
942 static gint
943 timeout_callback (gpointer data)
944 {
945   Closure *closure = data;
946
947   model_step (closure->model, 1);
948
949   closure->i++;
950   if (closure->i == 1) {
951     gtk_widget_queue_draw (closure->drawing_area);
952     closure->i = 0;
953     closure->frame_count++;
954   }
955
956   if (closure->frame_count == 200) {
957     struct timeval end, elapsed;
958     double total;
959     char text[50];
960
961     closure->frame_count = 0;
962     gettimeofday (&end, NULL);
963     if (closure->start.tv_usec > end.tv_usec) {
964       end.tv_usec += 1000000;
965       end.tv_sec--;
966     }
967
968     elapsed.tv_usec = end.tv_usec - closure->start.tv_usec;
969     elapsed.tv_sec = end.tv_sec - closure->start.tv_sec;
970
971     total = elapsed.tv_sec + ((double) elapsed.tv_usec / 1e6);
972     if (total < 0) {
973       total = 0;
974     }
975     closure->start = end;
976     snprintf (text, sizeof text, "Frames per second: %.2f", 200 / total);
977     gtk_label_set_text (GTK_LABEL (closure->fps_label), text);
978   }
979
980   return TRUE;
981 }
982
983 int
984 main (int argc, char *argv[])
985 {
986   Closure closure;
987   Model model;
988
989   gtk_init (&argc, &argv);
990   model_init_rope (&model);
991   create_window (&closure);
992   closure.i = 0;
993   gtk_widget_show_all (gtk_widget_get_toplevel (closure.drawing_area));
994   closure.model = &model;
995   closure.frame_count = 0;
996   gettimeofday (&closure.start, NULL);
997   g_timeout_add (100, timeout_callback, &closure);
998   gtk_main ();
999
1000   return 0;
1001 }