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