summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2022-05-27 16:12:46 -0700
committerDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2022-05-27 16:14:38 -0700
commita0ca7ab5421d001f50fabbc6c139ded380f597d8 (patch)
tree3d10554311f8644d7fec76f4a2f41d8cc38a6f2f /web
parent51447980c54eab163773b80211a73dbfb3636e79 (diff)
downloaddartboat-a0ca7ab5421d001f50fabbc6c139ded380f597d8.tar.gz
dartboat-a0ca7ab5421d001f50fabbc6c139ded380f597d8.tar.xz
web: refactor svg drawing and related code
Diffstat (limited to 'web')
-rw-r--r--web/static/sw.js2
-rw-r--r--web/web_dom.c30
-rw-r--r--web/web_dom.h1
-rw-r--r--web/web_svg.c241
-rw-r--r--web/web_svg.h1
5 files changed, 166 insertions, 109 deletions
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 <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
-#include <emscripten/emscripten.h>
-
#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();