]> git.cworth.org Git - akamaru/blob - akamaru.c
Add grid model.
[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  */
17
18 #include <gtk/gtk.h>
19 #include <cairo.h>
20 #include <cairo-xlib.h>
21 #include <gdk/gdkx.h>
22 #include <stdlib.h>
23 #include <math.h>
24
25 const double ground_friction = 0.1, ground_level = 400;
26 const double box_left = 200, box_top = 200, box_bottom = 210;
27 const double elasticity = 0.7;
28 const double edge_fuzz = 1;
29
30 typedef struct _xy_pair Point;
31 typedef struct _xy_pair Vector;
32 struct _xy_pair {
33   double x, y;
34 };
35
36 typedef struct _Object Object;
37 typedef struct _Stick Stick;
38 typedef struct _Offset Offset;
39 typedef struct _Model Model;
40
41 struct _Object {
42   Vector force;
43
44   Point position;
45   Point previous_position;
46   Vector velocity;
47
48   double mass;
49   double theta;
50 };
51
52 struct _Stick {
53   Object *a, *b;
54   int length;
55 };
56
57 struct _Offset {
58   Object *a, *b;
59   int dx, dy;
60 };
61
62 struct _Model {
63   int num_objects;
64   Object *objects;
65   int num_sticks;
66   Stick *sticks;
67   int num_offsets;
68   Offset *offsets;
69   double k;
70   double friction;
71
72   Object *anchor_object;
73   Vector anchor_position;
74
75   double theta;
76 };
77
78 static void
79 model_init_snake (Model *model)
80 {
81   const int num_objects = 20;
82   const int num_sticks = num_objects * 2 - 3;
83   int i;
84
85   model->objects = g_new (Object, num_objects);
86   model->num_objects = num_objects;
87   model->sticks = g_new (Stick, num_sticks);
88   model->num_sticks = num_sticks;
89   model->num_offsets = 0;
90
91   for (i = 0; i < num_objects; i++) {
92     model->objects[i].position.x = random() % 200 + 20;
93     model->objects[i].position.y = random() % 200 + 20;
94     model->objects[i].previous_position.x = random() % 200 + 20;
95     model->objects[i].previous_position.y = random() % 200 + 20;
96
97     if (i + 1 < num_objects) {
98       model->sticks[i * 2].a = &model->objects[i];
99       model->sticks[i * 2].b = &model->objects[i + 1];
100       model->sticks[i * 2].length = random() % 20 + 20;
101     }
102     if (i + 2 < num_objects) {
103       model->sticks[i * 2 + 1].a = &model->objects[i];
104       model->sticks[i * 2 + 1].b = &model->objects[i + 2];
105       model->sticks[i * 2 + 1].length = random() % 20 + 20;
106     }
107   }
108
109   model->anchor_object = NULL;
110 }
111
112 static void
113 model_init_rope (Model *model)
114 {
115   const int num_objects = 20;
116   const int num_sticks = num_objects - 1;
117   const int stick_length = 5;
118   int i;
119
120   model->objects = g_new (Object, num_objects);
121   model->num_objects = num_objects;
122   model->sticks = g_new (Stick, num_sticks);
123   model->num_sticks = num_sticks;
124   model->offsets = NULL;
125   model->num_offsets = 0;
126
127   for (i = 0; i < num_objects; i++) {
128     model->objects[i].position.x = 200;
129     model->objects[i].position.y = 40 + i * stick_length;
130     model->objects[i].previous_position.x = 200;
131     model->objects[i].previous_position.y = 40 + i * stick_length;
132
133     if (i + 1 < num_objects) {
134       model->sticks[i].a = &model->objects[i];
135       model->sticks[i].b = &model->objects[i + 1];
136       model->sticks[i].length = stick_length;
137     }
138   }
139
140   model->anchor_object = NULL;
141 }
142
143 static void
144 model_init_curtain (Model *model)
145 {
146   const int num_ropes = 5;
147   const int num_rope_objects = 15;
148   const int num_objects = num_ropes * num_rope_objects;
149   const int num_sticks = num_ropes * (num_rope_objects - 1);
150   const int stick_length = 10;
151   const int rope_offset = 30;
152   double x, y;
153   int i, j, index, stick_index;
154
155   model->objects = g_new (Object, num_objects);
156   model->num_objects = num_objects;
157   model->sticks = g_new (Stick, num_sticks);
158   model->num_sticks = num_sticks;
159   model->offsets = g_new (Offset, num_ropes - 1);
160   model->num_offsets = num_ropes - 1;
161
162   for (i = 0; i < num_ropes; i++) {
163     for (j = 0; j < num_rope_objects; j++) {
164       x = 200 + i * rope_offset;
165       y = 40 + j * stick_length;
166       index = i * num_rope_objects + j;
167       model->objects[index].position.x = x;
168       model->objects[index].position.y = y;
169       model->objects[index].previous_position.x = x;
170       model->objects[index].previous_position.y = y;
171
172       if (j + 1 < num_rope_objects) {
173         stick_index = i * (num_rope_objects - 1) + j;
174         model->sticks[stick_index].a = &model->objects[index];
175         model->sticks[stick_index].b = &model->objects[index + 1];
176         model->sticks[stick_index].length = stick_length;
177       }
178     }
179
180     if (i + 1 < num_ropes) {
181       model->offsets[i].a = &model->objects[i * num_rope_objects];
182       model->offsets[i].b = &model->objects[(i + 1) * num_rope_objects];
183       model->offsets[i].dx = rope_offset;
184       model->offsets[i].dy = 0;
185     }
186   }
187
188   model->anchor_object = NULL;
189 }
190
191 static void
192 model_init_grid (Model *model)
193 {
194   const int num_ropes = 15;
195   const int num_rope_objects = 15;
196   const int num_objects = num_ropes * num_rope_objects;
197   const int num_sticks = num_ropes * (num_rope_objects - 1) +
198     (num_ropes - 1) * num_rope_objects;
199   const int stick_length = 10;
200   const int rope_offset = 10;
201   double x, y;
202   int i, j, index, stick_index;
203
204   model->objects = g_new (Object, num_objects);
205   model->num_objects = num_objects;
206   model->sticks = g_new (Stick, num_sticks);
207   model->num_sticks = num_sticks;
208   model->offsets = g_new (Offset, num_ropes - 1);
209   model->num_offsets = num_ropes - 1;
210
211   for (i = 0; i < num_ropes; i++) {
212     for (j = 0; j < num_rope_objects; j++) {
213       x = 200 + i * rope_offset;
214       y = 40 + j * stick_length;
215       index = i * num_rope_objects + j;
216       model->objects[index].position.x = x;
217       model->objects[index].position.y = y;
218       model->objects[index].previous_position.x = x;
219       model->objects[index].previous_position.y = y;
220
221       if (i + 1 < num_ropes) {
222         stick_index = i * num_rope_objects + j;
223         model->sticks[stick_index].a = &model->objects[index];
224         model->sticks[stick_index].b = &model->objects[index + num_rope_objects];
225         model->sticks[stick_index].length = stick_length;
226       }
227
228       if (j + 1 < num_rope_objects) {
229         stick_index =
230           (num_ropes - 1) * num_rope_objects + i * (num_rope_objects - 1) + j;
231         model->sticks[stick_index].a = &model->objects[index];
232         model->sticks[stick_index].b = &model->objects[index + 1];
233         model->sticks[stick_index].length = stick_length;
234       }
235     }
236
237     if (i + 1 < num_ropes) {
238       model->offsets[i].a = &model->objects[i * num_rope_objects];
239       model->offsets[i].b = &model->objects[(i + 1) * num_rope_objects];
240       model->offsets[i].dx = rope_offset;
241       model->offsets[i].dy = 0;
242     }
243   }
244
245   model->anchor_object = NULL;
246 }
247
248 static void
249 model_fini (Model *model)
250 {
251   g_free (model->objects);
252   model->objects = NULL;
253   model->num_objects = 0;
254   g_free (model->sticks);
255   model->sticks = NULL;
256   model->sticks = 0;
257   g_free (model->offsets);
258   model->offsets = NULL;
259   model->num_offsets = 0;
260 }
261
262 static void
263 model_accumulate_forces (Model *model)
264 {
265   int i;
266
267   for (i = 0; i < model->num_objects; i++) {
268     model->objects[i].force.x = 0;
269     model->objects[i].force.y = 2;
270   }
271 }
272
273 static void
274 model_integrate (Model *model, double step)
275 {
276   double x, y;
277   Object *o;
278   int i;
279
280   for (i = 0; i < model->num_objects; i++) {
281     o = &model->objects[i];
282     x = o->position.x;
283     y = o->position.y;
284     
285     o->position.x =
286       x + (x - o->previous_position.x) + o->force.x * step * step;
287     o->position.y =
288       y + (y - o->previous_position.y) + o->force.y * step * step;
289
290     o->previous_position.x = x;
291     o->previous_position.y = y;
292   }
293 }
294
295 static void
296 model_constrain (Model *model, double step)
297 {
298   double dx, dy, x, y, distance, fraction;
299   int i;
300
301   /* Anchor object constraint. */
302   if (model->anchor_object != NULL) {
303     model->anchor_object->position.x = model->anchor_position.x;
304     model->anchor_object->position.y = model->anchor_position.y;
305     model->anchor_object->previous_position.x = model->anchor_position.x;
306     model->anchor_object->previous_position.y = model->anchor_position.y;
307   }
308
309   /* FIXME: this should be "is point inside box" test instead. Figure
310    * out from previous_position which edge the point has passed
311    * through and reflect in that. */
312   for (i = 0; i < model->num_objects; i++) {
313     x = model->objects[i].position.x;
314     y = model->objects[i].position.y;
315     if (box_top - edge_fuzz <= y &&
316         model->objects[i].previous_position.y <= box_top + edge_fuzz &&
317         x < box_left) {
318       model->objects[i].position.y = box_top - (y - box_top) * elasticity;
319       model->objects[i].previous_position.y =
320         box_top - (model->objects[i].previous_position.y - box_top) * elasticity;
321     }
322   }
323
324   /* Ground collision detection constraints.  This puts a ground level
325    * in to make sure the points don't fall off the screen. */
326   for (i = 0; i < model->num_objects; i++) {
327     x = model->objects[i].position.x;
328     y = model->objects[i].position.y;
329
330     if (model->objects[i].position.y > ground_level) {
331       model->objects[i].position.y =
332         ground_level - (model->objects[i].position.y - ground_level) * elasticity;
333       model->objects[i].previous_position.y =
334         ground_level - (model->objects[i].previous_position.y - ground_level) * elasticity;
335
336       /* Friction on impact */
337       model->objects[i].position.x =
338         model->objects[i].position.x * (1 - ground_friction) +
339         model->objects[i].previous_position.x * ground_friction;
340     }
341   }
342
343   /* Offset constraints. */
344   for (i = 0; i < model->num_offsets; i++) {
345     x = (model->offsets[i].a->position.x + model->offsets[i].b->position.x) / 2;
346     y = (model->offsets[i].a->position.y + model->offsets[i].b->position.y) / 2;
347     model->offsets[i].a->position.x = x - model->offsets[i].dx / 2;
348     model->offsets[i].a->position.y = y - model->offsets[i].dy / 2;
349     model->offsets[i].b->position.x = x + model->offsets[i].dx / 2;
350     model->offsets[i].b->position.y = y + model->offsets[i].dy / 2;
351   }
352
353 #if 1
354   /* Stick constraints. */
355   for (i = 0; i < model->num_sticks; i++) {
356     x = model->sticks[i].a->position.x;
357     y = model->sticks[i].a->position.y;
358     dx = model->sticks[i].b->position.x - x;
359     dy = model->sticks[i].b->position.y - y;
360     distance = sqrt (dx * dx + dy * dy);
361     fraction = (distance - model->sticks[i].length) / distance / 2;
362     model->sticks[i].a->position.x = x + dx * fraction;
363     model->sticks[i].a->position.y = y + dy * fraction;
364     model->sticks[i].b->position.x = x + dx * (1 - fraction);
365     model->sticks[i].b->position.y = y + dy * (1 - fraction);
366   }
367 #else
368   /* Stick constraints, without square roots. */
369   squared = stick_length * stick_length;
370   for (i = 0; i < model->num_objects - 1; i++) {
371     j = i + 1;
372     x = model->objects[i].position.x;
373     y = model->objects[i].position.y;
374     dx = model->objects[j].position.x - x;
375     dy = model->objects[j].position.y - y;
376     fraction = squared / (dx * dx + dy * dy + squared) - 0.5;
377     model->objects[i].position.x = x + dx * fraction;
378     model->objects[i].position.y = y + dy * fraction;
379     model->objects[j].position.x = x + dx * (1 - fraction);
380     model->objects[j].position.y = y + dy * (1 - fraction);
381   }
382 #endif
383 }
384
385 static void
386 model_step (Model *model, double delta_t)
387 {
388   int i;
389
390   model_accumulate_forces (model);
391   model_integrate (model, delta_t);
392
393   for (i = 0; i < 15; i++)
394     model_constrain (model, delta_t);
395
396   model->theta += delta_t;
397 }
398
399 static double
400 object_distance (Object *object, double x, double y)
401 {
402   double dx, dy;
403
404   dx = object->position.x - x;
405   dy = object->position.y - y;
406
407   return sqrt (dx*dx + dy*dy);
408 }
409
410 static Object *
411 model_find_nearest (Model *model, double x, double y)
412 {
413   Object *object;
414   double distance, min_distance;
415   int i;
416
417   for (i = 0; i < model->num_objects; i++) {
418     distance = object_distance (&model->objects[i], x, y);
419     if (i == 0 || distance < min_distance) {
420       min_distance = distance;
421       object = &model->objects[i];
422     }
423   }
424
425   return object;
426 }
427
428 typedef struct _Color Color;
429 struct _Color {
430   double red, green, blue;
431 };
432
433 static void
434 draw_sticks (cairo_t *cr,
435              Model   *model,
436              Color   *color)
437 {
438   int i;
439
440   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 1);
441   cairo_new_path (cr);
442   cairo_set_line_width (cr, 2);
443   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
444   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
445
446   for (i = 0; i < model->num_sticks; i++) {
447     cairo_move_to (cr,
448                    model->sticks[i].a->position.x,
449                    model->sticks[i].a->position.y);
450     cairo_line_to (cr,
451                    model->sticks[i].b->position.x,
452                    model->sticks[i].b->position.y);
453   }
454
455   cairo_stroke (cr);
456 }
457
458 static void
459 draw_offsets (cairo_t *cr,
460               Model   *model,
461               Color   *color)
462 {
463   int i;
464
465   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.5);
466   cairo_new_path (cr);
467   cairo_set_line_width (cr, 4);
468   cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
469   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
470
471   for (i = 0; i < model->num_offsets; i++) {
472     cairo_move_to (cr,
473                    model->offsets[i].a->position.x,
474                    model->offsets[i].a->position.y);
475     cairo_line_to (cr,
476                    model->offsets[i].b->position.x,
477                    model->offsets[i].b->position.y);
478   }
479
480   cairo_stroke (cr);
481 }
482
483 static void
484 draw_constraints (cairo_t *cr,
485                   Model   *model,
486                   Color   *color)
487 {
488   cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.5);
489
490   cairo_move_to (cr, 0, ground_level);
491   cairo_line_to (cr, 1500, ground_level);
492   cairo_line_to (cr, 1500, ground_level + 10);
493   cairo_line_to (cr, 0, ground_level + 10);
494   cairo_close_path (cr);
495
496   cairo_move_to (cr, 0, box_top);
497   cairo_line_to (cr, box_left, box_top);
498   cairo_line_to (cr, box_left, box_bottom);
499   cairo_line_to (cr, 0, box_bottom);
500   cairo_close_path (cr);
501
502   cairo_fill (cr);
503 }
504
505 static void
506 draw_objects (cairo_t *cr, Model *model, Color *color)
507 {
508   int i;
509
510   for (i = 0; i < model->num_objects; i++) {
511   }
512 }
513
514 static Color blue = { 0, 0, 1 };
515 static Color red = { 1, 0, 0 };
516 static Color black = { 0, 0, 0 };
517 static Color white = { 1, 1, 1 };
518
519 static gboolean
520 expose_event (GtkWidget      *widget,
521               GdkEventExpose *event,
522               gpointer        data)
523 {
524   Model *model = data;
525   cairo_t *cr;
526
527   cr = gdk_cairo_create (widget->window);
528
529   cairo_set_source_rgb (cr, 1, 1, 1);
530   cairo_paint (cr);
531
532   draw_constraints (cr, model, &red);
533   draw_sticks (cr, model, &black);
534   draw_offsets (cr, model, &blue);
535   draw_objects (cr, model, &white);
536
537   cairo_destroy (cr);
538
539   return TRUE;
540 }
541
542 static gboolean
543 button_press_event (GtkWidget      *widget,
544                     GdkEventButton *event,
545                     gpointer        data)
546 {
547   Model *model = data;
548
549   if (event->button != 1)
550     return TRUE;
551
552   model->anchor_position.x = event->x;
553   model->anchor_position.y = event->y;
554   model->anchor_object = model_find_nearest (model, event->x, event->y);
555
556   return TRUE;
557 }
558
559 static gboolean
560 button_release_event (GtkWidget      *widget,
561                       GdkEventButton *event,
562                       gpointer        data)
563 {
564   Model *model = data;
565
566   if ((event->state & GDK_BUTTON1_MASK) == 0)
567     return TRUE;
568
569   model->anchor_object = NULL;
570
571   return TRUE;
572 }
573
574 static gboolean
575 motion_notify_event (GtkWidget      *widget,
576                      GdkEventMotion *event,
577                      gpointer        data)
578 {
579   Model *model = data;
580   int x, y;
581   GdkModifierType state;
582
583   gdk_window_get_pointer (event->window, &x, &y, &state);
584
585   model->anchor_position.x = x + 0.5;
586   model->anchor_position.y = y + 0.5;
587
588   return TRUE;
589 }
590
591 typedef void (*ModelInitFunc) (Model *model);
592
593 static void
594 model_changed (GtkComboBox *combo, gpointer user_data)
595 {
596   Model *model = user_data;
597   GtkTreeIter iter;
598   GtkTreeModel *tree_model;
599   ModelInitFunc init;
600   char *name;
601
602   tree_model = gtk_combo_box_get_model (combo);
603   if (!gtk_combo_box_get_active_iter (combo, &iter))
604     return;
605
606   gtk_tree_model_get (tree_model, &iter, 0, &name, 1, &init, -1);
607
608   model_fini (model);
609   (*init) (model);
610 }
611
612 static GtkTreeModel *
613 create_model_store (void)
614 {
615   static struct {
616     const char *name;
617     ModelInitFunc init;
618   } models[] = {
619     { "Rope", model_init_rope },
620     { "Snake", model_init_snake },
621     { "Curtain", model_init_curtain },
622     { "Grid", model_init_grid }
623   };
624
625   GtkTreeIter iter;
626   GtkTreeStore *store;
627   gint i;
628
629   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
630
631   for (i = 0; i < G_N_ELEMENTS(models); i++) {
632     gtk_tree_store_append (store, &iter, NULL);
633     gtk_tree_store_set (store, &iter,
634                         0, models[i].name, 1, models[i].init, -1);
635  }
636   
637   return GTK_TREE_MODEL (store);
638
639 }
640
641 static GtkWidget *
642 create_model_combo (Model *model)
643 {
644   GtkWidget *hbox;
645   GtkWidget *combo, *label;
646   GtkTreeModel *store;
647   GtkCellRenderer *renderer;
648
649   hbox = gtk_hbox_new (FALSE, 8);
650
651   label = gtk_label_new_with_mnemonic ("_Model:");
652   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
653
654   store = create_model_store ();
655   combo = gtk_combo_box_new_with_model (store);
656   gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
657   g_object_unref (store);
658
659   renderer = gtk_cell_renderer_text_new ();
660   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
661   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
662                                   "text", 0,
663                                   NULL);
664
665   gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
666   gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
667   g_signal_connect (combo, "changed",
668                     G_CALLBACK (model_changed), model);
669
670   return hbox;
671 }
672
673 static GtkWidget *
674 create_window (Model *model)
675 {
676   GtkWidget *window;
677   GtkWidget *frame;
678   GtkWidget *vbox;
679   GtkWidget *da;
680   GtkWidget *model_combo;
681
682   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
683   gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
684
685   g_signal_connect (window, "destroy",
686                     G_CALLBACK (gtk_main_quit), &window);
687
688   gtk_container_set_border_width (GTK_CONTAINER (window), 8);
689
690   vbox = gtk_vbox_new (FALSE, 8);
691   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
692   gtk_container_add (GTK_CONTAINER (window), vbox);
693
694   /*
695    * Create the drawing area
696    */
697       
698   frame = gtk_frame_new (NULL);
699   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
700   gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
701       
702   da = gtk_drawing_area_new ();
703   /* set a minimum size */
704   gtk_widget_set_size_request (da, 600, 500);
705
706   gtk_container_add (GTK_CONTAINER (frame), da);
707
708   /* Signals used to handle backing pixmap */
709       
710   g_signal_connect (da, "expose_event",
711                     G_CALLBACK (expose_event), model);
712       
713   /* Event signals */
714       
715   g_signal_connect (da, "motion_notify_event",
716                     G_CALLBACK (motion_notify_event), model);
717   g_signal_connect (da, "button_press_event",
718                     G_CALLBACK (button_press_event), model);
719   g_signal_connect (da, "button_release_event",
720                     G_CALLBACK (button_release_event), model);
721
722   /* Ask to receive events the drawing area doesn't normally
723    * subscribe to
724    */
725   gtk_widget_set_events (da, gtk_widget_get_events (da)
726                          | GDK_LEAVE_NOTIFY_MASK
727                          | GDK_BUTTON_PRESS_MASK
728                          | GDK_BUTTON_RELEASE_MASK
729                          | GDK_POINTER_MOTION_MASK
730                          | GDK_POINTER_MOTION_HINT_MASK);
731
732   model_combo = create_model_combo (model);
733   gtk_box_pack_start (GTK_BOX (vbox), model_combo, FALSE, FALSE, 0);
734
735   return da;
736 }
737
738 typedef struct _Closure Closure;
739 struct _Closure {
740   GtkWidget *drawing_area;
741   Model *model;
742   int i;
743 };
744
745 static gint
746 timeout_callback (gpointer data)
747 {
748   Closure *closure = data;
749   int i;
750
751   for (i = 0; i < 3; i++)
752     model_step (closure->model, 0.5);
753
754   closure->i++;
755   if (closure->i == 1) {
756     gtk_widget_queue_draw (closure->drawing_area);
757     closure->i = 0;
758   }
759
760   return TRUE;
761 }
762
763 int
764 main (int argc, char *argv[])
765 {
766   Closure closure;
767   Model model;
768
769   gtk_init (&argc, &argv);
770   model_init_rope (&model);
771   closure.drawing_area = create_window (&model);
772   closure.i = 0;
773   gtk_widget_show_all (gtk_widget_get_toplevel (closure.drawing_area));
774   closure.model = &model;
775   g_timeout_add (100, timeout_callback, &closure);
776   gtk_main ();
777
778   return 0;
779 }