tizen 2.3.1 release
[framework/graphics/cairo.git] / util / show-polygon.c
1 #define _GNU_SOURCE
2 #include <gtk/gtk.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <gdk/gdkkeysyms.h>
7 #include <math.h>
8
9 typedef struct _point {
10     gdouble x, y;
11 } point_t;
12 typedef struct _edge {
13     point_t p1, p2;
14     gdouble top, bot;
15     int dir;
16 } edge_t;
17 typedef struct _box {
18     point_t p1, p2;
19 } box_t;
20
21 typedef struct _polygon {
22     struct _polygon *next, *prev;
23     int num_edges;
24     int size;
25     edge_t edges[0];
26 } polygon_t;
27
28 typedef struct _PolygonView {
29     GtkWidget widget;
30
31     cairo_surface_t *pixmap;
32     int pixmap_width, pixmap_height;
33
34     box_t extents;
35     polygon_t *polygons;
36
37     double px, py;
38
39     gint mag_x, mag_y;
40     gint mag_size;
41     gdouble mag_zoom;
42     gboolean in_mag_drag;
43     gint mag_drag_x, mag_drag_y;
44 } PolygonView;
45
46 typedef struct _PolygonViewClass {
47     GtkWidgetClass parent_class;
48 } PolygonViewClass;
49
50 G_DEFINE_TYPE (PolygonView, polygon_view, GTK_TYPE_WIDGET)
51
52 double highlight = -1;
53
54 static void draw_edges (cairo_t *cr, polygon_t *p, gdouble sf, int dir)
55 {
56     int n;
57
58     if (dir < 0)
59         cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
60     else
61         cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
62
63     for (n = 0; n < p->num_edges; n++) {
64         const edge_t *e = &p->edges[n];
65         double dx, dy;
66         double x1, x2;
67
68         if (e->dir != dir)
69             continue;
70
71         dx = e->p2.x - e->p1.x;
72         dy = e->p2.y - e->p1.y;
73
74         x1 = e->p1.x + (e->top - e->p1.y) / dy * dx;
75         x2 = e->p1.x + (e->bot - e->p1.y) / dy * dx;
76
77         cairo_arc (cr, x1, e->top, 2/sf, 0, 2*M_PI);
78         cairo_arc (cr, x2, e->bot, 2/sf, 0, 2*M_PI);
79         cairo_fill (cr);
80     }
81
82     if (dir < 0)
83         cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.5);
84     else
85         cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5);
86
87     for (n = 0; n < p->num_edges; n++) {
88         const edge_t *e = &p->edges[n];
89
90         if (e->dir != dir)
91             continue;
92
93         cairo_move_to (cr, e->p1.x, e->p1.y);
94         cairo_line_to (cr, e->p2.x, e->p2.y);
95     }
96     cairo_save (cr); {
97         cairo_identity_matrix (cr);
98         cairo_set_line_width (cr, 1.);
99         cairo_stroke (cr);
100     } cairo_restore (cr);
101
102     if (dir < 0)
103         cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
104     else
105         cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
106
107     for (n = 0; n < p->num_edges; n++) {
108         const edge_t *e = &p->edges[n];
109         double dx, dy;
110         double x1, x2;
111
112         if (e->dir != dir)
113             continue;
114
115         dx = e->p2.x - e->p1.x;
116         dy = e->p2.y - e->p1.y;
117
118         x1 = e->p1.x + (e->top - e->p1.y) / dy * dx;
119         x2 = e->p1.x + (e->bot - e->p1.y) / dy * dx;
120
121         cairo_move_to (cr, x1, e->top);
122         cairo_line_to (cr, x2, e->bot);
123     }
124     cairo_save (cr); {
125         cairo_identity_matrix (cr);
126         cairo_set_line_width (cr, 1.);
127         cairo_stroke (cr);
128     } cairo_restore (cr);
129 }
130
131 static void draw_polygon (cairo_t *cr, polygon_t *p, gdouble sf)
132 {
133     draw_edges (cr, p, sf, -1);
134
135     draw_edges (cr, p, sf, +1);
136 }
137
138 static cairo_surface_t *
139 pixmap_create (PolygonView *self, cairo_surface_t *target)
140 {
141     cairo_surface_t *surface =
142         cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
143                                       self->widget.allocation.width,
144                                       self->widget.allocation.height);
145     cairo_t *cr = cairo_create (surface);
146     polygon_t *polygon;
147     gdouble sf_x, sf_y, sf;
148     gdouble mid, dim;
149     gdouble x0,  y0;
150     box_t extents;
151
152     cairo_set_source_rgb (cr, 1, 1, 1);
153     cairo_paint (cr);
154
155     if (self->polygons == NULL) {
156         cairo_destroy(cr);
157         return surface;
158     }
159
160     extents = self->extents;
161
162     mid = (extents.p2.x + extents.p1.x) / 2.;
163     dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
164     sf_x = self->widget.allocation.width / dim / 2;
165
166     mid = (extents.p2.y + extents.p1.y) / 2.;
167     dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
168     sf_y = self->widget.allocation.height / dim / 2;
169
170     sf = MIN (sf_x, sf_y);
171
172     mid = (extents.p2.x + extents.p1.x) / 2.;
173     dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
174     x0 = mid - dim;
175     mid = (extents.p2.y + extents.p1.y) / 2.;
176     dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
177     y0 = mid - dim;
178
179     cairo_save (cr); {
180         cairo_scale (cr, sf, sf);
181         cairo_translate (cr, -x0, -y0);
182
183         for (polygon = self->polygons; polygon; polygon = polygon->next) {
184             if (polygon->num_edges == 0)
185                 continue;
186
187             draw_polygon (cr, polygon, sf);
188         }
189
190         if (highlight != -1) {
191             cairo_move_to (cr, extents.p1.x, highlight);
192             cairo_line_to (cr, extents.p2.x, highlight);
193             cairo_set_source_rgb (cr, 0, .7, 0);
194             cairo_save (cr);
195             cairo_identity_matrix (cr);
196             cairo_set_line_width (cr, 1.);
197             cairo_stroke (cr);
198             cairo_restore (cr);
199         }
200     } cairo_restore (cr);
201
202     cairo_destroy (cr);
203     return surface;
204 }
205
206 static void
207 polygon_view_draw (PolygonView *self, cairo_t *cr)
208 {
209     polygon_t *polygon;
210     gdouble sf_x, sf_y, sf;
211     gdouble mid, dim;
212     gdouble x0,  y0;
213     box_t extents;
214
215     extents = self->extents;
216
217     mid = (extents.p2.x + extents.p1.x) / 2.;
218     dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
219     sf_x = self->widget.allocation.width / dim / 2;
220
221     mid = (extents.p2.y + extents.p1.y) / 2.;
222     dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
223     sf_y = self->widget.allocation.height / dim / 2;
224
225     sf = MIN (sf_x, sf_y);
226
227     mid = (extents.p2.x + extents.p1.x) / 2.;
228     dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
229     x0 = mid - dim;
230     mid = (extents.p2.y + extents.p1.y) / 2.;
231     dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
232     y0 = mid - dim;
233
234     if (self->pixmap_width != self->widget.allocation.width ||
235         self->pixmap_height != self->widget.allocation.height)
236     {
237         cairo_surface_destroy (self->pixmap);
238         self->pixmap = pixmap_create (self, cairo_get_target (cr));
239         self->pixmap_width = self->widget.allocation.width;
240         self->pixmap_height = self->widget.allocation.height;
241     }
242
243     cairo_set_source_surface (cr, self->pixmap, 0, 0);
244     cairo_paint (cr);
245
246     if (self->polygons == NULL)
247         return;
248
249     /* draw a zoom view of the area around the mouse */
250     if (1) {
251         double zoom = self->mag_zoom;
252         int size = self->mag_size;
253         int mag_x = self->mag_x;
254         int mag_y = self->mag_y;
255
256         if (1) {
257             if (self->px + size < self->widget.allocation.width/2)
258                 mag_x = self->px + size/4;
259             else
260                 mag_x = self->px - size/4 - size;
261             mag_y = self->py - size/2;
262             if (mag_y < 0)
263                 mag_y = 0;
264             if (mag_y + size > self->widget.allocation.height)
265                 mag_y = self->widget.allocation.height - size;
266         }
267
268         cairo_save (cr); {
269             /* bottom right */
270             cairo_rectangle (cr, mag_x, mag_y, size, size);
271             cairo_stroke_preserve (cr);
272             cairo_set_source_rgb (cr, 1, 1, 1);
273             cairo_fill_preserve (cr);
274             cairo_clip (cr);
275
276             /* compute roi in extents */
277             cairo_translate (cr, mag_x + size/2, mag_y + size/2);
278
279             cairo_save (cr); {
280                 cairo_scale (cr, zoom, zoom);
281                 cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
282                 for (polygon = self->polygons; polygon; polygon = polygon->next) {
283                     if (polygon->num_edges == 0)
284                         continue;
285
286                     draw_polygon (cr, polygon, zoom);
287                 }
288
289                 if (highlight != -1) {
290                     cairo_move_to (cr, extents.p1.x, highlight);
291                     cairo_line_to (cr, extents.p2.x, highlight);
292                     cairo_set_source_rgb (cr, 0, .7, 0);
293                     cairo_save (cr);
294                     cairo_identity_matrix (cr);
295                     cairo_set_line_width (cr, 1.);
296                     cairo_stroke (cr);
297                     cairo_restore (cr);
298                 }
299             } cairo_restore (cr);
300
301             /* grid */
302             cairo_save (cr); {
303                 int i;
304
305                 cairo_translate (cr,
306                                  -zoom*fmod (self->px/sf + x0, 1.),
307                                  -zoom*fmod (self->py/sf + y0, 1.));
308                 zoom /= 2;
309                 for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
310                     cairo_move_to (cr, zoom*i, -size/2);
311                     cairo_line_to (cr, zoom*i, size/2 + zoom);
312                     cairo_move_to (cr, -size/2, zoom*i);
313                     cairo_line_to (cr, size/2 + zoom, zoom*i);
314                 }
315                 zoom *= 2;
316                 cairo_set_source_rgba (cr, .7, .7, .7, .5);
317                 cairo_set_line_width (cr, 1.);
318                 cairo_stroke (cr);
319
320                 for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
321                     cairo_move_to (cr, zoom*i, -size/2);
322                     cairo_line_to (cr, zoom*i, size/2 + zoom);
323                     cairo_move_to (cr, -size/2, zoom*i);
324                     cairo_line_to (cr, size/2 + zoom, zoom*i);
325                 }
326                 cairo_set_source_rgba (cr, .1, .1, .1, .5);
327                 cairo_set_line_width (cr, 2.);
328                 cairo_stroke (cr);
329             } cairo_restore (cr);
330
331         } cairo_restore (cr);
332     }
333 }
334
335 static gboolean
336 polygon_view_expose (GtkWidget *w, GdkEventExpose *ev)
337 {
338     PolygonView *self = (PolygonView *) w;
339     cairo_t *cr;
340
341     cr = gdk_cairo_create (w->window);
342     gdk_cairo_region (cr, ev->region);
343     cairo_clip (cr);
344
345     polygon_view_draw (self, cr);
346
347     cairo_destroy (cr);
348     return FALSE;
349 }
350
351 static gboolean
352 polygon_view_key_press (GtkWidget *w, GdkEventKey *ev)
353 {
354     switch (ev->keyval) {
355     case GDK_Escape:
356     case GDK_Q:
357         gtk_main_quit ();
358         break;
359     }
360
361     return FALSE;
362 }
363
364 static gboolean
365 polygon_view_button_press (GtkWidget *w, GdkEventButton *ev)
366 {
367     PolygonView *self = (PolygonView *) w;
368
369     if (ev->x < self->mag_x ||
370         ev->y < self->mag_y ||
371         ev->x > self->mag_x + self->mag_size ||
372         ev->y > self->mag_y + self->mag_size)
373     {
374     }
375     else
376     {
377         self->in_mag_drag = TRUE;
378         self->mag_drag_x = ev->x;
379         self->mag_drag_y = ev->y;
380     }
381
382     return FALSE;
383 }
384
385 static gboolean
386 polygon_view_button_release (GtkWidget *w, GdkEventButton *ev)
387 {
388     PolygonView *self = (PolygonView *) w;
389
390     self->in_mag_drag = FALSE;
391
392     return FALSE;
393 }
394
395 static void
396 polygon_view_update_mouse (PolygonView *self, GdkEventMotion *ev)
397 {
398     self->px = ev->x;
399     self->py = ev->y;
400
401     gtk_widget_queue_draw (&self->widget);
402 }
403
404 static void
405 polygon_view_update_magnifier (PolygonView *self, gint *xy)
406 {
407     self->mag_x = xy[0];
408     self->mag_y = xy[1];
409
410     gtk_widget_queue_draw (&self->widget);
411 }
412
413 static gboolean
414 polygon_view_motion (GtkWidget *w, GdkEventMotion *ev)
415 {
416     PolygonView *self = (PolygonView *) w;
417
418     if (self->in_mag_drag) {
419         int xy[2];
420
421         xy[0] = self->mag_x + ev->x - self->mag_drag_x;
422         xy[1] = self->mag_y + ev->y - self->mag_drag_y;
423
424         polygon_view_update_magnifier (self, xy);
425
426         self->mag_drag_x = ev->x;
427         self->mag_drag_y = ev->y;
428     } else if (ev->x < self->mag_x ||
429                ev->y < self->mag_y ||
430                ev->x > self->mag_x + self->mag_size ||
431                ev->y > self->mag_y + self->mag_size)
432     {
433         polygon_view_update_mouse (self, ev);
434     }
435
436     return FALSE;
437 }
438
439 static void
440 polygon_view_realize (GtkWidget *widget)
441 {
442     GdkWindowAttr attributes;
443
444     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
445
446     attributes.window_type = GDK_WINDOW_CHILD;
447     attributes.x = widget->allocation.x;
448     attributes.y = widget->allocation.y;
449     attributes.width  = widget->allocation.width;
450     attributes.height = widget->allocation.height;
451     attributes.wclass = GDK_INPUT_OUTPUT;
452     attributes.visual = gtk_widget_get_visual (widget);
453     attributes.colormap = gtk_widget_get_colormap (widget);
454     attributes.event_mask = gtk_widget_get_events (widget) |
455                             GDK_BUTTON_PRESS_MASK |
456                             GDK_BUTTON_RELEASE_MASK |
457                             GDK_KEY_PRESS_MASK |
458                             GDK_KEY_RELEASE_MASK |
459                             GDK_POINTER_MOTION_MASK |
460                             GDK_BUTTON_MOTION_MASK |
461                             GDK_EXPOSURE_MASK;
462
463     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
464                                      &attributes,
465                                      GDK_WA_X | GDK_WA_Y |
466                                      GDK_WA_VISUAL | GDK_WA_COLORMAP);
467     gdk_window_set_user_data (widget->window, widget);
468
469     widget->style = gtk_style_attach (widget->style, widget->window);
470     gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
471 }
472
473 static void
474 polygon_view_size_allocate (GtkWidget *w, GdkRectangle *r)
475 {
476     PolygonView *self = (PolygonView *) w;
477
478     GTK_WIDGET_CLASS (polygon_view_parent_class)->size_allocate (w, r);
479
480     self->mag_x = w->allocation.width - self->mag_size - 10;
481     self->mag_y = w->allocation.height - self->mag_size - 10;
482 }
483
484 static void
485 polygon_view_finalize (GObject *obj)
486 {
487     G_OBJECT_CLASS (polygon_view_parent_class)->finalize (obj);
488 }
489
490 static void
491 polygon_view_class_init (PolygonViewClass *klass)
492 {
493     GObjectClass *object_class = (GObjectClass *) klass;
494     GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
495
496     object_class->finalize = polygon_view_finalize;
497
498     widget_class->realize = polygon_view_realize;
499     widget_class->size_allocate = polygon_view_size_allocate;
500     widget_class->expose_event = polygon_view_expose;
501     widget_class->key_press_event = polygon_view_key_press;
502     widget_class->button_press_event = polygon_view_button_press;
503     widget_class->button_release_event = polygon_view_button_release;
504     widget_class->motion_notify_event = polygon_view_motion;
505 }
506
507 static void
508 polygon_view_init (PolygonView *self)
509 {
510     self->mag_zoom = 64;
511     self->mag_size = 200;
512
513     self->extents.p1.x = G_MAXDOUBLE;
514     self->extents.p1.y = G_MAXDOUBLE;
515     self->extents.p2.x = -G_MAXDOUBLE;
516     self->extents.p2.y = -G_MAXDOUBLE;
517
518     GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
519 }
520
521 static polygon_t *
522 _polygon_add_edge (PolygonView *view, polygon_t *polygon,
523                    point_t *p1, point_t *p2,
524                    gdouble top, gdouble bot, int dir)
525 {
526     if (polygon == NULL)
527         return NULL;
528
529     if (top < view->extents.p1.y)
530         view->extents.p1.y = top;
531     if (bot > view->extents.p2.y)
532         view->extents.p2.y = bot;
533
534     if (p1->x < view->extents.p1.x)
535         view->extents.p1.x = p1->x;
536     if (p1->x > view->extents.p2.x)
537         view->extents.p2.x = p1->x;
538     if (p2->x < view->extents.p1.x)
539         view->extents.p1.x = p2->x;
540     if (p2->x > view->extents.p2.x)
541         view->extents.p2.x = p2->x;
542
543     if (polygon->num_edges == polygon->size) {
544         int newsize = 2 * polygon->size;
545         void *newpolygon;
546
547         newpolygon = g_realloc (polygon,
548                               sizeof (polygon_t) + newsize * sizeof (edge_t));
549         if (newpolygon == NULL)
550             return polygon;
551
552         polygon = newpolygon;
553         polygon->size = newsize;
554
555         if (polygon->next != NULL)
556             polygon->next->prev = newpolygon;
557         if (polygon->prev != NULL)
558             polygon->prev->next = newpolygon;
559         else
560             view->polygons = newpolygon;
561     }
562
563     polygon->edges[polygon->num_edges].p1 = *p1;
564     polygon->edges[polygon->num_edges].p2 = *p2;
565     polygon->edges[polygon->num_edges].top = top;
566     polygon->edges[polygon->num_edges].bot = bot;
567     polygon->edges[polygon->num_edges].dir = dir;
568     polygon->num_edges++;
569
570     return polygon;
571 }
572
573 static polygon_t *
574 polygon_new (PolygonView *view)
575 {
576     polygon_t *t;
577
578     t = g_malloc (sizeof (polygon_t) + 128 * sizeof (edge_t));
579     t->prev = NULL;
580     t->next = view->polygons;
581     if (view->polygons)
582         view->polygons->prev = t;
583     view->polygons = t;
584
585     t->size = 128;
586     t->num_edges = 0;
587
588     return t;
589 }
590
591 int
592 main (int argc, char **argv)
593 {
594     PolygonView *view;
595     polygon_t *polygon = NULL;
596     GtkWidget *window;
597     FILE *file;
598     char *line = NULL;
599     size_t len = 0;
600
601     gtk_init (&argc, &argv);
602
603     view = g_object_new (polygon_view_get_type (), NULL);
604
605     file = fopen (argv[1], "r");
606     if (file != NULL) {
607         while (getline (&line, &len, file) != -1) {
608             point_t p1, p2;
609             double top, bottom;
610             int dir;
611
612             if (strncmp (line, "polygon: ", sizeof("polygon: ")-1) == 0) {
613                 if (polygon && polygon->num_edges) {
614                     g_print ("read polygon with %d edges\n", polygon->num_edges);
615
616                     polygon = polygon_new (view);
617                 } else if (polygon == NULL)
618                     polygon = polygon_new (view);
619             } else if (sscanf (line, "  [%*d] = [(%lf, %lf), (%lf, %lf)], top=%lf, bottom=%lf, dir=%d", &p1.x, &p1.y, &p2.x, &p2.y, &top, &bottom, &dir) == 7) {
620                 polygon = _polygon_add_edge (view, polygon, &p1, &p2,
621                                              top, bottom, dir);
622             }
623         }
624
625         if (polygon && polygon->num_edges)
626             g_print ("read polygon with %d edges\n", polygon->num_edges);
627
628         g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
629                  view->extents.p1.x, view->extents.p1.y,
630                  view->extents.p2.x, view->extents.p2.y);
631         fclose (file);
632     }
633
634     if (argc > 2)
635         highlight = atof (argv[2]);
636
637     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
638     g_signal_connect (window, "delete-event",
639                       G_CALLBACK (gtk_main_quit), NULL);
640     gtk_widget_set_size_request (window, 800, 800);
641     gtk_container_add (GTK_CONTAINER (window), &view->widget);
642     gtk_widget_show_all (window);
643
644     gtk_main ();
645     return 0;
646 }