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