@sarmal/core 0.18.0 → 0.20.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/README.md +1 -12
- package/dist/auto-init.cjs +379 -20
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.d.cts +4 -0
- package/dist/auto-init.d.ts +4 -0
- package/dist/auto-init.js +379 -20
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +85 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -8
- package/dist/index.d.ts +53 -8
- package/dist/index.js +85 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
S as SarmalInstance,
|
|
5
5
|
C as CurveDef,
|
|
6
6
|
R as RendererOptions,
|
|
7
|
+
P as Point,
|
|
7
8
|
a as SarmalOptions,
|
|
8
9
|
} from "./types-frtEoAq6.cjs";
|
|
9
10
|
export {
|
|
10
11
|
J as JumpOptions,
|
|
11
|
-
P as Point,
|
|
12
12
|
b as RuntimeRenderOptions,
|
|
13
13
|
c as SeekOptions,
|
|
14
14
|
T as TrailColor,
|
|
@@ -31,8 +31,8 @@ import "./curves/star4.cjs";
|
|
|
31
31
|
import "./curves/star7.cjs";
|
|
32
32
|
|
|
33
33
|
interface SVGRendererOptions extends BaseRendererOptions {
|
|
34
|
-
/**
|
|
35
|
-
container:
|
|
34
|
+
/** SVG element the renderer draws into directly */
|
|
35
|
+
container: SVGSVGElement;
|
|
36
36
|
engine: Engine;
|
|
37
37
|
/** @default 'Loading' */
|
|
38
38
|
ariaLabel?: string;
|
|
@@ -47,22 +47,25 @@ interface SVGSarmalOptions extends Omit<SVGRendererOptions, "container" | "engin
|
|
|
47
47
|
*/
|
|
48
48
|
declare function createSVGRenderer(options: SVGRendererOptions): SarmalInstance;
|
|
49
49
|
/**
|
|
50
|
-
* Creates a sarmal animation inside
|
|
51
|
-
* The
|
|
50
|
+
* Creates a sarmal animation directly inside an `<svg>` element using an SVG renderer.
|
|
51
|
+
* The passed `<svg>` element is set to `viewBox="0 0 100 100"` and animated via requestAnimationFrame
|
|
52
52
|
*
|
|
53
53
|
* @example
|
|
54
54
|
* ```ts
|
|
55
55
|
* import { createSarmalSVG, epitrochoid7 } from '@sarmal/core'
|
|
56
|
-
*
|
|
56
|
+
*
|
|
57
|
+
* // <svg id="spinner"></svg> in your HTML
|
|
58
|
+
* const svg = document.getElementById('spinner')
|
|
59
|
+
* const sarmal = createSarmalSVG(svg, epitrochoid7)
|
|
57
60
|
*
|
|
58
61
|
* // To control manually, use autoStart: false
|
|
59
|
-
* const controlled = createSarmalSVG(
|
|
62
|
+
* const controlled = createSarmalSVG(svg, rose5, { autoStart: false })
|
|
60
63
|
* controlled.play() // Start when ready
|
|
61
64
|
* controlled.pause() // Pause later
|
|
62
65
|
* ```
|
|
63
66
|
*/
|
|
64
67
|
declare function createSarmalSVG(
|
|
65
|
-
container:
|
|
68
|
+
container: SVGSVGElement,
|
|
66
69
|
curveDef: CurveDef,
|
|
67
70
|
options?: SVGSarmalOptions,
|
|
68
71
|
): SarmalInstance;
|
|
@@ -89,6 +92,45 @@ declare function createEngine(curveDef: CurveDef, trailLength?: number): Engine;
|
|
|
89
92
|
*/
|
|
90
93
|
declare function createRenderer(options: RendererOptions): SarmalInstance;
|
|
91
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Evaluates a closed Catmull-Rom spline through every point in `points`
|
|
97
|
+
*
|
|
98
|
+
* The spline treats `points` as a **closed loop**
|
|
99
|
+
* The last point wraps back to the first,
|
|
100
|
+
* and each segment uses a phantom predecessor / successor so the
|
|
101
|
+
* curve passes exactly through every control point.
|
|
102
|
+
*
|
|
103
|
+
* @param points At least 1 point. An empty array yields `(0, 0)`. A single point returns that point for every `t`
|
|
104
|
+
* @param t Parametric position along the closed loop. Wraps into `[0, 2π)` automatically, so values outside that range are remapped rather than rejected
|
|
105
|
+
* @returns The `(x, y)` position on the spline at time `t`
|
|
106
|
+
*/
|
|
107
|
+
declare function evaluateCatmullRom(points: Array<[number, number]>, t: number): Point;
|
|
108
|
+
/**
|
|
109
|
+
* The returned curve definition produces a closed Catmull-Rom spline that
|
|
110
|
+
* passes through every point in order, looping back from the last point to the first.
|
|
111
|
+
*
|
|
112
|
+
* @param points Array of control points in **normalized `[−1, 1]` space**,
|
|
113
|
+
* matching the playground's draw-mode coordinate system.
|
|
114
|
+
* ! Must contain at least 3 points.
|
|
115
|
+
* @returns A `CurveDef` with `period: 2π` and the spline evaluator as its `fn`.
|
|
116
|
+
* `name` is set to `"custom"`.
|
|
117
|
+
* @throws If `points` has fewer than 3 entries.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* import { createSarmal, drawCurve } from '@sarmal/core'
|
|
122
|
+
*
|
|
123
|
+
* const curve = drawCurve([
|
|
124
|
+
* [-0.5, 0.3],
|
|
125
|
+
* [ 0.2, -0.8],
|
|
126
|
+
* [ 0.7, 0.4],
|
|
127
|
+
* ])
|
|
128
|
+
*
|
|
129
|
+
* createSarmal(canvas, curve)
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function drawCurve(points: Array<[number, number]>): CurveDef;
|
|
133
|
+
|
|
92
134
|
/**
|
|
93
135
|
* Creates a sarmal animation on a canvas element
|
|
94
136
|
*
|
|
@@ -113,6 +155,7 @@ export {
|
|
|
113
155
|
BaseRendererOptions,
|
|
114
156
|
CurveDef,
|
|
115
157
|
Engine,
|
|
158
|
+
Point,
|
|
116
159
|
RendererOptions,
|
|
117
160
|
type SVGRendererOptions,
|
|
118
161
|
type SVGSarmalOptions,
|
|
@@ -124,5 +167,7 @@ export {
|
|
|
124
167
|
createSVGRenderer,
|
|
125
168
|
createSarmal,
|
|
126
169
|
createSarmalSVG,
|
|
170
|
+
drawCurve,
|
|
171
|
+
evaluateCatmullRom,
|
|
127
172
|
palettes,
|
|
128
173
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
S as SarmalInstance,
|
|
5
5
|
C as CurveDef,
|
|
6
6
|
R as RendererOptions,
|
|
7
|
+
P as Point,
|
|
7
8
|
a as SarmalOptions,
|
|
8
9
|
} from "./types-frtEoAq6.js";
|
|
9
10
|
export {
|
|
10
11
|
J as JumpOptions,
|
|
11
|
-
P as Point,
|
|
12
12
|
b as RuntimeRenderOptions,
|
|
13
13
|
c as SeekOptions,
|
|
14
14
|
T as TrailColor,
|
|
@@ -31,8 +31,8 @@ import "./curves/star4.js";
|
|
|
31
31
|
import "./curves/star7.js";
|
|
32
32
|
|
|
33
33
|
interface SVGRendererOptions extends BaseRendererOptions {
|
|
34
|
-
/**
|
|
35
|
-
container:
|
|
34
|
+
/** SVG element the renderer draws into directly */
|
|
35
|
+
container: SVGSVGElement;
|
|
36
36
|
engine: Engine;
|
|
37
37
|
/** @default 'Loading' */
|
|
38
38
|
ariaLabel?: string;
|
|
@@ -47,22 +47,25 @@ interface SVGSarmalOptions extends Omit<SVGRendererOptions, "container" | "engin
|
|
|
47
47
|
*/
|
|
48
48
|
declare function createSVGRenderer(options: SVGRendererOptions): SarmalInstance;
|
|
49
49
|
/**
|
|
50
|
-
* Creates a sarmal animation inside
|
|
51
|
-
* The
|
|
50
|
+
* Creates a sarmal animation directly inside an `<svg>` element using an SVG renderer.
|
|
51
|
+
* The passed `<svg>` element is set to `viewBox="0 0 100 100"` and animated via requestAnimationFrame
|
|
52
52
|
*
|
|
53
53
|
* @example
|
|
54
54
|
* ```ts
|
|
55
55
|
* import { createSarmalSVG, epitrochoid7 } from '@sarmal/core'
|
|
56
|
-
*
|
|
56
|
+
*
|
|
57
|
+
* // <svg id="spinner"></svg> in your HTML
|
|
58
|
+
* const svg = document.getElementById('spinner')
|
|
59
|
+
* const sarmal = createSarmalSVG(svg, epitrochoid7)
|
|
57
60
|
*
|
|
58
61
|
* // To control manually, use autoStart: false
|
|
59
|
-
* const controlled = createSarmalSVG(
|
|
62
|
+
* const controlled = createSarmalSVG(svg, rose5, { autoStart: false })
|
|
60
63
|
* controlled.play() // Start when ready
|
|
61
64
|
* controlled.pause() // Pause later
|
|
62
65
|
* ```
|
|
63
66
|
*/
|
|
64
67
|
declare function createSarmalSVG(
|
|
65
|
-
container:
|
|
68
|
+
container: SVGSVGElement,
|
|
66
69
|
curveDef: CurveDef,
|
|
67
70
|
options?: SVGSarmalOptions,
|
|
68
71
|
): SarmalInstance;
|
|
@@ -89,6 +92,45 @@ declare function createEngine(curveDef: CurveDef, trailLength?: number): Engine;
|
|
|
89
92
|
*/
|
|
90
93
|
declare function createRenderer(options: RendererOptions): SarmalInstance;
|
|
91
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Evaluates a closed Catmull-Rom spline through every point in `points`
|
|
97
|
+
*
|
|
98
|
+
* The spline treats `points` as a **closed loop**
|
|
99
|
+
* The last point wraps back to the first,
|
|
100
|
+
* and each segment uses a phantom predecessor / successor so the
|
|
101
|
+
* curve passes exactly through every control point.
|
|
102
|
+
*
|
|
103
|
+
* @param points At least 1 point. An empty array yields `(0, 0)`. A single point returns that point for every `t`
|
|
104
|
+
* @param t Parametric position along the closed loop. Wraps into `[0, 2π)` automatically, so values outside that range are remapped rather than rejected
|
|
105
|
+
* @returns The `(x, y)` position on the spline at time `t`
|
|
106
|
+
*/
|
|
107
|
+
declare function evaluateCatmullRom(points: Array<[number, number]>, t: number): Point;
|
|
108
|
+
/**
|
|
109
|
+
* The returned curve definition produces a closed Catmull-Rom spline that
|
|
110
|
+
* passes through every point in order, looping back from the last point to the first.
|
|
111
|
+
*
|
|
112
|
+
* @param points Array of control points in **normalized `[−1, 1]` space**,
|
|
113
|
+
* matching the playground's draw-mode coordinate system.
|
|
114
|
+
* ! Must contain at least 3 points.
|
|
115
|
+
* @returns A `CurveDef` with `period: 2π` and the spline evaluator as its `fn`.
|
|
116
|
+
* `name` is set to `"custom"`.
|
|
117
|
+
* @throws If `points` has fewer than 3 entries.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* import { createSarmal, drawCurve } from '@sarmal/core'
|
|
122
|
+
*
|
|
123
|
+
* const curve = drawCurve([
|
|
124
|
+
* [-0.5, 0.3],
|
|
125
|
+
* [ 0.2, -0.8],
|
|
126
|
+
* [ 0.7, 0.4],
|
|
127
|
+
* ])
|
|
128
|
+
*
|
|
129
|
+
* createSarmal(canvas, curve)
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function drawCurve(points: Array<[number, number]>): CurveDef;
|
|
133
|
+
|
|
92
134
|
/**
|
|
93
135
|
* Creates a sarmal animation on a canvas element
|
|
94
136
|
*
|
|
@@ -113,6 +155,7 @@ export {
|
|
|
113
155
|
BaseRendererOptions,
|
|
114
156
|
CurveDef,
|
|
115
157
|
Engine,
|
|
158
|
+
Point,
|
|
116
159
|
RendererOptions,
|
|
117
160
|
type SVGRendererOptions,
|
|
118
161
|
type SVGSarmalOptions,
|
|
@@ -124,5 +167,7 @@ export {
|
|
|
124
167
|
createSVGRenderer,
|
|
125
168
|
createSarmal,
|
|
126
169
|
createSarmalSVG,
|
|
170
|
+
drawCurve,
|
|
171
|
+
evaluateCatmullRom,
|
|
127
172
|
palettes,
|
|
128
173
|
};
|
package/dist/index.js
CHANGED
|
@@ -298,12 +298,20 @@ function computeNormal(trail, i) {
|
|
|
298
298
|
const tangent = computeTangent(trail, i);
|
|
299
299
|
return { x: -tangent.y, y: tangent.x };
|
|
300
300
|
}
|
|
301
|
-
function computeTrailQuad(
|
|
301
|
+
function computeTrailQuad(
|
|
302
|
+
trail,
|
|
303
|
+
i,
|
|
304
|
+
trailCount,
|
|
305
|
+
toX,
|
|
306
|
+
toY,
|
|
307
|
+
minWidth = TRAIL_MIN_WIDTH,
|
|
308
|
+
maxWidth = TRAIL_MAX_WIDTH,
|
|
309
|
+
) {
|
|
302
310
|
const progress = i / (trailCount - 1);
|
|
303
311
|
const nextProgress = (i + 1) / (trailCount - 1);
|
|
304
312
|
const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
305
|
-
const w0 = (
|
|
306
|
-
const w1 = (
|
|
313
|
+
const w0 = (minWidth + progress * (maxWidth - minWidth)) / 2;
|
|
314
|
+
const w1 = (minWidth + nextProgress * (maxWidth - minWidth)) / 2;
|
|
307
315
|
const curr = trail[i];
|
|
308
316
|
const next = trail[i + 1];
|
|
309
317
|
const n0 = computeNormal(trail, i);
|
|
@@ -867,62 +875,61 @@ function createSVGRenderer(options) {
|
|
|
867
875
|
let trailPalette = resolveTrailPalette(trailColor);
|
|
868
876
|
const ariaLabel = options.ariaLabel ?? "Loading";
|
|
869
877
|
warnIfTrailColorMismatch(trailColor, trailStyle);
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
svg.setAttribute("aria-label", ariaLabel);
|
|
878
|
+
const viewSize = 100;
|
|
879
|
+
const headRadius = options.headRadius ?? 1.5;
|
|
880
|
+
const svgTrailMinWidth = 0.25;
|
|
881
|
+
const svgTrailMaxWidth = 1.25;
|
|
882
|
+
const svgSkeletonStrokeWidth = "0.75";
|
|
883
|
+
container.setAttribute("viewBox", `0 0 ${viewSize} ${viewSize}`);
|
|
884
|
+
container.setAttribute("role", "img");
|
|
885
|
+
container.setAttribute("aria-label", ariaLabel);
|
|
886
|
+
const group = el("g");
|
|
880
887
|
const titleEl = el("title");
|
|
881
888
|
titleEl.textContent = ariaLabel;
|
|
882
|
-
|
|
889
|
+
group.appendChild(titleEl);
|
|
883
890
|
const skeletonPath = el("path");
|
|
884
891
|
skeletonPath.setAttribute("data-sarmal-role", "skeleton");
|
|
885
892
|
skeletonPath.setAttribute("fill", "none");
|
|
886
893
|
skeletonPath.setAttribute("stroke", skeletonColor);
|
|
887
894
|
skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY));
|
|
888
|
-
skeletonPath.setAttribute("stroke-width",
|
|
895
|
+
skeletonPath.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
889
896
|
if (skeletonColor === "transparent") {
|
|
890
897
|
skeletonPath.setAttribute("visibility", "hidden");
|
|
891
898
|
}
|
|
892
|
-
|
|
899
|
+
group.appendChild(skeletonPath);
|
|
893
900
|
const skeletonPathA = el("path");
|
|
894
901
|
skeletonPathA.setAttribute("fill", "none");
|
|
895
902
|
skeletonPathA.setAttribute("stroke", skeletonColor);
|
|
896
|
-
skeletonPathA.setAttribute("stroke-width",
|
|
903
|
+
skeletonPathA.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
897
904
|
skeletonPathA.setAttribute("visibility", "hidden");
|
|
898
|
-
|
|
905
|
+
group.appendChild(skeletonPathA);
|
|
899
906
|
const skeletonPathB = el("path");
|
|
900
907
|
skeletonPathB.setAttribute("fill", "none");
|
|
901
908
|
skeletonPathB.setAttribute("stroke", skeletonColor);
|
|
902
|
-
skeletonPathB.setAttribute("stroke-width",
|
|
909
|
+
skeletonPathB.setAttribute("stroke-width", svgSkeletonStrokeWidth);
|
|
903
910
|
skeletonPathB.setAttribute("visibility", "hidden");
|
|
904
|
-
|
|
911
|
+
group.appendChild(skeletonPathB);
|
|
905
912
|
let morphPathABuilt = "";
|
|
906
913
|
let morphPathBBuilt = "";
|
|
907
914
|
const trailPaths = [];
|
|
908
915
|
for (let i = 0; i < poolSize; i++) {
|
|
909
916
|
const path = el("path");
|
|
910
917
|
path.setAttribute("fill", trailSolid);
|
|
911
|
-
|
|
918
|
+
group.appendChild(path);
|
|
912
919
|
trailPaths.push(path);
|
|
913
920
|
}
|
|
914
921
|
const headCircle = el("circle");
|
|
915
922
|
headCircle.setAttribute("data-sarmal-role", "head");
|
|
916
923
|
headCircle.setAttribute("fill", headColor);
|
|
917
924
|
headCircle.setAttribute("r", String(headRadius));
|
|
918
|
-
|
|
919
|
-
container.appendChild(
|
|
925
|
+
group.appendChild(headCircle);
|
|
926
|
+
container.appendChild(group);
|
|
920
927
|
let gradientAnimTime = 0;
|
|
921
928
|
let scale = 1;
|
|
922
929
|
let offsetX = 0;
|
|
923
930
|
let offsetY = 0;
|
|
924
931
|
function applyBoundaries(skeleton2) {
|
|
925
|
-
const b = computeBoundaries(skeleton2,
|
|
932
|
+
const b = computeBoundaries(skeleton2, viewSize, viewSize);
|
|
926
933
|
if (b) {
|
|
927
934
|
scale = b.scale;
|
|
928
935
|
offsetX = b.offsetX;
|
|
@@ -958,6 +965,8 @@ function createSVGRenderer(options) {
|
|
|
958
965
|
trailCount,
|
|
959
966
|
px,
|
|
960
967
|
py,
|
|
968
|
+
svgTrailMinWidth,
|
|
969
|
+
svgTrailMaxWidth,
|
|
961
970
|
);
|
|
962
971
|
const d = `M${l0x.toFixed(2)} ${l0y.toFixed(2)} L${l1x.toFixed(2)} ${l1y.toFixed(2)} L${r1x.toFixed(2)} ${r1y.toFixed(2)} L${r0x.toFixed(2)} ${r0y.toFixed(2)} Z`;
|
|
963
972
|
trailPaths[i].setAttribute("d", d);
|
|
@@ -1080,7 +1089,7 @@ function createSVGRenderer(options) {
|
|
|
1080
1089
|
morphResolve = null;
|
|
1081
1090
|
morphReject = null;
|
|
1082
1091
|
}
|
|
1083
|
-
|
|
1092
|
+
group.remove();
|
|
1084
1093
|
},
|
|
1085
1094
|
...enginePassthroughs(engine),
|
|
1086
1095
|
morphTo(target, options2) {
|
|
@@ -1433,6 +1442,55 @@ var curves = {
|
|
|
1433
1442
|
lame,
|
|
1434
1443
|
};
|
|
1435
1444
|
|
|
1445
|
+
// src/catmull-rom.ts
|
|
1446
|
+
var PERIOD = 2 * Math.PI;
|
|
1447
|
+
function catmullRom1D(p0, p1, p2, p3, u) {
|
|
1448
|
+
const u2 = u * u;
|
|
1449
|
+
const u3 = u2 * u;
|
|
1450
|
+
return (
|
|
1451
|
+
0.5 *
|
|
1452
|
+
(2 * p1 +
|
|
1453
|
+
(-p0 + p2) * u +
|
|
1454
|
+
(2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +
|
|
1455
|
+
(-p0 + 3 * p1 - 3 * p2 + p3) * u3)
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
function evaluateCatmullRom(points, t) {
|
|
1459
|
+
const N = points.length;
|
|
1460
|
+
if (N === 0) {
|
|
1461
|
+
return { x: 0, y: 0 };
|
|
1462
|
+
}
|
|
1463
|
+
if (N === 1) {
|
|
1464
|
+
return { x: points[0][0], y: points[0][1] };
|
|
1465
|
+
}
|
|
1466
|
+
t = ((t % PERIOD) + PERIOD) % PERIOD;
|
|
1467
|
+
const segmentSize = PERIOD / N;
|
|
1468
|
+
let i = Math.floor(t / segmentSize);
|
|
1469
|
+
if (i >= N) {
|
|
1470
|
+
i = N - 1;
|
|
1471
|
+
}
|
|
1472
|
+
let u = (t - i * segmentSize) / segmentSize;
|
|
1473
|
+
u = Math.max(0, Math.min(1, u));
|
|
1474
|
+
const p0 = points[(i - 1 + N) % N];
|
|
1475
|
+
const p1 = points[i];
|
|
1476
|
+
const p2 = points[(i + 1) % N];
|
|
1477
|
+
const p3 = points[(i + 2) % N];
|
|
1478
|
+
return {
|
|
1479
|
+
x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
|
|
1480
|
+
y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
function drawCurve(points) {
|
|
1484
|
+
if (points.length < 3) {
|
|
1485
|
+
throw new Error(`drawCurve requires at least 3 points, received ${points.length}.`);
|
|
1486
|
+
}
|
|
1487
|
+
return {
|
|
1488
|
+
name: "custom",
|
|
1489
|
+
fn: (t) => evaluateCatmullRom(points, t),
|
|
1490
|
+
period: PERIOD,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1436
1494
|
// src/index.ts
|
|
1437
1495
|
function createSarmal(canvas, curveDef, options) {
|
|
1438
1496
|
const { trailLength, ...rendererOpts } = options ?? {};
|
|
@@ -1450,8 +1508,10 @@ export {
|
|
|
1450
1508
|
createSarmalSVG,
|
|
1451
1509
|
curves,
|
|
1452
1510
|
deltoid,
|
|
1511
|
+
drawCurve,
|
|
1453
1512
|
epicycloid3,
|
|
1454
1513
|
epitrochoid7,
|
|
1514
|
+
evaluateCatmullRom,
|
|
1455
1515
|
lame,
|
|
1456
1516
|
lissajous32,
|
|
1457
1517
|
lissajous43,
|