From a0ca7ab5421d001f50fabbc6c139ded380f597d8 Mon Sep 17 00:00:00 2001 From: David Vazgenovich Shakaryan Date: Fri, 27 May 2022 16:12:46 -0700 Subject: web: refactor svg drawing and related code --- board.h | 14 ++-- web/static/sw.js | 2 +- web/web_dom.c | 30 +++++-- web/web_dom.h | 1 + web/web_svg.c | 241 +++++++++++++++++++++++++++++++------------------------ web/web_svg.h | 1 + 6 files changed, 173 insertions(+), 116 deletions(-) diff --git a/board.h b/board.h index cc60e62..6e06993 100644 --- a/board.h +++ b/board.h @@ -25,17 +25,17 @@ // distance from centre to apex of outer wire // must be ordered outwards from centre #define FOREACH_RING(M) \ - M(BULL, INNER_DIAMETER_BULL/2 + WIRE_WIDTH/2) \ - M(25, INNER_DIAMETER_25/2 + WIRE_WIDTH/2) \ + M(BULL, (INNER_DIAMETER_BULL / 2) + (WIRE_WIDTH / 2)) \ + M(25, (INNER_DIAMETER_25 / 2) + (WIRE_WIDTH / 2)) \ M(SMALL, TREBLE_OUTER_EDGE - WIRE_WIDTH - TREBLE_INSIDE_WIDTH - \ - WIRE_WIDTH/2) \ - M(TREBLE, TREBLE_OUTER_EDGE - WIRE_WIDTH/2) \ + (WIRE_WIDTH / 2)) \ + M(TREBLE, TREBLE_OUTER_EDGE - (WIRE_WIDTH / 2)) \ M(BIG, DOUBLE_OUTER_EDGE - WIRE_WIDTH - DOUBLE_INSIDE_WIDTH - \ - WIRE_WIDTH/2) \ - M(DOUBLE, DOUBLE_OUTER_EDGE - WIRE_WIDTH/2) + (WIRE_WIDTH / 2)) \ + M(DOUBLE, DOUBLE_OUTER_EDGE - (WIRE_WIDTH / 2)) #define GEN_RING_ENUM(X, D) R_ ## X, #define GEN_RING_STR(X, D) #X, -#define GEN_RING_OUTER_DIST(X, D) D, +#define GEN_RING_OUTER_DIST(X, D) (D), extern int SECTORS[]; extern double OUTER_DISTS[]; diff --git a/web/static/sw.js b/web/static/sw.js index 263c288..acb2c20 100644 --- a/web/static/sw.js +++ b/web/static/sw.js @@ -1,5 +1,5 @@ const CACHE_PREFIX = 'dartboat-' -const CACHE_VERSION = '26'; +const CACHE_VERSION = '27'; const CACHE_NAME = `${CACHE_PREFIX}${CACHE_VERSION}`; const CACHE_FILES = [ diff --git a/web/web_dom.c b/web/web_dom.c index 54a6c8d..2467fb6 100644 --- a/web/web_dom.c +++ b/web/web_dom.c @@ -1,5 +1,6 @@ #include "web_dom.h" +#include #include #include #include @@ -56,7 +57,9 @@ void dom_elem_free(struct dom_elem *e) free(e); } -void dom_elem_add_attr(struct dom_elem *e, char *name, char *val) +// this function takes ownership of the passed val string, freeing it when the +// dom_elem is freed. +static void dom_elem_add_attrp(struct dom_elem *e, char *name, char *val) { if (e->n_attrs == e->size_attrs) { e->size_attrs += e->size_attrs ? e->size_attrs : 1; @@ -67,14 +70,31 @@ void dom_elem_add_attr(struct dom_elem *e, char *name, char *val) } e->attr_names[e->n_attrs] = strdup(name); - e->attr_vals[e->n_attrs++] = strdup(val); + e->attr_vals[e->n_attrs++] = val; +} + +void dom_elem_add_attr(struct dom_elem *e, char *name, char *val) +{ + dom_elem_add_attrp(e, name, strdup(val)); } void dom_elem_add_attrd(struct dom_elem *e, char *name, double val) { - char str[512]; - sprintf(str, "%f", val); - dom_elem_add_attr(e, name, str); + char *str; + asprintf(&str, "%f", val); + dom_elem_add_attrp(e, name, str); +} + +void dom_elem_add_attrf(struct dom_elem *e, char *name, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + char *str; + vasprintf(&str, fmt, args); + dom_elem_add_attrp(e, name, str); + + va_end(args); } void dom_elem_add_child(struct dom_elem *e, struct dom_elem *child) diff --git a/web/web_dom.h b/web/web_dom.h index 45afd15..7806916 100644 --- a/web/web_dom.h +++ b/web/web_dom.h @@ -16,6 +16,7 @@ struct dom_elem *dom_text_init(char *content); void dom_elem_free(struct dom_elem *e); void dom_elem_add_attr(struct dom_elem *e, char *name, char *val); void dom_elem_add_attrd(struct dom_elem *e, char *name, double val); +void dom_elem_add_attrf(struct dom_elem *e, char *name, char *fmt, ...); void dom_elem_add_child(struct dom_elem *e, struct dom_elem *child); void dom_append_elemv(char *sel, int elemc, struct dom_elem **elemv); diff --git a/web/web_svg.c b/web/web_svg.c index 1d0b9aa..125f8a4 100644 --- a/web/web_svg.c +++ b/web/web_svg.c @@ -3,80 +3,104 @@ #include "web_opts.h" #include "board.h" -#include "comp.h" #include #include #include -#include - #define C_BLACK "#272b2c" #define C_WHITE "#fbe3ba" #define C_RED "#f6302f" #define C_GREEN "#22912d" #define C_WIRE "#909ca0" #define C_WIRE_INNER "#d8e6ec" +#define C_NUMBER_RING "#ddd" +#define C_NUMBER "#fff" +#define C_POINT "#33f" +#define C_POINT_OUTLINE "#ff0" + +#define POINT_RADIUS 6 -static inline struct dom_elem *svg_elem_init(char *name, int size_attrs) +static struct dom_elem *svg_elem_init(char *name, int size_attrs) { return dom_elem_init("http://www.w3.org/2000/svg", name, size_attrs); } +static struct dom_elem *gen_circle(double r, char *col) +{ + struct dom_elem *e = svg_elem_init("circle", 2); + dom_elem_add_attrd(e, "r", r); + dom_elem_add_attr(e, "fill", col); + + return e; +} + // draws a ring if angles are equal static struct dom_elem *gen_arc(double a1, double a2, double r1, double r2, char *col) { - struct ccoords cc1 = pol_to_cart( - (struct pcoords){ .a = (a1 == a2 ? 1 : a2), .r = r1 }); - struct ccoords cc2 = pol_to_cart( - (struct pcoords){ .a = (a1 == a2 ? 0 : a1), .r = r1 }); - struct ccoords cc3 = pol_to_cart( - (struct pcoords){ .a = (a1 == a2 ? 0 : a1), .r = r2 }); - struct ccoords cc4 = pol_to_cart( - (struct pcoords){ .a = (a1 == a2 ? 1 : a2), .r = r2 }); + bool ring = (a1 == a2); + if (ring) { + // these values are not significant. the ring is drawn as two + // consecutive arcs, so the path goes from one angle to the + // other and back to the first. + a1 = 0; + a2 = 1; + } + + struct ccoords cc1 = pol_to_cart((struct pcoords){ .a = a2, .r = r1 }); + struct ccoords cc2 = pol_to_cart((struct pcoords){ .a = a1, .r = r1 }); + struct ccoords cc3 = pol_to_cart((struct pcoords){ .a = a1, .r = r2 }); + struct ccoords cc4 = pol_to_cart((struct pcoords){ .a = a2, .r = r2 }); struct dom_elem *e = svg_elem_init("path", 2); dom_elem_add_attr(e, "fill", col); + // (arc) + // M - move to inner start + // A - draw arc to inner end + // L - draw line to outer end + // A - draw arc to outer start + // Z - draw line to inner start + // + // (ring) + // M - move to inner 1 + // A - draw arc to inner 2 + // A - draw arc to inner 1 + // Z - draw zero-length line to close path + // M - move to outer 2 + // A - draw arc to outer 1 + // A - draw arc to outer 2 + // Z - draw zero-length line to close path + // + // for default `nonzero' fill-rule to work for rings, the inner and + // outer arcs must go in opposite directions char buf[512]; - int buflen = 0; - buflen += sprintf(buf + buflen, "M%f %f", cc1.x, cc1.y); - buflen += sprintf(buf + buflen, "A%f %f 0 0 0 %f %f", - r1, r1, cc2.x, cc2.y); - if (a1 == a2) - buflen += sprintf(buf + buflen, "A%f %f 0 1 0 %f %f", + int len = 0; + len += sprintf(buf + len, "M%f %fA%f %f 0 0 0 %f %fL", + cc1.x, cc1.y, r1, r1, cc2.x, cc2.y); + if (ring) + len += -1 + sprintf(buf + len - 1, "A%f %f 0 1 0 %f %fZM", r1, r1, cc1.x, cc1.y); - buflen += sprintf(buf + buflen, "L%f %f", cc3.x, cc3.y); - buflen += sprintf(buf + buflen, "A%f %f 0 0 1 %f %f", - r2, r2, cc4.x, cc4.y); - if (a1 == a2) - buflen += sprintf(buf + buflen, "A%f %f 0 1 1 %f %f", + len += sprintf(buf + len, "%f %fA%f %f 0 0 1 %f %fZ", + cc3.x, cc3.y, r2, r2, cc4.x, cc4.y); + if (ring) + sprintf(buf + len - 1, "A%f %f 0 1 1 %f %fZ", r2, r2, cc3.x, cc3.y); - buf[buflen++] = 'Z'; - buf[buflen++] = 0; dom_elem_add_attr(e, "d", buf); return e; } -static struct dom_elem *gen_segment(double a, double r1, double r2, char *col) -{ - return gen_arc(a - SECTOR_WIDTH/2, a + SECTOR_WIDTH/2, r1, r2, col); -} - static struct dom_elem *gen_ring(double r, double w, char *col) { - return gen_arc(0, 0, r - w/2, r + w/2, col); + return gen_arc(0, 0, r - (w / 2), r + (w / 2), col); } -static struct dom_elem *gen_circle(double r, char *col) +static struct dom_elem *gen_segment(double a, double r1, double r2, char *col) { - struct dom_elem *e = svg_elem_init("circle", 2); - dom_elem_add_attr(e, "fill", col); - dom_elem_add_attrd(e, "r", r); - - return e; + return gen_arc(a - (SECTOR_WIDTH / 2), a + (SECTOR_WIDTH / 2), r1, r2, + col); } static struct dom_elem *gen_line(double a, double r1, double r2, double w, @@ -86,67 +110,95 @@ static struct dom_elem *gen_line(double a, double r1, double r2, double w, struct ccoords cc2 = pol_to_cart((struct pcoords){ .a = a, .r = r2 }); struct dom_elem *e = svg_elem_init("line", 6); - dom_elem_add_attr(e, "stroke", col); dom_elem_add_attrd(e, "x1", cc1.x); dom_elem_add_attrd(e, "y1", cc1.y); dom_elem_add_attrd(e, "x2", cc2.x); dom_elem_add_attrd(e, "y2", cc2.y); + dom_elem_add_attr(e, "stroke", col); dom_elem_add_attrd(e, "stroke-width", w); return e; } -static int draw_spider(int elemc, struct dom_elem **elemv) +static int gen_segments(struct dom_elem **elemv) { - for (int i = 5; i > 1; --i) { + int elemc = 0; + + for (int i = R_DOUBLE; i > R_25; --i) { + for (int j = 0; j < NUM_SECTORS; ++j) { + double a = 90 - (j * SECTOR_WIDTH); + if (a < 0) + a += 360; + elemv[elemc++] = gen_segment(a, OUTER_DISTS[i - 1], + OUTER_DISTS[i], + i % 2 ? (j % 2 ? C_GREEN : C_RED) : + (j % 2 ? C_WHITE : C_BLACK)); + } + } + + elemv[elemc++] = gen_circle(OUTER_DISTS[R_25], C_GREEN); + elemv[elemc++] = gen_circle(OUTER_DISTS[R_BULL], C_RED); + + return elemc; +} + +static int gen_wires(struct dom_elem **elemv) +{ + int elemc = 0; + + for (int i = R_DOUBLE; i > R_25; --i) { elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH, C_WIRE); - elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH/2, + elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH / 2, C_WIRE_INNER); } for (int i = 0; i < NUM_SECTORS; ++i) { - double a = 90 - i*SECTOR_WIDTH - SECTOR_WIDTH/2; - if (a < 0) a += 360; - - elemv[elemc++] = gen_line(a, OUTER_DISTS[1], - OUTER_DISTS[5] + 10, WIRE_WIDTH, C_WIRE); - elemv[elemc++] = gen_line(a, OUTER_DISTS[1], - OUTER_DISTS[5] + 10, WIRE_WIDTH/2, C_WIRE_INNER); + double a = 90 - (i * SECTOR_WIDTH) - (SECTOR_WIDTH / 2); + if (a < 0) + a += 360; + elemv[elemc++] = gen_line(a, OUTER_DISTS[R_25], + OUTER_DISTS[R_DOUBLE] + 10, WIRE_WIDTH, C_WIRE); + elemv[elemc++] = gen_line(a, OUTER_DISTS[R_25], + OUTER_DISTS[R_DOUBLE] + 10, WIRE_WIDTH / 2, + C_WIRE_INNER); } - for (int i = 1; i >= 0; --i) { + for (int i = R_25; i >= R_BULL; --i) { elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH, C_WIRE); - elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH/2, + elemv[elemc++] = gen_ring(OUTER_DISTS[i], WIRE_WIDTH / 2, C_WIRE_INNER); } return elemc; } -static int draw_numbers(int elemc, struct dom_elem **elemv) +static int gen_numbers(struct dom_elem **elemv) { - elemv[elemc++] = gen_ring(DIAMETER/2 - WIRE_WIDTH*4, WIRE_WIDTH, - "#ddd"); + int elemc = 0; + + elemv[elemc++] = gen_ring((DIAMETER / 2) - (WIRE_WIDTH * 4), + WIRE_WIDTH, C_NUMBER_RING); + + int r = (DIAMETER / 2) - (33 / 2); + for (int i = 0; i < NUM_SECTORS; ++i) { + struct dom_elem *e = svg_elem_init("text", 5); + elemv[elemc++] = e; - int r = DIAMETER/2 - 33/2; - for (int i = 0; i < 20; ++i) { - struct dom_elem *e = elemv[elemc++] = svg_elem_init("text", 5); dom_elem_add_attr(e, "font-size", "33"); - dom_elem_add_attr(e, "fill", "#fff"); dom_elem_add_attr(e, "text-anchor", "middle"); dom_elem_add_attr(e, "dominant-baseline", "central"); + dom_elem_add_attr(e, "fill", C_NUMBER); - char buf[512]; - double a = 90 - i*SECTOR_WIDTH; - if (a < 0) a += 360; + double a = 90 - (i * SECTOR_WIDTH); + if (a < 0) + a += 360; struct ccoords cc = pol_to_cart( (struct pcoords){ .a = a, .r = r }); - sprintf(buf, "scale(1 -1) translate(%f %f) rotate(%f)", - cc.x, -cc.y, a <= 180 ? 90 -a : 270 -a); - dom_elem_add_attr(e, "transform", buf); + dom_elem_add_attrf(e, "transform", + "scale(1 -1) translate(%f %f) rotate(%f)", + cc.x, -cc.y, a <= 180 ? 90 - a : 270 - a); - e->content = malloc(3); - sprintf(e->content, "%d", SECTORS[i]); + asprintf(&e->content, "%d", SECTORS[i]); } return elemc; @@ -157,24 +209,10 @@ static void draw_board() struct dom_elem **elemv = malloc(200 * sizeof(*elemv)); int elemc = 0; - elemv[elemc++] = gen_circle(DIAMETER/2, C_BLACK); - - for (int i = 5; i > 1; --i) { - for (int j = 0; j < NUM_SECTORS; ++j) { - double a = 90 - j*SECTOR_WIDTH; - if (a < 0) a += 360; - elemv[elemc++] = gen_segment(a, OUTER_DISTS[i-1], - OUTER_DISTS[i], - i % 2 ? (j % 2 ? C_GREEN : C_RED) : - (j % 2 ? C_WHITE : C_BLACK)); - } - } - - elemv[elemc++] = gen_circle(OUTER_DISTS[1], C_GREEN); - elemv[elemc++] = gen_circle(OUTER_DISTS[0], C_RED); - - elemc = draw_spider(elemc, elemv); - elemc = draw_numbers(elemc, elemv); + elemv[elemc++] = gen_circle(DIAMETER / 2, C_BLACK); + elemc += gen_segments(elemv + elemc); + elemc += gen_wires(elemv + elemc); + elemc += gen_numbers(elemv + elemc); dom_append_elemv("#dartboard .base", elemc, elemv); @@ -185,22 +223,21 @@ static void draw_board() static void create_board() { - char buf[512]; - struct dom_elem *e = svg_elem_init("svg", 2); dom_elem_add_attr(e, "id", "dartboard"); - sprintf(buf, "0 0 %f %f", DIAMETER, DIAMETER); - dom_elem_add_attr(e, "viewBox", buf); + dom_elem_add_attrf(e, "viewBox", "0 0 %f %f", DIAMETER, DIAMETER); dom_append_elemv("#dartboard-container", 1, &e); + free(e); struct dom_elem **elemv = malloc(2 * sizeof(*elemv)); int elemc = 0; - sprintf(buf, "translate(%f %f) scale(1 -1)", DIAMETER/2, DIAMETER/2); for (int i = 0; i < 2; ++i) { struct dom_elem *e = svg_elem_init("g", 2); dom_elem_add_attr(e, "class", i ? "overlay" : "base"); - dom_elem_add_attr(e, "transform", buf); + dom_elem_add_attrf(e, "transform", + "translate(%f %f) scale(1 -1)", + DIAMETER / 2, DIAMETER / 2); elemv[elemc++] = e; } @@ -211,19 +248,23 @@ static void create_board() free(elemv); } +void svg_init() +{ + create_board(); + draw_board(); +} + void svg_draw_point(double x, double y) { - struct dom_elem *e = gen_circle(6, "#33f"); - dom_elem_add_attr(e, "stroke", "#ff0"); - dom_elem_add_attr(e, "stroke-width", "2"); + struct dom_elem *e = gen_circle(POINT_RADIUS, C_POINT); dom_elem_add_attrd(e, "cx", x); dom_elem_add_attrd(e, "cy", y); + dom_elem_add_attr(e, "stroke", C_POINT_OUTLINE); + dom_elem_add_attr(e, "stroke-width", "2"); - if (delay_ms >= SVG_THROW_ANIM_MS) { - char buf[64]; - sprintf(buf, "animation: throw-anim %dms;", SVG_THROW_ANIM_MS); - dom_elem_add_attr(e, "style", buf); - } + if (delay_ms >= SVG_THROW_ANIM_MS) + dom_elem_add_attrf(e, "style", "animation: throw-anim %dms;", + SVG_THROW_ANIM_MS); dom_append_elemv("#dartboard .overlay", 1, &e); dom_elem_free(e); @@ -233,9 +274,3 @@ void svg_clear_points() { dom_set_content("#dartboard .overlay", NULL); } - -void svg_init() -{ - create_board(); - draw_board(); -} diff --git a/web/web_svg.h b/web/web_svg.h index 9110200..33fd023 100644 --- a/web/web_svg.h +++ b/web/web_svg.h @@ -4,6 +4,7 @@ #define SVG_THROW_ANIM_MS 250 void svg_init(); + void svg_draw_point(double x, double y); void svg_clear_points(); -- cgit v1.2.3-70-g09d2