@classytic/stage 0.1.0 → 0.3.0
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/LICENSE +1 -1
- package/README.md +7 -3
- package/dist/assets/index.mjs +0 -1
- package/dist/assets/kit/glyphs.d.mts +5 -5
- package/dist/assets/kit/glyphs.mjs +4 -4
- package/dist/builder/Palette.mjs +50 -89
- package/dist/builder/SceneBuilder.mjs +18 -76
- package/dist/builder/editor.d.mts +2 -1
- package/dist/builder/editor.mjs +14 -4
- package/dist/builder/tools.d.mts +1 -1
- package/dist/builder/tools.mjs +1 -1
- package/dist/chem/index.d.mts +5 -5
- package/dist/chem/index.mjs +4 -4
- package/dist/circuit/index.d.mts +92 -0
- package/dist/circuit/index.mjs +333 -0
- package/dist/core/clock.d.mts +1 -1
- package/dist/core/clock.mjs +3 -3
- package/dist/core/control.d.mts +1 -1
- package/dist/core/control.mjs +2 -2
- package/dist/core/coords.d.mts +4 -4
- package/dist/core/coords.mjs +1 -1
- package/dist/core/index.d.mts +2 -1
- package/dist/core/index.mjs +2 -1
- package/dist/core/learner.d.mts +1 -1
- package/dist/core/learner.mjs +1 -1
- package/dist/core/math.d.mts +26 -0
- package/dist/core/math.mjs +37 -0
- package/dist/core/motion.d.mts +6 -6
- package/dist/core/motion.mjs +6 -6
- package/dist/core/richText.d.mts +2 -2
- package/dist/core/vec.d.mts +1 -1
- package/dist/field/index.d.mts +11 -3
- package/dist/field/index.mjs +20 -3
- package/dist/finance/bizsim.d.mts +93 -0
- package/dist/finance/bizsim.mjs +117 -0
- package/dist/finance/index.d.mts +118 -0
- package/dist/finance/index.mjs +203 -0
- package/dist/index.d.mts +6 -3
- package/dist/index.mjs +6 -4
- package/dist/interaction/MovableDot.d.mts +17 -1
- package/dist/interaction/MovableDot.mjs +160 -43
- package/dist/interaction/index.d.mts +2 -1
- package/dist/interaction/index.mjs +2 -1
- package/dist/interaction/useDraggable.d.mts +1 -1
- package/dist/interaction/useDraggable.mjs +55 -5
- package/dist/interaction/usePressSpring.d.mts +24 -0
- package/dist/interaction/usePressSpring.mjs +67 -0
- package/dist/logic/ast.d.mts +1 -1
- package/dist/logic/index.mjs +8 -3
- package/dist/logic/minimize.d.mts +2 -2
- package/dist/logic/minimize.mjs +56 -18
- package/dist/logic/table.mjs +1 -1
- package/dist/math/ast.d.mts +1 -1
- package/dist/math/ast.mjs +1 -1
- package/dist/math/calculus.d.mts +22 -1
- package/dist/math/calculus.mjs +224 -11
- package/dist/math/compile.d.mts +8 -0
- package/dist/math/compile.mjs +40 -0
- package/dist/math/defs.mjs +1 -1
- package/dist/math/index.d.mts +5 -4
- package/dist/math/index.mjs +5 -4
- package/dist/math/latex.mjs +8 -0
- package/dist/math/parse.mjs +2 -2
- package/dist/math/tokenize.mjs +1 -1
- package/dist/primitives/CanvasLayer.mjs +3 -3
- package/dist/primitives/Dot.d.mts +2 -15
- package/dist/primitives/Dot.mjs +6 -4
- package/dist/primitives/Grid.d.mts +41 -14
- package/dist/primitives/Grid.mjs +100 -22
- package/dist/primitives/Label.d.mts +2 -15
- package/dist/primitives/Label.mjs +4 -3
- package/dist/primitives/Lines.d.mts +4 -32
- package/dist/primitives/Lines.mjs +10 -8
- package/dist/primitives/Shapes.d.mts +5 -43
- package/dist/primitives/Shapes.mjs +12 -10
- package/dist/primitives/Tex.mjs +2 -2
- package/dist/primitives/index.d.mts +2 -2
- package/dist/primitives/index.mjs +2 -2
- package/dist/primitives/props.mjs +31 -0
- package/dist/scene/Scene.d.mts +6 -1
- package/dist/scene/Scene.mjs +16 -41
- package/dist/scene/assets.d.mts +2 -2
- package/dist/scene/assets.mjs +2 -2
- package/dist/scene/commands.d.mts +10 -1
- package/dist/scene/commands.mjs +42 -2
- package/dist/scene/evaluators.mjs +2 -2
- package/dist/scene/index.d.mts +2 -2
- package/dist/scene/index.mjs +2 -2
- package/dist/scene/migrate.mjs +1 -1
- package/dist/scene/sims.mjs +1 -1
- package/dist/scene/types.d.mts +4 -4
- package/dist/sim/particles.d.mts +2 -2
- package/dist/sim/rate.d.mts +4 -4
- package/dist/sim/rate.mjs +1 -1
- package/dist/sim/registry.d.mts +1 -1
- package/dist/sim/sampler.d.mts +5 -5
- package/dist/sim/thermal.d.mts +3 -3
- package/dist/sim/thermal.mjs +2 -2
- package/dist/sim/types.d.mts +2 -2
- package/dist/sim/wave.d.mts +2 -2
- package/dist/sim/wave.mjs +2 -2
- package/dist/steps/index.d.mts +1 -1
- package/dist/steps/index.mjs +3 -3
- package/dist/thermo/index.d.mts +1 -1
- package/dist/thermo/index.mjs +1 -1
- package/dist/view/Stage.mjs +6 -13
- package/dist/view/useInView.mjs +1 -1
- package/package.json +37 -25
- package/styles.css +141 -1
- package/dist/assets/kit/index.mjs +0 -4
package/dist/primitives/Dot.mjs
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { fmt } from "../core/coords.mjs";
|
|
4
4
|
import { useCoords } from "../core/context.mjs";
|
|
5
|
+
import { geomPropsEqual } from "./props.mjs";
|
|
6
|
+
import { memo } from "react";
|
|
5
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
8
|
|
|
7
9
|
//#region src/primitives/Dot.tsx
|
|
8
|
-
function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
|
|
10
|
+
const Dot = memo(function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
|
|
9
11
|
const [px, py] = useCoords().toPx(x, y);
|
|
10
12
|
return /* @__PURE__ */ jsx("circle", {
|
|
11
13
|
cx: fmt(px),
|
|
@@ -14,9 +16,9 @@ function Dot({ x, y, r = 5, color = "var(--stage-accent)", opacity = 1 }) {
|
|
|
14
16
|
fill: color,
|
|
15
17
|
opacity
|
|
16
18
|
});
|
|
17
|
-
}
|
|
19
|
+
}, geomPropsEqual);
|
|
18
20
|
/** A point with a contrast ring (reads clearly over any background). */
|
|
19
|
-
function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
|
|
21
|
+
const Point = memo(function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
|
|
20
22
|
const [px, py] = useCoords().toPx(x, y);
|
|
21
23
|
return /* @__PURE__ */ jsxs("g", {
|
|
22
24
|
opacity,
|
|
@@ -34,7 +36,7 @@ function Point({ x, y, r = 6, color = "var(--stage-accent)", opacity = 1 }) {
|
|
|
34
36
|
strokeWidth: 1.5
|
|
35
37
|
})]
|
|
36
38
|
});
|
|
37
|
-
}
|
|
39
|
+
}, geomPropsEqual);
|
|
38
40
|
|
|
39
41
|
//#endregion
|
|
40
42
|
export { Dot, Point };
|
|
@@ -1,30 +1,57 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
1
|
//#region src/primitives/Grid.d.ts
|
|
4
2
|
/** A "nice" step (1,2,5 × 10ⁿ) so ~`target` gridlines span the range. */
|
|
5
3
|
declare function niceStep(span: number, target?: number): number;
|
|
6
4
|
interface GridProps {
|
|
7
5
|
step?: number;
|
|
6
|
+
/** Per-axis overrides (win over `step`); keeps a tall-thin or short-wide plot
|
|
7
|
+
* from inheriting the other axis's spacing. */
|
|
8
|
+
stepX?: number;
|
|
9
|
+
stepY?: number;
|
|
8
10
|
color?: string;
|
|
9
11
|
}
|
|
10
|
-
declare
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
declare const Grid: import("react").NamedExoticComponent<GridProps>;
|
|
13
|
+
/**
|
|
14
|
+
* A spot the axis numbers must leave clear: a point in math coordinates, widened by a half-size and
|
|
15
|
+
* shifted by an offset, both in px. A dot drawn on an axis sits exactly where that axis prints its
|
|
16
|
+
* numbers (an intercept on the y-axis lands on the y tick labels, a focus on the x-axis on the x
|
|
17
|
+
* ones), so without this the number is half-covered by the dot and both read badly.
|
|
18
|
+
*/
|
|
19
|
+
interface ClearZone {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
/** Half-width in px (default 10, a dot plus a margin). */
|
|
23
|
+
rx?: number;
|
|
24
|
+
/** Half-height in px (default 10). */
|
|
25
|
+
ry?: number;
|
|
26
|
+
dx?: number;
|
|
27
|
+
dy?: number;
|
|
28
|
+
}
|
|
29
|
+
/** The zone a `<Label>` occupies, from the same props, so a figure can keep the axis numbers off it. */
|
|
30
|
+
declare function clearOfLabel(x: number, y: number, text: string, {
|
|
31
|
+
size,
|
|
32
|
+
anchor,
|
|
33
|
+
dx,
|
|
34
|
+
dy
|
|
35
|
+
}?: {
|
|
36
|
+
size?: number;
|
|
37
|
+
anchor?: 'start' | 'middle' | 'end';
|
|
38
|
+
dx?: number;
|
|
39
|
+
dy?: number;
|
|
40
|
+
}): ClearZone;
|
|
14
41
|
interface AxesProps {
|
|
15
42
|
color?: string;
|
|
16
43
|
ticks?: boolean;
|
|
17
44
|
step?: number;
|
|
45
|
+
/** Per-axis tick spacing overrides (win over `step`). */
|
|
46
|
+
stepX?: number;
|
|
47
|
+
stepY?: number;
|
|
18
48
|
/** Show the numeric value at each tick (and a "0" at the origin) so learners
|
|
19
|
-
* can READ coordinates off the grid
|
|
49
|
+
* can READ coordinates off the grid, needed for graphs where the answer is a
|
|
20
50
|
* point (systems, plotting). Default off (most figures want a clean axis). */
|
|
21
51
|
labels?: boolean;
|
|
52
|
+
/** Places the numbers must leave clear, such as the dots and labels the figure draws on an axis. */
|
|
53
|
+
keepClear?: readonly ClearZone[];
|
|
22
54
|
}
|
|
23
|
-
declare
|
|
24
|
-
color,
|
|
25
|
-
ticks,
|
|
26
|
-
step,
|
|
27
|
-
labels
|
|
28
|
-
}: AxesProps): ReactNode;
|
|
55
|
+
declare const Axes: import("react").NamedExoticComponent<AxesProps>;
|
|
29
56
|
//#endregion
|
|
30
|
-
export { Axes, AxesProps, Grid, GridProps, niceStep };
|
|
57
|
+
export { Axes, AxesProps, ClearZone, Grid, GridProps, clearOfLabel, niceStep };
|
package/dist/primitives/Grid.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useCoords } from "../core/context.mjs";
|
|
4
|
+
import { memo } from "react";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
6
|
|
|
6
7
|
//#region src/primitives/Grid.tsx
|
|
@@ -17,11 +18,12 @@ function tickValues(min, max, step) {
|
|
|
17
18
|
for (let v = start; v <= max + step * 1e-6; v += step) out.push(Math.round(v / step) * step);
|
|
18
19
|
return out;
|
|
19
20
|
}
|
|
20
|
-
function Grid({ step, color = "var(--stage-grid)" }) {
|
|
21
|
+
const Grid = memo(function Grid({ step, stepX, stepY, color = "var(--stage-grid)" }) {
|
|
21
22
|
const c = useCoords();
|
|
22
|
-
const
|
|
23
|
+
const sx = stepX ?? step ?? niceStep(c.view.xMax - c.view.xMin);
|
|
24
|
+
const sy = stepY ?? step ?? niceStep(c.view.yMax - c.view.yMin);
|
|
23
25
|
const lines = [];
|
|
24
|
-
for (const x of tickValues(c.view.xMin, c.view.xMax,
|
|
26
|
+
for (const x of tickValues(c.view.xMin, c.view.xMax, sx)) {
|
|
25
27
|
const a = c.toPx(x, c.view.yMin);
|
|
26
28
|
const b = c.toPx(x, c.view.yMax);
|
|
27
29
|
lines.push(/* @__PURE__ */ jsx("line", {
|
|
@@ -33,7 +35,7 @@ function Grid({ step, color = "var(--stage-grid)" }) {
|
|
|
33
35
|
strokeWidth: 1
|
|
34
36
|
}, `gx${x}`));
|
|
35
37
|
}
|
|
36
|
-
for (const y of tickValues(c.view.yMin, c.view.yMax,
|
|
38
|
+
for (const y of tickValues(c.view.yMin, c.view.yMax, sy)) {
|
|
37
39
|
const a = c.toPx(c.view.xMin, y);
|
|
38
40
|
const b = c.toPx(c.view.xMax, y);
|
|
39
41
|
lines.push(/* @__PURE__ */ jsx("line", {
|
|
@@ -46,15 +48,49 @@ function Grid({ step, color = "var(--stage-grid)" }) {
|
|
|
46
48
|
}, `gy${y}`));
|
|
47
49
|
}
|
|
48
50
|
return /* @__PURE__ */ jsx("g", { children: lines });
|
|
51
|
+
});
|
|
52
|
+
/** Roughly how wide a label renders, in px: digits and letters average a little over half an em. */
|
|
53
|
+
const textWidth = (text, size) => text.length * size * .58;
|
|
54
|
+
/** The zone a `<Label>` occupies, from the same props, so a figure can keep the axis numbers off it. */
|
|
55
|
+
function clearOfLabel(x, y, text, { size = 14, anchor = "middle", dx = 0, dy = 0 } = {}) {
|
|
56
|
+
const w = textWidth(text, size);
|
|
57
|
+
return {
|
|
58
|
+
x,
|
|
59
|
+
y,
|
|
60
|
+
dx: dx + (anchor === "start" ? w / 2 : anchor === "end" ? -w / 2 : 0),
|
|
61
|
+
dy,
|
|
62
|
+
rx: w / 2 + 3,
|
|
63
|
+
ry: size * .5 + 2
|
|
64
|
+
};
|
|
49
65
|
}
|
|
50
|
-
const fmtTick = (v) =>
|
|
51
|
-
|
|
66
|
+
const fmtTick = (v) => {
|
|
67
|
+
if (Number.isInteger(v)) return String(v);
|
|
68
|
+
const r = Math.round(v * 100) / 100;
|
|
69
|
+
return String(r);
|
|
70
|
+
};
|
|
71
|
+
const hits = (a, b) => a.l < b.r && b.l < a.r && a.t < b.b && b.t < a.b;
|
|
72
|
+
const Axes = memo(function Axes({ color = "var(--stage-axis)", ticks = true, step, stepX, stepY, labels = false, keepClear }) {
|
|
52
73
|
const c = useCoords();
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
const sx = stepX ?? step ?? niceStep(c.view.xMax - c.view.xMin);
|
|
75
|
+
const sy = stepY ?? step ?? niceStep(c.view.yMax - c.view.yMin);
|
|
76
|
+
/**
|
|
77
|
+
* Where the axes sit when the origin is off-screen.
|
|
78
|
+
*
|
|
79
|
+
* Drawing them at coordinate 0 unconditionally means that a view which does not contain 0 puts
|
|
80
|
+
* the axis line, every tick and every number outside the canvas, so the reader loses the scale
|
|
81
|
+
* entirely and cannot tell what they are looking at. Pin each axis to the nearest edge instead,
|
|
82
|
+
* which is what a plotting library does: the ticks and numbers stay readable, and the axis is
|
|
83
|
+
* still in the right place whenever the origin IS in view.
|
|
84
|
+
*/
|
|
85
|
+
const axisY = Math.min(c.view.yMax, Math.max(c.view.yMin, 0));
|
|
86
|
+
const axisX = Math.min(c.view.xMax, Math.max(c.view.xMin, 0));
|
|
87
|
+
const originInView = axisX === 0 && axisY === 0;
|
|
88
|
+
const yLabelsRight = axisX <= c.view.xMin;
|
|
89
|
+
const xLabelsAbove = axisY <= c.view.yMin;
|
|
90
|
+
const xAxisA = c.toPx(c.view.xMin, axisY);
|
|
91
|
+
const xAxisB = c.toPx(c.view.xMax, axisY);
|
|
92
|
+
const yAxisA = c.toPx(axisX, c.view.yMin);
|
|
93
|
+
const yAxisB = c.toPx(axisX, c.view.yMax);
|
|
58
94
|
const labelStyle = {
|
|
59
95
|
paintOrder: "stroke",
|
|
60
96
|
stroke: "var(--stage-bg)",
|
|
@@ -87,9 +123,52 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, labels = false
|
|
|
87
123
|
strokeWidth: 1.5
|
|
88
124
|
}, "ay")];
|
|
89
125
|
if (ticks) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
const xs = tickValues(c.view.xMin, c.view.xMax, sx).filter((x) => !(originInView && Math.abs(x) < 1e-9));
|
|
127
|
+
const ys = tickValues(c.view.yMin, c.view.yMax, sy).filter((y) => !(originInView && Math.abs(y) < 1e-9));
|
|
128
|
+
const origin = c.toPx(0, 0);
|
|
129
|
+
const xLabelAt = (x) => {
|
|
130
|
+
const p = c.toPx(x, axisY);
|
|
131
|
+
return [p[0], p[1] + (xLabelsAbove ? -12 : 14)];
|
|
132
|
+
};
|
|
133
|
+
const yLabelAt = (y) => {
|
|
134
|
+
const p = c.toPx(axisX, y);
|
|
135
|
+
return [p[0] + (yLabelsRight ? 8 : -8), p[1]];
|
|
136
|
+
};
|
|
137
|
+
const yAnchor = yLabelsRight ? "start" : "end";
|
|
138
|
+
const shown = /* @__PURE__ */ new Set();
|
|
139
|
+
if (labels) {
|
|
140
|
+
const taken = (keepClear ?? []).map((z) => {
|
|
141
|
+
const [zx, zy] = c.toPx(z.x, z.y);
|
|
142
|
+
const px = zx + (z.dx ?? 0);
|
|
143
|
+
const py = zy + (z.dy ?? 0);
|
|
144
|
+
const rx = z.rx ?? 10;
|
|
145
|
+
const ry = z.ry ?? 10;
|
|
146
|
+
return {
|
|
147
|
+
l: px - rx,
|
|
148
|
+
r: px + rx,
|
|
149
|
+
t: py - ry,
|
|
150
|
+
b: py + ry
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
const place = (key, [px, py], text, anchor) => {
|
|
154
|
+
const w = textWidth(text, 11);
|
|
155
|
+
const l = anchor === "middle" ? px - w / 2 : anchor === "end" ? px - w : px;
|
|
156
|
+
const box = {
|
|
157
|
+
l,
|
|
158
|
+
r: l + w,
|
|
159
|
+
t: py - 5,
|
|
160
|
+
b: py + 5
|
|
161
|
+
};
|
|
162
|
+
if (taken.some((t) => hits(box, t))) return;
|
|
163
|
+
taken.push(box);
|
|
164
|
+
shown.add(key);
|
|
165
|
+
};
|
|
166
|
+
if (originInView) place("l0", [origin[0] - 8, origin[1] + 12], "0", "end");
|
|
167
|
+
for (const x of xs) place(`lx${x}`, xLabelAt(x), fmtTick(x), "middle");
|
|
168
|
+
for (const y of ys) place(`ly${y}`, yLabelAt(y), fmtTick(y), yAnchor);
|
|
169
|
+
}
|
|
170
|
+
for (const x of xs) {
|
|
171
|
+
const p = c.toPx(x, axisY);
|
|
93
172
|
nodes.push(/* @__PURE__ */ jsx("line", {
|
|
94
173
|
x1: p[0],
|
|
95
174
|
y1: p[1] - 4,
|
|
@@ -98,11 +177,10 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, labels = false
|
|
|
98
177
|
stroke: color,
|
|
99
178
|
strokeWidth: 1.5
|
|
100
179
|
}, `tx${x}`));
|
|
101
|
-
if (
|
|
180
|
+
if (shown.has(`lx${x}`)) nodes.push(num(`lx${x}`, ...xLabelAt(x), fmtTick(x), "middle"));
|
|
102
181
|
}
|
|
103
|
-
for (const y of
|
|
104
|
-
|
|
105
|
-
const p = c.toPx(0, y);
|
|
182
|
+
for (const y of ys) {
|
|
183
|
+
const p = c.toPx(axisX, y);
|
|
106
184
|
nodes.push(/* @__PURE__ */ jsx("line", {
|
|
107
185
|
x1: p[0] - 4,
|
|
108
186
|
y1: p[1],
|
|
@@ -111,12 +189,12 @@ function Axes({ color = "var(--stage-axis)", ticks = true, step, labels = false
|
|
|
111
189
|
stroke: color,
|
|
112
190
|
strokeWidth: 1.5
|
|
113
191
|
}, `ty${y}`));
|
|
114
|
-
if (
|
|
192
|
+
if (shown.has(`ly${y}`)) nodes.push(num(`ly${y}`, ...yLabelAt(y), fmtTick(y), yAnchor));
|
|
115
193
|
}
|
|
116
|
-
if (
|
|
194
|
+
if (shown.has("l0")) nodes.push(num("l0", origin[0] - 8, origin[1] + 12, "0", "end"));
|
|
117
195
|
}
|
|
118
196
|
return /* @__PURE__ */ jsx("g", { children: nodes });
|
|
119
|
-
}
|
|
197
|
+
});
|
|
120
198
|
|
|
121
199
|
//#endregion
|
|
122
|
-
export { Axes, Grid, niceStep };
|
|
200
|
+
export { Axes, Grid, clearOfLabel, niceStep };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
2
|
-
|
|
3
1
|
//#region src/primitives/Label.d.ts
|
|
4
2
|
interface LabelProps {
|
|
5
3
|
x: number;
|
|
@@ -16,18 +14,7 @@ interface LabelProps {
|
|
|
16
14
|
}
|
|
17
15
|
/** Upright pixel-space text with a background-colored outline for legibility.
|
|
18
16
|
* Renders `_`/`^` as real SVG sub/superscripts via the shared `parseRichText`
|
|
19
|
-
* grammar (the same one labs' HTML `<RichText>` uses
|
|
20
|
-
declare
|
|
21
|
-
x,
|
|
22
|
-
y,
|
|
23
|
-
text,
|
|
24
|
-
color,
|
|
25
|
-
size,
|
|
26
|
-
dx,
|
|
27
|
-
dy,
|
|
28
|
-
anchor,
|
|
29
|
-
baseline,
|
|
30
|
-
weight
|
|
31
|
-
}: LabelProps): ReactNode;
|
|
17
|
+
* grammar (the same one labs' HTML `<RichText>` uses, one source of truth). */
|
|
18
|
+
declare const Label: import("react").NamedExoticComponent<LabelProps>;
|
|
32
19
|
//#endregion
|
|
33
20
|
export { Label, LabelProps };
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import { fmt } from "../core/coords.mjs";
|
|
4
4
|
import { parseRichText } from "../core/richText.mjs";
|
|
5
5
|
import { useCoords } from "../core/context.mjs";
|
|
6
|
+
import { memo } from "react";
|
|
6
7
|
import { jsx } from "react/jsx-runtime";
|
|
7
8
|
|
|
8
9
|
//#region src/primitives/Label.tsx
|
|
9
10
|
/** Upright pixel-space text with a background-colored outline for legibility.
|
|
10
11
|
* Renders `_`/`^` as real SVG sub/superscripts via the shared `parseRichText`
|
|
11
|
-
* grammar (the same one labs' HTML `<RichText>` uses
|
|
12
|
-
function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy = 0, anchor = "middle", baseline = "middle", weight = 600 }) {
|
|
12
|
+
* grammar (the same one labs' HTML `<RichText>` uses, one source of truth). */
|
|
13
|
+
const Label = memo(function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy = 0, anchor = "middle", baseline = "middle", weight = 600 }) {
|
|
13
14
|
const [px, py] = useCoords().toPx(x, y);
|
|
14
15
|
const spans = parseRichText(text);
|
|
15
16
|
const shift = size * .32;
|
|
@@ -39,7 +40,7 @@ function Label({ x, y, text, color = "var(--stage-fg)", size = 14, dx = 0, dy =
|
|
|
39
40
|
}, i);
|
|
40
41
|
})
|
|
41
42
|
});
|
|
42
|
-
}
|
|
43
|
+
});
|
|
43
44
|
|
|
44
45
|
//#endregion
|
|
45
46
|
export { Label };
|
|
@@ -1,50 +1,22 @@
|
|
|
1
1
|
import { Vec2 } from "../core/vec.mjs";
|
|
2
2
|
import { StyleProps } from "./props.mjs";
|
|
3
|
-
import { ReactNode } from "react";
|
|
4
3
|
|
|
5
4
|
//#region src/primitives/Lines.d.ts
|
|
6
5
|
interface SegmentProps extends StyleProps {
|
|
7
6
|
from: Vec2;
|
|
8
7
|
to: Vec2;
|
|
9
8
|
}
|
|
10
|
-
declare
|
|
11
|
-
from,
|
|
12
|
-
to,
|
|
13
|
-
color,
|
|
14
|
-
weight,
|
|
15
|
-
opacity,
|
|
16
|
-
dashed
|
|
17
|
-
}: SegmentProps): ReactNode;
|
|
9
|
+
declare const Segment: import("react").NamedExoticComponent<SegmentProps>;
|
|
18
10
|
interface LineProps extends StyleProps {
|
|
19
11
|
from: Vec2;
|
|
20
12
|
to: Vec2;
|
|
21
13
|
}
|
|
22
|
-
declare
|
|
23
|
-
|
|
24
|
-
to,
|
|
25
|
-
color,
|
|
26
|
-
weight,
|
|
27
|
-
opacity,
|
|
28
|
-
dashed
|
|
29
|
-
}: LineProps): ReactNode;
|
|
30
|
-
declare function Ray({
|
|
31
|
-
from,
|
|
32
|
-
to,
|
|
33
|
-
color,
|
|
34
|
-
weight,
|
|
35
|
-
opacity,
|
|
36
|
-
dashed
|
|
37
|
-
}: LineProps): ReactNode;
|
|
14
|
+
declare const Line: import("react").NamedExoticComponent<LineProps>;
|
|
15
|
+
declare const Ray: import("react").NamedExoticComponent<LineProps>;
|
|
38
16
|
interface VectorProps extends StyleProps {
|
|
39
17
|
tail?: Vec2;
|
|
40
18
|
tip: Vec2;
|
|
41
19
|
}
|
|
42
|
-
declare
|
|
43
|
-
tail,
|
|
44
|
-
tip,
|
|
45
|
-
color,
|
|
46
|
-
weight,
|
|
47
|
-
opacity
|
|
48
|
-
}: VectorProps): ReactNode;
|
|
20
|
+
declare const Vector: import("react").NamedExoticComponent<VectorProps>;
|
|
49
21
|
//#endregion
|
|
50
22
|
export { Line, LineProps, Ray, Segment, SegmentProps, Vector, VectorProps };
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { fmt } from "../core/coords.mjs";
|
|
4
4
|
import { useCoords } from "../core/context.mjs";
|
|
5
|
+
import { geomPropsEqual } from "./props.mjs";
|
|
6
|
+
import { memo } from "react";
|
|
5
7
|
import { jsx } from "react/jsx-runtime";
|
|
6
8
|
|
|
7
9
|
//#region src/primitives/Lines.tsx
|
|
@@ -14,7 +16,7 @@ const extend = (c, a, b, back) => {
|
|
|
14
16
|
const big = Math.hypot(c.width, c.height) * 2;
|
|
15
17
|
return [back ? [a[0] - ux * big, a[1] - uy * big] : a, [a[0] + ux * big, a[1] + uy * big]];
|
|
16
18
|
};
|
|
17
|
-
function Segment({ from, to, color = "var(--stage-fg)", weight = 2, opacity = 1, dashed }) {
|
|
19
|
+
const Segment = memo(function Segment({ from, to, color = "var(--stage-fg)", weight = 2, opacity = 1, dashed }) {
|
|
18
20
|
const c = useCoords();
|
|
19
21
|
const a = c.toPx(from.x, from.y);
|
|
20
22
|
const b = c.toPx(to.x, to.y);
|
|
@@ -29,8 +31,8 @@ function Segment({ from, to, color = "var(--stage-fg)", weight = 2, opacity = 1,
|
|
|
29
31
|
strokeLinecap: "round",
|
|
30
32
|
strokeDasharray: dashed ? "6 6" : void 0
|
|
31
33
|
});
|
|
32
|
-
}
|
|
33
|
-
function Line({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1, dashed }) {
|
|
34
|
+
}, geomPropsEqual);
|
|
35
|
+
const Line = memo(function Line({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1, dashed }) {
|
|
34
36
|
const c = useCoords();
|
|
35
37
|
const [p1, p2] = extend(c, c.toPx(from.x, from.y), c.toPx(to.x, to.y), true);
|
|
36
38
|
return /* @__PURE__ */ jsx("line", {
|
|
@@ -43,8 +45,8 @@ function Line({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1,
|
|
|
43
45
|
opacity,
|
|
44
46
|
strokeDasharray: dashed ? "6 6" : void 0
|
|
45
47
|
});
|
|
46
|
-
}
|
|
47
|
-
function Ray({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1, dashed }) {
|
|
48
|
+
}, geomPropsEqual);
|
|
49
|
+
const Ray = memo(function Ray({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1, dashed }) {
|
|
48
50
|
const c = useCoords();
|
|
49
51
|
const [p1, p2] = extend(c, c.toPx(from.x, from.y), c.toPx(to.x, to.y), false);
|
|
50
52
|
return /* @__PURE__ */ jsx("line", {
|
|
@@ -57,8 +59,8 @@ function Ray({ from, to, color = "var(--stage-axis)", weight = 2, opacity = 1, d
|
|
|
57
59
|
opacity,
|
|
58
60
|
strokeDasharray: dashed ? "6 6" : void 0
|
|
59
61
|
});
|
|
60
|
-
}
|
|
61
|
-
function Vector({ tail = {
|
|
62
|
+
}, geomPropsEqual);
|
|
63
|
+
const Vector = memo(function Vector({ tail = {
|
|
62
64
|
x: 0,
|
|
63
65
|
y: 0
|
|
64
66
|
}, tip, color = "var(--stage-accent-2)", weight = 2.5, opacity = 1 }) {
|
|
@@ -76,7 +78,7 @@ function Vector({ tail = {
|
|
|
76
78
|
strokeLinecap: "round",
|
|
77
79
|
markerEnd: "url(#stage-arrow)"
|
|
78
80
|
});
|
|
79
|
-
}
|
|
81
|
+
}, geomPropsEqual);
|
|
80
82
|
|
|
81
83
|
//#endregion
|
|
82
84
|
export { Line, Ray, Segment, Vector };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Vec2 } from "../core/vec.mjs";
|
|
2
2
|
import { StyleProps } from "./props.mjs";
|
|
3
|
-
import { ReactNode } from "react";
|
|
4
3
|
|
|
5
4
|
//#region src/primitives/Shapes.d.ts
|
|
6
5
|
interface CircleProps extends StyleProps {
|
|
@@ -9,15 +8,7 @@ interface CircleProps extends StyleProps {
|
|
|
9
8
|
fill?: string;
|
|
10
9
|
fillOpacity?: number;
|
|
11
10
|
}
|
|
12
|
-
declare
|
|
13
|
-
center,
|
|
14
|
-
r,
|
|
15
|
-
color,
|
|
16
|
-
fill,
|
|
17
|
-
fillOpacity,
|
|
18
|
-
weight,
|
|
19
|
-
opacity
|
|
20
|
-
}: CircleProps): ReactNode;
|
|
11
|
+
declare const Circle: import("react").NamedExoticComponent<CircleProps>;
|
|
21
12
|
interface EllipseProps extends StyleProps {
|
|
22
13
|
center: Vec2;
|
|
23
14
|
rx: number;
|
|
@@ -25,52 +16,23 @@ interface EllipseProps extends StyleProps {
|
|
|
25
16
|
fill?: string;
|
|
26
17
|
fillOpacity?: number;
|
|
27
18
|
}
|
|
28
|
-
declare
|
|
29
|
-
center,
|
|
30
|
-
rx,
|
|
31
|
-
ry,
|
|
32
|
-
color,
|
|
33
|
-
fill,
|
|
34
|
-
fillOpacity,
|
|
35
|
-
weight,
|
|
36
|
-
opacity
|
|
37
|
-
}: EllipseProps): ReactNode;
|
|
19
|
+
declare const Ellipse: import("react").NamedExoticComponent<EllipseProps>;
|
|
38
20
|
interface PolygonProps extends StyleProps {
|
|
39
21
|
points: Vec2[];
|
|
40
22
|
fill?: string;
|
|
41
23
|
fillOpacity?: number;
|
|
42
24
|
}
|
|
43
|
-
declare
|
|
44
|
-
points,
|
|
45
|
-
color,
|
|
46
|
-
fill,
|
|
47
|
-
fillOpacity,
|
|
48
|
-
weight,
|
|
49
|
-
opacity
|
|
50
|
-
}: PolygonProps): ReactNode;
|
|
25
|
+
declare const Polygon: import("react").NamedExoticComponent<PolygonProps>;
|
|
51
26
|
interface PolylineProps extends StyleProps {
|
|
52
27
|
points: Vec2[];
|
|
53
28
|
}
|
|
54
|
-
declare
|
|
55
|
-
points,
|
|
56
|
-
color,
|
|
57
|
-
weight,
|
|
58
|
-
opacity,
|
|
59
|
-
dashed
|
|
60
|
-
}: PolylineProps): ReactNode;
|
|
29
|
+
declare const Polyline: import("react").NamedExoticComponent<PolylineProps>;
|
|
61
30
|
/** Low-level pixel-space path (d is already in pixels). */
|
|
62
31
|
interface PathProps extends StyleProps {
|
|
63
32
|
d: string;
|
|
64
33
|
fill?: string;
|
|
65
34
|
fillOpacity?: number;
|
|
66
35
|
}
|
|
67
|
-
declare
|
|
68
|
-
d,
|
|
69
|
-
color,
|
|
70
|
-
fill,
|
|
71
|
-
fillOpacity,
|
|
72
|
-
weight,
|
|
73
|
-
opacity
|
|
74
|
-
}: PathProps): ReactNode;
|
|
36
|
+
declare const Path: import("react").NamedExoticComponent<PathProps>;
|
|
75
37
|
//#endregion
|
|
76
38
|
export { Circle, CircleProps, Ellipse, EllipseProps, Path, PathProps, Polygon, PolygonProps, Polyline, PolylineProps };
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { fmt } from "../core/coords.mjs";
|
|
4
4
|
import { useCoords } from "../core/context.mjs";
|
|
5
|
+
import { geomPropsEqual } from "./props.mjs";
|
|
6
|
+
import { memo } from "react";
|
|
5
7
|
import { jsx } from "react/jsx-runtime";
|
|
6
8
|
|
|
7
9
|
//#region src/primitives/Shapes.tsx
|
|
@@ -9,7 +11,7 @@ const toPoints = (c, points) => points.map((p) => {
|
|
|
9
11
|
const [x, y] = c.toPx(p.x, p.y);
|
|
10
12
|
return `${fmt(x)},${fmt(y)}`;
|
|
11
13
|
}).join(" ");
|
|
12
|
-
function Circle({ center, r, color = "var(--stage-accent)", fill = "none", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
14
|
+
const Circle = memo(function Circle({ center, r, color = "var(--stage-accent)", fill = "none", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
13
15
|
const c = useCoords();
|
|
14
16
|
const [cx, cy] = c.toPx(center.x, center.y);
|
|
15
17
|
return /* @__PURE__ */ jsx("ellipse", {
|
|
@@ -23,8 +25,8 @@ function Circle({ center, r, color = "var(--stage-accent)", fill = "none", fillO
|
|
|
23
25
|
fillOpacity: fill === "none" ? void 0 : fillOpacity,
|
|
24
26
|
opacity
|
|
25
27
|
});
|
|
26
|
-
}
|
|
27
|
-
function Ellipse({ center, rx, ry, color = "var(--stage-accent)", fill = "none", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
28
|
+
}, geomPropsEqual);
|
|
29
|
+
const Ellipse = memo(function Ellipse({ center, rx, ry, color = "var(--stage-accent)", fill = "none", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
28
30
|
const c = useCoords();
|
|
29
31
|
const [cx, cy] = c.toPx(center.x, center.y);
|
|
30
32
|
return /* @__PURE__ */ jsx("ellipse", {
|
|
@@ -38,8 +40,8 @@ function Ellipse({ center, rx, ry, color = "var(--stage-accent)", fill = "none",
|
|
|
38
40
|
fillOpacity: fill === "none" ? void 0 : fillOpacity,
|
|
39
41
|
opacity
|
|
40
42
|
});
|
|
41
|
-
}
|
|
42
|
-
function Polygon({ points, color = "var(--stage-accent)", fill = "var(--stage-accent)", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
43
|
+
}, geomPropsEqual);
|
|
44
|
+
const Polygon = memo(function Polygon({ points, color = "var(--stage-accent)", fill = "var(--stage-accent)", fillOpacity = .12, weight = 2, opacity = 1 }) {
|
|
43
45
|
return /* @__PURE__ */ jsx("polygon", {
|
|
44
46
|
points: toPoints(useCoords(), points),
|
|
45
47
|
stroke: color,
|
|
@@ -49,8 +51,8 @@ function Polygon({ points, color = "var(--stage-accent)", fill = "var(--stage-ac
|
|
|
49
51
|
opacity,
|
|
50
52
|
strokeLinejoin: "round"
|
|
51
53
|
});
|
|
52
|
-
}
|
|
53
|
-
function Polyline({ points, color = "var(--stage-fg)", weight = 2, opacity = 1, dashed }) {
|
|
54
|
+
}, geomPropsEqual);
|
|
55
|
+
const Polyline = memo(function Polyline({ points, color = "var(--stage-fg)", weight = 2, opacity = 1, dashed }) {
|
|
54
56
|
return /* @__PURE__ */ jsx("polyline", {
|
|
55
57
|
points: toPoints(useCoords(), points),
|
|
56
58
|
fill: "none",
|
|
@@ -61,8 +63,8 @@ function Polyline({ points, color = "var(--stage-fg)", weight = 2, opacity = 1,
|
|
|
61
63
|
strokeLinecap: "round",
|
|
62
64
|
strokeDasharray: dashed ? "6 6" : void 0
|
|
63
65
|
});
|
|
64
|
-
}
|
|
65
|
-
function Path({ d, color = "var(--stage-fg)", fill = "none", fillOpacity = 1, weight = 2, opacity = 1 }) {
|
|
66
|
+
}, geomPropsEqual);
|
|
67
|
+
const Path = memo(function Path({ d, color = "var(--stage-fg)", fill = "none", fillOpacity = 1, weight = 2, opacity = 1 }) {
|
|
66
68
|
return /* @__PURE__ */ jsx("path", {
|
|
67
69
|
d,
|
|
68
70
|
stroke: color,
|
|
@@ -73,7 +75,7 @@ function Path({ d, color = "var(--stage-fg)", fill = "none", fillOpacity = 1, we
|
|
|
73
75
|
strokeLinejoin: "round",
|
|
74
76
|
strokeLinecap: "round"
|
|
75
77
|
});
|
|
76
|
-
}
|
|
78
|
+
}, geomPropsEqual);
|
|
77
79
|
|
|
78
80
|
//#endregion
|
|
79
81
|
export { Circle, Ellipse, Path, Polygon, Polyline };
|
package/dist/primitives/Tex.mjs
CHANGED
|
@@ -8,11 +8,11 @@ import katex from "katex";
|
|
|
8
8
|
|
|
9
9
|
//#region src/primitives/Tex.tsx
|
|
10
10
|
/**
|
|
11
|
-
* Tex
|
|
11
|
+
* Tex, KaTeX maths in a <foreignObject>, rendered AT RENDER TIME (not in an
|
|
12
12
|
* effect) so the markup is identical on the server and the client: SSR/static
|
|
13
13
|
* exports show real maths (not raw `\left…\right`), there's no raw-then-hydrate
|
|
14
14
|
* flash, and hydration matches. KaTeX is a static import kept in its OWN module so
|
|
15
|
-
* only Tex's importers pull it (stage's neverBundle leaves `katex` external
|
|
15
|
+
* only Tex's importers pull it (stage's neverBundle leaves `katex` external, the
|
|
16
16
|
* consumer resolves the peer; non-Tex consumers never bundle it).
|
|
17
17
|
*
|
|
18
18
|
* Needs the consumer to load `katex/dist/katex.min.css` for proper glyphs.
|
|
@@ -4,7 +4,7 @@ import { Line, LineProps, Ray, Segment, SegmentProps, Vector, VectorProps } from
|
|
|
4
4
|
import { Circle, CircleProps, Ellipse, EllipseProps, Path, PathProps, Polygon, PolygonProps, Polyline, PolylineProps } from "./Shapes.mjs";
|
|
5
5
|
import { Label, LabelProps } from "./Label.mjs";
|
|
6
6
|
import { Tex, TexProps } from "./Tex.mjs";
|
|
7
|
-
import { Axes, AxesProps, Grid, GridProps, niceStep } from "./Grid.mjs";
|
|
7
|
+
import { Axes, AxesProps, ClearZone, Grid, GridProps, clearOfLabel, niceStep } from "./Grid.mjs";
|
|
8
8
|
import { OfXProps, OfYProps, ParametricProps, Plot } from "./Plot.mjs";
|
|
9
9
|
import { CanvasLayer, CanvasLayerProps } from "./CanvasLayer.mjs";
|
|
10
|
-
export { type A11yProps, Axes, type AxesProps, CanvasLayer, type CanvasLayerProps, Circle, type CircleProps, Dot, type DotProps, Ellipse, type EllipseProps, Grid, type GridProps, Label, type LabelProps, Line, type LineProps, type OfXProps, type OfYProps, type ParametricProps, Path, type PathProps, Plot, Point, Polygon, type PolygonProps, Polyline, type PolylineProps, Ray, Segment, type SegmentProps, type StyleProps, Tex, type TexProps, Vector, type VectorProps, niceStep };
|
|
10
|
+
export { type A11yProps, Axes, type AxesProps, CanvasLayer, type CanvasLayerProps, Circle, type CircleProps, type ClearZone, Dot, type DotProps, Ellipse, type EllipseProps, Grid, type GridProps, Label, type LabelProps, Line, type LineProps, type OfXProps, type OfYProps, type ParametricProps, Path, type PathProps, Plot, Point, Polygon, type PolygonProps, Polyline, type PolylineProps, Ray, Segment, type SegmentProps, type StyleProps, Tex, type TexProps, Vector, type VectorProps, clearOfLabel, niceStep };
|
|
@@ -3,8 +3,8 @@ import { Line, Ray, Segment, Vector } from "./Lines.mjs";
|
|
|
3
3
|
import { Circle, Ellipse, Path, Polygon, Polyline } from "./Shapes.mjs";
|
|
4
4
|
import { Label } from "./Label.mjs";
|
|
5
5
|
import { Tex } from "./Tex.mjs";
|
|
6
|
-
import { Axes, Grid, niceStep } from "./Grid.mjs";
|
|
6
|
+
import { Axes, Grid, clearOfLabel, niceStep } from "./Grid.mjs";
|
|
7
7
|
import { Plot } from "./Plot.mjs";
|
|
8
8
|
import { CanvasLayer } from "./CanvasLayer.mjs";
|
|
9
9
|
|
|
10
|
-
export { Axes, CanvasLayer, Circle, Dot, Ellipse, Grid, Label, Line, Path, Plot, Point, Polygon, Polyline, Ray, Segment, Tex, Vector, niceStep };
|
|
10
|
+
export { Axes, CanvasLayer, Circle, Dot, Ellipse, Grid, Label, Line, Path, Plot, Point, Polygon, Polyline, Ray, Segment, Tex, Vector, clearOfLabel, niceStep };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/primitives/props.ts
|
|
2
|
+
const MAX_DEPTH = 4;
|
|
3
|
+
const valEq = (a, b, depth) => {
|
|
4
|
+
if (Object.is(a, b)) return true;
|
|
5
|
+
if (depth <= 0) return false;
|
|
6
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
7
|
+
if ("$$typeof" in a || "$$typeof" in b) return false;
|
|
8
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
9
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
10
|
+
for (let i = 0; i < a.length; i++) if (!valEq(a[i], b[i], depth - 1)) return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
const ka = Object.keys(a);
|
|
14
|
+
if (ka.length !== Object.keys(b).length) return false;
|
|
15
|
+
for (const k of ka) {
|
|
16
|
+
if (!(k in b)) return false;
|
|
17
|
+
if (!valEq(a[k], b[k], depth - 1)) return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Value-based props comparator for `React.memo` on geometry primitives.
|
|
23
|
+
* Labs recreate `Vec2`/`points` objects every render (often 60×/s under a frame
|
|
24
|
+
* tick), so identity-based memo never hits; comparing coordinate VALUES lets
|
|
25
|
+
* static shapes skip reconciliation while anything that actually moved still
|
|
26
|
+
* re-renders. Functions and React elements compare by reference (safe fallback).
|
|
27
|
+
*/
|
|
28
|
+
const geomPropsEqual = (prev, next) => valEq(prev, next, MAX_DEPTH);
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { geomPropsEqual };
|