2 * Hic Est Leo -- Line and Area Symbolizer
4 * (c) 2014 Martin Mares <mj@ucw.cz>
9 #include <ucw/fastbuf.h>
10 #include <ucw/mempool.h>
18 static void sym_line_attrs(struct sym_line *l, struct svg *svg)
20 svg_set_attr(svg, "fill", "none");
21 svg_set_attr_color(svg, "stroke", l->color);
22 svg_set_attr_dimen(svg, "stroke-width", l->width);
24 svg_set_attr_float(svg, "stroke-opacity", l->opacity);
29 // This is the default (butt)
33 svg_set_attr(svg, "stroke-linecap", osm_val_decode(l->line_cap));
36 osm_obj_warn(l->s.o, "Unknown stroke-linecap: %s", osm_val_decode(l->line_cap));
42 // This is the default
43 if (l->miter_limit != 4)
44 svg_set_attr_float(svg, "stroke-miterlimit", l->miter_limit);
48 svg_set_attr(svg, "stroke-linejoin", osm_val_decode(l->line_join));
51 osm_obj_warn(l->s.o, "Unknown stroke-linejoin: %s", osm_val_decode(l->line_join));
56 struct fastbuf *fb = svg_fb_open(svg);
57 for (uns i=0; i < GARY_SIZE(l->dash_pattern); i++)
61 // FIXME: This is dimension-sensitive
62 // FIXME: Also, inkscape doesn't handle units in dash lengths
63 bprintf(fb, "%.6g", l->dash_pattern[i]);
65 svg_fb_close_as_attr(svg, "stroke-dasharray");
67 svg_set_attr_float(svg, "stroke-dashoffset", l->dash_offset);
71 static void append_node_list(struct svg *svg, clist *list)
73 struct osm_node *first = NULL;
74 OSM_FOR_EACH_BEGIN(struct osm_node *, n, *list)
79 svg_path_move_to(svg, n->x, n->y);
84 svg_path_line_to(svg, n->x, n->y);
89 static void make_path(struct osm_object *o, struct svg *svg)
96 struct osm_way *w = (struct osm_way *) o;
97 append_node_list(svg, &w->nodes);
100 case OSM_TYPE_MULTIPOLYGON:
102 struct osm_multipolygon *m = (struct osm_multipolygon *) o;
103 CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
104 append_node_list(svg, &b->nodes);
113 static void sym_line_draw(struct symbol *sym, struct svg *svg)
115 struct sym_line *l = (struct sym_line *) sym;
116 make_path(sym->o, svg);
117 sym_line_attrs(l, svg);
121 static void sym_line_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
123 if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
126 struct sym_line *main_sl = NULL;
128 // Line and its casing are two similar objects
129 for (uns casing=0; casing<2; casing++)
132 if (!style_get_number(si, PROP_WIDTH + casing, &w))
135 struct sym_line *sl = sym_line_new(o);
142 osm_obj_warn(o, "Casing around no line");
145 sl->width += main_sl->width;
150 sl->color = 0x808080;
151 style_get_color(si, PROP_FILL_COLOR, &sl->color);
152 style_get_color(si, PROP_COLOR + casing, &sl->color);
155 style_get_number(si, PROP_OPACITY + casing, &sl->opacity);
157 sl->line_cap = style_get_ident(si, PROP_LINECAP + casing) ? : VALUE_NONE;
158 sl->line_join = style_get_ident(si, PROP_LINEJOIN + casing) ? : VALUE_ROUND;
159 sl->miter_limit = 10;
160 style_get_number(si, PROP_MITERLIMIT + casing, &sl->miter_limit);
162 struct style_prop *dashes = style_get(si, PROP_DASHES + casing);
163 if (!dashes || dashes->type == PROP_TYPE_IDENT && dashes->val.id == VALUE_NONE)
165 else if (dashes->type == PROP_TYPE_LIST)
167 GARY_INIT_ALLOC(sl->dash_pattern, 0, mp_get_allocator(sym_mp));
168 CLIST_FOR_EACH(struct style_val_list_entry *, e, *dashes->val.list)
170 if (e->val.type == PROP_TYPE_NUMBER)
171 *GARY_PUSH(sl->dash_pattern) = e->val.val.number;
174 osm_obj_warn(o, "Invalid dash pattern");
178 style_get_number(si, PROP_DASHES_OFFSET + casing, &sl->dash_offset);
181 osm_obj_warn(o, "Invalid dash pattern");
183 sym_plan(&sl->s, sym_zindex(o, si, casing ? 2 : 3));
187 static bool sym_line_same_p(struct symbol *xx, struct symbol *yy)
189 struct sym_line *x = (struct sym_line *) xx;
190 struct sym_line *y = (struct sym_line *) yy;
192 if (!(x->width == y->width &&
193 x->color == y->color &&
194 x->opacity == y->opacity &&
195 x->line_cap == y->line_cap &&
196 x->line_join == y->line_join &&
197 x->miter_limit == y->miter_limit &&
198 x->dash_offset == y->dash_offset))
201 if (x->dash_pattern || y->dash_pattern)
203 if (!x->dash_pattern || !y->dash_pattern)
206 if (GARY_SIZE(x->dash_pattern) != GARY_SIZE(y->dash_pattern))
209 uint dashes = GARY_SIZE(x->dash_pattern);
210 for (uint i=0; i < dashes; i++)
211 if (x->dash_pattern[i] != y->dash_pattern[i])
218 static struct symbol *sym_line_clone(struct symbol *src)
220 struct sym_line *s = (struct sym_line *) src;
221 struct sym_line *d = sym_line_new(s->s.o);
222 memcpy((byte *)d + sizeof(struct symbol), (byte *)s + sizeof(struct symbol), sizeof(struct sym_line) - sizeof(struct symbol));
226 struct symbolizer symbolizer_line = {
228 .draw = sym_line_draw,
230 .same_p = sym_line_same_p,
231 .clone = sym_line_clone,
234 struct sym_line *sym_line_new(struct osm_object *o)
236 return sym_new(SYMBOLIZER_LINE, o, sizeof(struct sym_line));
239 /*** Images along line ***/
241 static void lineimg_node_list(struct sym_lineimg *sli, clist *nodes, struct svg *svg)
243 double phase = sli->phase;
244 double step = sli->sir.width + sli->spacing;
245 struct osm_node *prev = NULL;
246 OSM_FOR_EACH_BEGIN(struct osm_node *, n, *nodes)
250 double dx = n->x - prev->x;
251 double dy = n->y - prev->y;
252 double len = hypot(dx, dy);
254 while (len - dist > 1e-10)
256 // FIXME: Rotate images along the path?
257 double next = MIN(len - dist, step - phase);
260 if (step - phase < 1e-10)
262 struct svg_icon_request sir = sli->sir;
263 sir.x = prev->x + dx * dist / len;
264 sir.y = prev->y + dy * dist / len;
265 svg_icon_put(svg, &sir);
275 static void sym_lineimg_draw(struct symbol *sym, struct svg *svg)
277 struct sym_lineimg *li = (struct sym_lineimg *) sym;
278 struct osm_object *o = li->s.o;
284 struct osm_way *w = (struct osm_way *) o;
285 lineimg_node_list(li, &w->nodes, svg);
288 case OSM_TYPE_MULTIPOLYGON:
290 struct osm_multipolygon *m = (struct osm_multipolygon *) o;
291 CLIST_FOR_EACH(struct osm_mpg_boundary *, b, m->boundaries)
292 lineimg_node_list(li, &b->nodes, svg);
300 static void sym_lineimg_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
302 if (o->type != OSM_TYPE_WAY && o->type != OSM_TYPE_MULTIPOLYGON)
305 osm_val_t icon_name = style_get_string(si, PROP_REPEAT_IMAGE);
309 struct sym_lineimg *sli = sym_lineimg_new(o);
311 struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(icon_name));
312 sli->sir.icon = icon;
313 sli->sir.width = icon->width;
314 sli->sir.height = icon->height;
315 style_scale(si, &sli->sir.width, &sli->sir.height, PROP_REPEAT_IMAGE_WIDTH, PROP_REPEAT_IMAGE_HEIGHT);
317 // FIXME: Better handling of defaults in style_get_number()
319 // FIXME: align and offset are not supported yet
320 sli->align = style_get_ident(si, PROP_REPEAT_IMAGE_ALIGN);
322 style_get_number(si, PROP_REPEAT_IMAGE_OFFSET, &sli->offset);
325 style_get_number(si, PROP_REPEAT_IMAGE_SPACING, &sli->spacing);
327 style_get_number(si, PROP_REPEAT_IMAGE_PHASE, &sli->phase);
329 sym_plan(&sli->s, sym_zindex(o, si, 3));
332 struct symbolizer symbolizer_lineimg = {
334 .draw = sym_lineimg_draw,
335 .gen = sym_lineimg_gen,
338 struct sym_lineimg *sym_lineimg_new(struct osm_object *o)
340 return sym_new(SYMBOLIZER_LINEIMG, o, sizeof(struct sym_lineimg));
345 static void sym_area_draw(struct symbol *sym, struct svg *svg)
347 struct sym_area *a = (struct sym_area *) sym;
349 for (int i=0; i<2; i++)
353 if (a->fill_color == COLOR_NONE)
355 make_path(sym->o, svg);
356 svg_set_attr_color(svg, "fill", a->fill_color);
360 if (!a->fill_pattern)
362 make_path(sym->o, svg);
363 svg_set_attr(svg, "fill", a->fill_pattern->paint_server);
365 if (a->fill_opacity != 1)
366 svg_set_attr_float(svg, "opacity", a->fill_opacity);
367 if (sym->o->type == OSM_TYPE_MULTIPOLYGON)
368 svg_set_attr(svg, "fill-rule", "evenodd");
373 static void sym_area_gen(struct osm_object *o, struct style_info *si, struct svg *svg UNUSED)
375 if (!(o->type == OSM_TYPE_WAY && osm_way_cyclic_p((struct osm_way *) o) ||
376 o->type == OSM_TYPE_MULTIPOLYGON))
380 if (!style_get_color(si, PROP_FILL_COLOR, &color))
383 osm_val_t pattern = style_get_string(si, PROP_FILL_PATTERN);
384 struct svg_pattern *patt = NULL;
387 struct svg_icon *icon = svg_icon_load(svg, osm_val_decode(pattern));
388 struct svg_pattern_request spr = {
390 .width = icon->width,
391 .height = icon->height
393 style_scale(si, &spr.width, &spr.height, PROP_FILL_PATTERN_WIDTH, PROP_FILL_PATTERN_HEIGHT);
394 patt = svg_icon_to_pattern(svg, &spr);
397 if (color == COLOR_NONE && !patt)
400 struct sym_area *sa = sym_area_new(o);
401 sa->fill_color = color;
402 sa->fill_pattern = patt;
404 sa->fill_opacity = 1;
405 style_get_number(si, PROP_FILL_OPACITY, &sa->fill_opacity);
407 sym_plan(&sa->s, sym_zindex(o, si, 1));
410 struct symbolizer symbolizer_area = {
412 .draw = sym_area_draw,
416 struct sym_area *sym_area_new(struct osm_object *o)
418 return sym_new(SYMBOLIZER_AREA, o, sizeof(struct sym_area));