gum-jsx 1.6.2 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/docs/code/Arc.jsx +2 -2
- package/docs/code/Ellipse.jsx +2 -2
- package/docs/code/Images.jsx +1 -1
- package/docs/code/Line.jsx +1 -1
- package/docs/code/Math.jsx +12 -10
- package/docs/code/Network.jsx +1 -1
- package/docs/code/Points.jsx +4 -1
- package/docs/code/Shape.jsx +1 -1
- package/docs/code/SymFill.jsx +1 -1
- package/docs/code/SymShape.jsx +3 -4
- package/docs/code/Tables.jsx +3 -2
- package/docs/text/Box.md +1 -1
- package/docs/text/Element.md +11 -14
- package/docs/text/Gum.md +5 -5
- package/docs/text/Math.md +2 -1
- package/docs/text/SymFill.md +2 -1
- package/docs/text/SymLine.md +2 -1
- package/docs/text/SymPoints.md +2 -1
- package/docs/text/SymShape.md +2 -1
- package/docs/text/SymSpline.md +1 -0
- package/gala/code/atomic_orbitals.jsx +1 -2
- package/gala/code/complex_plot.jsx +1 -1
- package/gala/code/pendulum_physics.jsx +3 -3
- package/gala/code/plot_manual.jsx +2 -2
- package/gala/code/polygon_slide.jsx +3 -4
- package/gala/code/spline_star.jsx +2 -2
- package/package.json +1 -1
- package/scripts/gum.ts +14 -11
- package/scripts/test.ts +0 -69
- package/skills/gum-jsx/SKILL.md +12 -15
- package/skills/gum-jsx/references/gala/atomic_orbitals.md +1 -2
- package/skills/gum-jsx/references/gala/complex_plot.md +1 -1
- package/skills/gum-jsx/references/gala/pendulum_physics.md +3 -3
- package/skills/gum-jsx/references/gala/plot_manual.md +2 -2
- package/skills/gum-jsx/references/gala/polygon_slide.md +3 -4
- package/skills/gum-jsx/references/gala/spline_star.md +2 -2
- package/skills/gum-jsx/references/geometry.md +6 -6
- package/skills/gum-jsx/references/layout.md +5 -2
- package/skills/gum-jsx/references/networks.md +1 -1
- package/skills/gum-jsx/references/symbolic.md +12 -8
- package/skills/gum-jsx/references/utilities.md +18 -14
- package/src/elems/core.ts +33 -13
- package/src/elems/geometry.ts +10 -12
- package/src/elems/layout.ts +5 -7
- package/src/elems/network.ts +4 -3
- package/src/elems/plot.ts +19 -35
- package/src/elems/symbolic.ts +30 -24
- package/src/eval.ts +13 -3
- package/src/gum.ts +3 -3
- package/src/lib/interp.ts +10 -4
- package/src/lib/types.ts +1 -2
- package/src/lib/utils.ts +32 -24
- package/src/render.ts +7 -27
|
@@ -29,9 +29,9 @@ const arcRad = 0.25
|
|
|
29
29
|
// Computed positions
|
|
30
30
|
const rodRot = 90 - rodAng
|
|
31
31
|
const eqnY = pivotY + rodLen
|
|
32
|
-
const [ bobX, bobY ] =
|
|
33
|
-
const [ midX, midY ] =
|
|
34
|
-
const [ tenX, tenY ] =
|
|
32
|
+
const [ bobX, bobY ] = polard(rodRot, rodLen, [pivotX, pivotY])
|
|
33
|
+
const [ midX, midY ] = polard(rodRot, 0.50 * rodLen, [pivotX, pivotY])
|
|
34
|
+
const [ tenX, tenY ] = polard(rodRot, 0.75 * rodLen, [pivotX, pivotY])
|
|
35
35
|
|
|
36
36
|
return <Box margin={0.06}>
|
|
37
37
|
<VStack spacing={0.05}>
|
|
@@ -11,8 +11,8 @@ const aspect = 2
|
|
|
11
11
|
const ratio = pi / aspect
|
|
12
12
|
return <Box margin={0.3}>
|
|
13
13
|
<Group coord={[0, 1, 2*pi, -1]} aspect={aspect}>
|
|
14
|
-
<HMesh locs={5}
|
|
15
|
-
<VMesh locs={5}
|
|
14
|
+
<HMesh locs={5} opacity={0.15} />
|
|
15
|
+
<VMesh locs={5} opacity={0.15} />
|
|
16
16
|
<HAxis ticks={5} lim={[0, 2*pi]} pos={[pi, -1]} size={[2*pi, 0.08]} />
|
|
17
17
|
<VAxis ticks={5} lim={[-1, 1]} pos={[0, 0]} size={[0.08*ratio, 2]} />
|
|
18
18
|
<SymLine fy={sin} xlim={[0, 2*pi]} />
|
|
@@ -16,11 +16,10 @@ const shapes = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
const RegularPolygon = ({ n, ...args }) =>
|
|
19
|
-
<SymShape {...args}
|
|
20
|
-
aspect spin={90*(n-2)/n}
|
|
19
|
+
<SymShape {...args} aspect
|
|
21
20
|
xlim={[-1, 1]} ylim={[-1, 1]}
|
|
22
|
-
tvals={linspace(0, 2*pi, n
|
|
23
|
-
|
|
21
|
+
tvals={linspace(0, 2*pi, n, false)}
|
|
22
|
+
f={t => polar(t+pi/2*(n-2)/n)}
|
|
24
23
|
/>
|
|
25
24
|
|
|
26
25
|
return <Slide title="Simple Regular Polygons" wrap={25}>
|
|
@@ -19,8 +19,8 @@ const theta0 = linspace(0, 2 * pi, n, false).map(t => t - pi / 2)
|
|
|
19
19
|
const theta1 = theta0.map(t => t + pi / n)
|
|
20
20
|
|
|
21
21
|
// get inner/outer point positions
|
|
22
|
-
const points0 = theta0.map(t => polar(t
|
|
23
|
-
const points1 = theta1.map(t => polar(t
|
|
22
|
+
const points0 = theta0.map(t => polar(t))
|
|
23
|
+
const points1 = theta1.map(t => polar(t, R))
|
|
24
24
|
const points = zip(points0, points1).flat()
|
|
25
25
|
|
|
26
26
|
// return full spline
|
|
@@ -33,8 +33,8 @@ Prompt: two ellipses, one wider and one taller
|
|
|
33
33
|
Generated code:
|
|
34
34
|
```jsx
|
|
35
35
|
<Group>
|
|
36
|
-
<Ellipse pos={[0.3, 0.2]}
|
|
37
|
-
<Ellipse pos={[0.6, 0.6]}
|
|
36
|
+
<Ellipse pos={[0.3, 0.2]} rad={[0.2, 0.1]} />
|
|
37
|
+
<Ellipse pos={[0.6, 0.6]} rad={[0.2, 0.25]} />
|
|
38
38
|
</Group>
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -57,8 +57,8 @@ Prompt: elliptical and circular arcs using start and end angles
|
|
|
57
57
|
Generated code:
|
|
58
58
|
```jsx
|
|
59
59
|
<Group>
|
|
60
|
-
<Arc pos={[0.3, 0.5]}
|
|
61
|
-
<Arc pos={[0.7, 0.5]}
|
|
60
|
+
<Arc pos={[0.3, 0.5]} rad={[0.2, 0.15]} start={-45} end={210} stroke={blue} stroke-width={2} />
|
|
61
|
+
<Arc pos={[0.7, 0.5]} rad={0.15} start={90} end={-150} stroke={red} stroke-width={2} />
|
|
62
62
|
</Group>
|
|
63
63
|
```
|
|
64
64
|
|
|
@@ -81,7 +81,7 @@ Prompt: draw a piecewise line spiraling outwards (with dots at vertices)
|
|
|
81
81
|
|
|
82
82
|
Generated code:
|
|
83
83
|
```jsx
|
|
84
|
-
const spiral = linspace(0, 5, 25).map(t => polar(
|
|
84
|
+
const spiral = linspace(0, 5, 25).map(t => polar(2*pi * t, t/5))
|
|
85
85
|
return <Box margin>
|
|
86
86
|
<Graph coord={[-1, -1, 1, 1]}>
|
|
87
87
|
<Line points={spiral} />
|
|
@@ -107,7 +107,7 @@ Prompt: draw a stop sign
|
|
|
107
107
|
|
|
108
108
|
Generated code:
|
|
109
109
|
```jsx
|
|
110
|
-
const hexagon = linspace(0,
|
|
110
|
+
const hexagon = linspace(0, 2*pi, 8, false).map(t => polar(t))
|
|
111
111
|
return <Box fill="#bbb" rounded padding margin>
|
|
112
112
|
<Graph xlim={[-1, 1]} ylim={[-1, 1]} aspect={1}>
|
|
113
113
|
<Shape points={hexagon} fill="#CC0202" stroke={white} stroke_width={20} spin={180/8} />
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
This is a simple container class allowing you to add padding, margins, and a border to a single **Element**. It's pretty versatile and is often used to set up the outermost positioning of a figure. Mirroring the standard CSS definitions, padding is space inside the border and margin is space outside the border. This has no border by default, but there is a specialized subclass of this called **Frame** that defaults to `border = 1`.
|
|
8
8
|
|
|
9
|
-
**Box** can be pretty handly in various situations. It is differentiated from **Group** in that it will adopt the `aspect` of the child element. This is useful if you want to do something like shift an element up or down by a certain amount while maintaining its aspect ratio. Simply wrap it in a **Box** and set child's `pos` to the desired offset.
|
|
9
|
+
**Box** can be pretty handly in various situations. It is differentiated from **Group** in that it will adopt the `aspect` of the first child element. This is useful if you want to do something like shift an element up or down by a certain amount while maintaining its aspect ratio. Simply wrap it in a **Box** and set child's `pos` to the desired offset.
|
|
10
10
|
|
|
11
11
|
There are multiple ways to specify padding and margins. If given as a scalar, it is constant across all sides. If two values are given, they correspond to the horizontal and vertical sides. If four values are given, they correspond to `[left, top, right, bottom]`.
|
|
12
12
|
|
|
@@ -124,7 +124,10 @@ Prompt: A plot of three different increasing curves of varying steepness and mul
|
|
|
124
124
|
|
|
125
125
|
Generated code:
|
|
126
126
|
```jsx
|
|
127
|
-
<Plot
|
|
127
|
+
<Plot coord={[-1, -1, 1, 1]} margin={0.2} grid
|
|
128
|
+
xlabel="time (seconds)" ylabel="space (meters)"
|
|
129
|
+
title="Spacetime Vibes"
|
|
130
|
+
>
|
|
128
131
|
<Points point-size={0.04} points={[
|
|
129
132
|
[0, 0.5], [0.5, 0], [-0.5, 0], [0, -0.5]
|
|
130
133
|
]} />
|
|
@@ -103,6 +103,6 @@ Generated code:
|
|
|
103
103
|
<Node id="test" pos={[0.75, 0.25]} wrap={6}>This is a test of wrapping capabilities</Node>
|
|
104
104
|
<Node id="ball" pos={[0.75, 0.75]}><Ellipse aspect={1.5} fill={blue}/></Node>
|
|
105
105
|
<Edge start="hello" end="test" />
|
|
106
|
-
<Edge start="hello" end="ball" start-side="s"
|
|
106
|
+
<Edge start="hello" end="ball" start-side="s" />
|
|
107
107
|
</Network>
|
|
108
108
|
```
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
*Inherits*: **Group** > **Element**
|
|
6
6
|
|
|
7
|
-
Flexible interface to generate sets of points symbolically or in combination with fixed inputs. The most common usage is to specify the range for x-values with `xlim` and a function to plot with `fy`. But you can specify the transpose with `ylim`/`fx`, or do a fully parametric path using `tlim
|
|
7
|
+
Flexible interface to generate sets of points symbolically or in combination with fixed inputs. The most common usage is to specify the range for x-values with `xlim` and a function to plot with `fy`. But you can specify the transpose with `ylim`/`fx`, or do a fully parametric path using `tlim` with either `f` or `fx`/`fy`.
|
|
8
8
|
|
|
9
9
|
You can also specify point size functionally with `point-size` and the shape with `point-shape`. Both of these functions take `(x, y, t, i)` values as inputs and return the desired value for each point. As with other groups, the **SymPoints** element itself can still be laid out with the normal `size`/`xsize`/`ysize` element parameters.
|
|
10
10
|
|
|
11
11
|
Parameters:
|
|
12
|
+
- `f` — a function mapping t-values to `[x, y]` points
|
|
12
13
|
- `fx`/`fy` — a function mapping from x-values, y-values, or t-values
|
|
13
14
|
- `point-size` = `0.05` — a size or a function mapping from `(x, y, t, i)` values to a size
|
|
14
15
|
- `point-shape` = `Dot` — a shape or function mapping from `(x, y, t, i)` values to a shape
|
|
@@ -35,9 +36,10 @@ return <Plot xlim={[0, 2*pi]} ylim={[-1.5, 1.5]} grid fill={lightgray} margin={[
|
|
|
35
36
|
|
|
36
37
|
Flexible interface to generate two-dimensional paths symbolically or in combination with fixed inputs. There are variety of acceptable input combinations, but the most common usage is to specify the range to use for x-values with `xlim` and a function to plot with `fy`. To plot a polygon instead of a line, use **SymShape**.
|
|
37
38
|
|
|
38
|
-
Alternatively, you can specify the transpose with `ylim`/`fx`, or
|
|
39
|
+
Alternatively, you can specify the transpose with `ylim`/`fx`, or do a fully parametric path using `tlim` with either `f` or `fx`/`fy`. In any of these cases, one can either specify limits with `xlim`/`ylim`/`tlim` or specific values with `xvals`/`yvals`/`tvals`.
|
|
39
40
|
|
|
40
41
|
Parameters:
|
|
42
|
+
- `f` — a function mapping t-values to `[x, y]` points
|
|
41
43
|
- `fx`/`fy` — a function mapping from x-values, y-values, or t-values
|
|
42
44
|
- `xlim`/`ylim`/`tlim` — a pair of numbers specifying variable limits
|
|
43
45
|
- `xvals`/`yvals`/`tvals` — a list of x-values, y-values, or t-values to use
|
|
@@ -59,9 +61,10 @@ Generated code:
|
|
|
59
61
|
|
|
60
62
|
*Inherits*: **Shape** > **Pointstring** > **Element**
|
|
61
63
|
|
|
62
|
-
Flexible interface to generate shapes symbolically or in combination with fixed inputs. Operates similarly to **Shape**, but generates a shape from
|
|
64
|
+
Flexible interface to generate shapes symbolically or in combination with fixed inputs. Operates similarly to **Shape**, but generates a shape from points generated by `f` or `fx`/`fy`.
|
|
63
65
|
|
|
64
66
|
Parameters:
|
|
67
|
+
- `f` — a function mapping t-values to `[x, y]` points
|
|
65
68
|
- `fx`/`fy` — a function mapping from x-values, y-values, or t-values
|
|
66
69
|
- `xlim`/`ylim`/`tlim` — a pair of numbers specifying variable limits
|
|
67
70
|
- `xvals`/`yvals`/`tvals` — a list of x-values, y-values, or t-values to use
|
|
@@ -73,12 +76,11 @@ Prompt: Draw a rounded star shape with a blue fill. Wrap it in a rounded frame.
|
|
|
73
76
|
|
|
74
77
|
Generated code:
|
|
75
78
|
```jsx
|
|
76
|
-
const rad = t => 1 - 0.
|
|
79
|
+
const rad = t => 1 - 0.2 * cos(5 * (t - pi/2))
|
|
77
80
|
return <Frame rounded padding margin>
|
|
78
81
|
<SymShape aspect fill={blue}
|
|
79
82
|
tlim={[0, 2*pi]} N={200}
|
|
80
|
-
|
|
81
|
-
fy={t => rad(t) * cos(t)}
|
|
83
|
+
f={t => polar(t, rad(t))}
|
|
82
84
|
/>
|
|
83
85
|
</Frame>
|
|
84
86
|
```
|
|
@@ -91,6 +93,7 @@ Flexible interface to generate smooth two-dimensional spline curves symbolically
|
|
|
91
93
|
|
|
92
94
|
|
|
93
95
|
Parameters:
|
|
96
|
+
- `f` — a function mapping t-values to `[x, y]` points
|
|
94
97
|
- `fx`/`fy` — a function mapping from x-values, y-values, or t-values
|
|
95
98
|
- `xlim`/`ylim`/`tlim` — a pair of numbers specifying variable limits
|
|
96
99
|
- `xvals`/`yvals`/`tvals` — a list of x-values, y-values, or t-values to use
|
|
@@ -118,9 +121,10 @@ return <Plot xlim={[0, 2*pi]} ylim={[-1, 1]} grid margin={0.15} aspect={phi}>
|
|
|
118
121
|
|
|
119
122
|
*Inherits*: **Fill** > **Shape** > **Pointstring** > **Element**
|
|
120
123
|
|
|
121
|
-
Flexible interface to generate filled in paths symbolically or in combination with fixed inputs. This generates a polygon by running through the points generated by `fx1`/`fy1` and then backwards through the points generated by `fx2`/`fy2`. To generate a simple filled curve, pass your function to `fy1` and let `fy2` be `0`.
|
|
124
|
+
Flexible interface to generate filled in paths symbolically or in combination with fixed inputs. This generates a polygon by running through the points generated by `f1` or `fx1`/`fy1` and then backwards through the points generated by `f2` or `fx2`/`fy2`. To generate a simple filled curve, pass your function to `fy1` and let `fy2` be `0`.
|
|
122
125
|
|
|
123
126
|
Parameters:
|
|
127
|
+
- `f1`/`f2` — functions mapping t-values to `[x, y]` points for either bound
|
|
124
128
|
- `fx1`/`fy1` — a function generating one of the bounds for the fill (or a constant)
|
|
125
129
|
- `fx2`/`fy2` — a function generating the other bound for the fill (or a constant)
|
|
126
130
|
- `xlim`/`ylim`/`tlim` — a pair of numbers specifying variable limits
|
|
@@ -135,7 +139,7 @@ Generated code:
|
|
|
135
139
|
```jsx
|
|
136
140
|
const decay = x => exp(-0.1*x) * sin(x)
|
|
137
141
|
return <Graph xlim={[0, 6*pi]} ylim={[-1, 1]} aspect={phi}>
|
|
138
|
-
<SymFill
|
|
142
|
+
<SymFill f1={t => [t, decay(t)]} f2={t => [t, 0]} tlim={[0, 6*pi]} fill={blue} fill-opacity={0.5} N={250} />
|
|
139
143
|
<SymLine fy={decay} N={250} />
|
|
140
144
|
</Graph>
|
|
141
145
|
```
|
|
@@ -29,25 +29,28 @@ Here we collect a variety of global mathematical functions and constants. You ca
|
|
|
29
29
|
- `round(x)` — the rounding function
|
|
30
30
|
- `clamp(x, lim=[0, 1])` — clamp `x` to the range `lim`
|
|
31
31
|
- `rescale(x, lim=[0, 1])` — linearly rescale `x` to the range `lim`
|
|
32
|
-
- `polar(
|
|
32
|
+
- `polar(theta, radius=1, center=[0, 0])` — convert polar coordinates (`theta` in radians, `radius` scalar or size vector) to a 2D point around `center`
|
|
33
|
+
- `polard(angle, radius=1, center=[0, 0])` — same as `polar` but takes `angle` in degrees
|
|
33
34
|
|
|
34
35
|
Angles use gum's usual screen-space convention: `0` points right and `90` points down.
|
|
35
36
|
|
|
36
37
|
**Example**
|
|
37
38
|
|
|
38
|
-
Prompt:
|
|
39
|
+
Prompt: draw a spirograph in a box for a set of example parameters
|
|
39
40
|
|
|
40
41
|
Generated code:
|
|
41
42
|
```jsx
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</Graph>
|
|
43
|
+
const [R, r, d, k] = [10, 7, 4, 7]
|
|
44
|
+
const fx = t => (R - r) * cos(t) + d * cos(((R - r) / r) * t)
|
|
45
|
+
const fy = t => (R - r) * sin(t) - d * sin(((R - r) / r) * t)
|
|
46
|
+
return <TitleFrame title="Spirograph" padding={0.2} margin rounded>
|
|
47
|
+
<Graph aspect coord={[-R, -R, R, R]}>
|
|
48
|
+
<Circle pos={[0, 0]} rad={R} stroke={darkgray} stroke-dasharray={10} />
|
|
49
|
+
<Circle pos={[0, 0]} rad={R - r} stroke-dasharray={5} />
|
|
50
|
+
<SymSpline fx={fx} fy={fy} tlim={[0, 2*pi*k]} stroke={blue} stroke-width={2} />
|
|
51
|
+
</Graph>
|
|
52
|
+
<Span pos={[0.5, 1.1]} ysize={0.05} font-family={mono}>R = 10 | r = 7 | d = 4</Span>
|
|
53
|
+
</TitleFrame>
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
## Arrays
|
|
@@ -146,9 +149,10 @@ Prompt: load "data.csv" and plot each row as a blue dot
|
|
|
146
149
|
|
|
147
150
|
Generated code:
|
|
148
151
|
```jsx
|
|
149
|
-
return <Graph
|
|
152
|
+
return <Graph aspect coord={[0, 0, 10, 10]}>
|
|
153
|
+
<Mesh2D xlocs={10} ylocs={10} opacity={0.25} />
|
|
150
154
|
{loadTable('data.csv').map(({ x, y }) =>
|
|
151
|
-
<Dot pos={[x, y]}
|
|
155
|
+
<Dot pos={[x, y]} size={0.5} fill={blue} />
|
|
152
156
|
)}
|
|
153
157
|
</Graph>
|
|
154
158
|
```
|
|
@@ -171,7 +175,7 @@ Generated code:
|
|
|
171
175
|
```jsx
|
|
172
176
|
<Box rounded clip>
|
|
173
177
|
<Group aspect={2}>
|
|
174
|
-
<LoadImage id="image.png"
|
|
178
|
+
<LoadImage id="image.png" xrect={[0, 1]} />
|
|
175
179
|
</Group>
|
|
176
180
|
</Box>
|
|
177
181
|
```
|
package/src/elems/core.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { THEME } from '../lib/theme'
|
|
4
4
|
import { DEFAULTS as D, svgns, sans, light, blue, red, d2r } from '../lib/const'
|
|
5
|
-
import { is_scalar, abs, cos, sin, tan, cot, mul2, div2, filter_object, expand_rect, rect_box, cbox_rect, rect_cbox, merge_points, merge_rects,
|
|
5
|
+
import { is_scalar, abs, cos, sin, tan, cot, mul2, div2, filter_object, expand_rect, rect_box, cbox_rect, rect_cbox, merge_points, merge_rects, join_limits, ensure_pair, rounder, heavisign, abs_min, abs_max, rect_radial, rotate_aspect, remap_rect, rescaler, resizer, rect_size, vector_angle, polard, upright_rect } from '../lib/utils'
|
|
6
6
|
import { random } from '../lib/rng'
|
|
7
7
|
|
|
8
|
-
import type { Point, Rect, Size, AlignValue, Align, Side, Attrs, MNumber, MPoint, Spec } from '../lib/types'
|
|
8
|
+
import type { Point, Rect, Size, AlignValue, Align, Side, Attrs, MNumber, MPoint, Spec, Limit } from '../lib/types'
|
|
9
9
|
|
|
10
10
|
//
|
|
11
11
|
// rect embedding
|
|
@@ -118,7 +118,7 @@ function adjust_rotate(rotate: number, prect: Rect, coord: Rect): number {
|
|
|
118
118
|
const csize = rect_size(coord)
|
|
119
119
|
const psize = rect_size(prect)
|
|
120
120
|
const proj = div2(psize, csize)
|
|
121
|
-
const vec =
|
|
121
|
+
const vec = polard(rotate, proj)
|
|
122
122
|
return vector_angle(vec)
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -217,7 +217,7 @@ class Context {
|
|
|
217
217
|
const transform = rotate1 ? rotate_repr(rotate1, [ x0, y0 ], this.prec) : undefined
|
|
218
218
|
|
|
219
219
|
// broadcast align into [ halign, valign ] components
|
|
220
|
-
const [ hafrac, vafrac ] =
|
|
220
|
+
const [ hafrac, vafrac ] = ensure_pair(align).map(align_frac)
|
|
221
221
|
const [ x, y ] = [
|
|
222
222
|
x0 + (0.5 - hafrac) * (w - w0),
|
|
223
223
|
y0 + (0.5 - vafrac) * (h - h0),
|
|
@@ -259,7 +259,7 @@ function props_repr(d: Attrs, prec: number): string {
|
|
|
259
259
|
|
|
260
260
|
// reserved keys
|
|
261
261
|
const SPEC_KEYS = [ 'rect', 'coord', 'aspect', 'aspect0', 'expand', 'align', 'upright', 'offset', 'rotate', 'rotate_adjust', 'rotate_invar' ]
|
|
262
|
-
const HELP_KEYS = [ 'pos', 'size', 'xsize', 'ysize', 'flex', 'spin', 'orient' ]
|
|
262
|
+
const HELP_KEYS = [ 'pos', 'size', 'xsize', 'ysize', 'rad', 'xrad', 'yrad', 'xrect', 'yrect', 'flex', 'spin', 'orient' ]
|
|
263
263
|
const EXTR_KEYS = [ 'stack_size' ]
|
|
264
264
|
const RESERVED_KEYS = [ ...SPEC_KEYS, ...HELP_KEYS, ...EXTR_KEYS ]
|
|
265
265
|
|
|
@@ -300,6 +300,11 @@ interface ElementArgs extends SpecArgs {
|
|
|
300
300
|
size?: number | Size
|
|
301
301
|
xsize?: number
|
|
302
302
|
ysize?: number
|
|
303
|
+
rad?: number | Size
|
|
304
|
+
xrad?: number
|
|
305
|
+
yrad?: number
|
|
306
|
+
xrect?: number | Limit
|
|
307
|
+
yrect?: number | Limit
|
|
303
308
|
flex?: boolean
|
|
304
309
|
spin?: number
|
|
305
310
|
orient?: number
|
|
@@ -316,7 +321,7 @@ class Element {
|
|
|
316
321
|
attr: Attrs
|
|
317
322
|
|
|
318
323
|
constructor(args: ElementArgs = {}) {
|
|
319
|
-
const { tag, unary, children, pos, size, xsize, ysize, flex, spin, orient, ...attr0 } = args
|
|
324
|
+
const { tag, unary, children, pos, size: size0, xsize: xsize0, ysize: ysize0, rad, xrad, yrad, xrect, yrect, flex, spin, orient, ...attr0 } = args
|
|
320
325
|
const [ spec, attr ] = spec_split(attr0, false)
|
|
321
326
|
this.args = args
|
|
322
327
|
|
|
@@ -332,12 +337,27 @@ class Element {
|
|
|
332
337
|
this.spec = spec
|
|
333
338
|
this.attr = attr
|
|
334
339
|
|
|
335
|
-
// handle pos/
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
340
|
+
// handle pos/rad/xrad/yrad conveniences
|
|
341
|
+
const [ x, y ] = pos ?? D.pos
|
|
342
|
+
const size = rad != null ? mul2(rad, 2) : size0
|
|
343
|
+
const xsize = xrad != null ? 2 * xrad : xsize0
|
|
344
|
+
const ysize = yrad != null ? 2 * yrad : ysize0
|
|
345
|
+
|
|
346
|
+
// handle rect conveniences
|
|
347
|
+
if (this.spec.rect != null) {
|
|
348
|
+
// already have a rect
|
|
349
|
+
} else if (size != null) {
|
|
350
|
+
const [ w, h ] = ensure_pair(size)
|
|
351
|
+
this.spec.rect = cbox_rect([ x, y, w, h ])
|
|
352
|
+
} else if (xrect != null || yrect != null) {
|
|
353
|
+
const xrect1 = ensure_pair(xrect ?? x)
|
|
354
|
+
const yrect1 = ensure_pair(yrect ?? y)
|
|
355
|
+
this.spec.rect = join_limits({ h: xrect1, v: yrect1 })
|
|
356
|
+
if (xrect == null || yrect == null) this.spec.expand = true
|
|
357
|
+
} else if (xsize != null || ysize != null) {
|
|
358
|
+
const [ w, h ] = ensure_pair([ xsize ?? 0, ysize ?? 0 ])
|
|
359
|
+
this.spec.rect = cbox_rect([ x, y, w, h ])
|
|
360
|
+
if (xsize == null || ysize == null) this.spec.expand = true
|
|
341
361
|
}
|
|
342
362
|
|
|
343
363
|
// various convenience conversions
|
|
@@ -642,7 +662,7 @@ class Svg extends Group {
|
|
|
642
662
|
constructor(args: SvgArgs = {}) {
|
|
643
663
|
const { children: children0, size : size0 = D.svg_size, padding = 1, bare = false, dims = true, filters, aspect: aspect0 = 'auto', view: view0, style, xmlns = svgns, font_family = sans, font_weight = light, prec = D.prec, ...attr } = THEME(args, 'Svg')
|
|
644
664
|
const children = ensure_children(children0)
|
|
645
|
-
const size_base =
|
|
665
|
+
const size_base = ensure_pair(size0)
|
|
646
666
|
|
|
647
667
|
// precompute aspect info
|
|
648
668
|
const aspect = aspect0 == 'auto' ? children_aspect(children) : aspect0
|
package/src/elems/geometry.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// geometry elements
|
|
2
2
|
|
|
3
3
|
import { THEME } from '../lib/theme'
|
|
4
|
-
import { DEFAULTS as D,
|
|
5
|
-
import { is_boolean, is_scalar, is_array, ensure_vector, ensure_point, check_array, upright_limits, rounder, abs, rect_radial, make_mpoint,
|
|
6
|
-
import { cubic_spline_data } from '../lib/interp'
|
|
4
|
+
import { DEFAULTS as D, none, gray } from '../lib/const'
|
|
5
|
+
import { is_boolean, is_scalar, is_array, ensure_vector, ensure_point, check_array, upright_limits, rounder, abs, rect_radial, make_mpoint, merge_points, ensure_pair, add2, sub2, mul2, div2, norm, angle_direc, unit_direc, vector_angle, polard, prefix_split} from '../lib/utils'
|
|
6
|
+
import { cubic_spline_data, cubic_spline_tangent } from '../lib/interp'
|
|
7
7
|
import { Context, Element, Group, Rectangle } from './core'
|
|
8
8
|
|
|
9
9
|
import type { Point, Rect, Limit, Grad, Attrs, MPoint, Orient, Rounded, Direc } from '../lib/types'
|
|
@@ -186,12 +186,11 @@ interface RayArgs extends LineArgs {
|
|
|
186
186
|
class Ray extends Line {
|
|
187
187
|
constructor(args: RayArgs = {}) {
|
|
188
188
|
const { angle = 0, loc = D.pos, size = 0.5, ...attr } = THEME(args, 'Ray')
|
|
189
|
-
const theta = angle * d2r
|
|
190
189
|
const [ x, y ] = loc
|
|
191
190
|
const [ rx, ry ] = ensure_vector(size, 2)
|
|
192
191
|
const points: Point[] = [
|
|
193
192
|
[ x, y ],
|
|
194
|
-
|
|
193
|
+
polard(angle, [ rx, ry ], [ x, y ])
|
|
195
194
|
]
|
|
196
195
|
super({ points, ...attr })
|
|
197
196
|
this.args = args
|
|
@@ -553,9 +552,8 @@ class CubicSplineCmd extends Command {
|
|
|
553
552
|
|
|
554
553
|
args(ctx: Context): string {
|
|
555
554
|
// use dir if provided, otherwise use tan
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const tan2 = this.dir2 != null ? mul2(this.dir2, dist) : this.tan2
|
|
555
|
+
const tan1 = cubic_spline_tangent(this.pos1, this.pos2, this.dir1, this.tan1)
|
|
556
|
+
const tan2 = cubic_spline_tangent(this.pos1, this.pos2, this.dir2, this.tan2)
|
|
559
557
|
if (tan1 == null || tan2 == null) throw new Error('Spline tangent must be defined')
|
|
560
558
|
|
|
561
559
|
// compute scaled tangents
|
|
@@ -624,7 +622,7 @@ function parse_rounded(rounded: Rounded): Point[] {
|
|
|
624
622
|
const [ rx, ry ] = rounded
|
|
625
623
|
rounded = [[rx, ry], [rx, ry], [rx, ry], [rx, ry]]
|
|
626
624
|
}
|
|
627
|
-
return rounded.map(
|
|
625
|
+
return rounded.map(ensure_pair)
|
|
628
626
|
}
|
|
629
627
|
|
|
630
628
|
interface RoundedRectArgs extends ElementArgs {
|
|
@@ -717,9 +715,9 @@ class Arc extends Path {
|
|
|
717
715
|
constructor(args: ArcArgs = {}) {
|
|
718
716
|
const { start, end, upright = true, ...attr } = THEME(args, 'Arc')
|
|
719
717
|
if (start == null || end == null) throw new Error('Must provide `start` and `end` angles')
|
|
720
|
-
const [
|
|
721
|
-
const large = (
|
|
722
|
-
const [ point0, point1 ] = [
|
|
718
|
+
const [ angle0, angle1 ] = upright_limits([ start, end ])
|
|
719
|
+
const large = (angle1 - angle0) > 180
|
|
720
|
+
const [ point0, point1 ] = [ angle0, angle1 ].map(t => polard(t, 0.5, [0.5, 0.5]))
|
|
723
721
|
const children = [ new MoveCmd(point0), new ArcCmd(point1, 0.5, true, large) ]
|
|
724
722
|
super({ children, upright, ...attr })
|
|
725
723
|
this.args = args
|
package/src/elems/layout.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { THEME } from '../lib/theme'
|
|
4
4
|
import { DEFAULTS as D, none } from '../lib/const'
|
|
5
|
-
import { is_scalar, ensure_vector,
|
|
5
|
+
import { is_scalar, ensure_vector, ensure_pair, log, exp, max, sum, zip, div2, cumsum, reshape, repeat, meshgrid, padvec, normalize, mean, identity, invert, aspect_invariant, check_singleton, check_array, rect_center, rect_radius, join_limits, radial_rect, norm_side, intersperse, prefix_split, merge_points } from '../lib/utils'
|
|
6
6
|
import { wrapWidths } from '../lib/wrap'
|
|
7
7
|
|
|
8
8
|
import { Context, Group, Element, Rectangle, Spacer, spec_split, align_frac, ensure_children } from './core'
|
|
@@ -328,7 +328,7 @@ function computeGridLayout(children: Element[][], rows: number, cols: number, {
|
|
|
328
328
|
const aspect_ideal = exp(log_mu - mean(widths.map(log)) + mean(heights.map(log)))
|
|
329
329
|
|
|
330
330
|
// adjust widths and heights to account for spacing
|
|
331
|
-
const [spacex, spacey] =
|
|
331
|
+
const [spacex, spacey] = ensure_pair(spacing)
|
|
332
332
|
const [scalex, scaley] = [1 - spacex * (cols-1), 1 - spacey * (rows-1)]
|
|
333
333
|
widths = widths.map(w => scalex * w)
|
|
334
334
|
heights = heights.map(h => scaley * h)
|
|
@@ -382,11 +382,9 @@ class Grid extends Group {
|
|
|
382
382
|
const aspect = aspect0 ?? aspect_ideal
|
|
383
383
|
|
|
384
384
|
// make grid
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const items = zip(children, rects).map(([ child, rect ]) =>
|
|
389
|
-
child.clone({ rect })
|
|
385
|
+
const mesh = meshgrid(rranges, cranges)
|
|
386
|
+
const items = zip(children, mesh).map(([ child, [ ylim, xlim ] ]) =>
|
|
387
|
+
child.clone({ xrect: xlim, yrect: ylim })
|
|
390
388
|
)
|
|
391
389
|
|
|
392
390
|
// pass to Group
|
package/src/elems/network.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// network elements
|
|
2
2
|
|
|
3
3
|
import { THEME } from '../lib/theme'
|
|
4
|
-
import { abs, sub2, mul2, check_singleton, is_string, rect_center, side_direc, prefix_split } from '../lib/utils'
|
|
4
|
+
import { abs, sub2, mul2, check_singleton, is_string, rect_center, side_direc, prefix_split, join_limits } from '../lib/utils'
|
|
5
5
|
|
|
6
6
|
import { Context, Element, Group, ensure_children } from './core'
|
|
7
|
-
import type { ElementArgs, GroupArgs } from './core'
|
|
8
7
|
import { Frame } from './layout'
|
|
9
8
|
import { Arrow } from './geometry'
|
|
10
9
|
import { Text } from './text'
|
|
11
10
|
|
|
11
|
+
import type { ElementArgs, GroupArgs } from './core'
|
|
12
12
|
import type { AlignValue, Point, Side } from '../lib/types'
|
|
13
13
|
|
|
14
14
|
//
|
|
@@ -135,8 +135,9 @@ class Edge extends Element {
|
|
|
135
135
|
|
|
136
136
|
class Network extends Group {
|
|
137
137
|
constructor(args: GroupArgs = {}) {
|
|
138
|
-
const { children: children0, coord, ...attr0 } = THEME(args, 'Network')
|
|
138
|
+
const { children: children0, xlim, ylim, coord: coord0, ...attr0 } = THEME(args, 'Network')
|
|
139
139
|
const [ node_attr, edge_attr, attr ] = prefix_split([ 'node', 'edge' ], attr0)
|
|
140
|
+
const coord = coord0 ?? join_limits({ h: xlim, v: ylim })
|
|
140
141
|
const children = ensure_children(children0)
|
|
141
142
|
|
|
142
143
|
// process nodes and make label map
|
package/src/elems/plot.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { THEME } from '../lib/theme'
|
|
4
4
|
import { DEFAULTS as D, none, blue, white } from '../lib/const'
|
|
5
|
-
import { sign, abs, linspace, invert_orient, join_limits, ensure_vector, is_scalar, is_string, is_object, ensure_singleton, check_singleton, rounder, enumerate, aspect_invariant, rect_aspect, merge_rects, expand_limits, flip_rect, resolve_limits, smoothstep, prefix_split, prefix_join } from '../lib/utils'
|
|
5
|
+
import { sign, abs, linspace, invert_orient, join_limits, split_limits, ensure_vector, is_scalar, is_string, is_object, ensure_singleton, check_singleton, rounder, enumerate, aspect_invariant, rect_aspect, merge_rects, expand_limits, flip_rect, resolve_limits, smoothstep, prefix_split, prefix_join } from '../lib/utils'
|
|
6
6
|
import { Span } from './text'
|
|
7
7
|
|
|
8
8
|
import { Element, Group, Spacer, spec_split, is_element, ensure_children } from './core'
|
|
@@ -416,10 +416,10 @@ interface LegendArgs extends ElementArgs {
|
|
|
416
416
|
|
|
417
417
|
class Mesh extends Scale {
|
|
418
418
|
constructor(args: MeshArgs = {}) {
|
|
419
|
-
const { children: children0, locs: locs0, direc = 'h',
|
|
420
|
-
const
|
|
421
|
-
const
|
|
422
|
-
super({ locs, direc,
|
|
419
|
+
const { children: children0, locs: locs0 = 10, direc = 'h', xlim, ylim, coord, ...attr } = THEME(args, 'Mesh')
|
|
420
|
+
const { [direc]: lim, [invert_orient(direc)]: span } = resolve_limits(xlim, ylim, coord as Rect)
|
|
421
|
+
const locs = auto_array(locs0, lim ?? D.lim)
|
|
422
|
+
super({ locs, direc, span, xlim, ylim, coord, ...attr })
|
|
423
423
|
this.args = args
|
|
424
424
|
}
|
|
425
425
|
}
|
|
@@ -442,23 +442,9 @@ class VMesh extends Mesh {
|
|
|
442
442
|
|
|
443
443
|
class Mesh2D extends Group {
|
|
444
444
|
constructor(args: Mesh2DArgs = {}) {
|
|
445
|
-
let { children: children0, locs, xlocs, ylocs, direc = 'h', xlim
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
xlocs ??= locs
|
|
449
|
-
ylocs ??= locs
|
|
450
|
-
xspan ??= xlim
|
|
451
|
-
yspan ??= ylim
|
|
452
|
-
|
|
453
|
-
// convert locs to arrays
|
|
454
|
-
xlocs = auto_array(xlocs, xlim)
|
|
455
|
-
ylocs = auto_array(ylocs, ylim)
|
|
456
|
-
|
|
457
|
-
// create meshes
|
|
458
|
-
const hmesh = new HMesh({ locs: xlocs, span: yspan, lim: xlim, ...attr })
|
|
459
|
-
const vmesh = new VMesh({ locs: ylocs, span: xspan, lim: ylim, ...attr })
|
|
460
|
-
|
|
461
|
-
// pass to Group
|
|
445
|
+
let { children: children0, locs, xlocs, ylocs, direc = 'h', xlim, ylim, coord, ...attr } = THEME(args, 'Mesh2D')
|
|
446
|
+
const hmesh = new HMesh({ locs: xlocs ?? locs, xlim, ylim, coord })
|
|
447
|
+
const vmesh = new VMesh({ locs: ylocs ?? locs, xlim, ylim, coord })
|
|
462
448
|
super({ children: [ hmesh, vmesh ], ...attr })
|
|
463
449
|
this.args = args
|
|
464
450
|
}
|
|
@@ -527,8 +513,8 @@ function outer_limits(children: Element[], { xlim, ylim, padding = 0 }: { xlim?:
|
|
|
527
513
|
if (children.length == 0) return
|
|
528
514
|
|
|
529
515
|
// pull in child coordinate system
|
|
530
|
-
const
|
|
531
|
-
const {
|
|
516
|
+
const coord = merge_rects(children.map((c: Element) => c.graphCoord()))
|
|
517
|
+
const { h: xlim0, v: ylim0 } = resolve_limits(xlim, ylim, coord)
|
|
532
518
|
|
|
533
519
|
// expand with padding
|
|
534
520
|
const [ xpad, ypad ] = ensure_vector(padding, 2)
|
|
@@ -607,13 +593,13 @@ interface PlotArgs extends BoxArgs {
|
|
|
607
593
|
class Plot extends Box {
|
|
608
594
|
constructor(args: PlotArgs = {}) {
|
|
609
595
|
let {
|
|
610
|
-
children: children0, xlim, ylim, axis = true, xaxis, yaxis, xticks = 5, yticks = 5, xanchor, yanchor, grid, xgrid, ygrid, xlabel, ylabel, title, tick_size = 0.015, label_size = 0.05, label_offset = 0.125, title_size = 0.075, title_offset = 0.05, xlabel_size, ylabel_size, xlabel_offset, ylabel_offset, xtick_label_offset = 0.75, ytick_label_offset = 0.25, xtick_size, ytick_size, padding, margin, aspect: aspect0 = 'auto', clip, debug = false, ...attr0
|
|
596
|
+
children: children0, xlim, ylim, axis = true, xaxis, yaxis, xticks = 5, yticks = 5, xanchor, yanchor, grid, xgrid, ygrid, xlabel, ylabel, title, tick_size = 0.015, label_size = 0.05, label_offset = 0.125, title_size = 0.075, title_offset = 0.05, xlabel_size, ylabel_size, xlabel_offset, ylabel_offset, xtick_label_offset = 0.75, ytick_label_offset = 0.25, xtick_size, ytick_size, padding, margin, coord: coord0 = 'auto', aspect: aspect0 = 'auto', clip, debug = false, ...attr0
|
|
611
597
|
} = THEME(args, 'Plot')
|
|
612
598
|
const children = ensure_children(children0)
|
|
613
599
|
|
|
614
600
|
// determine coordinate system and aspect
|
|
615
|
-
const coord = outer_limits(children, { xlim, ylim, padding })
|
|
616
|
-
const [ xmin, ymin, xmax, ymax ] = coord
|
|
601
|
+
const coord = coord0 == 'auto' ? outer_limits(children, { xlim, ylim, padding }) : coord0
|
|
602
|
+
const [ xmin, ymin, xmax, ymax ] = coord ?? D.coord
|
|
617
603
|
xlim = [ xmin, xmax ]
|
|
618
604
|
ylim = [ ymin, ymax ]
|
|
619
605
|
|
|
@@ -666,9 +652,8 @@ class Plot extends Box {
|
|
|
666
652
|
if (xaxis === true) xaxis = new HAxis({ ticks: xticks, lim: xlim })
|
|
667
653
|
if (xaxis != null && xaxis !== false) {
|
|
668
654
|
const xtick_size1 = xtick_size * (ymax - ymin)
|
|
669
|
-
const xaxis_ylim
|
|
670
|
-
|
|
671
|
-
xaxis = xaxis.clone({ rect: xaxis_rect, ...xaxis_attr }) as HAxis
|
|
655
|
+
const xaxis_ylim = [ xanchor - xtick_size1, xanchor + xtick_size1 ]
|
|
656
|
+
xaxis = xaxis.clone({ xrect: xlim, yrect: xaxis_ylim, ...xaxis_attr }) as HAxis
|
|
672
657
|
fg_elems.push(xaxis)
|
|
673
658
|
}
|
|
674
659
|
|
|
@@ -676,16 +661,15 @@ class Plot extends Box {
|
|
|
676
661
|
if (yaxis === true) yaxis = new VAxis({ ticks: yticks, lim: ylim })
|
|
677
662
|
if (yaxis != null && yaxis !== false) {
|
|
678
663
|
const ytick_size1 = ytick_size * (xmax - xmin)
|
|
679
|
-
const yaxis_xlim
|
|
680
|
-
|
|
681
|
-
yaxis = yaxis.clone({ rect: yaxis_rect, ...yaxis_attr }) as VAxis
|
|
664
|
+
const yaxis_xlim = [ yanchor - ytick_size1, yanchor + ytick_size1 ]
|
|
665
|
+
yaxis = yaxis.clone({ xrect: yaxis_xlim, yrect: ylim, ...yaxis_attr }) as VAxis
|
|
682
666
|
fg_elems.push(yaxis)
|
|
683
667
|
}
|
|
684
668
|
|
|
685
669
|
// automatic xgrid generation
|
|
686
670
|
if (xgrid != null && xgrid !== false) {
|
|
687
671
|
const locs = (xgrid === true && xaxis != null && xaxis !== false) ? xaxis.locs : xgrid
|
|
688
|
-
const xgrid_elem = new HMesh({ locs: locs as number[],
|
|
672
|
+
const xgrid_elem = new HMesh({ locs: locs as number[], ...xgrid_attr })
|
|
689
673
|
bg_elems.unshift(xgrid_elem)
|
|
690
674
|
} else {
|
|
691
675
|
xgrid = undefined
|
|
@@ -694,7 +678,7 @@ class Plot extends Box {
|
|
|
694
678
|
// automatic ygrid generation
|
|
695
679
|
if (ygrid != null && ygrid !== false) {
|
|
696
680
|
const locs = (ygrid === true && yaxis != null && yaxis !== false) ? yaxis.locs : ygrid
|
|
697
|
-
const ygrid_elem = new VMesh({ locs: locs as number[],
|
|
681
|
+
const ygrid_elem = new VMesh({ locs: locs as number[], ...ygrid_attr })
|
|
698
682
|
bg_elems.unshift(ygrid_elem)
|
|
699
683
|
} else {
|
|
700
684
|
ygrid = undefined
|