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