@danielsimonjr/mathts-plot 0.4.10 → 0.4.12
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/dist/index.js +1235 -1126
- package/dist/render-file.js +140 -154
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,1235 +1,1344 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
// src/coerce.ts
|
|
5
|
-
import {
|
|
6
|
-
isMatrix,
|
|
7
|
-
isComplex,
|
|
8
|
-
isBigNumber,
|
|
9
|
-
isFraction,
|
|
10
|
-
number as toNumber
|
|
11
|
-
} from "@danielsimonjr/mathts-core";
|
|
1
|
+
import { evaluate, histogram as histogram$1, parse, range } from "@danielsimonjr/mathts-functions";
|
|
2
|
+
import { isBigNumber, isComplex, isFraction, isMatrix, number } from "@danielsimonjr/mathts-core";
|
|
3
|
+
//#region src/coerce.ts
|
|
12
4
|
function scalar(v) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
return Number(v);
|
|
5
|
+
if (typeof v === "number") return v;
|
|
6
|
+
if (typeof v === "bigint") return Number(v);
|
|
7
|
+
if (isComplex(v)) return v.re;
|
|
8
|
+
if (isBigNumber(v) || isFraction(v)) try {
|
|
9
|
+
return number(v);
|
|
10
|
+
} catch {
|
|
11
|
+
return NaN;
|
|
12
|
+
}
|
|
13
|
+
return Number(v);
|
|
24
14
|
}
|
|
25
15
|
function unwrap(raw) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
return raw;
|
|
16
|
+
if (isMatrix(raw)) try {
|
|
17
|
+
return raw.toArray();
|
|
18
|
+
} catch {
|
|
19
|
+
return raw;
|
|
20
|
+
}
|
|
21
|
+
return raw;
|
|
34
22
|
}
|
|
35
23
|
function coerce1d(raw) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
const u = unwrap(raw);
|
|
25
|
+
const arr = Array.isArray(u) ? u.flat(Infinity) : u instanceof Float64Array ? Array.from(u) : [];
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const v of arr) {
|
|
28
|
+
const n = scalar(v);
|
|
29
|
+
if (Number.isFinite(n)) out.push(n);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
44
32
|
}
|
|
45
33
|
function coerce1dPositional(raw) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return arr.map((v) => scalar(v));
|
|
34
|
+
const u = unwrap(raw);
|
|
35
|
+
return (Array.isArray(u) ? u.flat(Infinity) : u instanceof Float64Array ? Array.from(u) : []).map((v) => scalar(v));
|
|
49
36
|
}
|
|
50
37
|
function coerce2d(raw) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// src/scale.ts
|
|
38
|
+
const u = unwrap(raw);
|
|
39
|
+
if (!Array.isArray(u)) return [];
|
|
40
|
+
return u.map((row) => {
|
|
41
|
+
return (row instanceof Float64Array ? Array.from(row) : Array.isArray(row) ? row : [row]).map((v) => {
|
|
42
|
+
const n = scalar(v);
|
|
43
|
+
return Number.isFinite(n) ? n : NaN;
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/scale.ts
|
|
63
49
|
function extent(xs) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
function linearScale(dom,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
function logScale(dom,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
50
|
+
if (xs.length === 0) return [0, 1];
|
|
51
|
+
let lo = xs[0];
|
|
52
|
+
let hi = xs[0];
|
|
53
|
+
for (const v of xs) {
|
|
54
|
+
if (v < lo) lo = v;
|
|
55
|
+
if (v > hi) hi = v;
|
|
56
|
+
}
|
|
57
|
+
if (lo === hi) return [lo - 1, hi + 1];
|
|
58
|
+
return [lo, hi];
|
|
59
|
+
}
|
|
60
|
+
function linearScale(dom, range) {
|
|
61
|
+
const [d0, d1] = dom;
|
|
62
|
+
const [r0, r1] = range;
|
|
63
|
+
const m = (r1 - r0) / (d1 - d0);
|
|
64
|
+
return (v) => r0 + (v - d0) * m;
|
|
65
|
+
}
|
|
66
|
+
function logScale(dom, range) {
|
|
67
|
+
const d0 = Math.log10(Math.max(dom[0], Number.MIN_VALUE));
|
|
68
|
+
const d1 = Math.log10(Math.max(dom[1], Number.MIN_VALUE));
|
|
69
|
+
const [r0, r1] = range;
|
|
70
|
+
const m = (r1 - r0) / (d1 - d0);
|
|
71
|
+
return (v) => r0 + (Math.log10(Math.max(v, Number.MIN_VALUE)) - d0) * m;
|
|
86
72
|
}
|
|
87
73
|
function niceTicks(min, max, count = 5) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"#2c3e50"
|
|
74
|
+
if (!(max > min)) return [min];
|
|
75
|
+
const raw = (max - min) / count;
|
|
76
|
+
const mag = Math.pow(10, Math.floor(Math.log10(raw)));
|
|
77
|
+
const norm = raw / mag;
|
|
78
|
+
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
79
|
+
const start = Math.floor(min / step) * step;
|
|
80
|
+
const ticks = [];
|
|
81
|
+
for (let t = start; t <= max + step * .5; t += step) ticks.push(Math.round(t / step) * step);
|
|
82
|
+
return ticks;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/svg.ts
|
|
86
|
+
const PALETTE = [
|
|
87
|
+
"#2a4d8f",
|
|
88
|
+
"#c0392b",
|
|
89
|
+
"#27ae60",
|
|
90
|
+
"#8e44ad",
|
|
91
|
+
"#d68910",
|
|
92
|
+
"#16a085",
|
|
93
|
+
"#7f8c8d",
|
|
94
|
+
"#2c3e50"
|
|
110
95
|
];
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
96
|
+
const THEMES = {
|
|
97
|
+
light: {
|
|
98
|
+
fg: "#1a1a2e",
|
|
99
|
+
muted: "#5a5a72",
|
|
100
|
+
grid: "#e6e6ee",
|
|
101
|
+
axis: "#5a5a72",
|
|
102
|
+
bg: "#ffffff",
|
|
103
|
+
series: PALETTE
|
|
104
|
+
},
|
|
105
|
+
dark: {
|
|
106
|
+
fg: "#e8e8f0",
|
|
107
|
+
muted: "#a0a0b0",
|
|
108
|
+
grid: "#2c2c3a",
|
|
109
|
+
axis: "#a0a0b0",
|
|
110
|
+
bg: "#14141c",
|
|
111
|
+
series: PALETTE
|
|
112
|
+
}
|
|
128
113
|
};
|
|
129
114
|
function esc(s) {
|
|
130
|
-
|
|
115
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
131
116
|
}
|
|
132
117
|
function fmt(n) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
118
|
+
if (!Number.isFinite(n)) return "";
|
|
119
|
+
const a = Math.abs(n);
|
|
120
|
+
if (a !== 0 && (a < .001 || a >= 1e6)) return n.toExponential(2);
|
|
121
|
+
return String(Math.round(n * 1e6) / 1e6);
|
|
122
|
+
}
|
|
123
|
+
const r2 = (n) => Math.round(n * 100) / 100;
|
|
124
|
+
const ATTR_ENTITIES = {
|
|
125
|
+
"&": "&",
|
|
126
|
+
"<": "<",
|
|
127
|
+
">": ">",
|
|
128
|
+
"\"": """,
|
|
129
|
+
"'": "'"
|
|
145
130
|
};
|
|
146
131
|
function escAttr(v) {
|
|
147
|
-
|
|
132
|
+
return String(v).replace(/[&<>"']/g, (c) => ATTR_ENTITIES[c]);
|
|
148
133
|
}
|
|
149
134
|
function el(tag, attrs, body) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
135
|
+
let s = `<${tag}`;
|
|
136
|
+
for (const [name, value] of attrs) if (value !== void 0) s += ` ${name}="${escAttr(value)}"`;
|
|
137
|
+
return body === void 0 ? `${s}/>` : `${s}>${body}</${tag}>`;
|
|
153
138
|
}
|
|
154
|
-
|
|
155
|
-
|
|
139
|
+
const opacityAttr = (opacity) => opacity !== void 0 && opacity < 1 ? Math.round(opacity * 1e3) / 1e3 : void 0;
|
|
140
|
+
const points = (pts) => pts.map(([x, y]) => `${r2(x)},${r2(y)}`).join(" ");
|
|
156
141
|
function svgDoc(width, height, body, bg) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
["stroke", stroke],
|
|
181
|
-
["stroke-width", w],
|
|
182
|
-
["stroke-opacity", opacityAttr(opacity)]
|
|
183
|
-
]);
|
|
142
|
+
return el("svg", [
|
|
143
|
+
["xmlns", "http://www.w3.org/2000/svg"],
|
|
144
|
+
["viewBox", `0 0 ${width} ${height}`],
|
|
145
|
+
["width", "100%"],
|
|
146
|
+
["role", "img"]
|
|
147
|
+
], el("rect", [
|
|
148
|
+
["x", 0],
|
|
149
|
+
["y", 0],
|
|
150
|
+
["width", width],
|
|
151
|
+
["height", height],
|
|
152
|
+
["fill", bg]
|
|
153
|
+
]) + body);
|
|
154
|
+
}
|
|
155
|
+
function line$1(x1, y1, x2, y2, stroke, w = 1, opacity) {
|
|
156
|
+
return el("line", [
|
|
157
|
+
["x1", r2(x1)],
|
|
158
|
+
["y1", r2(y1)],
|
|
159
|
+
["x2", r2(x2)],
|
|
160
|
+
["y2", r2(y2)],
|
|
161
|
+
["stroke", stroke],
|
|
162
|
+
["stroke-width", w],
|
|
163
|
+
["stroke-opacity", opacityAttr(opacity)]
|
|
164
|
+
]);
|
|
184
165
|
}
|
|
185
166
|
function circle(cx, cy, r, fill, opacity) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
167
|
+
return el("circle", [
|
|
168
|
+
["cx", r2(cx)],
|
|
169
|
+
["cy", r2(cy)],
|
|
170
|
+
["r", r],
|
|
171
|
+
["fill", fill],
|
|
172
|
+
["fill-opacity", opacityAttr(opacity)]
|
|
173
|
+
]);
|
|
193
174
|
}
|
|
194
175
|
function rect(x, y, w, h, fill) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
176
|
+
return el("rect", [
|
|
177
|
+
["x", r2(x)],
|
|
178
|
+
["y", r2(y)],
|
|
179
|
+
["width", r2(w)],
|
|
180
|
+
["height", r2(h)],
|
|
181
|
+
["fill", fill]
|
|
182
|
+
]);
|
|
202
183
|
}
|
|
203
184
|
function polyline(pts, stroke, w = 2) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
185
|
+
return el("polyline", [
|
|
186
|
+
["points", points(pts)],
|
|
187
|
+
["fill", "none"],
|
|
188
|
+
["stroke", stroke],
|
|
189
|
+
["stroke-width", w]
|
|
190
|
+
]);
|
|
210
191
|
}
|
|
211
192
|
function polygon(pts, fill, stroke = "none") {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
193
|
+
return el("polygon", [
|
|
194
|
+
["points", points(pts)],
|
|
195
|
+
["fill", fill],
|
|
196
|
+
["stroke", stroke]
|
|
197
|
+
]);
|
|
217
198
|
}
|
|
218
199
|
function text(x, y, s, fill, anchor = "start", size = 12, transform) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
...pos,
|
|
227
|
-
["text-anchor", anchor],
|
|
228
|
-
["font-family", "system-ui,sans-serif"],
|
|
229
|
-
["font-size", size],
|
|
230
|
-
["fill", fill]
|
|
231
|
-
],
|
|
232
|
-
esc(s)
|
|
233
|
-
);
|
|
200
|
+
return el("text", [
|
|
201
|
+
...transform === void 0 ? [["x", r2(x)], ["y", r2(y)]] : [["transform", transform]],
|
|
202
|
+
["text-anchor", anchor],
|
|
203
|
+
["font-family", "system-ui,sans-serif"],
|
|
204
|
+
["font-size", size],
|
|
205
|
+
["fill", fill]
|
|
206
|
+
], esc(s));
|
|
234
207
|
}
|
|
235
208
|
function primSVG(p) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return polyline(p.pts, p.stroke, p.w);
|
|
245
|
-
case "polygon":
|
|
246
|
-
return polygon(p.pts, p.fill, p.stroke);
|
|
247
|
-
case "text":
|
|
248
|
-
return p.rotate !== void 0 ? text(
|
|
249
|
-
p.x,
|
|
250
|
-
p.y,
|
|
251
|
-
p.s,
|
|
252
|
-
p.fill,
|
|
253
|
-
p.anchor,
|
|
254
|
-
p.size,
|
|
255
|
-
`translate(${p.x},${p.y}) rotate(${p.rotate})`
|
|
256
|
-
) : text(p.x, p.y, p.s, p.fill, p.anchor, p.size);
|
|
257
|
-
}
|
|
209
|
+
switch (p.k) {
|
|
210
|
+
case "line": return line$1(p.x1, p.y1, p.x2, p.y2, p.stroke, p.w, p.opacity);
|
|
211
|
+
case "circle": return circle(p.cx, p.cy, p.r, p.fill, p.opacity);
|
|
212
|
+
case "rect": return rect(p.x, p.y, p.w, p.h, p.fill);
|
|
213
|
+
case "polyline": return polyline(p.pts, p.stroke, p.w);
|
|
214
|
+
case "polygon": return polygon(p.pts, p.fill, p.stroke);
|
|
215
|
+
case "text": return p.rotate !== void 0 ? text(p.x, p.y, p.s, p.fill, p.anchor, p.size, `translate(${p.x},${p.y}) rotate(${p.rotate})`) : text(p.x, p.y, p.s, p.fill, p.anchor, p.size);
|
|
216
|
+
}
|
|
258
217
|
}
|
|
259
218
|
function emitSVG(scene) {
|
|
260
|
-
|
|
219
|
+
return svgDoc(scene.width, scene.height, scene.prims.map(primSVG).join(""), scene.bg);
|
|
261
220
|
}
|
|
262
|
-
|
|
263
|
-
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/render-core.ts
|
|
264
223
|
function xy(layer) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
return pts;
|
|
224
|
+
const ys = coerce1dPositional(layer.y);
|
|
225
|
+
const xs = layer.x ? coerce1dPositional(layer.x) : ys.map((_, i) => i);
|
|
226
|
+
const n = Math.min(xs.length, ys.length);
|
|
227
|
+
const pts = [];
|
|
228
|
+
for (let i = 0; i < n; i++) if (Number.isFinite(xs[i]) && Number.isFinite(ys[i])) pts.push([xs[i], ys[i]]);
|
|
229
|
+
return pts;
|
|
273
230
|
}
|
|
274
231
|
function renderLine(layer, f, i) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
232
|
+
const color = layer.color ?? f.color(i);
|
|
233
|
+
const pts = xy(layer).map(([x, y]) => [f.px(x), f.py(y)]);
|
|
234
|
+
return [{
|
|
235
|
+
k: "polyline",
|
|
236
|
+
pts,
|
|
237
|
+
stroke: color,
|
|
238
|
+
w: 2
|
|
239
|
+
}, ...pts.map((p) => ({
|
|
240
|
+
k: "circle",
|
|
241
|
+
cx: p[0],
|
|
242
|
+
cy: p[1],
|
|
243
|
+
r: 2.5,
|
|
244
|
+
fill: color
|
|
245
|
+
}))];
|
|
281
246
|
}
|
|
282
247
|
function renderScatter(layer, f, i) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
248
|
+
const color = layer.color ?? f.color(i);
|
|
249
|
+
return xy(layer).map(([x, y]) => ({
|
|
250
|
+
k: "circle",
|
|
251
|
+
cx: f.px(x),
|
|
252
|
+
cy: f.py(y),
|
|
253
|
+
r: 3,
|
|
254
|
+
fill: color
|
|
255
|
+
}));
|
|
291
256
|
}
|
|
292
257
|
function renderBar(layer, f, i) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
258
|
+
const color = layer.color ?? f.color(i);
|
|
259
|
+
const pts = xy(layer);
|
|
260
|
+
const base = f.py(Math.max(0, f.ydom[0]));
|
|
261
|
+
const bw = pts.length > 1 ? Math.abs(f.px(pts[1][0]) - f.px(pts[0][0])) * .7 : 20;
|
|
262
|
+
return pts.map(([x, y]) => {
|
|
263
|
+
const yp = f.py(y);
|
|
264
|
+
return {
|
|
265
|
+
k: "rect",
|
|
266
|
+
x: f.px(x) - bw / 2,
|
|
267
|
+
y: Math.min(yp, base),
|
|
268
|
+
w: bw,
|
|
269
|
+
h: Math.abs(base - yp),
|
|
270
|
+
fill: color
|
|
271
|
+
};
|
|
272
|
+
});
|
|
308
273
|
}
|
|
309
274
|
function renderArea(layer, f, i) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
275
|
+
const color = layer.color ?? f.color(i);
|
|
276
|
+
const pts = xy(layer).map(([x, y]) => [f.px(x), f.py(y)]);
|
|
277
|
+
if (pts.length === 0) return [];
|
|
278
|
+
const base = f.py(Math.max(0, f.ydom[0]));
|
|
279
|
+
return [{
|
|
280
|
+
k: "polygon",
|
|
281
|
+
pts: [
|
|
282
|
+
[pts[0][0], base],
|
|
283
|
+
...pts,
|
|
284
|
+
[pts[pts.length - 1][0], base]
|
|
285
|
+
],
|
|
286
|
+
fill: color + "55",
|
|
287
|
+
stroke: "none"
|
|
288
|
+
}, {
|
|
289
|
+
k: "polyline",
|
|
290
|
+
pts,
|
|
291
|
+
stroke: color,
|
|
292
|
+
w: 2
|
|
293
|
+
}];
|
|
319
294
|
}
|
|
320
295
|
function renderStep(layer, f, i) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
296
|
+
const color = layer.color ?? f.color(i);
|
|
297
|
+
const pts = xy(layer).map(([x, y]) => [f.px(x), f.py(y)]);
|
|
298
|
+
const stepped = [];
|
|
299
|
+
for (let k = 0; k < pts.length; k++) {
|
|
300
|
+
stepped.push(pts[k]);
|
|
301
|
+
if (k < pts.length - 1) stepped.push([pts[k + 1][0], pts[k][1]]);
|
|
302
|
+
}
|
|
303
|
+
return [{
|
|
304
|
+
k: "polyline",
|
|
305
|
+
pts: stepped,
|
|
306
|
+
stroke: color,
|
|
307
|
+
w: 2
|
|
308
|
+
}];
|
|
329
309
|
}
|
|
330
310
|
function renderErrorbar(layer, f, i) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
311
|
+
const color = layer.color ?? f.color(i);
|
|
312
|
+
const ys = coerce1dPositional(layer.y);
|
|
313
|
+
const xs = layer.x ? coerce1dPositional(layer.x) : ys.map((_, k) => k);
|
|
314
|
+
const errs = coerce1dPositional(layer.yerr ?? []);
|
|
315
|
+
const n = Math.min(xs.length, ys.length);
|
|
316
|
+
const out = [];
|
|
317
|
+
for (let k = 0; k < n; k++) {
|
|
318
|
+
if (!Number.isFinite(xs[k]) || !Number.isFinite(ys[k])) continue;
|
|
319
|
+
const e = Number.isFinite(errs[k]) ? errs[k] : 0;
|
|
320
|
+
const cx = f.px(xs[k]);
|
|
321
|
+
const top = f.py(ys[k] + e);
|
|
322
|
+
const bot = f.py(ys[k] - e);
|
|
323
|
+
out.push({
|
|
324
|
+
k: "line",
|
|
325
|
+
x1: cx,
|
|
326
|
+
y1: top,
|
|
327
|
+
x2: cx,
|
|
328
|
+
y2: bot,
|
|
329
|
+
stroke: color,
|
|
330
|
+
w: 1.5
|
|
331
|
+
}, {
|
|
332
|
+
k: "line",
|
|
333
|
+
x1: cx - 3,
|
|
334
|
+
y1: top,
|
|
335
|
+
x2: cx + 3,
|
|
336
|
+
y2: top,
|
|
337
|
+
stroke: color,
|
|
338
|
+
w: 1.5
|
|
339
|
+
}, {
|
|
340
|
+
k: "line",
|
|
341
|
+
x1: cx - 3,
|
|
342
|
+
y1: bot,
|
|
343
|
+
x2: cx + 3,
|
|
344
|
+
y2: bot,
|
|
345
|
+
stroke: color,
|
|
346
|
+
w: 1.5
|
|
347
|
+
}, {
|
|
348
|
+
k: "circle",
|
|
349
|
+
cx,
|
|
350
|
+
cy: f.py(ys[k]),
|
|
351
|
+
r: 3,
|
|
352
|
+
fill: color
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
351
356
|
}
|
|
352
357
|
function renderQuiver(layer, f, i) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
358
|
+
const color = layer.color ?? f.color(i);
|
|
359
|
+
const xs = layer.x ? coerce1dPositional(layer.x) : [];
|
|
360
|
+
const ys = coerce1dPositional(layer.y);
|
|
361
|
+
const us = coerce1dPositional(layer.u ?? []);
|
|
362
|
+
const vs = coerce1dPositional(layer.v ?? []);
|
|
363
|
+
const n = Math.min(xs.length, ys.length, us.length, vs.length);
|
|
364
|
+
const out = [];
|
|
365
|
+
for (let k = 0; k < n; k++) {
|
|
366
|
+
if (![
|
|
367
|
+
xs[k],
|
|
368
|
+
ys[k],
|
|
369
|
+
us[k],
|
|
370
|
+
vs[k]
|
|
371
|
+
].every(Number.isFinite)) continue;
|
|
372
|
+
const x0 = f.px(xs[k]);
|
|
373
|
+
const y0 = f.py(ys[k]);
|
|
374
|
+
const x1 = f.px(xs[k] + us[k]);
|
|
375
|
+
const y1 = f.py(ys[k] + vs[k]);
|
|
376
|
+
const ang = Math.atan2(y1 - y0, x1 - x0);
|
|
377
|
+
const ah = 5;
|
|
378
|
+
out.push({
|
|
379
|
+
k: "line",
|
|
380
|
+
x1: x0,
|
|
381
|
+
y1: y0,
|
|
382
|
+
x2: x1,
|
|
383
|
+
y2: y1,
|
|
384
|
+
stroke: color,
|
|
385
|
+
w: 1.5
|
|
386
|
+
}, {
|
|
387
|
+
k: "line",
|
|
388
|
+
x1,
|
|
389
|
+
y1,
|
|
390
|
+
x2: x1 - ah * Math.cos(ang - .4),
|
|
391
|
+
y2: y1 - ah * Math.sin(ang - .4),
|
|
392
|
+
stroke: color,
|
|
393
|
+
w: 1.5
|
|
394
|
+
}, {
|
|
395
|
+
k: "line",
|
|
396
|
+
x1,
|
|
397
|
+
y1,
|
|
398
|
+
x2: x1 - ah * Math.cos(ang + .4),
|
|
399
|
+
y2: y1 - ah * Math.sin(ang + .4),
|
|
400
|
+
stroke: color,
|
|
401
|
+
w: 1.5
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
391
405
|
}
|
|
392
406
|
function renderLayer(layer, f, i) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
case "quiver":
|
|
407
|
-
return renderQuiver(layer, f, i);
|
|
408
|
-
default:
|
|
409
|
-
return renderLine(layer, f, i);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// src/tikz.ts
|
|
407
|
+
switch (layer.type) {
|
|
408
|
+
case "line": return renderLine(layer, f, i);
|
|
409
|
+
case "scatter": return renderScatter(layer, f, i);
|
|
410
|
+
case "bar": return renderBar(layer, f, i);
|
|
411
|
+
case "area": return renderArea(layer, f, i);
|
|
412
|
+
case "step": return renderStep(layer, f, i);
|
|
413
|
+
case "errorbar": return renderErrorbar(layer, f, i);
|
|
414
|
+
case "quiver": return renderQuiver(layer, f, i);
|
|
415
|
+
default: return renderLine(layer, f, i);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/tikz.ts
|
|
414
420
|
function texEsc(s) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
var TIKZ_COLOR_NAME = /^[A-Za-z][A-Za-z0-9!.]*$/;
|
|
421
|
+
return s.replace(/[\\&%$#_{}~^]/g, (c) => {
|
|
422
|
+
switch (c) {
|
|
423
|
+
case "\\": return "\\textbackslash{}";
|
|
424
|
+
case "~": return "\\textasciitilde{}";
|
|
425
|
+
case "^": return "\\textasciicircum{}";
|
|
426
|
+
default: return `\\${c}`;
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
const TIKZ_COLOR_NAME = /^[A-Za-z][A-Za-z0-9!.]*$/;
|
|
429
431
|
function tikzColor(c) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
432
|
+
if (c === "none") return {
|
|
433
|
+
color: "none",
|
|
434
|
+
alpha: 1
|
|
435
|
+
};
|
|
436
|
+
if (c.startsWith("#")) {
|
|
437
|
+
const h = c.slice(1);
|
|
438
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
439
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
440
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
441
|
+
const alpha = h.length >= 8 ? Math.round(parseInt(h.slice(6, 8), 16) / 255 * 100) / 100 : 1;
|
|
442
|
+
return {
|
|
443
|
+
color: `{rgb,255:red,${r};green,${g};blue,${b}}`,
|
|
444
|
+
alpha
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
color: TIKZ_COLOR_NAME.test(c) ? c : "black",
|
|
449
|
+
alpha: 1
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const round = (n) => Math.round(n * 100) / 100;
|
|
453
|
+
const ANCHOR = {
|
|
454
|
+
start: "base west",
|
|
455
|
+
middle: "base",
|
|
456
|
+
end: "base east"
|
|
446
457
|
};
|
|
447
458
|
function primTikZ(p, X, Y, s) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
459
|
+
const pt = (x, y) => `(${X(x)},${Y(y)})`;
|
|
460
|
+
const path = (pts) => pts.map(([x, y]) => pt(x, y)).join(" -- ");
|
|
461
|
+
switch (p.k) {
|
|
462
|
+
case "line": {
|
|
463
|
+
const { color } = tikzColor(p.stroke);
|
|
464
|
+
const op = p.opacity !== void 0 && p.opacity < 1 ? `,draw opacity=${round(p.opacity)}` : "";
|
|
465
|
+
return `\\draw[color=${color},line width=${round(p.w * s)}pt${op}] ${pt(p.x1, p.y1)} -- ${pt(p.x2, p.y2)};`;
|
|
466
|
+
}
|
|
467
|
+
case "polyline": {
|
|
468
|
+
const { color } = tikzColor(p.stroke);
|
|
469
|
+
return `\\draw[color=${color},line width=${round(p.w * s)}pt] ${path(p.pts)};`;
|
|
470
|
+
}
|
|
471
|
+
case "polygon": {
|
|
472
|
+
const f = tikzColor(p.fill);
|
|
473
|
+
const st = tikzColor(p.stroke);
|
|
474
|
+
const drawOpt = p.stroke === "none" ? "draw=none" : `draw=${st.color}`;
|
|
475
|
+
const op = f.alpha < 1 ? `,fill opacity=${f.alpha}` : "";
|
|
476
|
+
return `\\filldraw[fill=${f.color},${drawOpt}${op}] ${path(p.pts)} -- cycle;`;
|
|
477
|
+
}
|
|
478
|
+
case "rect": {
|
|
479
|
+
const f = tikzColor(p.fill);
|
|
480
|
+
return `\\fill${f.alpha < 1 ? `[fill opacity=${f.alpha}]` : ""}[color=${f.color}] ${pt(p.x, p.y)} rectangle ${pt(p.x + p.w, p.y + p.h)};`;
|
|
481
|
+
}
|
|
482
|
+
case "circle": {
|
|
483
|
+
const f = tikzColor(p.fill);
|
|
484
|
+
const a = (p.opacity ?? 1) * f.alpha;
|
|
485
|
+
const op = a < 1 ? `,fill opacity=${round(a)}` : "";
|
|
486
|
+
return `\\filldraw[fill=${f.color},draw=none${op}] ${pt(p.cx, p.cy)} circle (${round(p.r * s)}pt);`;
|
|
487
|
+
}
|
|
488
|
+
case "text": {
|
|
489
|
+
const { color } = tikzColor(p.fill);
|
|
490
|
+
const rot = p.rotate !== void 0 ? `,rotate=${p.rotate}` : "";
|
|
491
|
+
return `\\node[anchor=${ANCHOR[p.anchor]},text=${color},font=\\fontsize{${round(p.size * s)}}{${round(p.size * s * 1.2)}}\\selectfont${rot}] at ${pt(p.x, p.y)} {${texEsc(p.s)}};`;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
484
494
|
}
|
|
485
495
|
function emitTikZ(scene, opts = {}) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
${body.join("\n")}
|
|
496
|
-
\\end{tikzpicture}`;
|
|
497
|
-
if (opts.tikz?.standalone === false) return pic;
|
|
498
|
-
return `\\documentclass[tikz,border=2pt]{standalone}
|
|
499
|
-
\\begin{document}
|
|
500
|
-
${pic}
|
|
501
|
-
\\end{document}`;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// src/emit.ts
|
|
496
|
+
const s = opts.tikz?.scale ?? .75;
|
|
497
|
+
const X = (x) => round(x * s);
|
|
498
|
+
const Y = (y) => round((scene.height - y) * s);
|
|
499
|
+
const pic = `\\begin{tikzpicture}[x=1pt,y=1pt]\n${[`\\fill[color=${tikzColor(scene.bg).color}] (${X(0)},${Y(scene.height)}) rectangle (${X(scene.width)},${Y(0)});`, ...scene.prims.map((p) => primTikZ(p, X, Y, s))].join("\n")}\n\\end{tikzpicture}`;
|
|
500
|
+
if (opts.tikz?.standalone === false) return pic;
|
|
501
|
+
return `\\documentclass[tikz,border=2pt]{standalone}\n\\begin{document}\n${pic}\n\\end{document}`;
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region src/emit.ts
|
|
505
505
|
function emit(scene, opts = {}) {
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
506
|
+
return opts.format === "tikz" ? emitTikZ(scene, opts) : emitSVG(scene);
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/frame.ts
|
|
510
|
+
const MARGIN$4 = {
|
|
511
|
+
top: 34,
|
|
512
|
+
right: 20,
|
|
513
|
+
bottom: 46,
|
|
514
|
+
left: 64
|
|
515
|
+
};
|
|
511
516
|
function combinedExtent(layers, pick) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
517
|
+
const all = [];
|
|
518
|
+
for (const l of layers) all.push(...pick(l));
|
|
519
|
+
return extent(all);
|
|
515
520
|
}
|
|
516
521
|
function noDataScene(width, height, theme) {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
]
|
|
532
|
-
};
|
|
522
|
+
return {
|
|
523
|
+
width,
|
|
524
|
+
height,
|
|
525
|
+
bg: theme.bg,
|
|
526
|
+
prims: [{
|
|
527
|
+
k: "text",
|
|
528
|
+
x: width / 2,
|
|
529
|
+
y: height / 2,
|
|
530
|
+
s: "no data",
|
|
531
|
+
fill: theme.muted,
|
|
532
|
+
anchor: "middle",
|
|
533
|
+
size: 13
|
|
534
|
+
}]
|
|
535
|
+
};
|
|
533
536
|
}
|
|
534
537
|
function draw2D(layers, opts = {}) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
538
|
+
const width = opts.width ?? 520;
|
|
539
|
+
const height = opts.height ?? 320;
|
|
540
|
+
const theme = THEMES[opts.theme ?? "light"];
|
|
541
|
+
try {
|
|
542
|
+
if (layers.flatMap((l) => coerce1d(l.y)).length === 0) return emit(noDataScene(width, height, theme), opts);
|
|
543
|
+
const xdom = combinedExtent(layers, (l) => l.x ? coerce1d(l.x) : coerce1d(l.y).map((_, i) => i));
|
|
544
|
+
const ydom = combinedExtent(layers, (l) => coerce1d(l.y));
|
|
545
|
+
if (!Number.isFinite(xdom[0]) || !Number.isFinite(ydom[0])) return emit(noDataScene(width, height, theme), opts);
|
|
546
|
+
const innerX = [MARGIN$4.left, width - MARGIN$4.right];
|
|
547
|
+
const innerY = [height - MARGIN$4.bottom, MARGIN$4.top];
|
|
548
|
+
const xScaleFn = opts.x?.scale === "log" ? logScale : linearScale;
|
|
549
|
+
const yScaleFn = opts.y?.scale === "log" ? logScale : linearScale;
|
|
550
|
+
const px = xScaleFn(xdom, innerX);
|
|
551
|
+
const py = yScaleFn(ydom, innerY);
|
|
552
|
+
const frame = {
|
|
553
|
+
px,
|
|
554
|
+
py,
|
|
555
|
+
xdom,
|
|
556
|
+
ydom,
|
|
557
|
+
theme,
|
|
558
|
+
width,
|
|
559
|
+
height,
|
|
560
|
+
color: (i) => theme.series[i % theme.series.length]
|
|
561
|
+
};
|
|
562
|
+
const prims = [];
|
|
563
|
+
const xticks = niceTicks(xdom[0], xdom[1]).filter((t) => t >= xdom[0] && t <= xdom[1]);
|
|
564
|
+
const yticks = niceTicks(ydom[0], ydom[1]).filter((t) => t >= ydom[0] && t <= ydom[1]);
|
|
565
|
+
for (const t of xticks) prims.push({
|
|
566
|
+
k: "line",
|
|
567
|
+
x1: px(t),
|
|
568
|
+
y1: innerY[0],
|
|
569
|
+
x2: px(t),
|
|
570
|
+
y2: innerY[1],
|
|
571
|
+
stroke: theme.grid,
|
|
572
|
+
w: 1
|
|
573
|
+
});
|
|
574
|
+
for (const t of yticks) prims.push({
|
|
575
|
+
k: "line",
|
|
576
|
+
x1: innerX[0],
|
|
577
|
+
y1: py(t),
|
|
578
|
+
x2: innerX[1],
|
|
579
|
+
y2: py(t),
|
|
580
|
+
stroke: theme.grid,
|
|
581
|
+
w: 1
|
|
582
|
+
});
|
|
583
|
+
prims.push({
|
|
584
|
+
k: "line",
|
|
585
|
+
x1: MARGIN$4.left,
|
|
586
|
+
y1: height - MARGIN$4.bottom,
|
|
587
|
+
x2: width - MARGIN$4.right,
|
|
588
|
+
y2: height - MARGIN$4.bottom,
|
|
589
|
+
stroke: theme.axis,
|
|
590
|
+
w: 1
|
|
591
|
+
});
|
|
592
|
+
prims.push({
|
|
593
|
+
k: "line",
|
|
594
|
+
x1: MARGIN$4.left,
|
|
595
|
+
y1: MARGIN$4.top,
|
|
596
|
+
x2: MARGIN$4.left,
|
|
597
|
+
y2: height - MARGIN$4.bottom,
|
|
598
|
+
stroke: theme.axis,
|
|
599
|
+
w: 1
|
|
600
|
+
});
|
|
601
|
+
for (const t of xticks) prims.push({
|
|
602
|
+
k: "text",
|
|
603
|
+
x: px(t),
|
|
604
|
+
y: height - MARGIN$4.bottom + 16,
|
|
605
|
+
s: fmt(t),
|
|
606
|
+
fill: theme.muted,
|
|
607
|
+
anchor: "middle",
|
|
608
|
+
size: 11
|
|
609
|
+
});
|
|
610
|
+
for (const t of yticks) prims.push({
|
|
611
|
+
k: "text",
|
|
612
|
+
x: MARGIN$4.left - 8,
|
|
613
|
+
y: py(t) + 4,
|
|
614
|
+
s: fmt(t),
|
|
615
|
+
fill: theme.muted,
|
|
616
|
+
anchor: "end",
|
|
617
|
+
size: 11
|
|
618
|
+
});
|
|
619
|
+
if (opts.title) prims.push({
|
|
620
|
+
k: "text",
|
|
621
|
+
x: width / 2,
|
|
622
|
+
y: 20,
|
|
623
|
+
s: opts.title,
|
|
624
|
+
fill: theme.fg,
|
|
625
|
+
anchor: "middle",
|
|
626
|
+
size: 14
|
|
627
|
+
});
|
|
628
|
+
if (opts.xLabel) prims.push({
|
|
629
|
+
k: "text",
|
|
630
|
+
x: MARGIN$4.left + (width - MARGIN$4.left - MARGIN$4.right) / 2,
|
|
631
|
+
y: height - 8,
|
|
632
|
+
s: opts.xLabel,
|
|
633
|
+
fill: theme.fg,
|
|
634
|
+
anchor: "middle",
|
|
635
|
+
size: 12
|
|
636
|
+
});
|
|
637
|
+
if (opts.yLabel) prims.push({
|
|
638
|
+
k: "text",
|
|
639
|
+
x: 16,
|
|
640
|
+
y: MARGIN$4.top + (height - MARGIN$4.top - MARGIN$4.bottom) / 2,
|
|
641
|
+
s: opts.yLabel,
|
|
642
|
+
fill: theme.fg,
|
|
643
|
+
anchor: "middle",
|
|
644
|
+
size: 12,
|
|
645
|
+
rotate: -90
|
|
646
|
+
});
|
|
647
|
+
layers.forEach((l, i) => prims.push(...renderLayer(l, frame, i)));
|
|
648
|
+
if (opts.legend && layers.some((l) => l.label)) layers.forEach((l, i) => {
|
|
649
|
+
if (!l.label) return;
|
|
650
|
+
const ly = MARGIN$4.top + 4 + i * 16;
|
|
651
|
+
const c = l.color ?? frame.color(i);
|
|
652
|
+
prims.push({
|
|
653
|
+
k: "rect",
|
|
654
|
+
x: width - MARGIN$4.right - 120,
|
|
655
|
+
y: ly - 8,
|
|
656
|
+
w: 10,
|
|
657
|
+
h: 10,
|
|
658
|
+
fill: c
|
|
659
|
+
});
|
|
660
|
+
prims.push({
|
|
661
|
+
k: "text",
|
|
662
|
+
x: width - MARGIN$4.right - 106,
|
|
663
|
+
y: ly + 1,
|
|
664
|
+
s: l.label,
|
|
665
|
+
fill: theme.fg,
|
|
666
|
+
anchor: "start",
|
|
667
|
+
size: 11
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
return emit({
|
|
671
|
+
width,
|
|
672
|
+
height,
|
|
673
|
+
bg: theme.bg,
|
|
674
|
+
prims
|
|
675
|
+
}, opts);
|
|
676
|
+
} catch {
|
|
677
|
+
return emit(noDataScene(width, height, theme), opts);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
//#endregion
|
|
681
|
+
//#region src/marks2d.ts
|
|
682
|
+
function line(x, y, opts) {
|
|
683
|
+
return draw2D([{
|
|
684
|
+
type: "line",
|
|
685
|
+
x,
|
|
686
|
+
y
|
|
687
|
+
}], opts);
|
|
683
688
|
}
|
|
684
689
|
function scatter(x, y, opts) {
|
|
685
|
-
|
|
690
|
+
return draw2D([{
|
|
691
|
+
type: "scatter",
|
|
692
|
+
x,
|
|
693
|
+
y
|
|
694
|
+
}], opts);
|
|
686
695
|
}
|
|
687
696
|
function bar(x, y, opts) {
|
|
688
|
-
|
|
697
|
+
return draw2D([{
|
|
698
|
+
type: "bar",
|
|
699
|
+
x,
|
|
700
|
+
y
|
|
701
|
+
}], opts);
|
|
689
702
|
}
|
|
690
703
|
function area(x, y, opts) {
|
|
691
|
-
|
|
704
|
+
return draw2D([{
|
|
705
|
+
type: "area",
|
|
706
|
+
x,
|
|
707
|
+
y
|
|
708
|
+
}], opts);
|
|
692
709
|
}
|
|
693
710
|
function step(x, y, opts) {
|
|
694
|
-
|
|
711
|
+
return draw2D([{
|
|
712
|
+
type: "step",
|
|
713
|
+
x,
|
|
714
|
+
y
|
|
715
|
+
}], opts);
|
|
695
716
|
}
|
|
696
717
|
function errorbar(x, y, yerr, opts) {
|
|
697
|
-
|
|
718
|
+
return draw2D([{
|
|
719
|
+
type: "errorbar",
|
|
720
|
+
x,
|
|
721
|
+
y,
|
|
722
|
+
yerr
|
|
723
|
+
}], opts);
|
|
698
724
|
}
|
|
699
725
|
function quiver(x, y, u, v, opts) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
726
|
+
return draw2D([{
|
|
727
|
+
type: "quiver",
|
|
728
|
+
x,
|
|
729
|
+
y,
|
|
730
|
+
u,
|
|
731
|
+
v
|
|
732
|
+
}], opts);
|
|
733
|
+
}
|
|
734
|
+
//#endregion
|
|
735
|
+
//#region src/overlay.ts
|
|
704
736
|
function overlay(layers, opts) {
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
737
|
+
return draw2D(layers, opts);
|
|
738
|
+
}
|
|
739
|
+
//#endregion
|
|
740
|
+
//#region src/palette.ts
|
|
741
|
+
const STOPS = [
|
|
742
|
+
[
|
|
743
|
+
68,
|
|
744
|
+
1,
|
|
745
|
+
84
|
|
746
|
+
],
|
|
747
|
+
[
|
|
748
|
+
72,
|
|
749
|
+
40,
|
|
750
|
+
120
|
|
751
|
+
],
|
|
752
|
+
[
|
|
753
|
+
62,
|
|
754
|
+
74,
|
|
755
|
+
137
|
|
756
|
+
],
|
|
757
|
+
[
|
|
758
|
+
49,
|
|
759
|
+
104,
|
|
760
|
+
142
|
|
761
|
+
],
|
|
762
|
+
[
|
|
763
|
+
38,
|
|
764
|
+
130,
|
|
765
|
+
142
|
|
766
|
+
],
|
|
767
|
+
[
|
|
768
|
+
31,
|
|
769
|
+
158,
|
|
770
|
+
137
|
|
771
|
+
],
|
|
772
|
+
[
|
|
773
|
+
53,
|
|
774
|
+
183,
|
|
775
|
+
121
|
|
776
|
+
],
|
|
777
|
+
[
|
|
778
|
+
110,
|
|
779
|
+
206,
|
|
780
|
+
88
|
|
781
|
+
],
|
|
782
|
+
[
|
|
783
|
+
253,
|
|
784
|
+
231,
|
|
785
|
+
37
|
|
786
|
+
]
|
|
719
787
|
];
|
|
720
|
-
|
|
788
|
+
const hex = (n) => Math.round(n).toString(16).padStart(2, "0");
|
|
721
789
|
function viridis(t) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
790
|
+
const s = Math.max(0, Math.min(1, t)) * (STOPS.length - 1);
|
|
791
|
+
const i = Math.min(STOPS.length - 2, Math.floor(s));
|
|
792
|
+
const f = s - i;
|
|
793
|
+
const a = STOPS[i];
|
|
794
|
+
const b = STOPS[i + 1];
|
|
795
|
+
const r = a[0] + (b[0] - a[0]) * f;
|
|
796
|
+
const g = a[1] + (b[1] - a[1]) * f;
|
|
797
|
+
const bl = a[2] + (b[2] - a[2]) * f;
|
|
798
|
+
return `#${hex(r)}${hex(g)}${hex(bl)}`;
|
|
799
|
+
}
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region src/contour.ts
|
|
802
|
+
const MARGIN$3 = {
|
|
803
|
+
top: 34,
|
|
804
|
+
right: 22,
|
|
805
|
+
bottom: 30,
|
|
806
|
+
left: 40
|
|
807
|
+
};
|
|
736
808
|
function contour(z, opts = {}) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
|
|
809
|
+
const width = opts.width ?? 520;
|
|
810
|
+
const height = opts.height ?? 320;
|
|
811
|
+
const theme = THEMES[opts.theme ?? "light"];
|
|
812
|
+
const grid = coerce2d(z);
|
|
813
|
+
const rows = grid.length;
|
|
814
|
+
const cols = rows ? grid[0].length : 0;
|
|
815
|
+
if (rows < 2 || cols < 2) return emit({
|
|
816
|
+
width,
|
|
817
|
+
height,
|
|
818
|
+
bg: theme.bg,
|
|
819
|
+
prims: [{
|
|
820
|
+
k: "text",
|
|
821
|
+
x: width / 2,
|
|
822
|
+
y: height / 2,
|
|
823
|
+
s: "no data",
|
|
824
|
+
fill: theme.muted,
|
|
825
|
+
anchor: "middle",
|
|
826
|
+
size: 13
|
|
827
|
+
}]
|
|
828
|
+
}, opts);
|
|
829
|
+
let lo = Infinity;
|
|
830
|
+
let hi = -Infinity;
|
|
831
|
+
for (const row of grid) for (const v of row) if (Number.isFinite(v)) {
|
|
832
|
+
if (v < lo) lo = v;
|
|
833
|
+
if (v > hi) hi = v;
|
|
834
|
+
}
|
|
835
|
+
const nLevels = opts.levels ?? 10;
|
|
836
|
+
const px = linearScale([0, cols - 1], [MARGIN$3.left, width - MARGIN$3.right]);
|
|
837
|
+
const py = linearScale([0, rows - 1], [height - MARGIN$3.bottom, MARGIN$3.top]);
|
|
838
|
+
const prims = [];
|
|
839
|
+
const span = hi > lo ? hi - lo : 1;
|
|
840
|
+
for (let li = 1; li <= nLevels; li++) {
|
|
841
|
+
const level = lo + span * li / (nLevels + 1);
|
|
842
|
+
const color = viridis(li / (nLevels + 1));
|
|
843
|
+
const interp = (va, vb) => (level - va) / (vb - va);
|
|
844
|
+
for (let r = 0; r < rows - 1; r++) for (let c = 0; c < cols - 1; c++) {
|
|
845
|
+
const tl = grid[r][c];
|
|
846
|
+
const tr = grid[r][c + 1];
|
|
847
|
+
const br = grid[r + 1][c + 1];
|
|
848
|
+
const bl = grid[r + 1][c];
|
|
849
|
+
if (![
|
|
850
|
+
tl,
|
|
851
|
+
tr,
|
|
852
|
+
br,
|
|
853
|
+
bl
|
|
854
|
+
].every(Number.isFinite)) continue;
|
|
855
|
+
const cross = [];
|
|
856
|
+
if (tl < level !== tr < level) cross.push([c + interp(tl, tr), r]);
|
|
857
|
+
if (tr < level !== br < level) cross.push([c + 1, r + interp(tr, br)]);
|
|
858
|
+
if (bl < level !== br < level) cross.push([c + interp(bl, br), r + 1]);
|
|
859
|
+
if (tl < level !== bl < level) cross.push([c, r + interp(tl, bl)]);
|
|
860
|
+
for (let k = 0; k + 1 < cross.length; k += 2) {
|
|
861
|
+
const [ax, ay] = cross[k];
|
|
862
|
+
const [bx, by] = cross[k + 1];
|
|
863
|
+
prims.push({
|
|
864
|
+
k: "line",
|
|
865
|
+
x1: px(ax),
|
|
866
|
+
y1: py(ay),
|
|
867
|
+
x2: px(bx),
|
|
868
|
+
y2: py(by),
|
|
869
|
+
stroke: color,
|
|
870
|
+
w: 1.5
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (opts.title) prims.push({
|
|
876
|
+
k: "text",
|
|
877
|
+
x: width / 2,
|
|
878
|
+
y: 20,
|
|
879
|
+
s: opts.title,
|
|
880
|
+
fill: theme.fg,
|
|
881
|
+
anchor: "middle",
|
|
882
|
+
size: 14
|
|
883
|
+
});
|
|
884
|
+
return emit({
|
|
885
|
+
width,
|
|
886
|
+
height,
|
|
887
|
+
bg: theme.bg,
|
|
888
|
+
prims
|
|
889
|
+
}, opts);
|
|
890
|
+
}
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/three/project.ts
|
|
821
893
|
function project(p, cam) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
894
|
+
const az = cam.azim * Math.PI / 180;
|
|
895
|
+
const el = cam.elev * Math.PI / 180;
|
|
896
|
+
const [x, y, z] = p;
|
|
897
|
+
const xa = x * Math.cos(az) - y * Math.sin(az);
|
|
898
|
+
const ya = x * Math.sin(az) + y * Math.cos(az);
|
|
899
|
+
return [
|
|
900
|
+
xa,
|
|
901
|
+
z * Math.cos(el) - ya * Math.sin(el),
|
|
902
|
+
z * Math.sin(el) + ya * Math.cos(el)
|
|
903
|
+
];
|
|
904
|
+
}
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/three/surface.ts
|
|
907
|
+
const MARGIN$2 = 40;
|
|
836
908
|
function compareDepth(a, b) {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
909
|
+
const d = b.depth - a.depth;
|
|
910
|
+
if (Math.abs(d) > 1e-9) return d;
|
|
911
|
+
return a.index - b.index;
|
|
840
912
|
}
|
|
841
913
|
function surface(z, opts = {}) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
914
|
+
const width = opts.width ?? 520;
|
|
915
|
+
const height = opts.height ?? 320;
|
|
916
|
+
const theme = THEMES[opts.theme ?? "light"];
|
|
917
|
+
const grid = coerce2d(z);
|
|
918
|
+
const rows = grid.length;
|
|
919
|
+
const cols = rows ? grid[0].length : 0;
|
|
920
|
+
if (rows < 2 || cols < 2) return emit({
|
|
921
|
+
width,
|
|
922
|
+
height,
|
|
923
|
+
bg: theme.bg,
|
|
924
|
+
prims: [{
|
|
925
|
+
k: "text",
|
|
926
|
+
x: width / 2,
|
|
927
|
+
y: height / 2,
|
|
928
|
+
s: "no data",
|
|
929
|
+
fill: theme.muted,
|
|
930
|
+
anchor: "middle",
|
|
931
|
+
size: 13
|
|
932
|
+
}]
|
|
933
|
+
}, opts);
|
|
934
|
+
const cam = {
|
|
935
|
+
azim: opts.azim ?? 45,
|
|
936
|
+
elev: opts.elev ?? 25
|
|
937
|
+
};
|
|
938
|
+
let zlo = Infinity;
|
|
939
|
+
let zhi = -Infinity;
|
|
940
|
+
for (const row of grid) for (const v of row) if (Number.isFinite(v)) {
|
|
941
|
+
if (v < zlo) zlo = v;
|
|
942
|
+
if (v > zhi) zhi = v;
|
|
943
|
+
}
|
|
944
|
+
const zspan = zhi > zlo ? zhi - zlo : 1;
|
|
945
|
+
const proj = [];
|
|
946
|
+
let sxlo = Infinity, sxhi = -Infinity, sylo = Infinity, syhi = -Infinity;
|
|
947
|
+
for (let r = 0; r < rows; r++) {
|
|
948
|
+
const prow = [];
|
|
949
|
+
for (let c = 0; c < cols; c++) {
|
|
950
|
+
const p = project([
|
|
951
|
+
c / (cols - 1) * 2 - 1,
|
|
952
|
+
r / (rows - 1) * 2 - 1,
|
|
953
|
+
(grid[r][c] - zlo) / zspan * 2 - 1
|
|
954
|
+
], cam);
|
|
955
|
+
prow.push(p);
|
|
956
|
+
if (p[0] < sxlo) sxlo = p[0];
|
|
957
|
+
if (p[0] > sxhi) sxhi = p[0];
|
|
958
|
+
if (p[1] < sylo) sylo = p[1];
|
|
959
|
+
if (p[1] > syhi) syhi = p[1];
|
|
960
|
+
}
|
|
961
|
+
proj.push(prow);
|
|
962
|
+
}
|
|
963
|
+
const sxSpan = sxhi > sxlo ? sxhi - sxlo : 1;
|
|
964
|
+
const sySpan = syhi > sylo ? syhi - sylo : 1;
|
|
965
|
+
const toScreen = (p) => [MARGIN$2 + (p[0] - sxlo) / sxSpan * (width - 80), height - MARGIN$2 - (p[1] - sylo) / sySpan * (height - 80)];
|
|
966
|
+
const kind = opts.kind ?? "filled";
|
|
967
|
+
const prims = [];
|
|
968
|
+
if (kind === "wireframe") {
|
|
969
|
+
for (let r = 0; r < rows; r++) prims.push({
|
|
970
|
+
k: "polyline",
|
|
971
|
+
pts: proj[r].map(toScreen),
|
|
972
|
+
stroke: theme.series[0],
|
|
973
|
+
w: 1
|
|
974
|
+
});
|
|
975
|
+
for (let c = 0; c < cols; c++) prims.push({
|
|
976
|
+
k: "polyline",
|
|
977
|
+
pts: proj.map((row) => toScreen(row[c])),
|
|
978
|
+
stroke: theme.series[0],
|
|
979
|
+
w: 1
|
|
980
|
+
});
|
|
981
|
+
} else {
|
|
982
|
+
const quads = [];
|
|
983
|
+
for (let r = 0; r < rows - 1; r++) for (let c = 0; c < cols - 1; c++) {
|
|
984
|
+
const corners = [
|
|
985
|
+
proj[r][c],
|
|
986
|
+
proj[r][c + 1],
|
|
987
|
+
proj[r + 1][c + 1],
|
|
988
|
+
proj[r + 1][c]
|
|
989
|
+
];
|
|
990
|
+
const depth = (corners[0][2] + corners[1][2] + corners[2][2] + corners[3][2]) / 4;
|
|
991
|
+
const meanZ = (grid[r][c] + grid[r][c + 1] + grid[r + 1][c + 1] + grid[r + 1][c]) / 4;
|
|
992
|
+
quads.push({
|
|
993
|
+
depth,
|
|
994
|
+
index: quads.length,
|
|
995
|
+
pts: corners.map(toScreen),
|
|
996
|
+
shade: (meanZ - zlo) / zspan
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
quads.sort(compareDepth);
|
|
1000
|
+
for (const q of quads) prims.push({
|
|
1001
|
+
k: "polygon",
|
|
1002
|
+
pts: q.pts,
|
|
1003
|
+
fill: viridis(q.shade),
|
|
1004
|
+
stroke: theme.grid
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
if (opts.title) prims.push({
|
|
1008
|
+
k: "text",
|
|
1009
|
+
x: width / 2,
|
|
1010
|
+
y: 20,
|
|
1011
|
+
s: opts.title,
|
|
1012
|
+
fill: theme.fg,
|
|
1013
|
+
anchor: "middle",
|
|
1014
|
+
size: 14
|
|
1015
|
+
});
|
|
1016
|
+
return emit({
|
|
1017
|
+
width,
|
|
1018
|
+
height,
|
|
1019
|
+
bg: theme.bg,
|
|
1020
|
+
prims
|
|
1021
|
+
}, opts);
|
|
1022
|
+
}
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/plot.ts
|
|
945
1025
|
function isLayerArray(v) {
|
|
946
|
-
|
|
1026
|
+
return Array.isArray(v) && v.length > 0 && typeof v[0] === "object" && v[0] !== null && "type" in v[0];
|
|
947
1027
|
}
|
|
948
1028
|
function freeVars(source) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1029
|
+
const names = /* @__PURE__ */ new Set();
|
|
1030
|
+
const funcs = /* @__PURE__ */ new Set();
|
|
1031
|
+
try {
|
|
1032
|
+
const node = parse(source);
|
|
1033
|
+
if (typeof node.traverse === "function") node.traverse((n) => {
|
|
1034
|
+
const nn = n;
|
|
1035
|
+
if (nn.type === "SymbolNode" && nn.name) names.add(nn.name);
|
|
1036
|
+
if (nn.type === "FunctionNode" && nn.name) funcs.add(nn.name);
|
|
1037
|
+
});
|
|
1038
|
+
} catch {}
|
|
1039
|
+
funcs.forEach((f) => names.delete(f));
|
|
1040
|
+
[
|
|
1041
|
+
"pi",
|
|
1042
|
+
"e",
|
|
1043
|
+
"tau"
|
|
1044
|
+
].forEach((c) => names.delete(c));
|
|
1045
|
+
return [...names];
|
|
965
1046
|
}
|
|
966
1047
|
function plotExpr(source, opts) {
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1048
|
+
const { kind, ...rest } = opts;
|
|
1049
|
+
const from = opts.from ?? -10;
|
|
1050
|
+
const to = opts.to ?? 10;
|
|
1051
|
+
const n = opts.samples ?? 200;
|
|
1052
|
+
const scope = opts.scope ?? {};
|
|
1053
|
+
const grid = range(from, to, (to - from) / (n - 1), true).toArray?.() ?? Array.from({ length: n }, (_, i) => from + (to - from) * i / (n - 1));
|
|
1054
|
+
const vars = freeVars(source);
|
|
1055
|
+
if (vars.length <= 1) {
|
|
1056
|
+
const v = vars[0] ?? "x";
|
|
1057
|
+
return line(grid, grid.map((val) => {
|
|
1058
|
+
try {
|
|
1059
|
+
return Number(evaluate(source, {
|
|
1060
|
+
...scope,
|
|
1061
|
+
[v]: val
|
|
1062
|
+
}));
|
|
1063
|
+
} catch {
|
|
1064
|
+
return NaN;
|
|
1065
|
+
}
|
|
1066
|
+
}), {
|
|
1067
|
+
xLabel: v,
|
|
1068
|
+
title: source,
|
|
1069
|
+
...rest
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
const [vx, vy] = vars;
|
|
1073
|
+
const g2 = grid.map((yv) => grid.map((xv) => {
|
|
1074
|
+
try {
|
|
1075
|
+
return Number(evaluate(source, {
|
|
1076
|
+
...scope,
|
|
1077
|
+
[vx]: xv,
|
|
1078
|
+
[vy]: yv
|
|
1079
|
+
}));
|
|
1080
|
+
} catch {
|
|
1081
|
+
return NaN;
|
|
1082
|
+
}
|
|
1083
|
+
}));
|
|
1084
|
+
return kind === "3d" || kind === "surface" ? surface(g2, {
|
|
1085
|
+
title: source,
|
|
1086
|
+
...rest
|
|
1087
|
+
}) : contour(g2, {
|
|
1088
|
+
title: source,
|
|
1089
|
+
...rest
|
|
1090
|
+
});
|
|
997
1091
|
}
|
|
998
1092
|
function plot(a, b, c) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1093
|
+
if (typeof a === "string") return plotExpr(a, b ?? {});
|
|
1094
|
+
if (isLayerArray(a)) return overlay(a, b ?? {});
|
|
1095
|
+
if (Array.isArray(b) || b instanceof Float64Array) return line(a, b, c);
|
|
1096
|
+
return line(void 0, a, b ?? {});
|
|
1003
1097
|
}
|
|
1004
1098
|
function toTikZ(a, b, c) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1099
|
+
if (typeof a === "string") return plot(a, {
|
|
1100
|
+
...b ?? {},
|
|
1101
|
+
format: "tikz"
|
|
1102
|
+
});
|
|
1103
|
+
if (Array.isArray(a) && a.length > 0 && typeof a[0] === "object" && a[0] !== null && "type" in a[0]) return plot(a, {
|
|
1104
|
+
...b ?? {},
|
|
1105
|
+
format: "tikz"
|
|
1106
|
+
});
|
|
1107
|
+
if (Array.isArray(b) || b instanceof Float64Array) return plot(a, b, {
|
|
1108
|
+
...c ?? {},
|
|
1109
|
+
format: "tikz"
|
|
1110
|
+
});
|
|
1111
|
+
return plot(a, {
|
|
1112
|
+
...b ?? {},
|
|
1113
|
+
format: "tikz"
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region src/histogram.ts
|
|
1015
1118
|
function histogram(data, opts = {}) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1119
|
+
const nums = coerce1d(data);
|
|
1120
|
+
if (nums.length === 0) return draw2D([], opts);
|
|
1121
|
+
const { counts, edges } = histogram$1(nums, opts.bins ?? 10);
|
|
1122
|
+
return draw2D([{
|
|
1123
|
+
type: "bar",
|
|
1124
|
+
x: counts.map((_, i) => (edges[i] + edges[i + 1]) / 2),
|
|
1125
|
+
y: counts,
|
|
1126
|
+
color: opts.palette?.[0]
|
|
1127
|
+
}], {
|
|
1128
|
+
yLabel: "count",
|
|
1129
|
+
...opts
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
//#endregion
|
|
1133
|
+
//#region src/heatmap.ts
|
|
1134
|
+
const MARGIN$1 = {
|
|
1135
|
+
top: 34,
|
|
1136
|
+
right: 22,
|
|
1137
|
+
bottom: 30,
|
|
1138
|
+
left: 40
|
|
1139
|
+
};
|
|
1028
1140
|
function heatmap(z, opts = {}) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
return emit({ width, height, bg: theme.bg, prims }, opts);
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// src/three/points3d.ts
|
|
1094
|
-
var MARGIN5 = 40;
|
|
1141
|
+
const width = opts.width ?? 520;
|
|
1142
|
+
const height = opts.height ?? 320;
|
|
1143
|
+
const theme = THEMES[opts.theme ?? "light"];
|
|
1144
|
+
const grid = coerce2d(z);
|
|
1145
|
+
const rows = grid.length;
|
|
1146
|
+
const cols = rows ? grid[0].length : 0;
|
|
1147
|
+
if (rows === 0 || cols === 0) return emit({
|
|
1148
|
+
width,
|
|
1149
|
+
height,
|
|
1150
|
+
bg: theme.bg,
|
|
1151
|
+
prims: [{
|
|
1152
|
+
k: "text",
|
|
1153
|
+
x: width / 2,
|
|
1154
|
+
y: height / 2,
|
|
1155
|
+
s: "no data",
|
|
1156
|
+
fill: theme.muted,
|
|
1157
|
+
anchor: "middle",
|
|
1158
|
+
size: 13
|
|
1159
|
+
}]
|
|
1160
|
+
}, opts);
|
|
1161
|
+
let lo = Infinity;
|
|
1162
|
+
let hi = -Infinity;
|
|
1163
|
+
for (const row of grid) for (const v of row) if (Number.isFinite(v)) {
|
|
1164
|
+
if (v < lo) lo = v;
|
|
1165
|
+
if (v > hi) hi = v;
|
|
1166
|
+
}
|
|
1167
|
+
const span = hi > lo ? hi - lo : 1;
|
|
1168
|
+
const cw = (width - MARGIN$1.left - MARGIN$1.right) / cols;
|
|
1169
|
+
const ch = (height - MARGIN$1.top - MARGIN$1.bottom) / rows;
|
|
1170
|
+
const prims = [];
|
|
1171
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {
|
|
1172
|
+
const v = grid[r][c];
|
|
1173
|
+
const color = Number.isFinite(v) ? viridis((v - lo) / span) : theme.grid;
|
|
1174
|
+
prims.push({
|
|
1175
|
+
k: "rect",
|
|
1176
|
+
x: MARGIN$1.left + c * cw,
|
|
1177
|
+
y: MARGIN$1.top + r * ch,
|
|
1178
|
+
w: cw + .5,
|
|
1179
|
+
h: ch + .5,
|
|
1180
|
+
fill: color
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
if (opts.title) prims.push({
|
|
1184
|
+
k: "text",
|
|
1185
|
+
x: width / 2,
|
|
1186
|
+
y: 20,
|
|
1187
|
+
s: opts.title,
|
|
1188
|
+
fill: theme.fg,
|
|
1189
|
+
anchor: "middle",
|
|
1190
|
+
size: 14
|
|
1191
|
+
});
|
|
1192
|
+
return emit({
|
|
1193
|
+
width,
|
|
1194
|
+
height,
|
|
1195
|
+
bg: theme.bg,
|
|
1196
|
+
prims
|
|
1197
|
+
}, opts);
|
|
1198
|
+
}
|
|
1199
|
+
//#endregion
|
|
1200
|
+
//#region src/three/points3d.ts
|
|
1201
|
+
const MARGIN = 40;
|
|
1095
1202
|
function minOf(a) {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1203
|
+
let m = Infinity;
|
|
1204
|
+
for (const v of a) if (v < m) m = v;
|
|
1205
|
+
return m;
|
|
1099
1206
|
}
|
|
1100
1207
|
function maxOf(a) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1208
|
+
let m = -Infinity;
|
|
1209
|
+
for (const v of a) if (v > m) m = v;
|
|
1210
|
+
return m;
|
|
1104
1211
|
}
|
|
1105
1212
|
function projectAll(x, y, z, cam) {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1213
|
+
const xs = coerce1d(x);
|
|
1214
|
+
const ys = coerce1d(y);
|
|
1215
|
+
const zs = coerce1d(z);
|
|
1216
|
+
const n = Math.min(xs.length, ys.length, zs.length);
|
|
1217
|
+
const nx = (a) => {
|
|
1218
|
+
const lo = minOf(a);
|
|
1219
|
+
const hi = maxOf(a);
|
|
1220
|
+
const s = hi > lo ? hi - lo : 1;
|
|
1221
|
+
return (v) => (v - lo) / s * 2 - 1;
|
|
1222
|
+
};
|
|
1223
|
+
const fx = nx(xs.slice(0, n));
|
|
1224
|
+
const fy = nx(ys.slice(0, n));
|
|
1225
|
+
const fz = nx(zs.slice(0, n));
|
|
1226
|
+
const screen = [];
|
|
1227
|
+
for (let i = 0; i < n; i++) screen.push(project([
|
|
1228
|
+
fx(xs[i]),
|
|
1229
|
+
fy(ys[i]),
|
|
1230
|
+
fz(zs[i])
|
|
1231
|
+
], cam));
|
|
1232
|
+
return { screen };
|
|
1122
1233
|
}
|
|
1123
1234
|
function render(x, y, z, opts, mode) {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1235
|
+
const width = opts.width ?? 520;
|
|
1236
|
+
const height = opts.height ?? 320;
|
|
1237
|
+
const theme = THEMES[opts.theme ?? "light"];
|
|
1238
|
+
const { screen } = projectAll(x, y, z, {
|
|
1239
|
+
azim: opts.azim ?? 45,
|
|
1240
|
+
elev: opts.elev ?? 25
|
|
1241
|
+
});
|
|
1242
|
+
if (screen.length === 0) return emit({
|
|
1243
|
+
width,
|
|
1244
|
+
height,
|
|
1245
|
+
bg: theme.bg,
|
|
1246
|
+
prims: [{
|
|
1247
|
+
k: "text",
|
|
1248
|
+
x: width / 2,
|
|
1249
|
+
y: height / 2,
|
|
1250
|
+
s: "no data",
|
|
1251
|
+
fill: theme.muted,
|
|
1252
|
+
anchor: "middle",
|
|
1253
|
+
size: 13
|
|
1254
|
+
}]
|
|
1255
|
+
}, opts);
|
|
1256
|
+
let sxlo = Infinity, sxhi = -Infinity, sylo = Infinity, syhi = -Infinity;
|
|
1257
|
+
for (const p of screen) {
|
|
1258
|
+
if (p[0] < sxlo) sxlo = p[0];
|
|
1259
|
+
if (p[0] > sxhi) sxhi = p[0];
|
|
1260
|
+
if (p[1] < sylo) sylo = p[1];
|
|
1261
|
+
if (p[1] > syhi) syhi = p[1];
|
|
1262
|
+
}
|
|
1263
|
+
const sxSpan = sxhi > sxlo ? sxhi - sxlo : 1;
|
|
1264
|
+
const sySpan = syhi > sylo ? syhi - sylo : 1;
|
|
1265
|
+
const toScreen = (p) => [MARGIN + (p[0] - sxlo) / sxSpan * (width - 80), height - MARGIN - (p[1] - sylo) / sySpan * (height - 80)];
|
|
1266
|
+
const color = theme.series[0];
|
|
1267
|
+
const prims = [];
|
|
1268
|
+
if (mode === "line") {
|
|
1269
|
+
const depths = screen.map((p) => p[2]);
|
|
1270
|
+
const dlo = minOf(depths);
|
|
1271
|
+
const dhi = maxOf(depths);
|
|
1272
|
+
const dspan = dhi > dlo ? dhi - dlo : 1;
|
|
1273
|
+
const segs = [];
|
|
1274
|
+
for (let i = 0; i + 1 < screen.length; i++) {
|
|
1275
|
+
const a = screen[i];
|
|
1276
|
+
const b = screen[i + 1];
|
|
1277
|
+
segs.push({
|
|
1278
|
+
a,
|
|
1279
|
+
b,
|
|
1280
|
+
depth: (a[2] + b[2]) / 2
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
segs.sort((s1, s2) => s2.depth - s1.depth);
|
|
1284
|
+
for (const sg of segs) {
|
|
1285
|
+
const opacity = .4 + .6 * (1 - (sg.depth - dlo) / dspan);
|
|
1286
|
+
const [x1, y1] = toScreen(sg.a);
|
|
1287
|
+
const [x2, y2] = toScreen(sg.b);
|
|
1288
|
+
prims.push({
|
|
1289
|
+
k: "line",
|
|
1290
|
+
x1,
|
|
1291
|
+
y1,
|
|
1292
|
+
x2,
|
|
1293
|
+
y2,
|
|
1294
|
+
stroke: color,
|
|
1295
|
+
w: 2,
|
|
1296
|
+
opacity
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
const depths = screen.map((p) => p[2]);
|
|
1301
|
+
const dlo = minOf(depths);
|
|
1302
|
+
const dhi = maxOf(depths);
|
|
1303
|
+
const dspan = dhi > dlo ? dhi - dlo : 1;
|
|
1304
|
+
const sorted = screen.slice().sort((a, b) => b[2] - a[2]);
|
|
1305
|
+
for (const p of sorted) {
|
|
1306
|
+
const [sx, sy] = toScreen(p);
|
|
1307
|
+
const opacity = .4 + .6 * (1 - (p[2] - dlo) / dspan);
|
|
1308
|
+
prims.push({
|
|
1309
|
+
k: "circle",
|
|
1310
|
+
cx: sx,
|
|
1311
|
+
cy: sy,
|
|
1312
|
+
r: 3,
|
|
1313
|
+
fill: color,
|
|
1314
|
+
opacity
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
if (opts.title) prims.push({
|
|
1319
|
+
k: "text",
|
|
1320
|
+
x: width / 2,
|
|
1321
|
+
y: 20,
|
|
1322
|
+
s: opts.title,
|
|
1323
|
+
fill: theme.fg,
|
|
1324
|
+
anchor: "middle",
|
|
1325
|
+
size: 14
|
|
1326
|
+
});
|
|
1327
|
+
return emit({
|
|
1328
|
+
width,
|
|
1329
|
+
height,
|
|
1330
|
+
bg: theme.bg,
|
|
1331
|
+
prims
|
|
1332
|
+
}, opts);
|
|
1206
1333
|
}
|
|
1207
1334
|
function scatter3d(x, y, z, opts = {}) {
|
|
1208
|
-
|
|
1335
|
+
return render(x, y, z, opts, "points");
|
|
1209
1336
|
}
|
|
1210
1337
|
function curve3d(x, y, z, opts = {}) {
|
|
1211
|
-
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
area,
|
|
1219
|
-
bar,
|
|
1220
|
-
contour,
|
|
1221
|
-
curve3d,
|
|
1222
|
-
errorbar,
|
|
1223
|
-
heatmap,
|
|
1224
|
-
histogram,
|
|
1225
|
-
line2 as line,
|
|
1226
|
-
overlay,
|
|
1227
|
-
plot,
|
|
1228
|
-
quiver,
|
|
1229
|
-
scatter,
|
|
1230
|
-
scatter3d,
|
|
1231
|
-
step,
|
|
1232
|
-
surface,
|
|
1233
|
-
toTikZ,
|
|
1234
|
-
viridis
|
|
1235
|
-
};
|
|
1338
|
+
return render(x, y, z, opts, "line");
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/index.ts
|
|
1342
|
+
const VERSION = "0.4.12";
|
|
1343
|
+
//#endregion
|
|
1344
|
+
export { VERSION, area, bar, contour, curve3d, errorbar, heatmap, histogram, line, overlay, plot, quiver, scatter, scatter3d, step, surface, toTikZ, viridis };
|