6dced9a60d862adf8d174939900a31c6783fd84f
[navit-package] / src / graphics.c
1 #include <glib.h>
2 #include <stdio.h>
3 #include <math.h>
4 #include "debug.h"
5 #include "string.h"
6 #include "draw_info.h"
7 #include "point.h"
8 #include "graphics.h"
9 #include "projection.h"
10 #include "map.h"
11 #include "coord.h"
12 #include "transform.h"
13 #include "plugin.h"
14 #include "profile.h"
15 #include "mapset.h"
16 #include "route.h"
17 #include "util.h"
18 #include "layout.h"
19
20 struct graphics
21 {
22         struct graphics_priv *priv;
23         struct graphics_methods meth;
24         struct graphics_font *font[16];
25         struct graphics_gc *gc[3];
26         int ready;
27 };
28
29 struct displaylist {
30         GHashTable *dl;
31 };
32
33 struct graphics *
34 graphics_new(const char *type, struct attr **attrs)
35 {
36         struct graphics *this_;
37         struct graphics_priv * (*new)(struct graphics_methods *meth, struct attr **attrs);
38
39         new=plugin_get_graphics_type(type);
40         if (! new)
41                 return NULL;    
42         this_=g_new0(struct graphics, 1);
43         this_->priv=(*new)(&this_->meth, attrs);
44         return this_;
45 }
46
47 struct graphics *
48 graphics_overlay_new(struct graphics *parent, struct point *p, int w, int h)
49 {
50         struct graphics *this_;
51         this_=g_new0(struct graphics, 1);
52         this_->priv=parent->meth.overlay_new(parent->priv, &this_->meth, p, w, h);
53         return this_;
54 }
55
56
57 void
58 graphics_init(struct graphics *this_)
59 {
60         this_->gc[0]=graphics_gc_new(this_);
61         graphics_gc_set_background(this_->gc[0], &(struct color) { 0xffff, 0xefef, 0xb7b7 });
62         graphics_gc_set_foreground(this_->gc[0], &(struct color) { 0xffff, 0xefef, 0xb7b7 });
63         this_->gc[1]=graphics_gc_new(this_);
64         graphics_gc_set_background(this_->gc[1], &(struct color) { 0x0000, 0x0000, 0x0000 });
65         graphics_gc_set_foreground(this_->gc[1], &(struct color) { 0xffff, 0xffff, 0xffff });
66         this_->gc[2]=graphics_gc_new(this_);
67         graphics_gc_set_background(this_->gc[2], &(struct color) { 0xffff, 0xffff, 0xffff });
68         graphics_gc_set_foreground(this_->gc[2], &(struct color) { 0xffff, 0xffff, 0xffff });
69         this_->meth.background_gc(this_->priv, this_->gc[0]->priv);
70 }
71
72 void *
73 graphics_get_data(struct graphics *this_, char *type)
74 {
75         return (this_->meth.get_data(this_->priv, type));
76 }
77
78 void
79 graphics_register_resize_callback(struct graphics *this_, void (*callback)(void *data, int w, int h), void *data)
80 {
81         this_->meth.register_resize_callback(this_->priv, callback, data);
82 }
83
84 void
85 graphics_register_button_callback(struct graphics *this_, void (*callback)(void *data, int pressed, int button, struct point *p), void *data)
86 {
87         this_->meth.register_button_callback(this_->priv, callback, data);
88 }
89
90 void
91 graphics_register_motion_callback(struct graphics *this_, void (*callback)(void *data, struct point *p), void *data)
92 {
93         this_->meth.register_motion_callback(this_->priv, callback, data);
94 }
95
96 struct graphics_font *
97 graphics_font_new(struct graphics *gra, int size)
98 {
99         struct graphics_font *this_;
100
101         this_=g_new0(struct graphics_font,1);
102         this_->priv=gra->meth.font_new(gra->priv, &this_->meth, size);
103         return this_;
104 }
105
106 struct graphics_gc *
107 graphics_gc_new(struct graphics *gra)
108 {
109         struct graphics_gc *this_;
110
111         this_=g_new0(struct graphics_gc,1);
112         this_->priv=gra->meth.gc_new(gra->priv, &this_->meth);
113         return this_;
114 }
115
116 void
117 graphics_gc_set_foreground(struct graphics_gc *gc, struct color *c)
118 {
119         gc->meth.gc_set_foreground(gc->priv, c);
120 }
121
122 void
123 graphics_gc_set_background(struct graphics_gc *gc, struct color *c)
124 {
125         gc->meth.gc_set_background(gc->priv, c);
126 }
127
128 void
129 graphics_gc_set_linewidth(struct graphics_gc *gc, int width)
130 {
131         gc->meth.gc_set_linewidth(gc->priv, width);
132 }
133
134 struct graphics_image *
135 graphics_image_new(struct graphics *gra, char *path)
136 {
137         struct graphics_image *this_;
138
139         this_=g_new0(struct graphics_image,1);
140         this_->priv=gra->meth.image_new(gra->priv, &this_->meth, path, &this_->width, &this_->height, &this_->hot);
141         if (! this_->priv) {
142                 g_free(this_);
143                 this_=NULL;
144         }
145         return this_;
146 }
147
148 void
149 graphics_draw_restore(struct graphics *this_, struct point *p, int w, int h)
150 {
151         this_->meth.draw_restore(this_->priv, p, w, h);
152 }
153
154 void
155 graphics_draw_mode(struct graphics *this_, enum draw_mode_num mode)
156 {
157         this_->meth.draw_mode(this_->priv, mode);
158 }
159
160 void
161 graphics_draw_lines(struct graphics *this_, struct graphics_gc *gc, struct point *p, int count)
162 {
163         this_->meth.draw_lines(this_->priv, gc->priv, p, count);
164 }
165
166 void
167 graphics_draw_circle(struct graphics *this_, struct graphics_gc *gc, struct point *p, int r)
168 {
169         this_->meth.draw_circle(this_->priv, gc->priv, p, r);
170 }
171
172
173 void
174 graphics_draw_rectangle(struct graphics *this_, struct graphics_gc *gc, struct point *p, int w, int h)
175 {
176         this_->meth.draw_rectangle(this_->priv, gc->priv, p, w, h);
177 }
178
179
180 #include "attr.h"
181 #include "popup.h"
182 #include <stdio.h>
183
184 #if 0
185 static void
186 popup_view_html(struct popup_item *item, char *file)
187 {
188         char command[1024];
189         sprintf(command,"firefox %s", file);
190         system(command);
191 }
192
193 static void
194 graphics_popup(struct display_list *list, struct popup_item **popup)
195 {
196         struct item *item;
197         struct attr attr;
198         struct map_rect *mr;
199         struct coord c;
200         struct popup_item *curr_item,*last=NULL;
201         item=list->data;
202         mr=map_rect_new(item->map, NULL, NULL, 0);
203         printf("id hi=0x%x lo=0x%x\n", item->id_hi, item->id_lo);
204         item=map_rect_get_item_byid(mr, item->id_hi, item->id_lo);
205         if (item) {
206                 if (item_attr_get(item, attr_name, &attr)) {
207                         curr_item=popup_item_new_text(popup,attr.u.str,1);
208                         if (item_attr_get(item, attr_info_html, &attr)) {
209                                 popup_item_new_func(&last,"HTML Info",1, popup_view_html, g_strdup(attr.u.str));
210                         }
211                         if (item_attr_get(item, attr_price_html, &attr)) {
212                                 popup_item_new_func(&last,"HTML Preis",2, popup_view_html, g_strdup(attr.u.str));
213                         }
214                         curr_item->submenu=last;
215                 }
216         }
217         map_rect_destroy(mr);
218 }
219 #endif
220
221 struct displayitem {
222         struct item item;
223         char *label;
224         int displayed;
225         int count;
226         struct point pnt[0];
227 };
228
229 static int
230 xdisplay_free_list(gpointer key, gpointer value, gpointer user_data)
231 {
232         GList *h, *l;
233         h=value;
234         l=h;
235         while (l) {
236                 struct displayitem *di=l->data;
237                 if (! di->displayed && di->item.type < type_line) 
238                         dbg(1,"warning: item '%s' not displayed\n", item_to_name(di->item.type));
239                 g_free(l->data);
240                 l=g_list_next(l);
241         }
242         g_list_free(h);
243         return TRUE;
244 }
245
246 static void
247 xdisplay_free(GHashTable *display_list)
248 {
249         g_hash_table_foreach_remove(display_list, xdisplay_free_list, NULL);
250 }
251
252 void
253 display_add(struct displaylist *displaylist, struct item *item, int count, struct point *pnt, char *label)
254 {
255         struct displayitem *di;
256         int len;
257         GList *l;
258         char *p;
259
260         len=sizeof(*di)+count*sizeof(*pnt);
261         if (label)
262                 len+=strlen(label)+1;
263
264         p=g_malloc(len);
265
266         di=(struct displayitem *)p;
267         di->displayed=0;
268         p+=sizeof(*di)+count*sizeof(*pnt);
269         di->item=*item;
270         if (label) {
271                 di->label=p;
272                 strcpy(di->label, label);
273         } else 
274                 di->label=NULL;
275         di->count=count;
276         memcpy(di->pnt, pnt, count*sizeof(*pnt));
277
278         l=g_hash_table_lookup(displaylist->dl, GINT_TO_POINTER(item->type));
279         l=g_list_prepend(l, di);
280         g_hash_table_insert(displaylist->dl, GINT_TO_POINTER(item->type), l);
281 }
282
283
284 static void
285 label_line(struct graphics *gra, struct graphics_gc *fg, struct graphics_gc *bg, struct graphics_font *font, struct point *p, int count, char *label)
286 {
287         int i,x,y,tl;
288         double dx,dy,l;
289         struct point p_t;
290
291         tl=strlen(label)*400;
292         for (i = 0 ; i < count-1 ; i++) {
293                 dx=p[i+1].x-p[i].x;
294                 dx*=100;
295                 dy=p[i+1].y-p[i].y;
296                 dy*=100;
297                 l=(int)sqrt((float)(dx*dx+dy*dy));
298                 if (l > tl) {
299                         x=p[i].x;
300                         y=p[i].y;
301                         if (dx < 0) {
302                                 dx=-dx;
303                                 dy=-dy;
304                                 x=p[i+1].x;
305                                 y=p[i+1].y;
306                         }
307                         x+=(l-tl)*dx/l/200;
308                         y+=(l-tl)*dy/l/200;
309                         x-=dy*45/l/10;
310                         y+=dx*45/l/10;
311                         p_t.x=x;
312                         p_t.y=y;
313         #if 0
314                         printf("display_text: '%s', %d, %d, %d, %d %d\n", label, x, y, dx*0x10000/l, dy*0x10000/l, l);
315         #endif
316                         gra->meth.draw_text(gra->priv, fg->priv, bg->priv, font->priv, label, &p_t, dx*0x10000/l, dy*0x10000/l);
317                 }
318         }
319 }
320
321
322 static void
323 xdisplay_draw_elements(struct graphics *gra, GHashTable *display_list, struct itemtype *itm)
324 {
325         struct element *e;
326         GList *l,*ls,*es,*types;
327         enum item_type type;
328         struct graphics_gc *gc;
329         struct graphics_image *img;
330         struct point p;
331
332         es=itm->elements;       
333         while (es) {
334                 e=es->data;
335                 types=itm->type;
336                 while (types) {
337                         type=GPOINTER_TO_INT(types->data);
338                         ls=g_hash_table_lookup(display_list, GINT_TO_POINTER(type));
339                         l=ls;
340                         gc=NULL;
341                         img=NULL;
342                         while (l) {
343                                 struct displayitem *di;
344                                 di=l->data;
345                                 di->displayed=1;
346                                 if (! gc) {
347                                         gc=graphics_gc_new(gra);
348                                         gc->meth.gc_set_foreground(gc->priv, &e->color);
349                                 }
350                                 switch (e->type) {
351                                 case element_polygon:
352                                         gra->meth.draw_polygon(gra->priv, gc->priv, di->pnt, di->count);
353                                         break;
354                                 case element_polyline:
355                                         if (e->u.polyline.width > 1) 
356                                                 gc->meth.gc_set_linewidth(gc->priv, e->u.polyline.width);
357                                         gra->meth.draw_lines(gra->priv, gc->priv, di->pnt, di->count);
358                                         break;
359                                 case element_circle:
360                                         if (e->u.circle.width > 1) 
361                                                 gc->meth.gc_set_linewidth(gc->priv, e->u.polyline.width);
362                                         gra->meth.draw_circle(gra->priv, gc->priv, &di->pnt[0], e->u.circle.radius);
363                                         p.x=di->pnt[0].x+3;
364                                         p.y=di->pnt[0].y+10;
365                                         if (! gra->font[e->label_size])
366                                                 gra->font[e->label_size]=graphics_font_new(gra, e->label_size*20);
367                                         gra->meth.draw_text(gra->priv, gra->gc[2]->priv, gra->gc[1]->priv, gra->font[e->label_size]->priv, di->label, &p, 0x10000, 0);
368                                         break;
369                                 case element_label:
370                                         if (di->label) {
371                                                 if (! gra->font[e->label_size])
372                                                         gra->font[e->label_size]=graphics_font_new(gra, e->label_size*20);
373                                                 label_line(gra, gra->gc[2], gra->gc[1], gra->font[e->label_size], di->pnt, di->count, di->label);
374                                         }
375                                         break;
376                                 case element_icon:
377                                         if (!img) {
378                                                 char *icon=g_strdup_printf("xpm/%s", e->u.icon.src);
379                                                 img=graphics_image_new(gra, icon);
380                                                 g_free(icon);
381                                                 if (! img)
382                                                         g_warning("failed to load icon '%s'\n", e->u.icon.src);
383                                         }
384                                         if (img) {
385                                                 p.x=di->pnt[0].x - img->hot.x;
386                                                 p.y=di->pnt[0].y - img->hot.y;
387                                                 gra->meth.draw_image(gra->priv, gra->gc[0]->priv, &p, img->priv);
388                                         }
389                                         break;
390                                 case element_image:
391                                         printf("image: '%s'\n", di->label);
392                                         gra->meth.draw_image_warp(gra->priv, gra->gc[0]->priv, di->pnt, di->count, di->label);
393                                         break;
394                                 default:
395                                         printf("Unhandled element type %d\n", e->type);
396                                 
397                                 }
398                                 l=g_list_next(l);
399                         }
400                         types=g_list_next(types);       
401                 }
402                 es=g_list_next(es);
403         }       
404 }
405
406 static void
407 xdisplay_draw_layer(GHashTable *display_list, struct graphics *gra, struct layer *lay, int order)
408 {
409         GList *itms;
410         struct itemtype *itm;
411
412         itms=lay->itemtypes;
413         while (itms) {
414                 itm=itms->data;
415                 if (order >= itm->order_min && order <= itm->order_max) 
416                         xdisplay_draw_elements(gra, display_list, itm);
417                 itms=g_list_next(itms);
418         }
419 }
420
421 static void
422 xdisplay_draw_layout(GHashTable *display_list, struct graphics *gra, struct layout *l, int order)
423 {
424         GList *lays;
425         struct layer *lay;
426         
427         lays=l->layers;
428         while (lays) {
429                 lay=lays->data;
430                 xdisplay_draw_layer(display_list, gra, lay, order);
431                 lays=g_list_next(lays);
432         }
433 }
434
435 static void
436 xdisplay_draw(GHashTable *display_list, struct graphics *gra, GList *layouts, int order)
437 {
438         struct layout *l;
439
440         while (layouts) {
441                 l=layouts->data;
442                 xdisplay_draw_layout(display_list, gra, l, order);
443                 return;
444                 layouts=g_list_next(layouts);
445         }
446 }
447
448 extern void *route_selection;
449
450 static void
451 do_draw(struct displaylist *displaylist, struct transformation *t, GList *mapsets, int order, struct route *route)
452 {
453         struct map_selection sel;
454         struct map_rect *mr;
455         struct item *item;
456         struct mapset *ms;
457         struct map *m;
458         enum projection pro;
459         struct mapset_handle *h;
460         struct coord c;
461         int conv,count,max=16384;
462         struct point pnt[max];
463         struct attr attr;
464         struct coord_rect r;
465
466         if (! mapsets)
467                 return;
468         sel.next=NULL;
469         sel.order[layer_town]=1*order;
470         sel.order[layer_street]=order;
471         sel.order[layer_poly]=1*order;
472         ms=mapsets->data;
473         h=mapset_open(ms);
474         while ((m=mapset_next(h, 1))) {
475                 pro=map_projection(m);
476                 conv=map_requires_conversion(m);
477                 transform_rect(t, pro, &sel.rect);
478                 if (route_selection)
479                         mr=map_rect_new(m, route_selection);
480                 else
481                         mr=map_rect_new(m, &sel);
482                 while ((item=map_rect_get_item(mr))) {
483                         if (item->type < type_line) {
484                                 item_coord_get(item, &c, 1);
485                                 if (!transform(t, pro, &c, &pnt[0])) {
486                                         dbg(1,"not visible\n");
487                                         continue;
488                                 }
489                                 count=1;
490                         } else {
491                                 count=0;
492                                 while (count < max) {
493                                         if (!item_coord_get(item, &c, 1))
494                                                 break;
495                                         if (! count) {
496                                                 r.lu=c;
497                                                 r.rl=c;
498                                         } else
499                                                 coord_rect_extend(&r, &c);
500                                         transform(t, pro, &c, &pnt[count]);
501                                         if (! count || pnt[count].x != pnt[count-1].x || pnt[count].y != pnt[count-1].y)
502                                                 count++;
503                                                 
504                                 }
505                                 g_assert(count < max);
506                                 if (!transform_contains(t, pro, &r)) {
507                                         dbg(1,"not visible\n");
508                                         continue;
509                                 }
510                                 if (route && route_contains(route, item)) {
511                                         struct item ritem;
512                                         ritem=*item;
513                                         ritem.type=type_street_route;
514                                         display_add(displaylist, &ritem, count, pnt, NULL);
515                                 }
516                         }
517                         if (!item_attr_get(item, attr_label, &attr))
518                                 attr.u.str=NULL;
519                         if (conv && attr.u.str && attr.u.str[0]) {
520                                 char *str=map_convert_string(m, attr.u.str);
521                                 display_add(displaylist, item, count, pnt, str);
522                                 map_convert_free(str);
523                         } else
524                                 display_add(displaylist, item, count, pnt, attr.u.str);
525                 }
526                 map_rect_destroy(mr);
527         }
528         mapset_close(h);
529 }
530
531 int
532 graphics_ready(struct graphics *this_)
533 {
534         return this_->ready;
535 }
536
537 void
538 graphics_displaylist_draw(struct graphics *gra, struct displaylist *displaylist, struct transformation *trans, GList *layouts, struct route *route)
539 {
540         int order=transform_get_order(trans);
541         gra->meth.draw_mode(gra->priv, draw_mode_begin);
542         if (route)
543                 route_draw(route, trans, displaylist);
544         xdisplay_draw(displaylist->dl, gra, layouts, order);
545 }
546
547
548 void
549 graphics_draw(struct graphics *gra, struct displaylist *displaylist, GList *mapsets, struct transformation *trans, GList *layouts, struct route *route)
550 {
551         int order=transform_get_order(trans);
552
553         dbg(1,"enter");
554
555 #if 0
556         printf("scale=%d center=0x%x,0x%x mercator scale=%f\n", scale, co->trans->center.x, co->trans->center.y, transform_scale(co->trans->center.y));
557 #endif
558         
559         xdisplay_free(displaylist->dl);
560         dbg(1,"order=%d\n", order);
561
562
563 #if 0
564         for (i = 0 ; i < data_window_type_end; i++) {
565                 data_window_begin(co->data_window[i]);  
566         }
567 #endif
568         profile(0,NULL);
569         do_draw(displaylist, trans, mapsets, order, route);
570         profile(1,"do_draw");
571         graphics_displaylist_draw(gra, displaylist, trans, layouts, route);
572         profile(1,"xdisplay_draw");
573         profile(0,"end");
574   
575         gra->meth.draw_mode(gra->priv, draw_mode_end);
576 #if 0
577         for (i = 0 ; i < data_window_type_end; i++) {
578                 data_window_end(co->data_window[i]);    
579         }
580 #endif
581         gra->ready=1;
582 }
583
584
585 struct displaylist_handle {
586         GList *hl_head,*hl,*l;
587 };
588
589
590 struct displaylist_handle *
591 graphics_displaylist_open(struct displaylist *displaylist)
592 {
593         struct displaylist_handle *ret;
594
595         ret=g_new0(struct displaylist_handle, 1);
596         ret->hl_head=ret->hl=g_hash_to_list(displaylist->dl);
597
598         return ret;
599 }
600
601 struct displayitem *
602 graphics_displaylist_next(struct displaylist_handle *dlh)
603 {
604         struct displayitem *ret;
605         if (! dlh->l) {
606                 if (!dlh->hl)
607                         return NULL;
608                 dlh->l=dlh->hl->data;
609                 dlh->hl=g_list_next(dlh->hl);
610         }
611         ret=dlh->l->data;
612         dlh->l=g_list_next(dlh->l);
613         return ret;
614 }
615
616 void
617 graphics_displaylist_close(struct displaylist_handle *dlh)
618 {
619         g_list_free(dlh->hl_head);
620         g_free(dlh);
621 }
622
623 struct displaylist *
624 graphics_displaylist_new(void)
625 {
626         struct displaylist *ret=g_new(struct displaylist, 1);
627
628         ret->dl=g_hash_table_new(NULL,NULL);
629
630         return ret;
631 }
632
633 struct item *
634 graphics_displayitem_get_item(struct displayitem *di)
635 {
636         return &di->item;       
637 }
638
639 char *
640 graphics_displayitem_get_label(struct displayitem *di)
641 {
642         return di->label;
643 }
644
645 static int
646 within_dist_point(struct point *p0, struct point *p1, int dist)
647 {
648         if (p0->x == 32767 || p0->y == 32767 || p1->x == 32767 || p1->y == 32767)
649                 return 0;
650         if (p0->x == -32768 || p0->y == -32768 || p1->x == -32768 || p1->y == -32768)
651                 return 0;
652         if ((p0->x-p1->x)*(p0->x-p1->x) + (p0->y-p1->y)*(p0->y-p1->y) <= dist*dist) {
653                 return 1;
654         }
655         return 0;
656 }
657
658 static int
659 within_dist_line(struct point *p, struct point *line_p0, struct point *line_p1, int dist)
660 {
661         int vx,vy,wx,wy;
662         int c1,c2;
663         struct point line_p;
664
665         vx=line_p1->x-line_p0->x;
666         vy=line_p1->y-line_p0->y;
667         wx=p->x-line_p0->x;
668         wy=p->y-line_p0->y;
669
670         c1=vx*wx+vy*wy;
671         if ( c1 <= 0 )
672                 return within_dist_point(p, line_p0, dist);
673         c2=vx*vx+vy*vy;
674         if ( c2 <= c1 )
675                 return within_dist_point(p, line_p1, dist);
676
677         line_p.x=line_p0->x+vx*c1/c2;
678         line_p.y=line_p0->y+vy*c1/c2;
679         return within_dist_point(p, &line_p, dist);
680 }
681
682 static int
683 within_dist_polyline(struct point *p, struct point *line_pnt, int count, int dist, int close)
684 {
685         int i;
686         for (i = 0 ; i < count-1 ; i++) {
687                 if (within_dist_line(p,line_pnt+i,line_pnt+i+1,dist)) {
688                         return 1;
689                 }
690         }
691         if (close)
692                 return (within_dist_line(p,line_pnt,line_pnt+count-1,dist));
693         return 0;
694 }
695
696 static int
697 within_dist_polygon(struct point *p, struct point *poly_pnt, int count, int dist)
698 {
699         int i, j, c = 0;
700         for (i = 0, j = count-1; i < count; j = i++) {
701                 if ((((poly_pnt[i].y <= p->y) && ( p->y < poly_pnt[j].y )) ||
702                 ((poly_pnt[j].y <= p->y) && ( p->y < poly_pnt[i].y))) &&
703                 (p->x < (poly_pnt[j].x - poly_pnt[i].x) * (p->y - poly_pnt[i].y) / (poly_pnt[j].y - poly_pnt[i].y) + poly_pnt[i].x)) 
704                         c = !c;
705         }
706         if (! c)
707                 return within_dist_polyline(p, poly_pnt, count, dist, 1);
708         return c;
709 }
710
711 int
712 graphics_displayitem_within_dist(struct displayitem *di, struct point *p, int dist)
713 {
714         if (di->item.type < type_line) {
715                 return within_dist_point(p, &di->pnt[0], dist);
716         }
717         if (di->item.type < type_area) {
718                 return within_dist_polyline(p, di->pnt, di->count, dist, 0);
719         }
720         return within_dist_polygon(p, di->pnt, di->count, dist);
721 }