]> mj.ucw.cz Git - leo.git/blob - sym-line.c
Labelling: Small fixes in rand*
[leo.git] / sym-line.c
1 /*
2  *      Hic Est Leo -- Line and Area Symbolizer
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/fastbuf.h>
9 #include <ucw/mempool.h>
10
11 #include <math.h>
12 #include <stdio.h>
13
14 #include "leo.h"
15 #include "osm.h"
16 #include "sym.h"
17 #include "labeller.h"
18
19 static void sym_line_attrs(struct sym_line *l, struct svg *svg)
20 {
21   svg_set_attr(svg, "fill", "none");
22   svg_set_attr_color(svg, "stroke", l->color);
23   svg_set_attr_dimen(svg, "stroke-width", l->width);
24   if (l->opacity != 1)
25     svg_set_attr_float(svg, "stroke-opacity", l->opacity);
26
27   switch (l->line_cap)
28     {
29     case VALUE_NONE:
30       // This is the default (butt)
31       break;
32     case VALUE_ROUND:
33     case VALUE_SQUARE:
34       svg_set_attr(svg, "stroke-linecap", osm_val_decode(l->line_cap));
35       break;
36     default:
37       osm_obj_warn(l->s.o, "Unknown stroke-linecap: %s", osm_val_decode(l->line_cap));
38     }
39
40   switch (l->line_join)
41     {
42     case VALUE_MITER:
43       // This is the default
44       if (l->miter_limit != 4)
45         svg_set_attr_float(svg, "stroke-miterlimit", l->miter_limit);
46       break;
47     case VALUE_ROUND:
48     case VALUE_BEVEL:
49       svg_set_attr(svg, "stroke-linejoin", osm_val_decode(l->line_join));
50       break;
51     default:
52       osm_obj_warn(l->s.o, "Unknown stroke-linejoin: %s", osm_val_decode(l->line_join));
53     }
54
55   if (l->dash_pattern)
56     {
57       struct fastbuf *fb = svg_fb_open(svg);
58       for (uns i=0; i < GARY_SIZE(l->dash_pattern); i++)
59         {
60           if (i)
61             bputc(fb, ',');
62           // FIXME: This is dimension-sensitive
63           // FIXME: Also, inkscape doesn't handle units in dash lengths
64           bprintf(fb, "%.6g", l->dash_pattern[i]);
65         }
66       svg_fb_close_as_attr(svg, "stroke-dasharray");
67       if (l->dash_offset)
68         svg_set_attr_float(svg, "stroke-dashoffset", l->dash_offset);
69     }
70 }
71
72 static void append_node_list(struct svg *svg, clist *list)
73 {
74   struct osm_node *first = NULL;
75   OSM_FOR_EACH_BEGIN(struct osm_node *, n, *list)
76     {
77       if (!first)
78         {
79           first = n;
80           svg_path_move_to(svg, n->x, n->y);
81         }
82       else if (n == first)
83         svg_path_close(svg);
84       else
85         svg_path_line_to(svg, n->x, n->y);
86     }
87   OSM_FOR_EACH_END;
88 }
89
90 static void make_path(struct osm_object *o, struct svg *svg)
91 {
92   svg_push_path(svg);
93   switch (o->type)
94     {
95     case OSM_TYPE_WAY:
96       {
97         struct osm_way *w = (struct osm_way *) o;
98         append_node_list(svg, &w->nodes);
99         break;
100       }
101     case OSM_TYPE_MULTIPOLYGON:
102       {
103         struct osm_multipolygon *m = (struct osm_multipolygon *) o;
104         CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
105           append_node_list(svg, &b->nodes);
106         break;
107       }
108     default:
109       ASSERT(0);
110     }
111   svg_path_end(svg);
112 }
113
114 static void sym_line_draw(struct symbol *sym, struct svg *svg)
115 {
116   struct sym_line *l = (struct sym_line *) sym;
117   make_path(sym->o, svg);
118   sym_line_attrs(l, svg);
119   svg_pop(svg);
120 }
121
122 static void sym_line_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
123 {
124   if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
125     return;
126
127   struct sym_line *main_sl = NULL;
128
129   // Line and its casing are two similar objects
130   for (uns casing=0; casing<2; casing++)
131     {
132       double w;
133       if (!style_get_number(si, PROP_WIDTH + casing, &w))
134         continue;
135
136       struct sym_line *sl = sym_line_new(o);
137       sl->width = w;
138
139       if (casing)
140         {
141           if (!main_sl)
142             {
143               osm_obj_warn(o, "Casing around no line");
144               continue;
145             }
146           sl->width += main_sl->width;
147         }
148       else
149         main_sl = sl;
150
151       sl->color = 0x808080;
152       style_get_color(si, PROP_FILL_COLOR, &sl->color);
153       style_get_color(si, PROP_COLOR + casing, &sl->color);
154
155       sl->opacity = 1;
156       style_get_number(si, PROP_OPACITY + casing, &sl->opacity);
157
158       sl->line_cap = style_get_ident(si, PROP_LINECAP + casing) ? : VALUE_NONE;
159       sl->line_join = style_get_ident(si, PROP_LINEJOIN + casing) ? : VALUE_ROUND;
160       sl->miter_limit = 10;
161       style_get_number(si, PROP_MITERLIMIT + casing, &sl->miter_limit);
162
163       struct style_prop *dashes = style_get(si, PROP_DASHES + casing);
164       if (!dashes || dashes->type == PROP_TYPE_IDENT && dashes->val.id == VALUE_NONE)
165         ;
166       else if (dashes->type == PROP_TYPE_LIST)
167         {
168           GARY_INIT_ALLOC(sl->dash_pattern, 0, mp_get_allocator(sym_mp));
169           CLIST_FOR_EACH(struct style_val_list_entry *, e, *dashes->val.list)
170             {
171               if (e->val.type == PROP_TYPE_NUMBER)
172                 *GARY_PUSH(sl->dash_pattern) = e->val.val.number;
173               else
174                 {
175                   osm_obj_warn(o, "Invalid dash pattern");
176                   break;
177                 }
178             }
179           style_get_number(si, PROP_DASHES_OFFSET + casing, &sl->dash_offset);
180         }
181       else
182         osm_obj_warn(o, "Invalid dash pattern");
183
184       if (o->type == OSM_TYPE_WAY)
185       {
186       if (casing)
187         sym_plan(&sl->s, sym_zindex(o, si, 2));
188       else
189       labeller_add_line(&sl->s, sym_zindex(o, si, casing ? 2 : 3));
190       }
191       //sym_plan(&sl->s, sym_zindex(o, si, casing ? 2 : 3));
192     }
193 }
194
195 struct symbolizer symbolizer_line = {
196   .name = "line",
197   .draw = sym_line_draw,
198   .gen = sym_line_gen,
199 };
200
201 struct sym_line *sym_line_new(struct osm_object *o)
202 {
203   return sym_new(SYMBOLIZER_LINE, o, sizeof(struct sym_line));
204 }
205
206 /*** Images along line ***/
207
208 static void lineimg_node_list(struct sym_lineimg *sli, clist *nodes, struct svg *svg)
209 {
210   double phase = sli->phase;
211   double step = sli->sir.width + sli->spacing;
212   struct osm_node *prev = NULL;
213   OSM_FOR_EACH_BEGIN(struct osm_node *, n, *nodes)
214     {
215       if (prev)
216         {
217           double dx = n->x - prev->x;
218           double dy = n->y - prev->y;
219           double len = hypot(dx, dy);
220           double dist = 0;
221           while (len - dist > 1e-10)
222             {
223               // FIXME: Rotate images along the path?
224               double next = MIN(len - dist, step - phase);
225               dist += next;
226               phase += next;
227               if (step - phase < 1e-10)
228                 {
229                   struct svg_icon_request sir = sli->sir;
230                   sir.x = prev->x + dx * dist / len;
231                   sir.y = prev->y + dy * dist / len;
232                   svg_icon_put(svg, &sir);
233                   phase -= step;
234                 }
235             }
236         }
237       prev = n;
238     }
239   OSM_FOR_EACH_END;
240 }
241
242 static void sym_lineimg_draw(struct symbol *sym, struct svg *svg)
243 {
244   struct sym_lineimg *li = (struct sym_lineimg *) sym;
245   struct osm_object *o = li->s.o;
246
247   switch (o->type)
248     {
249     case OSM_TYPE_WAY:
250       {
251         struct osm_way *w = (struct osm_way *) o;
252         lineimg_node_list(li, &w->nodes, svg);
253         break;
254       }
255     case OSM_TYPE_MULTIPOLYGON:
256       {
257         struct osm_multipolygon *m = (struct osm_multipolygon *) o;
258         CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
259           lineimg_node_list(li, &b->nodes, svg);
260         break;
261       }
262     default:
263       ASSERT(0);
264     }
265 }
266
267 static void sym_lineimg_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
268 {
269   if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
270     return;
271
272   osm_val_t icon_name = style_get_string(si, PROP_REPEAT_IMAGE);
273   if (!icon_name)
274     return;
275
276   struct sym_lineimg *sli = sym_lineimg_new(o);
277
278   struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(icon_name));
279   sli->sir.icon = icon;
280   sli->sir.width = icon->width;
281   sli->sir.height = icon->height;
282   style_scale(si, &sli->sir.width, &sli->sir.height, PROP_REPEAT_IMAGE_WIDTH, PROP_REPEAT_IMAGE_HEIGHT);
283
284   // FIXME: Better handling of defaults in style_get_number()
285 #if 0
286   // FIXME: align and offset are not supported yet
287   sli->align = style_get_ident(si, PROP_REPEAT_IMAGE_ALIGN);
288   sli->offset = 0;
289   style_get_number(si, PROP_REPEAT_IMAGE_OFFSET, &sli->offset);
290 #endif
291   sli->spacing = 0;
292   style_get_number(si, PROP_REPEAT_IMAGE_SPACING, &sli->spacing);
293   sli->phase = 0;
294   style_get_number(si, PROP_REPEAT_IMAGE_PHASE, &sli->phase);
295
296   sym_plan(&sli->s, sym_zindex(o, si, 3));
297 }
298
299 struct symbolizer symbolizer_lineimg = {
300   .name = "lineimg",
301   .draw = sym_lineimg_draw,
302   .gen = sym_lineimg_gen,
303 };
304
305 struct sym_lineimg *sym_lineimg_new(struct osm_object *o)
306 {
307   return sym_new(SYMBOLIZER_LINEIMG, o, sizeof(struct sym_lineimg));
308 }
309
310 /*** Areas ***/
311
312 static void sym_area_draw(struct symbol *sym, struct svg *svg)
313 {
314   struct sym_area *a = (struct sym_area *) sym;
315
316   for (int i=0; i<2; i++)
317     {
318       if (!i)
319         {
320           if (a->fill_color == COLOR_NONE)
321             continue;
322           make_path(sym->o, svg);
323           svg_set_attr_color(svg, "fill", a->fill_color);
324         }
325       else
326         {
327           if (!a->fill_pattern)
328             continue;
329           make_path(sym->o, svg);
330           svg_set_attr(svg, "fill", a->fill_pattern->paint_server);
331         }
332       if (a->fill_opacity != 1)
333         svg_set_attr_float(svg, "opacity", a->fill_opacity);
334       if (sym->o->type == OSM_TYPE_MULTIPOLYGON)
335         svg_set_attr(svg, "fill-rule", "evenodd");
336       svg_pop(svg);
337     }
338 }
339
340 static void sym_area_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
341 {
342   if (!(o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o) ||
343         o->type == OSM_TYPE_MULTIPOLYGON))
344     return;
345
346   color_t color;
347   if (!style_get_color(si, PROP_FILL_COLOR, &color))
348     color = COLOR_NONE;
349
350   osm_val_t pattern = style_get_string(si, PROP_FILL_PATTERN);
351   struct svg_pattern *patt = NULL;
352   if (pattern)
353     {
354       struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(pattern));
355       struct svg_pattern_request spr = {
356         .icon = icon,
357         .width = icon->width,
358         .height = icon->height
359       };
360       style_scale(si, &spr.width, &spr.height, PROP_FILL_PATTERN_WIDTH, PROP_FILL_PATTERN_HEIGHT);
361       patt = svg_icon_to_pattern(svg, &spr);
362     }
363
364   if (color == COLOR_NONE && !patt)
365     return;
366
367   struct sym_area *sa = sym_area_new(o);
368   sa->fill_color = color;
369   sa->fill_pattern = patt;
370
371   sa->fill_opacity = 1;
372   style_get_number(si, PROP_FILL_OPACITY, &sa->fill_opacity);
373
374   sym_plan(&sa->s, sym_zindex(o, si, 1));
375 }
376
377 struct symbolizer symbolizer_area = {
378   .name = "area",
379   .draw = sym_area_draw,
380   .gen = sym_area_gen,
381 };
382
383 struct sym_area *sym_area_new(struct osm_object *o)
384 {
385   return sym_new(SYMBOLIZER_AREA, o, sizeof(struct sym_area));
386 }