Skip to content

Commit 0ad9289

Browse files
feat(hiccup-svg): add deref() support for most shape args
1 parent 2a892cd commit 0ad9289

File tree

13 files changed

+188
-132
lines changed

13 files changed

+188
-132
lines changed

packages/hiccup-svg/src/api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import type { Maybe, MaybeDeref } from "@thi.ng/api";
2+
3+
export type AttribVal<T> = MaybeDeref<Maybe<T>>;
4+
15
export type Vec2Like = ArrayLike<number>;
26

37
// Reference:
@@ -35,3 +39,6 @@ export type PathSegment =
3539
| PathSegmentClose;
3640

3741
export type GradientStop = [string | number, string];
42+
43+
/** @internal */
44+
export const ZERO2 = [0, 0];

packages/hiccup-svg/src/circle.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1+
import { deref } from "@thi.ng/api/deref";
2+
import { ZERO2, type AttribVal, type Vec2Like } from "./api.js";
13
import { fattribs, ff } from "./format.js";
2-
import type { Vec2Like } from "./api.js";
34

45
export const circle = (
5-
p: Vec2Like,
6-
r: number,
6+
p: AttribVal<Vec2Like>,
7+
r: AttribVal<number>,
78
attribs?: any,
89
...body: any[]
9-
): any[] => [
10-
"circle",
11-
fattribs({
12-
...attribs,
13-
cx: ff(p[0]),
14-
cy: ff(p[1]),
15-
r: ff(r),
16-
}),
17-
...body,
18-
];
10+
): any[] => {
11+
p = deref(p) ?? ZERO2;
12+
return [
13+
"circle",
14+
fattribs(
15+
{
16+
...attribs,
17+
cx: ff(p[0]),
18+
cy: ff(p[1]),
19+
r: ff(deref(r) ?? 0),
20+
},
21+
"r"
22+
),
23+
...body,
24+
];
25+
};

packages/hiccup-svg/src/ellipse.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1+
import { deref } from "@thi.ng/api/deref";
2+
import { ZERO2, type AttribVal, type Vec2Like } from "./api.js";
13
import { fattribs, ff } from "./format.js";
2-
import type { Vec2Like } from "./api.js";
34

45
export const ellipse = (
5-
p: Vec2Like,
6-
rx: number,
7-
ry: number,
6+
p: AttribVal<Vec2Like>,
7+
rx: AttribVal<number>,
8+
ry: AttribVal<number>,
89
attribs?: any,
910
...body: any[]
10-
): any[] => [
11-
"ellipse",
12-
fattribs({
13-
...attribs,
14-
cx: ff(p[0]),
15-
cy: ff(p[1]),
16-
rx: ff(rx),
17-
ry: ff(ry),
18-
}),
19-
...body,
20-
];
11+
): any[] => {
12+
p = deref(p) ?? ZERO2;
13+
return [
14+
"ellipse",
15+
fattribs({
16+
...attribs,
17+
cx: ff(p[0]),
18+
cy: ff(p[1]),
19+
rx: ff(deref(rx) ?? 0),
20+
ry: ff(deref(ry) ?? 0),
21+
}),
22+
...body,
23+
];
24+
};

