1 /* -*- mode: c; c-basic-offset: 2 -*-
4 * gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) \
9 * http://en.wikipedia.org/wiki/Verlet_integration
10 * http://www.teknikus.dk/tj/gdc2001.htm
14 * - Add code to add boxes
16 * - Try out this idea: make constraint solver take mean of all
17 * corrections at the end instead of meaning as it goes.
22 #include <cairo-xlib.h>
29 const double ground_level = 500;
30 const double elasticity = 0.8;
32 typedef struct _xy_pair Point;
33 typedef struct _xy_pair Vector;
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;
51 Point previous_position;
79 struct _OffsetSpring {
102 int num_offset_springs;
103 OffsetSpring *offset_springs;
109 Object *anchor_object;
110 Vector anchor_position;
116 polygon_init (Polygon *p, int num_points, ...)
118 double dx, dy, length;
122 /* Polygons are defined counter-clock-wise in a coordinate system
123 * with the y-axis pointing down. */
125 va_start (ap, num_points);
126 p->num_points = num_points;
127 p->points = g_new (Point, num_points);
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);
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;
149 polygon_init_diamond (Polygon *polygon, double x, double y)
151 return polygon_init (polygon, 5,
160 polygon_init_rectangle (Polygon *polygon, double x0, double y0,
161 double x1, double y1)
163 return polygon_init (polygon, 4, x0, y0, x0, y1, x1, y1, x1, y0);
167 model_init_polygons (Model *model)
169 const int num_polygons = 5;
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);
178 polygon_init_rectangle (&model->polygons[4], 300, 320, 400, 350);
180 model->num_polygons = num_polygons;
184 model_init_snake (Model *model)
186 const int num_objects = 20;
187 const int num_sticks = num_objects * 2 - 3;
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);
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;
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;
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;
216 model->anchor_object = NULL;
220 model_init_rope (Model *model)
222 const int num_objects = 20;
223 const int num_sticks = num_objects - 1;
224 const int stick_length = 10;
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);
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;
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;
248 model->anchor_object = NULL;
252 model_init_curtain (Model *model)
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;
261 int i, j, index, stick_index;
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);
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;
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;
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;
296 model->offsets[0].objects[i] = &model->objects[i * num_rope_objects];
299 model->anchor_object = NULL;
303 model_init_grid (Model *model)
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;
313 int i, j, index, string_index;
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);
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;
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;
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;
347 if (j + 1 < num_rope_objects) {
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;
356 model->offsets[0].objects[i] = &model->objects[i * num_rope_objects];
359 model->anchor_object = NULL;
363 model_init_molecule (Model *model)
365 const int num_objects = 8;
366 const int num_springs = num_objects * 2;
367 const int spring_length = 50;
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;
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;
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;
397 model_init_wobbly (Model *model)
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;
404 int i, j, object_index, spring_index;
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;
413 model_init_polygons (model);
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;
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;
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;
450 model_fini (Model *model)
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);
460 model_accumulate_forces (Model *model)
463 double x, y, dx, dy, distance, displacement;
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;
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);
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;
487 for (i = 0; i < model->num_offset_springs; i++) {
489 (model->offset_springs[i].a->position.x +
490 model->offset_springs[i].b->position.x) / 2;
492 (model->offset_springs[i].a->position.y +
493 model->offset_springs[i].b->position.y) / 2;
495 x = middle.x - model->offset_springs[i].dx / 2;
496 y = middle.y - model->offset_springs[i].dy / 2;
498 dx = x - model->offset_springs[i].a->position.x;
499 dy = y - model->offset_springs[i].a->position.y;
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;
509 model_integrate (Model *model, double step)
515 for (i = 0; i < model->num_objects; i++) {
516 o = &model->objects[i];
521 x + 0.9 * (x - o->previous_position.x) + o->force.x * step * step;
523 y + 0.9 * (y - o->previous_position.y) + o->force.y * step * step;
525 o->previous_position.x = x;
526 o->previous_position.y = y;
530 /* The square root in the distance computation for the string and
531 * stick constraints can be aproximated using Newton:
534 * (model->sticks[i].length +
535 * (dx * dx + dy * dy) / model->sticks[i].length) / 2;
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()...
544 estimate_distance (double dx, double dy, double r)
546 #ifdef APPROXIMATE_SQUARE_ROOTS
547 return (r + (dx * dx + dy * dy) / r) / 2;
549 return sqrt (dx * dx + dy * dy);
554 polygon_contains_point (Polygon *polygon, Point *point)
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;
563 if (polygon->normals[i].x * dx + polygon->normals[i].y * dy >= 0)
571 polygon_reflect_object (Polygon *polygon, Object *object)
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);
586 n = &polygon->normals[i];
590 object->position.x -= (1 + elasticity) * distance * n->x;
591 object->position.y -= (1 + elasticity) * distance * n->y;
594 n->x * (object->previous_position.x - polygon->points[edge].x) +
595 n->y * (object->previous_position.y - polygon->points[edge].y);
597 object->previous_position.x -= (1 + elasticity) * distance * n->x;
598 object->previous_position.y -= (1 + elasticity) * distance * n->y;
602 model_constrain_polygon (Model *model, Polygon *polygon)
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]);
613 model_constrain_offset (Model *model, Offset *offset)
620 for (i = 0; i < offset->num_objects; i++) {
621 x += offset->objects[i]->position.x;
622 y += offset->objects[i]->position.y;
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;
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;
635 model_constrain (Model *model)
637 double dx, dy, x, y, distance, fraction;
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;
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)
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);
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);
678 /* Offset constraints. */
679 for (i = 0; i < model->num_offsets; i++)
680 model_constrain_offset (model, &model->offsets[i]);
682 /* Polygon constraints. */
683 for (i = 0; i < model->num_polygons; i++)
684 model_constrain_polygon (model, &model->polygons[i]);
688 model_step (Model *model, double delta_t)
692 model_accumulate_forces (model);
693 model_integrate (model, delta_t);
695 for (i = 0; i < 50; i++)
696 model_constrain (model);
698 model->theta += delta_t;
702 object_distance (Object *object, double x, double y)
706 dx = object->position.x - x;
707 dy = object->position.y - y;
709 return sqrt (dx*dx + dy*dy);
713 model_find_nearest (Model *model, double x, double y)
716 double distance, min_distance;
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];
730 typedef struct _Color Color;
732 double red, green, blue;
736 draw_sticks (cairo_t *cr,
742 cairo_set_source_rgba (cr, color->red, color->green, color->blue, 1);
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);
748 for (i = 0; i < model->num_sticks; i++) {
750 model->sticks[i].a->position.x,
751 model->sticks[i].a->position.y);
753 model->sticks[i].b->position.x,
754 model->sticks[i].b->position.y);
761 draw_strings (cairo_t *cr,
767 cairo_set_source_rgba (cr, color->red, color->green, color->blue, 1);
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);
773 for (i = 0; i < model->num_strings; i++) {
775 model->strings[i].a->position.x,
776 model->strings[i].a->position.y);
778 model->strings[i].b->position.x,
779 model->strings[i].b->position.y);
786 draw_offsets (cairo_t *cr,
792 cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.5);
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);
798 for (i = 0; i < model->num_offsets; i++) {
799 for (j = 0; j < model->offsets[i].num_objects; j++) {
801 model->offsets[i].objects[j]->position.x,
802 model->offsets[i].objects[j]->position.y);
810 draw_springs (cairo_t *cr,
816 cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.4);
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);
822 for (i = 0; i < model->num_springs; i++) {
824 model->springs[i].a->position.x,
825 model->springs[i].a->position.y);
827 model->springs[i].b->position.x,
828 model->springs[i].b->position.y);
835 draw_offset_springs (cairo_t *cr,
841 cairo_set_source_rgba (cr, color->red, color->green, color->blue, 0.4);
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);
847 for (i = 0; i < model->num_offset_springs; i++) {
849 model->offset_springs[i].a->position.x,
850 model->offset_springs[i].a->position.y);
852 model->offset_springs[i].b->position.x,
853 model->offset_springs[i].b->position.y);
860 draw_polygons (cairo_t *cr, Model *model, Color *color)
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);
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);
879 draw_objects (cairo_t *cr, Model *model, Color *color)
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,
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 };
897 typedef struct _Closure Closure;
899 GtkWidget *drawing_area;
900 GtkWidget *fps_label;
904 struct timeval start;
908 draw_model (GtkWidget *widget, Model *model)
912 cr = gdk_cairo_create (widget->window);
914 cairo_set_source_rgb (cr, 1, 1, 1);
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);
929 expose_event (GtkWidget *widget,
930 GdkEventExpose *event,
933 Closure *closure = data;
935 draw_model (widget, closure->model);
941 button_press_event (GtkWidget *widget,
942 GdkEventButton *event,
945 Closure *closure = data;
947 if (event->button != 1)
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,
959 button_release_event (GtkWidget *widget,
960 GdkEventButton *event,
963 Closure *closure = data;
965 if ((event->state & GDK_BUTTON1_MASK) == 0)
968 closure->model->anchor_object = NULL;
974 motion_notify_event (GtkWidget *widget,
975 GdkEventMotion *event,
978 Closure *closure = data;
980 GdkModifierType state;
982 gdk_window_get_pointer (event->window, &x, &y, &state);
984 closure->model->anchor_position.x = x + 0.5;
985 closure->model->anchor_position.y = y + 0.5;
990 typedef void (*ModelInitFunc) (Model *model);
993 model_changed (GtkComboBox *combo, gpointer user_data)
995 Closure *closure = user_data;
997 GtkTreeModel *tree_model;
1001 tree_model = gtk_combo_box_get_model (combo);
1002 if (!gtk_combo_box_get_active_iter (combo, &iter))
1005 gtk_tree_model_get (tree_model, &iter, 0, &name, 1, &init, -1);
1007 model_fini (closure->model);
1008 (*init) (closure->model);
1011 static GtkTreeModel *
1012 create_model_store (void)
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 }
1027 GtkTreeStore *store;
1030 store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
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);
1038 return GTK_TREE_MODEL (store);
1043 create_model_combo (Closure *closure)
1046 GtkWidget *combo, *label;
1047 GtkTreeModel *store;
1048 GtkCellRenderer *renderer;
1050 hbox = gtk_hbox_new (FALSE, 8);
1052 label = gtk_label_new_with_mnemonic ("_Model:");
1053 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
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);
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,
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);
1071 label = gtk_label_new ("Frames per second: 0");
1072 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1074 closure->fps_label = label;
1080 create_window (Closure *closure)
1086 GtkWidget *model_combo;
1088 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1089 gtk_window_set_title (GTK_WINDOW (window), "Akamaru");
1091 g_signal_connect (window, "destroy",
1092 G_CALLBACK (gtk_main_quit), &window);
1094 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
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);
1101 * Create the drawing area
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);
1108 da = gtk_drawing_area_new ();
1109 /* set a minimum size */
1110 gtk_widget_set_size_request (da, 200, 200);
1112 gtk_container_add (GTK_CONTAINER (frame), da);
1114 /* Signals used to handle backing pixmap */
1116 g_signal_connect (da, "expose_event",
1117 G_CALLBACK (expose_event), closure);
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);
1128 /* Ask to receive events the drawing area doesn't normally
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);
1138 model_combo = create_model_combo (closure);
1139 gtk_box_pack_start (GTK_BOX (vbox), model_combo, FALSE, FALSE, 0);
1141 closure->drawing_area = da;
1145 timeout_callback (gpointer data)
1147 Closure *closure = data;
1149 model_step (closure->model, 1);
1152 if (closure->i == 1) {
1153 gtk_widget_queue_draw (closure->drawing_area);
1155 closure->frame_count++;
1158 if (closure->frame_count == 200) {
1159 struct timeval end, elapsed;
1163 closure->frame_count = 0;
1164 gettimeofday (&end, NULL);
1165 if (closure->start.tv_usec > end.tv_usec) {
1166 end.tv_usec += 1000000;
1170 elapsed.tv_usec = end.tv_usec - closure->start.tv_usec;
1171 elapsed.tv_sec = end.tv_sec - closure->start.tv_sec;
1173 total = elapsed.tv_sec + ((double) elapsed.tv_usec / 1e6);
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);
1186 main (int argc, char *argv[])
1191 gtk_init (&argc, &argv);
1192 model_init_rope (&model);
1193 create_window (&closure);
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);