Tizen 2.0 Release
[profile/ivi/cairo.git] / util / show-traps.c
1 #define _GNU_SOURCE
2 #include <gtk/gtk.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <gdk/gdkkeysyms.h>
6 #include <math.h>
7
8 typedef struct _point {
9     gdouble x, y;
10 } point_t;
11 typedef struct _box {
12     point_t p1, p2;
13 } box_t;
14 typedef struct _line {
15     point_t p1, p2;
16 } line_t;
17 typedef struct _trapezoid {
18     gdouble top, bottom;
19     line_t left, right;
20 } trapezoid_t;
21 typedef struct _traps {
22     struct _traps *next, *prev;
23     box_t extents;
24     int num_traps;
25     int size;
26     trapezoid_t traps[0];
27 } traps_t;
28
29 typedef struct _edge {
30     line_t line;
31     gdouble top, bottom;
32     point_t p1, p2;
33     int dir;
34 } edge_t;
35 typedef struct _edges {
36     struct _edges *next, *prev;
37     box_t extents;
38     int num_edges;
39     int size;
40     edge_t edges[0];
41 } edges_t;
42
43 typedef struct _TrapView {
44     GtkWidget widget;
45
46     struct _TrapView *group_head;
47     struct _TrapView *group_next;
48     struct _TrapView *group_prev;
49
50     cairo_surface_t *pixmap;
51     int pixmap_width, pixmap_height;
52
53     traps_t *traps_list;
54     traps_t *current_traps;
55
56     edges_t *edges_list;
57     edges_t *current_edges;
58
59     double px, py;
60
61     gint mag_x, mag_y;
62     gint mag_size;
63     gdouble mag_zoom;
64     gboolean in_mag_drag;
65     gint mag_drag_x, mag_drag_y;
66 } TrapView;
67
68 typedef struct _TrapViewClass {
69     GtkWidgetClass parent_class;
70 } TrapViewClass;
71
72 G_DEFINE_TYPE (TrapView, trap_view, GTK_TYPE_WIDGET)
73
74 static gdouble
75 _compute_intersection_x_for_y (const line_t *line,
76                                gdouble y)
77 {
78     gdouble dx = line->p2.x - line->p1.x;
79     gdouble dy = line->p2.y - line->p1.y;
80     gdouble x;
81
82     if (y == line->p1.y)
83         return line->p1.x;
84     if (y == line->p2.y)
85         return line->p2.x;
86
87     x = line->p1.x;
88     if (dy != 0)
89         x +=  (y - line->p1.y)*dx/dy;
90     return x;
91 }
92
93 static void
94 _compute_intersection_point (const line_t *line,
95                              gdouble y,
96                              point_t *p)
97 {
98     p->x = _compute_intersection_x_for_y (line, p->y = y);
99 }
100
101 static cairo_surface_t *
102 pixmap_create (TrapView *self, cairo_surface_t *target)
103 {
104     cairo_surface_t *surface = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
105                                                              self->widget.allocation.width,
106                                                              self->widget.allocation.height);
107     cairo_t *cr;
108     traps_t *traps;
109     edges_t *edges;
110     gdouble sf_x, sf_y, sf;
111     gdouble mid, dim;
112     gdouble x0,  y0;
113     double dash[2] = {8, 8};
114     double dots[2] = {0., 1.};
115     int n;
116     box_t extents;
117     point_t p;
118
119     traps = self->current_traps;
120     if (traps == NULL)
121         return surface;
122     edges = self->current_edges;
123
124     extents = traps->extents;
125     if (edges != NULL) {
126         if (edges->extents.p1.x < extents.p1.x)
127             extents.p1.x = edges->extents.p1.x;
128         if (edges->extents.p1.y < extents.p1.y)
129             extents.p1.y = edges->extents.p1.y;
130         if (edges->extents.p2.x > extents.p2.x)
131             extents.p2.x = edges->extents.p2.x;
132         if (edges->extents.p2.y > extents.p2.y)
133             extents.p2.y = edges->extents.p2.y;
134     }
135
136     mid = (extents.p2.x + extents.p1.x) / 2.;
137     dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
138     sf_x = self->widget.allocation.width / dim / 2;
139
140     mid = (extents.p2.y + extents.p1.y) / 2.;
141     dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
142     sf_y = self->widget.allocation.height / dim / 2;
143
144     sf = MIN (sf_x, sf_y);
145
146     mid = (extents.p2.x + extents.p1.x) / 2.;
147     dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
148     x0 = mid - dim;
149     mid = (extents.p2.y + extents.p1.y) / 2.;
150     dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
151     y0 = mid - dim;
152
153     cr = cairo_create (surface);
154     cairo_set_source_rgb (cr, 1, 1, 1);
155     cairo_paint (cr);
156
157     cairo_save (cr);
158     cairo_scale (cr, sf, sf);
159     cairo_translate (cr, -x0, -y0);
160     cairo_set_source_rgba (cr, 0, 1, 0, .2);
161     for (n = 0; n < traps->num_traps; n++) {
162         const trapezoid_t *t = &traps->traps[n];
163
164         _compute_intersection_point (&t->left, t->top, &p);
165         cairo_move_to (cr, p.x, p.y);
166         _compute_intersection_point (&t->right, t->top, &p);
167         cairo_line_to (cr, p.x, p.y);
168         _compute_intersection_point (&t->right, t->bottom, &p);
169         cairo_line_to (cr, p.x, p.y);
170         _compute_intersection_point (&t->left, t->bottom, &p);
171         cairo_line_to (cr, p.x, p.y);
172         cairo_close_path (cr);
173         cairo_fill (cr);
174     }
175     cairo_restore (cr);
176
177     if (edges == NULL) {
178         cairo_save (cr);
179
180         /* top, bottom */
181         cairo_save (cr); {
182             cairo_matrix_t m;
183             cairo_matrix_init_scale (&m, sf, sf);
184             cairo_matrix_translate (&m, -x0, -y0);
185
186             cairo_set_line_width (cr, 1.);
187             cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
188             for (n = 0; n < traps->num_traps; n++) {
189                 const trapezoid_t *t = &traps->traps[n];
190
191                 _compute_intersection_point (&t->left, t->top, &p);
192                 cairo_matrix_transform_point (&m, &p.x, &p.y);
193                 cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
194                 cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
195
196                 _compute_intersection_point (&t->right, t->top, &p);
197                 cairo_matrix_transform_point (&m, &p.x, &p.y);
198                 cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
199                 cairo_stroke (cr);
200
201                 _compute_intersection_point (&t->left, t->bottom, &p);
202                 cairo_matrix_transform_point (&m, &p.x, &p.y);
203                 cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
204                 cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
205
206                 _compute_intersection_point (&t->right, t->bottom, &p);
207                 cairo_matrix_transform_point (&m, &p.x, &p.y);
208                 cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
209                 cairo_stroke (cr);
210             }
211         } cairo_restore (cr);
212
213         /* left extents */
214         cairo_save (cr); {
215             cairo_save (cr); {
216                 cairo_scale (cr, sf, sf);
217                 cairo_translate (cr, -x0, -y0);
218
219                 for (n = 0; n < traps->num_traps; n++) {
220                     const trapezoid_t *t = &traps->traps[n];
221                     cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
222                     cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
223                 }
224             } cairo_restore (cr);
225             cairo_set_source_rgb (cr, 1, 0, 0);
226             cairo_set_line_width (cr, 1.);
227             cairo_set_dash (cr, dash, 2, 0.);
228             cairo_stroke (cr);
229         } cairo_restore (cr);
230
231         /* left line */
232         cairo_save (cr); {
233             cairo_save (cr); {
234                 cairo_scale (cr, sf, sf);
235                 cairo_translate (cr, -x0, -y0);
236
237                 for (n = 0; n < traps->num_traps; n++) {
238                     const trapezoid_t *t = &traps->traps[n];
239                     _compute_intersection_point (&t->left, t->top, &p);
240                     cairo_move_to (cr, p.x, p.y);
241                     _compute_intersection_point (&t->left, t->bottom, &p);
242                     cairo_line_to (cr, p.x, p.y);
243                 }
244             } cairo_restore (cr);
245             cairo_set_source_rgb (cr, 1, 0, 0);
246             cairo_stroke (cr);
247         } cairo_restore (cr);
248
249         /* right extents */
250         cairo_save (cr); {
251             cairo_save (cr); {
252                 cairo_scale (cr, sf, sf);
253                 cairo_translate (cr, -x0, -y0);
254
255                 for (n = 0; n < traps->num_traps; n++) {
256                     const trapezoid_t *t = &traps->traps[n];
257                     cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
258                     cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
259                 }
260             } cairo_restore (cr);
261             cairo_set_source_rgb (cr, 0, 0, 1);
262             cairo_set_line_width (cr, 1.);
263             cairo_set_dash (cr, dash, 2, 0.);
264             cairo_stroke (cr);
265         } cairo_restore (cr);
266
267         /* right line */
268         cairo_save (cr); {
269             cairo_save (cr); {
270                 cairo_scale (cr, sf, sf);
271                 cairo_translate (cr, -x0, -y0);
272
273                 for (n = 0; n < traps->num_traps; n++) {
274                     const trapezoid_t *t = &traps->traps[n];
275                     _compute_intersection_point (&t->right, t->top, &p);
276                     cairo_move_to (cr, p.x, p.y);
277                     _compute_intersection_point (&t->right, t->bottom, &p);
278                     cairo_line_to (cr, p.x, p.y);
279                 } cairo_restore (cr);
280                 cairo_set_source_rgb (cr, 0, 0, 1);
281                 cairo_stroke (cr);
282             } cairo_restore (cr);
283         }
284
285         /* end-points */
286         cairo_save (cr); {
287             cairo_save (cr); {
288                 cairo_scale (cr, sf, sf);
289                 cairo_translate (cr, -x0, -y0);
290
291                 for (n = 0; n < traps->num_traps; n++) {
292                     const trapezoid_t *t = &traps->traps[n];
293                     _compute_intersection_point (&t->left, t->top, &p);
294                     cairo_move_to (cr, p.x, p.y);
295                     cairo_close_path (cr);
296                     _compute_intersection_point (&t->left, t->bottom, &p);
297                     cairo_move_to (cr, p.x, p.y);
298                     cairo_close_path (cr);
299                     _compute_intersection_point (&t->right, t->top, &p);
300                     cairo_move_to (cr, p.x, p.y);
301                     cairo_close_path (cr);
302                     _compute_intersection_point (&t->right, t->bottom, &p);
303                     cairo_move_to (cr, p.x, p.y);
304                     cairo_close_path (cr);
305                 }
306             } cairo_restore (cr);
307             cairo_set_source_rgb (cr, 0, 0, 0);
308             cairo_set_dash (cr, dots, 2, 0.);
309             cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
310             cairo_set_line_width (cr, 4.);
311             cairo_stroke (cr);
312         } cairo_restore (cr);
313
314         cairo_restore (cr);
315     } else {
316         cairo_save (cr);
317
318         for (n = 0; n < edges->num_edges; n++) {
319             const edge_t *e = &edges->edges[n];
320
321             cairo_save (cr); {
322                 cairo_scale (cr, sf, sf);
323                 cairo_translate (cr, -x0, -y0);
324                 cairo_move_to (cr, e->p1.x, e->p1.y);
325                 cairo_line_to (cr, e->p2.x, e->p2.y);
326             } cairo_restore (cr);
327
328             if (e->dir < 0) {
329                 cairo_set_source_rgb (cr, 0, 0, 1);
330                 cairo_set_dash (cr, dash, 2, dash[0]);
331             } else {
332                 cairo_set_source_rgb (cr, 1, 0, 0);
333                 cairo_set_dash (cr, dash, 2, 0.);
334             }
335
336             cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
337             cairo_set_line_width (cr, 1.);
338             cairo_stroke (cr);
339
340             cairo_save (cr); {
341                 cairo_scale (cr, sf, sf);
342                 cairo_translate (cr, -x0, -y0);
343                 cairo_move_to (cr, e->p1.x, e->p1.y);
344                 cairo_close_path (cr);
345                 cairo_move_to (cr, e->p2.x, e->p2.y);
346                 cairo_close_path (cr);
347             } cairo_restore (cr);
348             cairo_set_source_rgb (cr, 0, 0, 0);
349             cairo_set_dash (cr, dots, 2, 0.);
350             cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
351             cairo_set_line_width (cr, 4.);
352             cairo_stroke (cr);
353         }
354
355         cairo_restore (cr);
356     }
357
358     cairo_destroy (cr);
359     return surface;
360 }
361
362 static void
363 trap_view_draw (TrapView *self, cairo_t *cr)
364 {
365     traps_t *traps;
366     edges_t *edges;
367     gdouble sf_x, sf_y, sf;
368     gdouble mid, dim;
369     gdouble x0, y0;
370     double dash[2] = {8, 8};
371     int n;
372     box_t extents;
373     point_t p;
374
375     if (self->pixmap_width != self->widget.allocation.width ||
376         self->pixmap_height != self->widget.allocation.height)
377     {
378         cairo_surface_destroy (self->pixmap);
379         self->pixmap = pixmap_create (self, cairo_get_target (cr));
380         self->pixmap_width = self->widget.allocation.width;
381         self->pixmap_height = self->widget.allocation.height;
382     }
383
384     cairo_save (cr);
385     cairo_set_source_surface (cr, self->pixmap, 0, 0);
386     cairo_paint (cr);
387     cairo_restore (cr);
388
389     traps = self->current_traps;
390     if (traps == NULL)
391         return;
392
393     extents = traps->extents;
394     edges = self->current_edges;
395     if (edges != NULL) {
396         if (edges->extents.p1.x < extents.p1.x)
397             extents.p1.x = edges->extents.p1.x;
398         if (edges->extents.p1.y < extents.p1.y)
399             extents.p1.y = edges->extents.p1.y;
400         if (edges->extents.p2.x > extents.p2.x)
401             extents.p2.x = edges->extents.p2.x;
402         if (edges->extents.p2.y > extents.p2.y)
403             extents.p2.y = edges->extents.p2.y;
404     }
405
406     mid = (extents.p2.x + extents.p1.x) / 2.;
407     dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
408     sf_x = self->widget.allocation.width / dim / 2;
409
410     mid = (extents.p2.y + extents.p1.y) / 2.;
411     dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
412     sf_y = self->widget.allocation.height / dim / 2;
413
414     sf = MIN (sf_x, sf_y);
415
416     mid = (extents.p2.x + extents.p1.x) / 2.;
417     dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
418     x0 = mid - dim;
419     mid = (extents.p2.y + extents.p1.y) / 2.;
420     dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
421     y0 = mid - dim;
422
423     cairo_save (cr);
424
425     /* draw a zoom view of the area around the mouse */
426     {
427         double zoom = self->mag_zoom;
428         int size = self->mag_size;
429
430         cairo_save (cr);
431
432         /* bottom right */
433         cairo_rectangle (cr, self->mag_x, self->mag_y, size, size);
434         cairo_stroke_preserve (cr);
435         cairo_set_source_rgb (cr, 1, 1, 1);
436         cairo_fill_preserve (cr);
437         cairo_clip (cr);
438
439         /* compute roi in extents */
440         cairo_translate (cr, self->mag_x + size/2, self->mag_y + size/2);
441
442         cairo_save (cr);
443         cairo_scale (cr, zoom, zoom);
444         cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
445         for (n = 0; n < traps->num_traps; n++) {
446             const trapezoid_t *t = &traps->traps[n];
447
448             _compute_intersection_point (&t->left, t->top, &p);
449             cairo_move_to (cr, p.x, p.y);
450             _compute_intersection_point (&t->right, t->top, &p);
451             cairo_line_to (cr, p.x, p.y);
452             _compute_intersection_point (&t->right, t->bottom, &p);
453             cairo_line_to (cr, p.x, p.y);
454             _compute_intersection_point (&t->left, t->bottom, &p);
455             cairo_line_to (cr, p.x, p.y);
456             cairo_close_path (cr);
457             cairo_set_source_rgba (cr, 0, 1, 0, .2);
458             cairo_fill (cr);
459         }
460         cairo_restore (cr);
461
462         cairo_save (cr); {
463             cairo_matrix_t m;
464             cairo_matrix_init_scale (&m, zoom, zoom);
465             cairo_matrix_translate (&m, -(self->px / sf + x0), -(self->py /sf + y0));
466
467             cairo_set_source_rgb (cr, 0, 0, 0);
468             cairo_set_line_width (cr, 1.);
469             cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
470             for (n = 0; n < traps->num_traps; n++) {
471                 const trapezoid_t *t = &traps->traps[n];
472
473                 _compute_intersection_point (&t->left, t->top, &p);
474                 cairo_matrix_transform_point (&m, &p.x, &p.y);
475                 cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
476                 cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
477
478                 _compute_intersection_point (&t->right, t->top, &p);
479                 cairo_matrix_transform_point (&m, &p.x, &p.y);
480                 cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
481                 cairo_stroke (cr);
482
483                 _compute_intersection_point (&t->left, t->bottom, &p);
484                 cairo_matrix_transform_point (&m, &p.x, &p.y);
485                 cairo_move_to (cr, floor (p.x), floor (p.y) + .5);
486                 cairo_set_dash (cr, dash, 2, fmod (floor (p.x), dash[0] + dash[1]));
487
488                 _compute_intersection_point (&t->right, t->bottom, &p);
489                 cairo_matrix_transform_point (&m, &p.x, &p.y);
490                 cairo_line_to (cr, ceil (p.x), floor (p.y) + .5);
491                 cairo_stroke (cr);
492             }
493         } cairo_restore (cr);
494
495         cairo_save (cr); { /* left extents */
496             cairo_save (cr); {
497                 cairo_scale (cr, zoom, zoom);
498                 cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
499                 for (n = 0; n < traps->num_traps; n++) {
500                     const trapezoid_t *t = &traps->traps[n];
501                     cairo_move_to (cr, t->left.p1.x, t->left.p1.y);
502                     cairo_line_to (cr, t->left.p2.x, t->left.p2.y);
503                 }
504             } cairo_restore (cr);
505             cairo_set_source_rgb (cr, 1, 0, 0);
506             cairo_set_line_width (cr, .5);
507             cairo_set_dash (cr, dash, 2, 0.);
508             cairo_stroke (cr);
509         } cairo_restore (cr);
510         cairo_save (cr); { /* right extents */
511             cairo_save (cr); {
512                 cairo_scale (cr, zoom, zoom);
513                 cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
514
515                 for (n = 0; n < traps->num_traps; n++) {
516                     const trapezoid_t *t = &traps->traps[n];
517                     cairo_move_to (cr, t->right.p1.x, t->right.p1.y);
518                     cairo_line_to (cr, t->right.p2.x, t->right.p2.y);
519                 }
520             } cairo_restore (cr);
521             cairo_set_source_rgb (cr, 0, 0, 1);
522             cairo_set_line_width (cr, .5);
523             cairo_set_dash (cr, dash, 2, 0.);
524             cairo_stroke (cr);
525         } cairo_restore (cr);
526
527         cairo_save (cr); { /* left lines */
528             cairo_save (cr);
529             cairo_scale (cr, zoom, zoom);
530             cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
531             for (n = 0; n < traps->num_traps; n++) {
532                 const trapezoid_t *t = &traps->traps[n];
533                 _compute_intersection_point (&t->left, t->top, &p);
534                 cairo_move_to (cr, p.x, p.y);
535                 _compute_intersection_point (&t->left, t->bottom, &p);
536                 cairo_line_to (cr, p.x, p.y);
537             }
538             cairo_restore (cr);
539             cairo_set_source_rgb (cr, 1, 0, 0);
540             cairo_stroke (cr);
541         } cairo_restore (cr);
542         cairo_save (cr); { /* right lines */
543             cairo_save (cr);
544             cairo_scale (cr, zoom, zoom);
545             cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
546             for (n = 0; n < traps->num_traps; n++) {
547                 const trapezoid_t *t = &traps->traps[n];
548                 _compute_intersection_point (&t->right, t->top, &p);
549                 cairo_move_to (cr, p.x, p.y);
550                 _compute_intersection_point (&t->right, t->bottom, &p);
551                 cairo_line_to (cr, p.x, p.y);
552             }
553             cairo_restore (cr);
554             cairo_set_source_rgb (cr, 0, 0, 1);
555             cairo_stroke (cr);
556         } cairo_restore (cr);
557
558         /* end-points */
559         cairo_save (cr); {
560             double dots[2] = {0., 1.};
561
562             cairo_save (cr);
563             cairo_scale (cr, zoom, zoom);
564             cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
565             for (n = 0; n < traps->num_traps; n++) {
566                 const trapezoid_t *t = &traps->traps[n];
567                 _compute_intersection_point (&t->left, t->top, &p);
568                 cairo_move_to (cr, p.x, p.y);
569                 cairo_close_path (cr);
570                 _compute_intersection_point (&t->left, t->bottom, &p);
571                 cairo_move_to (cr, p.x, p.y);
572                 cairo_close_path (cr);
573                 _compute_intersection_point (&t->right, t->top, &p);
574                 cairo_move_to (cr, p.x, p.y);
575                 cairo_close_path (cr);
576                 _compute_intersection_point (&t->right, t->bottom, &p);
577                 cairo_move_to (cr, p.x, p.y);
578                 cairo_close_path (cr);
579             }
580             cairo_restore (cr);
581             cairo_set_source_rgb (cr, 0, 0, 0);
582             cairo_set_dash (cr, dots, 2, 0.);
583             cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
584             cairo_set_line_width (cr, 4.);
585             cairo_stroke (cr);
586         } cairo_restore (cr);
587
588         /* grid */
589         cairo_save (cr); {
590             int i;
591
592             cairo_translate (cr,
593                              -zoom*fmod (self->px/sf + x0, 1.),
594                              -zoom*fmod (self->py/sf + y0, 1.));
595             for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
596                 cairo_move_to (cr, zoom*i, -size/2);
597                 cairo_line_to (cr, zoom*i, size/2 + zoom);
598                 cairo_move_to (cr, -size/2, zoom*i);
599                 cairo_line_to (cr, size/2 + zoom, zoom*i);
600             }
601             cairo_set_source_rgba (cr, .7, .7, .7, .5);
602             cairo_set_line_width (cr, 1.);
603             cairo_stroke (cr);
604         } cairo_restore (cr);
605     }
606
607     cairo_restore (cr);
608 }
609
610 static gdouble
611 trapezoid_area (const trapezoid_t *t)
612 {
613     gdouble inner_left, inner_right;
614     gdouble outer_left, outer_right;
615     gdouble height;
616     gdouble area;
617
618     /* split into 3 sections: a rectangle with a pair of triangular bookends */
619     inner_left = _compute_intersection_x_for_y (&t->left, t->top);
620     outer_left = _compute_intersection_x_for_y (&t->left, t->bottom);
621     if (outer_left > inner_left) {
622         gdouble t = outer_left;
623         outer_left = inner_left;
624         inner_left = t;
625     }
626
627     inner_right = _compute_intersection_x_for_y (&t->right, t->top);
628     outer_right = _compute_intersection_x_for_y (&t->right, t->bottom);
629     if (outer_right > inner_right) {
630         gdouble t = outer_right;
631         outer_right = inner_right;
632         inner_right = t;
633     }
634
635     if (outer_left > outer_right) { /* reverse */
636         gdouble t;
637
638         t = outer_left;
639         outer_left = inner_right;
640         inner_right = t;
641
642         t = inner_left;
643         inner_left = outer_right;
644         outer_right = t;
645     }
646
647     height = t->bottom - t->top;
648     area  = (inner_left - outer_left) * height / 2;
649     area += (outer_right - inner_right) * height / 2;
650     area += (inner_right - inner_left) * height;
651
652     return area;
653 }
654
655 static gdouble
656 traps_compute_total_area (const traps_t *traps)
657 {
658     int n;
659     gdouble area = 0.;
660     for (n = 0; n < traps->num_traps; n++)
661         area += trapezoid_area (&traps->traps[n]);
662     return area;
663 }
664
665 static void
666 trap_view_draw_labels (TrapView *self, cairo_t *cr)
667 {
668     PangoLayout *layout;
669     gint width, height;
670     gdouble total_area;
671     gchar *str;
672     traps_t *traps;
673
674     traps = self->current_traps;
675     if (traps == NULL)
676         return;
677
678     /* convert total area from fixed-point (assuming 24.8) */
679     total_area = traps_compute_total_area (traps) / (256. * 256.);
680     str = g_strdup_printf ("Number of trapezoids:\t%d\n"
681                            "Total area of trapezoids:\t%.2f",
682                            traps->num_traps,
683                            total_area);
684     layout = gtk_widget_create_pango_layout (&self->widget, str);
685     g_free (str);
686
687     pango_layout_get_pixel_size (layout, &width, &height);
688
689     cairo_move_to (cr, 10, 10 + height);
690     pango_cairo_show_layout (cr, layout);
691     g_object_unref (layout);
692 }
693
694 static gboolean
695 trap_view_expose (GtkWidget *w, GdkEventExpose *ev)
696 {
697     TrapView *self = (TrapView *) w;
698     cairo_t *cr;
699
700     cr = gdk_cairo_create (w->window);
701     gdk_cairo_region (cr, ev->region);
702     cairo_clip (cr);
703
704     trap_view_draw (self, cr);
705     trap_view_draw_labels (self, cr);
706
707     cairo_destroy (cr);
708     return FALSE;
709 }
710
711 static void
712 trap_view_advance (TrapView *self)
713 {
714     if (self->current_traps && self->current_traps->prev)
715         self->current_traps = self->current_traps->prev;
716     if (self->current_edges && self->current_edges->prev)
717         self->current_edges = self->current_edges->prev;
718     self->pixmap_width = self->pixmap_height = 0;
719     gtk_widget_queue_draw (&self->widget);
720 }
721
722 static void
723 trap_view_back (TrapView *self)
724 {
725     if (self->current_traps && self->current_traps->next)
726         self->current_traps = self->current_traps->next;
727     if (self->current_edges && self->current_edges->next)
728         self->current_edges = self->current_edges->next;
729     self->pixmap_width = self->pixmap_height = 0;
730     gtk_widget_queue_draw (&self->widget);
731 }
732
733 static void
734 trap_view_group_foreach (TrapView *group, GFunc func, gpointer data)
735 {
736     while (group) {
737         func (group, data);
738         group = group->group_next;
739     }
740 }
741
742 static gboolean
743 trap_view_key_press (GtkWidget *w, GdkEventKey *ev)
744 {
745     TrapView *self = (TrapView *) w;
746
747     switch (ev->keyval) {
748     case GDK_BackSpace:
749         trap_view_group_foreach (self->group_head,
750                                  (GFunc) trap_view_back,
751                                  NULL);
752         break;
753
754     case GDK_space:
755         trap_view_group_foreach (self->group_head,
756                                  (GFunc) trap_view_advance,
757                                  NULL);
758         break;
759
760     case GDK_Return:
761         trap_view_group_foreach (self->group_head,
762                                  (GFunc) trap_view_advance,
763                                  NULL);
764         break;
765
766     case GDK_Escape:
767     case GDK_Q:
768         gtk_main_quit ();
769         break;
770     }
771
772     return FALSE;
773 }
774
775 static gboolean
776 trap_view_button_press (GtkWidget *w, GdkEventButton *ev)
777 {
778     TrapView *self = (TrapView *) w;
779
780     if (ev->x < self->mag_x ||
781         ev->y < self->mag_y ||
782         ev->x > self->mag_x + self->mag_size ||
783         ev->y > self->mag_y + self->mag_size)
784     {
785         if (ev->type == GDK_BUTTON_PRESS) {
786             if (self->current_traps == NULL)
787                 return FALSE;
788
789             if (ev->button == 1) {
790                 trap_view_group_foreach (self->group_head,
791                                          (GFunc) trap_view_advance,
792                                          NULL);
793             } else if (ev->button == 3) {
794                 trap_view_group_foreach (self->group_head,
795                                          (GFunc) trap_view_back,
796                                          NULL);
797             }
798         }
799     }
800     else
801     {
802         self->in_mag_drag = TRUE;
803         self->mag_drag_x = ev->x;
804         self->mag_drag_y = ev->y;
805     }
806
807     return FALSE;
808 }
809
810 static gboolean
811 trap_view_button_release (GtkWidget *w, GdkEventButton *ev)
812 {
813     TrapView *self = (TrapView *) w;
814
815     self->in_mag_drag = FALSE;
816
817     return FALSE;
818 }
819
820 static void
821 trap_view_update_mouse (TrapView *self, GdkEventMotion *ev)
822 {
823     self->px = ev->x;
824     self->py = ev->y;
825
826     gtk_widget_queue_draw (&self->widget);
827 }
828
829 static void
830 trap_view_update_magnifier (TrapView *self, gint *xy)
831 {
832     self->mag_x = xy[0];
833     self->mag_y = xy[1];
834
835     gtk_widget_queue_draw (&self->widget);
836 }
837
838 static gboolean
839 trap_view_motion (GtkWidget *w, GdkEventMotion *ev)
840 {
841     TrapView *self = (TrapView *) w;
842
843     if (self->in_mag_drag) {
844         int xy[2];
845
846         xy[0] = self->mag_x + ev->x - self->mag_drag_x;
847         xy[1] = self->mag_y + ev->y - self->mag_drag_y;
848
849         trap_view_group_foreach (self->group_head,
850                                  (GFunc) trap_view_update_magnifier,
851                                  xy);
852
853         self->mag_drag_x = ev->x;
854         self->mag_drag_y = ev->y;
855     } else if (ev->x < self->mag_x ||
856                ev->y < self->mag_y ||
857                ev->x > self->mag_x + self->mag_size ||
858                ev->y > self->mag_y + self->mag_size)
859     {
860         trap_view_group_foreach (self->group_head,
861                                  (GFunc) trap_view_update_mouse,
862                                  ev);
863     }
864
865     return FALSE;
866 }
867
868 static void
869 trap_view_realize (GtkWidget *widget)
870 {
871     GdkWindowAttr attributes;
872
873     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
874
875     attributes.window_type = GDK_WINDOW_CHILD;
876     attributes.x = widget->allocation.x;
877     attributes.y = widget->allocation.y;
878     attributes.width  = widget->allocation.width;
879     attributes.height = widget->allocation.height;
880     attributes.wclass = GDK_INPUT_OUTPUT;
881     attributes.visual = gtk_widget_get_visual (widget);
882     attributes.colormap = gtk_widget_get_colormap (widget);
883     attributes.event_mask = gtk_widget_get_events (widget) |
884                             GDK_BUTTON_PRESS_MASK |
885                             GDK_BUTTON_RELEASE_MASK |
886                             GDK_KEY_PRESS_MASK |
887                             GDK_KEY_RELEASE_MASK |
888                             GDK_POINTER_MOTION_MASK |
889                             GDK_BUTTON_MOTION_MASK |
890                             GDK_EXPOSURE_MASK;
891
892     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
893                                      &attributes,
894                                      GDK_WA_X | GDK_WA_Y |
895                                      GDK_WA_VISUAL | GDK_WA_COLORMAP);
896     gdk_window_set_user_data (widget->window, widget);
897
898     widget->style = gtk_style_attach (widget->style, widget->window);
899     gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
900 }
901
902 static void
903 trap_view_size_allocate (GtkWidget *w, GdkRectangle *r)
904 {
905     TrapView *self = (TrapView *) w;
906
907     GTK_WIDGET_CLASS (trap_view_parent_class)->size_allocate (w, r);
908
909     self->mag_x = w->allocation.width - self->mag_size - 10;
910     self->mag_y = w->allocation.height - self->mag_size - 10;
911 }
912
913 static void
914 trap_view_finalize (GObject *obj)
915 {
916     G_OBJECT_CLASS (trap_view_parent_class)->finalize (obj);
917 }
918
919 static void
920 trap_view_class_init (TrapViewClass *klass)
921 {
922     GObjectClass *object_class = (GObjectClass *) klass;
923     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
924
925     object_class->finalize = trap_view_finalize;
926
927     widget_class->realize = trap_view_realize;
928     widget_class->size_allocate = trap_view_size_allocate;
929     widget_class->expose_event = trap_view_expose;
930     widget_class->key_press_event = trap_view_key_press;
931     widget_class->button_press_event = trap_view_button_press;
932     widget_class->button_release_event = trap_view_button_release;
933     widget_class->motion_notify_event = trap_view_motion;
934 }
935
936 static void
937 trap_view_init (TrapView *self)
938 {
939     self->mag_zoom = 10;
940     self->mag_size = 200;
941
942     GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
943 }
944
945 static traps_t *
946 _traps_add_trapezoid (TrapView *tv, traps_t *traps, const trapezoid_t *trap)
947 {
948     if (trap->top < traps->extents.p1.y)
949         traps->extents.p1.y = trap->top;
950     if (trap->bottom > traps->extents.p2.y)
951         traps->extents.p2.y = trap->bottom;
952
953     if (trap->left.p1.x < traps->extents.p1.x)
954         traps->extents.p1.x = trap->left.p1.x;
955     if (trap->left.p2.x < traps->extents.p1.x)
956         traps->extents.p1.x = trap->left.p2.x;
957
958     if (trap->right.p1.x > traps->extents.p2.x)
959         traps->extents.p2.x = trap->right.p1.x;
960     if (trap->right.p2.x > traps->extents.p2.x)
961         traps->extents.p2.x = trap->right.p2.x;
962
963     if (traps->num_traps == traps->size) {
964         int newsize = 2 * traps->size;
965         void *newtraps;
966
967         newtraps = g_realloc (traps,
968                               sizeof (traps_t) + newsize * sizeof (trapezoid_t));
969         if (newtraps == NULL)
970             return traps;
971
972         if (tv->current_traps == traps)
973             tv->current_traps = newtraps;
974
975         traps = newtraps;
976         traps->size = newsize;
977
978         if (traps->next != NULL)
979             traps->next->prev = newtraps;
980         if (traps->prev != NULL)
981             traps->prev->next = newtraps;
982         else
983             tv->traps_list = newtraps;
984     }
985
986     traps->traps[traps->num_traps++] = *trap;
987
988     return traps;
989 }
990
991 static traps_t *
992 traps_new (TrapView *tv)
993 {
994     traps_t *t;
995
996     t = g_malloc (sizeof (traps_t) + 16 * sizeof (trapezoid_t));
997     t->prev = NULL;
998     t->next = tv->traps_list;
999     if (tv->traps_list)
1000         tv->traps_list->prev = t;
1001     tv->traps_list = t;
1002
1003     if (tv->current_traps == NULL)
1004         tv->current_traps = t;
1005
1006     t->size = 16;
1007     t->num_traps = 0;
1008     t->extents.p1.x = G_MAXDOUBLE;
1009     t->extents.p1.y = G_MAXDOUBLE;
1010     t->extents.p2.x = -G_MAXDOUBLE;
1011     t->extents.p2.y = -G_MAXDOUBLE;
1012
1013     return t;
1014 }
1015
1016 static edges_t *
1017 _edges_add_edge (TrapView *tv, edges_t *edges, edge_t *e)
1018 {
1019     if (e->top < edges->extents.p1.y)
1020         edges->extents.p1.y = e->top;
1021     if (e->bottom > edges->extents.p2.y)
1022         edges->extents.p2.y = e->bottom;
1023
1024     _compute_intersection_point (&e->line, e->top, &e->p1);
1025     _compute_intersection_point (&e->line, e->bottom, &e->p2);
1026
1027     if (e->p1.x < edges->extents.p1.x)
1028         edges->extents.p1.x = e->p1.x;
1029     if (e->p2.x < edges->extents.p1.x)
1030         edges->extents.p1.x = e->p2.x;
1031
1032     if (e->p1.x > edges->extents.p2.x)
1033         edges->extents.p2.x = e->p1.x;
1034     if (e->p2.x > edges->extents.p2.x)
1035         edges->extents.p2.x = e->p2.x;
1036
1037     if (edges->num_edges == edges->size) {
1038         int newsize = 2 * edges->size;
1039         void *newedges;
1040
1041         newedges = g_realloc (edges,
1042                               sizeof (edges_t) + newsize * sizeof (edge_t));
1043         if (newedges == NULL)
1044             return edges;
1045
1046         if (tv->current_edges == edges)
1047             tv->current_edges = newedges;
1048
1049         edges = newedges;
1050         edges->size = newsize;
1051
1052         if (edges->next != NULL)
1053             edges->next->prev = newedges;
1054         if (edges->prev != NULL)
1055             edges->prev->next = newedges;
1056         else
1057             tv->edges_list = newedges;
1058     }
1059
1060     edges->edges[edges->num_edges++] = *e;
1061
1062     return edges;
1063 }
1064
1065 static edges_t *
1066 edges_new (TrapView *tv)
1067 {
1068     edges_t *t;
1069
1070     t = g_malloc (sizeof (edges_t) + 16 * sizeof (edge_t));
1071     t->prev = NULL;
1072     t->next = tv->edges_list;
1073     if (tv->edges_list)
1074         tv->edges_list->prev = t;
1075     tv->edges_list = t;
1076
1077     if (tv->current_edges == NULL)
1078         tv->current_edges = t;
1079
1080     t->size = 16;
1081     t->num_edges = 0;
1082     t->extents.p1.x = G_MAXDOUBLE;
1083     t->extents.p1.y = G_MAXDOUBLE;
1084     t->extents.p2.x = -G_MAXDOUBLE;
1085     t->extents.p2.y = -G_MAXDOUBLE;
1086
1087     return t;
1088 }
1089
1090 int
1091 main (int argc, char **argv)
1092 {
1093     TrapView *tv, *group_head = NULL, *group_prev = NULL;
1094     GtkWidget *window, *hbox;
1095     FILE *file;
1096
1097     gtk_init (&argc, &argv);
1098
1099     hbox = gtk_hbox_new (TRUE, 0);
1100
1101     file = fopen (argv[1], "r");
1102     if (file != NULL) {
1103         char *line = NULL;
1104         size_t len = 0;
1105         traps_t *traps;
1106
1107         tv = g_object_new (trap_view_get_type (), NULL);
1108         gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
1109         gtk_widget_show (&tv->widget);
1110
1111         tv->group_prev = group_prev;
1112         tv->group_next = NULL;
1113         if (group_prev)
1114             group_prev->group_next = tv;
1115         group_prev = tv;
1116         if (group_head == NULL)
1117             group_head = tv;
1118         tv->group_head = group_head;
1119
1120         traps = traps_new (tv);
1121         while (getline (&line, &len, file) != -1) {
1122             trapezoid_t t;
1123
1124             if (sscanf (line,
1125                        "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
1126                        &t.top, &t.bottom,
1127                        &t.left.p1.x, &t.left.p1.y,
1128                        &t.left.p2.x, &t.left.p2.y,
1129                        &t.right.p1.x, &t.right.p1.y,
1130                        &t.right.p2.x, &t.right.p2.y) == 10) {
1131                 traps = _traps_add_trapezoid (tv, traps, &t);
1132             } else {
1133                 if (traps->num_traps) {
1134                     g_print ("read %d trapezoids\n", traps->num_traps);
1135                     g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1136                             traps->extents.p1.x, traps->extents.p1.y,
1137                             traps->extents.p2.x, traps->extents.p2.y);
1138                     traps = traps_new (tv);
1139                 }
1140             }
1141         }
1142         free (line);
1143         fclose (file);
1144
1145         if (traps->num_traps) {
1146             g_print ("read %d trapezoids\n", traps->num_traps);
1147             g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1148                      traps->extents.p1.x, traps->extents.p1.y,
1149                      traps->extents.p2.x, traps->extents.p2.y);
1150         }
1151     }
1152
1153     file = fopen (argv[2], "r");
1154     if (file != NULL) {
1155         char *line = NULL;
1156         size_t len = 0;
1157         traps_t *traps;
1158
1159         tv = g_object_new (trap_view_get_type (), NULL);
1160         gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
1161         gtk_widget_show (&tv->widget);
1162
1163         tv->group_prev = group_prev;
1164         tv->group_next = NULL;
1165         if (group_prev)
1166             group_prev->group_next = tv;
1167         group_prev = tv;
1168         if (group_head == NULL)
1169             group_head = tv;
1170         tv->group_head = group_head;
1171
1172         traps = traps_new (tv);
1173         while (getline (&line, &len, file) != -1) {
1174             trapezoid_t t;
1175
1176             if (sscanf (line,
1177                        "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
1178                        &t.top, &t.bottom,
1179                        &t.left.p1.x, &t.left.p1.y,
1180                        &t.left.p2.x, &t.left.p2.y,
1181                        &t.right.p1.x, &t.right.p1.y,
1182                        &t.right.p2.x, &t.right.p2.y) == 10) {
1183                 traps = _traps_add_trapezoid (tv, traps, &t);
1184             } else {
1185                 if (traps->num_traps) {
1186                     g_print ("read %d trapezoids\n", traps->num_traps);
1187                     g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1188                             traps->extents.p1.x, traps->extents.p1.y,
1189                             traps->extents.p2.x, traps->extents.p2.y);
1190                     traps = traps_new (tv);
1191                 }
1192             }
1193         }
1194         free (line);
1195         fclose (file);
1196
1197         if (traps->num_traps) {
1198             g_print ("read %d trapezoids\n", traps->num_traps);
1199             g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1200                      traps->extents.p1.x, traps->extents.p1.y,
1201                      traps->extents.p2.x, traps->extents.p2.y);
1202         }
1203     }
1204
1205 #if 1
1206     if (argc >=4) {
1207         file = fopen (argv[3], "r");
1208         if (file != NULL) {
1209             char *line = NULL;
1210             size_t len = 0;
1211             edges_t *edges;
1212
1213             edges = edges_new (tv);
1214             while (getline (&line, &len, file) != -1) {
1215                 edge_t e;
1216
1217                 if (sscanf (line,
1218                            "(%lf, %lf), (%lf, %lf) %lf %lf %d",
1219                            &e.line.p1.x, &e.line.p1.y,
1220                            &e.line.p2.x, &e.line.p2.y,
1221                            &e.top, &e.bottom,
1222                            &e.dir) == 7) {
1223                     edges = _edges_add_edge (tv, edges, &e);
1224                 } else {
1225                     if (edges->num_edges) {
1226                         g_print ("read %d edges\n", edges->num_edges);
1227                         g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1228                                 edges->extents.p1.x, edges->extents.p1.y,
1229                                 edges->extents.p2.x, edges->extents.p2.y);
1230                         edges = edges_new (tv);
1231                     }
1232                 }
1233             }
1234             free (line);
1235             fclose (file);
1236
1237             if (edges->num_edges) {
1238                 g_print ("read %d edges\n", edges->num_edges);
1239                 g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1240                          edges->extents.p1.x, edges->extents.p1.y,
1241                          edges->extents.p2.x, edges->extents.p2.y);
1242             }
1243         }
1244     }
1245 #else
1246     if (argc >= 4) {
1247         file = fopen (argv[3], "r");
1248         if (file != NULL) {
1249             char *line = NULL;
1250             size_t len = 0;
1251             traps_t *traps;
1252
1253             tv = g_object_new (trap_view_get_type (), NULL);
1254             gtk_box_pack_start (GTK_BOX (hbox), &tv->widget, TRUE, TRUE, 0);
1255             gtk_widget_show (&tv->widget);
1256
1257             tv->group_prev = group_prev;
1258             tv->group_next = NULL;
1259             if (group_prev)
1260                 group_prev->group_next = tv;
1261             group_prev = tv;
1262             if (group_head == NULL)
1263                 group_head = tv;
1264             tv->group_head = group_head;
1265
1266             traps = traps_new (tv);
1267             while (getline (&line, &len, file) != -1) {
1268                 trapezoid_t t;
1269
1270                 if (sscanf (line,
1271                             "%lf %lf L:(%lf, %lf), (%lf, %lf) R:(%lf, %lf), (%lf, %lf)",
1272                             &t.top, &t.bottom,
1273                             &t.left.p1.x, &t.left.p1.y,
1274                             &t.left.p2.x, &t.left.p2.y,
1275                             &t.right.p1.x, &t.right.p1.y,
1276                             &t.right.p2.x, &t.right.p2.y) == 10) {
1277                     traps = _traps_add_trapezoid (tv, traps, &t);
1278                 } else {
1279                     if (traps->num_traps) {
1280                         g_print ("read %d trapezoids\n", traps->num_traps);
1281                         g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1282                                  traps->extents.p1.x, traps->extents.p1.y,
1283                                  traps->extents.p2.x, traps->extents.p2.y);
1284                         traps = traps_new (tv);
1285                     }
1286                 }
1287             }
1288             free (line);
1289             fclose (file);
1290
1291             if (traps->num_traps) {
1292                 g_print ("read %d trapezoids\n", traps->num_traps);
1293                 g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
1294                          traps->extents.p1.x, traps->extents.p1.y,
1295                          traps->extents.p2.x, traps->extents.p2.y);
1296             }
1297         }
1298     }
1299 #endif
1300
1301     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1302     g_signal_connect (window, "delete-event",
1303                       G_CALLBACK (gtk_main_quit), NULL);
1304     gtk_widget_set_size_request (window, 512, 512);
1305     gtk_container_add (GTK_CONTAINER (window), hbox);
1306     gtk_widget_show (hbox);
1307     gtk_widget_show (window);
1308
1309     gtk_main ();
1310     return 0;
1311 }