packages/hiccup-svg/src/format.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { deref } from "@thi.ng/api/deref";
12
import { isArrayLike } from "@thi.ng/checks/is-arraylike";
23
import { isString } from "@thi.ng/checks/is-string";
34
import { css } from "@thi.ng/color/css/css";
@@ -44,7 +45,7 @@ const DEFAULT_NUMERIC_IDS = [
4445
const numericAttribs = (attribs: any, ids: string[]) => {
4546
let v: any;
4647
for (let id of DEFAULT_NUMERIC_IDS.concat(ids)) {
47-
typeof (v = attribs[id]) === "number" && (attribs[id] = ff(v));
48+
typeof (v = deref(attribs[id])) === "number" && (attribs[id] = ff(v));
4849
}
4950
return attribs;
5051
};
@@ -107,7 +108,7 @@ export const fattribs = (attribs: any, ...numericIDs: string[]) => {
107108
const ftransforms = (attribs: any) => {
108109
let v: any;
109110
if (
110-
(v = attribs.transform) ||
111+
(v = deref(attribs.transform)) ||
111112
attribs.translate ||
112113
attribs.scale != null ||
113114
attribs.rotate
@@ -132,15 +133,15 @@ const ftransforms = (attribs: any) => {
132133
const buildTransform = (attribs: any) => {
133134
const tx: string[] = [];
134135
let v: any;
135-
if ((v = attribs.translate)) {
136+
if ((v = deref(attribs.translate))) {
136137
tx.push(isString(v) ? v : `translate(${ff(v[0])} ${ff(v[1])})`);
137138
delete attribs.translate;
138139
}
139-
if ((v = attribs.rotate)) {
140+
if ((v = deref(attribs.rotate))) {
140141
tx.push(isString(v) ? v : `rotate(${ff((v * 180) / Math.PI)})`);
141142
delete attribs.rotate;
142143
}
143-
if ((v = attribs.scale) != null) {
144+
if ((v = deref(attribs.scale)) != null) {
144145
tx.push(
145146
isString(v)
146147
? v
@@ -165,12 +166,14 @@ const buildTransform = (attribs: any) => {
165166
*
166167
* @internal
167168
*/
168-
export const fcolor = (col: any) =>
169-
isString(col)
170-
? col[0] === "$"
171-
? `url(#${col.substring(1)})`
172-
: col
169+
export const fcolor = (col: any) => {
170+
const $col = deref(col);
171+
return isString($col)
172+
? $col[0] === "$"
173+
? `url(#${$col.substring(1)})`
174+
: $col
173175
: css(col);
176+
};
174177

175178
/** @internal */
176179
export const withoutKeys = (src: any, keys: Set<PropertyKey>) => {

packages/hiccup-svg/src/gradients.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type { Maybe } from "@thi.ng/api";
2-
import type { GradientStop, Vec2Like } from "./api.js";
2+
import { deref } from "@thi.ng/api/deref";
3+
import {
4+
ZERO2,
5+
type AttribVal,
6+
type GradientStop,
7+
type Vec2Like,
8+
} from "./api.js";
39
import { fattribs, fcolor, ff } from "./format.js";
410

511
const RE_ALPHA_COLOR =
@@ -26,12 +32,14 @@ const gradientStop = ([offset, col]: GradientStop) => {
2632

2733
export const linearGradient = (
2834
id: string,
29-
from: Vec2Like,
30-
to: Vec2Like,
31-
stops: GradientStop[],
35+
from: AttribVal<Vec2Like>,
36+
to: AttribVal<Vec2Like>,
37+
stops: AttribVal<GradientStop[]>,
3238
attribs?: any
33-
) =>
34-
gradient(
39+
) => {
40+
from = deref(from) ?? ZERO2;
41+
to = deref(to) ?? ZERO2;
42+
return gradient(
3543
"linearGradient",
3644
{
3745
...attribs,
@@ -41,19 +49,22 @@ export const linearGradient = (
4149
x2: ff(to[0]),
4250
y2: ff(to[1]),
4351
},
44-
stops
52+
deref(stops) ?? []
4553
);
54+
};
4655

4756
export const radialGradient = (
4857
id: string,
49-
from: Vec2Like,
50-
to: Vec2Like,
51-
fr: number,
52-
r: number,
53-
stops: GradientStop[],
58+
from: AttribVal<Vec2Like>,
59+
to: AttribVal<Vec2Like>,
60+
fr: AttribVal<number>,
61+
r: AttribVal<number>,
62+
stops: AttribVal<GradientStop[]>,
5463
attribs?: any
55-
) =>
56-
gradient(
64+
) => {
65+
from = deref(from) ?? ZERO2;
66+
to = deref(to) ?? ZERO2;
67+
return gradient(
5768
"radialGradient",
5869
{
5970
...attribs,
@@ -62,8 +73,9 @@ export const radialGradient = (
6273
fy: ff(from[1]),
6374
cx: ff(to[0]),
6475
cy: ff(to[1]),
65-
fr: ff(fr),
66-
r: ff(r),
76+
fr: ff(deref(fr) ?? 0),
77+
r: ff(deref(r) ?? 0),
6778
},
68-
stops
79+
deref(stops) ?? []
6980
);
81+
};

packages/hiccup-svg/src/image.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1+
import { deref } from "@thi.ng/api/deref";
2+
import { ZERO2, type AttribVal, type Vec2Like } from "./api.js";
13
import { fattribs, ff } from "./format.js";
2-
import type { Vec2Like } from "./api.js";
34

45
export const image = (
5-
pos: Vec2Like,
6-
url: string,
6+
pos: AttribVal<Vec2Like>,
7+
url: AttribVal<string>,
78
attribs?: any,
89
...body: any[]
9-
): any[] => [
10-
"image",
11-
fattribs({
12-
...attribs,
13-
// TODO replace w/ SVG2 `href` once Safari supports it
14-
"xlink:href": url,
15-
x: ff(pos[0]),
16-
y: ff(pos[1]),
17-
}),
18-
...body,
19-
];
10+
): any[] => {
11+
pos = deref(pos) ?? ZERO2;
12+
return [
13+
"image",
14+
fattribs({
15+
...attribs,
16+
// TODO replace w/ SVG2 `href` once Safari supports it
17+
"xlink:href": url,
18+
x: ff(pos[0]),
19+
y: ff(pos[1]),
20+
}),
21+
...body,
22+
];
23+
};

packages/hiccup-svg/src/line.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
1+
import { deref } from "@thi.ng/api/deref";
2+
import { ZERO2, type AttribVal, type Vec2Like } from "./api.js";
13
import { fattribs, ff } from "./format.js";
2-
import type { Vec2Like } from "./api.js";
34

45
export const line = (
5-
a: Vec2Like,
6-
b: Vec2Like,
6+
a: AttribVal<Vec2Like>,
7+
b: AttribVal<Vec2Like>,
78
attribs?: any,
89
...body: any[]
9-
): any[] => [
10-
"line",
11-
fattribs({
12-
...attribs,
13-
x1: ff(a[0]),
14-
y1: ff(a[1]),
15-
x2: ff(b[0]),
16-
y2: ff(b[1]),
17-
}),
18-
...body,
19-
];
10+
): any[] => {
11+
a = deref(a) ?? ZERO2;
12+
b = deref(b) ?? ZERO2;
13+
return [
14+
"line",
15+
fattribs({
16+
...attribs,
17+
x1: ff(a[0]),
18+
y1: ff(a[1]),
19+
x2: ff(b[0]),
20+
y2: ff(b[1]),
21+
}),
22+
...body,
23+
];
24+
};
2025

21-
export const hline = (y: number, attribs?: any) =>
22-
line([-1e6, y], [1e6, y], attribs);
26+
export const hline = (y: number, attribs?: any, length = 1e6) =>
27+
line([-length, y], [length, y], attribs);
2328

24-
export const vline = (x: number, attribs?: any) =>
25-
line([x, -1e6], [x, 1e6], attribs);
29+
export const vline = (x: number, attribs?: any, length = 1e6) =>
30+
line([x, -length], [x, length], attribs);

packages/hiccup-svg/src/path.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
import { deref } from "@thi.ng/api/deref";
2+
import type { AttribVal, PathSegment } from "./api.js";
13
import { fattribs, ff, fpoint, fpoints } from "./format.js";
2-
import type { PathSegment } from "./api.js";
34

45
const DEG = 180 / Math.PI;
56

67
export const path = (
7-
segments: PathSegment[],
8+
segments: AttribVal<Iterable<PathSegment>>,
89
attribs?: any,
910
...body: any[]
1011
): any[] => {
1112
let res = [];
12-
for (let seg of segments) {
13+
for (let seg of deref(segments) ?? []) {
1314
res.push(seg[0]);
1415
switch (seg[0].toLowerCase()) {
1516
case "a":

0 commit comments

Comments
 (0)