@sarmal/core 0.24.0 → 0.25.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.
Files changed (51) hide show
  1. package/dist/auto-init.cjs +113 -44
  2. package/dist/auto-init.cjs.map +1 -1
  3. package/dist/auto-init.js +113 -44
  4. package/dist/auto-init.js.map +1 -1
  5. package/dist/curves/artemis2.cjs +79 -11
  6. package/dist/curves/artemis2.cjs.map +1 -1
  7. package/dist/curves/artemis2.d.cts +1 -5
  8. package/dist/curves/artemis2.d.ts +1 -5
  9. package/dist/curves/artemis2.js +79 -11
  10. package/dist/curves/artemis2.js.map +1 -1
  11. package/dist/curves/astroid.d.cts +1 -1
  12. package/dist/curves/astroid.d.ts +1 -1
  13. package/dist/curves/deltoid.d.cts +1 -1
  14. package/dist/curves/deltoid.d.ts +1 -1
  15. package/dist/curves/epicycloid3.d.cts +1 -1
  16. package/dist/curves/epicycloid3.d.ts +1 -1
  17. package/dist/curves/epitrochoid7.d.cts +1 -1
  18. package/dist/curves/epitrochoid7.d.ts +1 -1
  19. package/dist/curves/index.cjs +103 -35
  20. package/dist/curves/index.cjs.map +1 -1
  21. package/dist/curves/index.d.cts +1 -1
  22. package/dist/curves/index.d.ts +1 -1
  23. package/dist/curves/index.js +103 -35
  24. package/dist/curves/index.js.map +1 -1
  25. package/dist/curves/lame.d.cts +1 -1
  26. package/dist/curves/lame.d.ts +1 -1
  27. package/dist/curves/lissajous32.d.cts +1 -1
  28. package/dist/curves/lissajous32.d.ts +1 -1
  29. package/dist/curves/lissajous43.d.cts +1 -1
  30. package/dist/curves/lissajous43.d.ts +1 -1
  31. package/dist/curves/rose3.d.cts +1 -1
  32. package/dist/curves/rose3.d.ts +1 -1
  33. package/dist/curves/rose5.d.cts +1 -1
  34. package/dist/curves/rose5.d.ts +1 -1
  35. package/dist/curves/rose52.d.cts +1 -1
  36. package/dist/curves/rose52.d.ts +1 -1
  37. package/dist/curves/star.d.cts +1 -1
  38. package/dist/curves/star.d.ts +1 -1
  39. package/dist/curves/star4.d.cts +1 -1
  40. package/dist/curves/star4.d.ts +1 -1
  41. package/dist/curves/star7.d.cts +1 -1
  42. package/dist/curves/star7.d.ts +1 -1
  43. package/dist/index.cjs +114 -87
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +25 -7
  46. package/dist/index.d.ts +25 -7
  47. package/dist/index.js +114 -88
  48. package/dist/index.js.map +1 -1
  49. package/dist/{types-CknrlCAf.d.cts → types-BZpzgNau.d.cts} +19 -1
  50. package/dist/{types-CknrlCAf.d.ts → types-BZpzgNau.d.ts} +19 -1
  51. package/package.json +1 -1
@@ -1,20 +1,88 @@
1
1
  'use strict';
2
2
 
3
- // src/curves/artemis2.ts
4
- var TWO_PI = Math.PI * 2;
5
- function artemis2Fn(t, _time, _params) {
6
- const a = 0.35, b = 0.15, ox = 0.175;
7
- const s = Math.sin(t), c = Math.cos(t);
8
- const denom = 1 + s * s;
3
+ // src/catmull-rom.ts
4
+ var PERIOD = 2 * Math.PI;
5
+ function catmullRom1D(p0, p1, p2, p3, u) {
6
+ const u2 = u * u;
7
+ const u3 = u2 * u;
8
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
9
+ }
10
+ function evaluateCatmullRom(points2, t) {
11
+ const N = points2.length;
12
+ if (N === 0) {
13
+ return { x: 0, y: 0 };
14
+ }
15
+ if (N === 1) {
16
+ return { x: points2[0][0], y: points2[0][1] };
17
+ }
18
+ t = (t % PERIOD + PERIOD) % PERIOD;
19
+ const segmentSize = PERIOD / N;
20
+ let i = Math.floor(t / segmentSize);
21
+ if (i >= N) {
22
+ i = N - 1;
23
+ }
24
+ let u = (t - i * segmentSize) / segmentSize;
25
+ u = Math.max(0, Math.min(1, u));
26
+ const p0 = points2[(i - 1 + N) % N];
27
+ const p1 = points2[i];
28
+ const p2 = points2[(i + 1) % N];
29
+ const p3 = points2[(i + 2) % N];
9
30
  return {
10
- x: c * (1 + a * c) / denom - ox,
11
- y: s * c * (1 + b * c) / denom
31
+ x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
32
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
12
33
  };
13
34
  }
35
+ function drawCurve(points2, opts) {
36
+ if (points2.length < 3) {
37
+ throw new Error(`drawCurve requires at least 3 points, received ${points2.length}.`);
38
+ }
39
+ const first = points2[0];
40
+ if (points2.every((p) => p[0] === first[0] && p[1] === first[1])) {
41
+ console.warn(
42
+ "[sarmal].drawCurve: all control points are identical. The curve will be a single point."
43
+ );
44
+ }
45
+ const maxAbs = points2.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);
46
+ if (maxAbs > 2) {
47
+ console.warn(
48
+ `[sarmal].drawCurve: control points extend to \xB1${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`
49
+ );
50
+ }
51
+ const pts = points2.map(([x, y]) => [x, y]);
52
+ return {
53
+ name: opts?.name ?? "drawn",
54
+ fn: (t) => evaluateCatmullRom(pts, t),
55
+ period: PERIOD,
56
+ kind: "drawn"
57
+ };
58
+ }
59
+
60
+ // src/curves/artemis2.ts
61
+ var points = [
62
+ [-0.44, -0.45],
63
+ [-0.53, -0.77],
64
+ [-0.82, -0.66],
65
+ [-0.82, -0.18],
66
+ [-0.25, -0.04],
67
+ [0.16, -0.49],
68
+ [-0.03, -0.87],
69
+ [-0.68, -0.94],
70
+ [-0.95, -0.61],
71
+ [-0.87, -0],
72
+ [-0.34, 0.21],
73
+ [0.27, -0.04],
74
+ [0.87, 0.06],
75
+ [0.87, 0.57],
76
+ [0.32, 0.66],
77
+ [-0.21, -0.43],
78
+ [-0.43, -0.81],
79
+ [-0.69, -0.84],
80
+ [-0.87, -0.66],
81
+ [-0.9, -0.47],
82
+ [-0.76, -0.35]
83
+ ];
14
84
  var artemis2 = {
15
- name: "Artemis II",
16
- fn: artemis2Fn,
17
- period: TWO_PI,
85
+ ...drawCurve(points, { name: "Artemis II" }),
18
86
  speed: 0.7
19
87
  };
20
88
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/curves/artemis2.ts"],"names":[],"mappings":";;;AAEA,IAAM,MAAA,GAAS,KAAK,EAAA,GAAK,CAAA;AASzB,SAAS,UAAA,CAAW,CAAA,EAAW,KAAA,EAAe,OAAA,EAAiC;AAC7E,EAAA,MAAM,CAAA,GAAI,IAAA,EACR,CAAA,GAAI,IAAA,EACJ,EAAA,GAAK,KAAA;AACP,EAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAC,GAClB,CAAA,GAAI,IAAA,CAAK,IAAI,CAAC,CAAA;AAChB,EAAA,MAAM,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACtB,EAAA,OAAO;AAAA,IACL,CAAA,EAAI,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,KAAM,KAAA,GAAQ,EAAA;AAAA,IAC/B,CAAA,EAAI,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,IAAI,CAAA,CAAA,GAAM;AAAA,GAC7B;AACF;AAMO,IAAM,QAAA,GAAqB;AAAA,EAChC,IAAA,EAAM,YAAA;AAAA,EACN,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ,MAAA;AAAA,EACR,KAAA,EAAO;AACT","file":"artemis2.cjs","sourcesContent":["import type { CurveDef } from \"../types\";\n\nconst TWO_PI = Math.PI * 2;\n\n/**\n * Artemis II free-return lunar trajectory\n * @see https://www.nasa.gov/wp-content/uploads/2025/09/artemis-ii-map-508.pdf\n * a = x-axis asymmetry (widens one lobe),\n * b = y-axis asymmetry,\n * ox = horizontal offset to visually center the shape\n */\nfunction artemis2Fn(t: number, _time: number, _params: Record<string, number>) {\n const a = 0.35,\n b = 0.15,\n ox = 0.175;\n const s = Math.sin(t),\n c = Math.cos(t);\n const denom = 1 + s * s;\n return {\n x: (c * (1 + a * c)) / denom - ox,\n y: (s * c * (1 + b * c)) / denom,\n };\n}\n\n/**\n * Artemis II free-return lunar trajectory curve\n * Traces the path of the Orion spacecraft during the Artemis II mission\n */\nexport const artemis2: CurveDef = {\n name: \"Artemis II\",\n fn: artemis2Fn,\n period: TWO_PI,\n speed: 0.7,\n};\n"]}
1
+ {"version":3,"sources":["../../src/catmull-rom.ts","../../src/curves/artemis2.ts"],"names":["points"],"mappings":"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","file":"artemis2.cjs","sourcesContent":["import type { ControlPoint, Point, CurveDef } from \"./types\";\n\n/**\n * One full loop around the spline maps to the parametric interval `[0, 2π)`,\n * matching the convention for built-in curves.\n */\nconst PERIOD = 2 * Math.PI;\n\n/**\n * Evaluates a one-dimensional Catmull-Rom spline segment given four control values.\n * The parameter `u` ranges from 0 (at `p1`) to 1 (at `p2`).\n * `p0` and `p3` are the phantom neighbours that influence the tangent at each endpoint.\n */\nfunction catmullRom1D(p0: number, p1: number, p2: number, p3: number, u: number): number {\n const u2 = u * u;\n const u3 = u2 * u;\n\n // The standard Catmull-Rom matrix form\n return (\n 0.5 *\n (2 * p1 +\n (-p0 + p2) * u +\n (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +\n (-p0 + 3 * p1 - 3 * p2 + p3) * u3)\n );\n}\n\n/**\n * Evaluates a closed Catmull-Rom spline through every point in `points`\n *\n * The spline treats `points` as a **closed loop**\n * The last point wraps back to the first,\n * and each segment uses a phantom predecessor / successor so the\n * curve passes exactly through every control point.\n *\n * @param points At least 1 point. An empty array yields `(0, 0)`. A single point returns that point for every `t`\n * @param t Parametric position along the closed loop. Wraps into `[0, 2π)` automatically, so values outside that range are remapped rather than rejected\n * @returns The `(x, y)` position on the spline at time `t`\n */\nexport function evaluateCatmullRom(points: Array<ControlPoint>, t: number): Point {\n const N = points.length;\n\n if (N === 0) {\n return { x: 0, y: 0 };\n }\n\n if (N === 1) {\n return { x: points[0]![0], y: points[0]![1] };\n }\n\n t = ((t % PERIOD) + PERIOD) % PERIOD;\n\n const segmentSize = PERIOD / N;\n let i = Math.floor(t / segmentSize);\n if (i >= N) {\n i = N - 1;\n }\n\n let u = (t - i * segmentSize) / segmentSize;\n u = Math.max(0, Math.min(1, u));\n\n const p0 = points[(i - 1 + N) % N]!;\n const p1 = points[i]!;\n const p2 = points[(i + 1) % N]!;\n const p3 = points[(i + 2) % N]!;\n\n return {\n x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),\n y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),\n };\n}\n\n/**\n * The returned curve definition produces a closed Catmull-Rom spline that\n * passes through every point in order, looping back from the last point to the first.\n *\n * @param points Array of control points in **normalized `[−1, 1]` space**,\n * matching the playground's draw-mode coordinate system.\n * ! Must contain at least 3 points.\n * @param opts Optional overrides for the returned `CurveDef`.\n * @param opts.name Display name for the curve. Defaults to `\"drawn\"`.\n * @returns A `CurveDef` with `period: 2π`, `kind: \"drawn\"`, and the spline evaluator as its `fn`.\n * @throws If `points` has fewer than 3 entries.\n *\n * @example\n * ```ts\n * import { createSarmal, drawCurve } from '@sarmal/core'\n *\n * const curve = drawCurve([\n * [-0.5, 0.3],\n * [ 0.2, -0.8],\n * [ 0.7, 0.4],\n * ])\n *\n * createSarmal(canvas, curve)\n * ```\n */\nexport function drawCurve(points: Array<ControlPoint>, opts?: { name?: string }): CurveDef {\n if (points.length < 3) {\n throw new Error(`drawCurve requires at least 3 points, received ${points.length}.`);\n }\n\n const first = points[0]!;\n if (points.every((p) => p[0] === first[0] && p[1] === first[1])) {\n console.warn(\n \"[sarmal].drawCurve: all control points are identical. The curve will be a single point.\",\n );\n }\n\n const maxAbs = points.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);\n if (maxAbs > 2) {\n console.warn(\n `[sarmal].drawCurve: control points extend to ±${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`,\n );\n }\n\n const pts = points.map(([x, y]) => [x, y] as ControlPoint);\n\n return {\n name: opts?.name ?? \"drawn\",\n fn: (t: number) => evaluateCatmullRom(pts, t),\n period: PERIOD,\n kind: \"drawn\",\n };\n}\n","import type { ControlPoint, CurveDef } from \"../types\";\n\nimport { drawCurve } from \"../catmull-rom\";\n\nconst points: Array<ControlPoint> = [\n [-0.44, -0.45],\n [-0.53, -0.77],\n [-0.82, -0.66],\n [-0.82, -0.18],\n [-0.25, -0.04],\n [0.16, -0.49],\n [-0.03, -0.87],\n [-0.68, -0.94],\n [-0.95, -0.61],\n [-0.87, -0.0],\n [-0.34, 0.21],\n [0.27, -0.04],\n [0.87, 0.06],\n [0.87, 0.57],\n [0.32, 0.66],\n [-0.21, -0.43],\n [-0.43, -0.81],\n [-0.69, -0.84],\n [-0.87, -0.66],\n [-0.9, -0.47],\n [-0.76, -0.35],\n];\n\nexport const artemis2: CurveDef = {\n ...drawCurve(points, { name: \"Artemis II\" }),\n speed: 0.7,\n};\n"]}
@@ -1,9 +1,5 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.cjs';
1
+ import { C as CurveDef } from '../types-BZpzgNau.cjs';
2
2
 
3
- /**
4
- * Artemis II free-return lunar trajectory curve
5
- * Traces the path of the Orion spacecraft during the Artemis II mission
6
- */
7
3
  declare const artemis2: CurveDef;
8
4
 
9
5
  export { artemis2 };
@@ -1,9 +1,5 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.js';
1
+ import { C as CurveDef } from '../types-BZpzgNau.js';
2
2
 
3
- /**
4
- * Artemis II free-return lunar trajectory curve
5
- * Traces the path of the Orion spacecraft during the Artemis II mission
6
- */
7
3
  declare const artemis2: CurveDef;
8
4
 
9
5
  export { artemis2 };
@@ -1,18 +1,86 @@
1
- // src/curves/artemis2.ts
2
- var TWO_PI = Math.PI * 2;
3
- function artemis2Fn(t, _time, _params) {
4
- const a = 0.35, b = 0.15, ox = 0.175;
5
- const s = Math.sin(t), c = Math.cos(t);
6
- const denom = 1 + s * s;
1
+ // src/catmull-rom.ts
2
+ var PERIOD = 2 * Math.PI;
3
+ function catmullRom1D(p0, p1, p2, p3, u) {
4
+ const u2 = u * u;
5
+ const u3 = u2 * u;
6
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
7
+ }
8
+ function evaluateCatmullRom(points2, t) {
9
+ const N = points2.length;
10
+ if (N === 0) {
11
+ return { x: 0, y: 0 };
12
+ }
13
+ if (N === 1) {
14
+ return { x: points2[0][0], y: points2[0][1] };
15
+ }
16
+ t = (t % PERIOD + PERIOD) % PERIOD;
17
+ const segmentSize = PERIOD / N;
18
+ let i = Math.floor(t / segmentSize);
19
+ if (i >= N) {
20
+ i = N - 1;
21
+ }
22
+ let u = (t - i * segmentSize) / segmentSize;
23
+ u = Math.max(0, Math.min(1, u));
24
+ const p0 = points2[(i - 1 + N) % N];
25
+ const p1 = points2[i];
26
+ const p2 = points2[(i + 1) % N];
27
+ const p3 = points2[(i + 2) % N];
7
28
  return {
8
- x: c * (1 + a * c) / denom - ox,
9
- y: s * c * (1 + b * c) / denom
29
+ x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
30
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
10
31
  };
11
32
  }
33
+ function drawCurve(points2, opts) {
34
+ if (points2.length < 3) {
35
+ throw new Error(`drawCurve requires at least 3 points, received ${points2.length}.`);
36
+ }
37
+ const first = points2[0];
38
+ if (points2.every((p) => p[0] === first[0] && p[1] === first[1])) {
39
+ console.warn(
40
+ "[sarmal].drawCurve: all control points are identical. The curve will be a single point."
41
+ );
42
+ }
43
+ const maxAbs = points2.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);
44
+ if (maxAbs > 2) {
45
+ console.warn(
46
+ `[sarmal].drawCurve: control points extend to \xB1${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`
47
+ );
48
+ }
49
+ const pts = points2.map(([x, y]) => [x, y]);
50
+ return {
51
+ name: opts?.name ?? "drawn",
52
+ fn: (t) => evaluateCatmullRom(pts, t),
53
+ period: PERIOD,
54
+ kind: "drawn"
55
+ };
56
+ }
57
+
58
+ // src/curves/artemis2.ts
59
+ var points = [
60
+ [-0.44, -0.45],
61
+ [-0.53, -0.77],
62
+ [-0.82, -0.66],
63
+ [-0.82, -0.18],
64
+ [-0.25, -0.04],
65
+ [0.16, -0.49],
66
+ [-0.03, -0.87],
67
+ [-0.68, -0.94],
68
+ [-0.95, -0.61],
69
+ [-0.87, -0],
70
+ [-0.34, 0.21],
71
+ [0.27, -0.04],
72
+ [0.87, 0.06],
73
+ [0.87, 0.57],
74
+ [0.32, 0.66],
75
+ [-0.21, -0.43],
76
+ [-0.43, -0.81],
77
+ [-0.69, -0.84],
78
+ [-0.87, -0.66],
79
+ [-0.9, -0.47],
80
+ [-0.76, -0.35]
81
+ ];
12
82
  var artemis2 = {
13
- name: "Artemis II",
14
- fn: artemis2Fn,
15
- period: TWO_PI,
83
+ ...drawCurve(points, { name: "Artemis II" }),
16
84
  speed: 0.7
17
85
  };
18
86
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/curves/artemis2.ts"],"names":[],"mappings":";AAEA,IAAM,MAAA,GAAS,KAAK,EAAA,GAAK,CAAA;AASzB,SAAS,UAAA,CAAW,CAAA,EAAW,KAAA,EAAe,OAAA,EAAiC;AAC7E,EAAA,MAAM,CAAA,GAAI,IAAA,EACR,CAAA,GAAI,IAAA,EACJ,EAAA,GAAK,KAAA;AACP,EAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAC,GAClB,CAAA,GAAI,IAAA,CAAK,IAAI,CAAC,CAAA;AAChB,EAAA,MAAM,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACtB,EAAA,OAAO;AAAA,IACL,CAAA,EAAI,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,KAAM,KAAA,GAAQ,EAAA;AAAA,IAC/B,CAAA,EAAI,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,IAAI,CAAA,CAAA,GAAM;AAAA,GAC7B;AACF;AAMO,IAAM,QAAA,GAAqB;AAAA,EAChC,IAAA,EAAM,YAAA;AAAA,EACN,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ,MAAA;AAAA,EACR,KAAA,EAAO;AACT","file":"artemis2.js","sourcesContent":["import type { CurveDef } from \"../types\";\n\nconst TWO_PI = Math.PI * 2;\n\n/**\n * Artemis II free-return lunar trajectory\n * @see https://www.nasa.gov/wp-content/uploads/2025/09/artemis-ii-map-508.pdf\n * a = x-axis asymmetry (widens one lobe),\n * b = y-axis asymmetry,\n * ox = horizontal offset to visually center the shape\n */\nfunction artemis2Fn(t: number, _time: number, _params: Record<string, number>) {\n const a = 0.35,\n b = 0.15,\n ox = 0.175;\n const s = Math.sin(t),\n c = Math.cos(t);\n const denom = 1 + s * s;\n return {\n x: (c * (1 + a * c)) / denom - ox,\n y: (s * c * (1 + b * c)) / denom,\n };\n}\n\n/**\n * Artemis II free-return lunar trajectory curve\n * Traces the path of the Orion spacecraft during the Artemis II mission\n */\nexport const artemis2: CurveDef = {\n name: \"Artemis II\",\n fn: artemis2Fn,\n period: TWO_PI,\n speed: 0.7,\n};\n"]}
1
+ {"version":3,"sources":["../../src/catmull-rom.ts","../../src/curves/artemis2.ts"],"names":["points"],"mappings":"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","file":"artemis2.js","sourcesContent":["import type { ControlPoint, Point, CurveDef } from \"./types\";\n\n/**\n * One full loop around the spline maps to the parametric interval `[0, 2π)`,\n * matching the convention for built-in curves.\n */\nconst PERIOD = 2 * Math.PI;\n\n/**\n * Evaluates a one-dimensional Catmull-Rom spline segment given four control values.\n * The parameter `u` ranges from 0 (at `p1`) to 1 (at `p2`).\n * `p0` and `p3` are the phantom neighbours that influence the tangent at each endpoint.\n */\nfunction catmullRom1D(p0: number, p1: number, p2: number, p3: number, u: number): number {\n const u2 = u * u;\n const u3 = u2 * u;\n\n // The standard Catmull-Rom matrix form\n return (\n 0.5 *\n (2 * p1 +\n (-p0 + p2) * u +\n (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 +\n (-p0 + 3 * p1 - 3 * p2 + p3) * u3)\n );\n}\n\n/**\n * Evaluates a closed Catmull-Rom spline through every point in `points`\n *\n * The spline treats `points` as a **closed loop**\n * The last point wraps back to the first,\n * and each segment uses a phantom predecessor / successor so the\n * curve passes exactly through every control point.\n *\n * @param points At least 1 point. An empty array yields `(0, 0)`. A single point returns that point for every `t`\n * @param t Parametric position along the closed loop. Wraps into `[0, 2π)` automatically, so values outside that range are remapped rather than rejected\n * @returns The `(x, y)` position on the spline at time `t`\n */\nexport function evaluateCatmullRom(points: Array<ControlPoint>, t: number): Point {\n const N = points.length;\n\n if (N === 0) {\n return { x: 0, y: 0 };\n }\n\n if (N === 1) {\n return { x: points[0]![0], y: points[0]![1] };\n }\n\n t = ((t % PERIOD) + PERIOD) % PERIOD;\n\n const segmentSize = PERIOD / N;\n let i = Math.floor(t / segmentSize);\n if (i >= N) {\n i = N - 1;\n }\n\n let u = (t - i * segmentSize) / segmentSize;\n u = Math.max(0, Math.min(1, u));\n\n const p0 = points[(i - 1 + N) % N]!;\n const p1 = points[i]!;\n const p2 = points[(i + 1) % N]!;\n const p3 = points[(i + 2) % N]!;\n\n return {\n x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),\n y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u),\n };\n}\n\n/**\n * The returned curve definition produces a closed Catmull-Rom spline that\n * passes through every point in order, looping back from the last point to the first.\n *\n * @param points Array of control points in **normalized `[−1, 1]` space**,\n * matching the playground's draw-mode coordinate system.\n * ! Must contain at least 3 points.\n * @param opts Optional overrides for the returned `CurveDef`.\n * @param opts.name Display name for the curve. Defaults to `\"drawn\"`.\n * @returns A `CurveDef` with `period: 2π`, `kind: \"drawn\"`, and the spline evaluator as its `fn`.\n * @throws If `points` has fewer than 3 entries.\n *\n * @example\n * ```ts\n * import { createSarmal, drawCurve } from '@sarmal/core'\n *\n * const curve = drawCurve([\n * [-0.5, 0.3],\n * [ 0.2, -0.8],\n * [ 0.7, 0.4],\n * ])\n *\n * createSarmal(canvas, curve)\n * ```\n */\nexport function drawCurve(points: Array<ControlPoint>, opts?: { name?: string }): CurveDef {\n if (points.length < 3) {\n throw new Error(`drawCurve requires at least 3 points, received ${points.length}.`);\n }\n\n const first = points[0]!;\n if (points.every((p) => p[0] === first[0] && p[1] === first[1])) {\n console.warn(\n \"[sarmal].drawCurve: all control points are identical. The curve will be a single point.\",\n );\n }\n\n const maxAbs = points.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);\n if (maxAbs > 2) {\n console.warn(\n `[sarmal].drawCurve: control points extend to ±${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`,\n );\n }\n\n const pts = points.map(([x, y]) => [x, y] as ControlPoint);\n\n return {\n name: opts?.name ?? \"drawn\",\n fn: (t: number) => evaluateCatmullRom(pts, t),\n period: PERIOD,\n kind: \"drawn\",\n };\n}\n","import type { ControlPoint, CurveDef } from \"../types\";\n\nimport { drawCurve } from \"../catmull-rom\";\n\nconst points: Array<ControlPoint> = [\n [-0.44, -0.45],\n [-0.53, -0.77],\n [-0.82, -0.66],\n [-0.82, -0.18],\n [-0.25, -0.04],\n [0.16, -0.49],\n [-0.03, -0.87],\n [-0.68, -0.94],\n [-0.95, -0.61],\n [-0.87, -0.0],\n [-0.34, 0.21],\n [0.27, -0.04],\n [0.87, 0.06],\n [0.87, 0.57],\n [0.32, 0.66],\n [-0.21, -0.43],\n [-0.43, -0.81],\n [-0.69, -0.84],\n [-0.87, -0.66],\n [-0.9, -0.47],\n [-0.76, -0.35],\n];\n\nexport const artemis2: CurveDef = {\n ...drawCurve(points, { name: \"Artemis II\" }),\n speed: 0.7,\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.cjs';
1
+ import { C as CurveDef } from '../types-BZpzgNau.cjs';
2
2
 
3
3
  /**
4
4
  * Astroid curve - a 4-cusped hypocycloid
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.js';
1
+ import { C as CurveDef } from '../types-BZpzgNau.js';
2
2
 
3
3
  /**
4
4
  * Astroid curve - a 4-cusped hypocycloid
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.cjs';
1
+ import { C as CurveDef } from '../types-BZpzgNau.cjs';
2
2
 
3
3
  /**
4
4
  * Deltoid curve - a 3-cusped hypocycloid
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.js';
1
+ import { C as CurveDef } from '../types-BZpzgNau.js';
2
2
 
3
3
  /**
4
4
  * Deltoid curve - a 3-cusped hypocycloid
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.cjs';
1
+ import { C as CurveDef } from '../types-BZpzgNau.cjs';
2
2
 
3
3
  /**
4
4
  * Epicycloid with 3 cusps
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.js';
1
+ import { C as CurveDef } from '../types-BZpzgNau.js';
2
2
 
3
3
  /**
4
4
  * Epicycloid with 3 cusps
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.cjs';
1
+ import { C as CurveDef } from '../types-BZpzgNau.cjs';
2
2
 
3
3
  /**
4
4
  * Epitrochoid with 7 lobes and dynamic variation
@@ -1,4 +1,4 @@
1
- import { C as CurveDef } from '../types-CknrlCAf.js';
1
+ import { C as CurveDef } from '../types-BZpzgNau.js';
2
2
 
3
3
  /**
4
4
  * Epitrochoid with 7 lobes and dynamic variation
@@ -1,25 +1,93 @@
1
1
  'use strict';
2
2
 
3
- // src/curves/artemis2.ts
4
- var TWO_PI = Math.PI * 2;
5
- function artemis2Fn(t, _time, _params) {
6
- const a = 0.35, b = 0.15, ox = 0.175;
7
- const s = Math.sin(t), c = Math.cos(t);
8
- const denom = 1 + s * s;
3
+ // src/catmull-rom.ts
4
+ var PERIOD = 2 * Math.PI;
5
+ function catmullRom1D(p0, p1, p2, p3, u) {
6
+ const u2 = u * u;
7
+ const u3 = u2 * u;
8
+ return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
9
+ }
10
+ function evaluateCatmullRom(points2, t) {
11
+ const N = points2.length;
12
+ if (N === 0) {
13
+ return { x: 0, y: 0 };
14
+ }
15
+ if (N === 1) {
16
+ return { x: points2[0][0], y: points2[0][1] };
17
+ }
18
+ t = (t % PERIOD + PERIOD) % PERIOD;
19
+ const segmentSize = PERIOD / N;
20
+ let i = Math.floor(t / segmentSize);
21
+ if (i >= N) {
22
+ i = N - 1;
23
+ }
24
+ let u = (t - i * segmentSize) / segmentSize;
25
+ u = Math.max(0, Math.min(1, u));
26
+ const p0 = points2[(i - 1 + N) % N];
27
+ const p1 = points2[i];
28
+ const p2 = points2[(i + 1) % N];
29
+ const p3 = points2[(i + 2) % N];
9
30
  return {
10
- x: c * (1 + a * c) / denom - ox,
11
- y: s * c * (1 + b * c) / denom
31
+ x: catmullRom1D(p0[0], p1[0], p2[0], p3[0], u),
32
+ y: catmullRom1D(p0[1], p1[1], p2[1], p3[1], u)
12
33
  };
13
34
  }
35
+ function drawCurve(points2, opts) {
36
+ if (points2.length < 3) {
37
+ throw new Error(`drawCurve requires at least 3 points, received ${points2.length}.`);
38
+ }
39
+ const first = points2[0];
40
+ if (points2.every((p) => p[0] === first[0] && p[1] === first[1])) {
41
+ console.warn(
42
+ "[sarmal].drawCurve: all control points are identical. The curve will be a single point."
43
+ );
44
+ }
45
+ const maxAbs = points2.reduce((m, p) => Math.max(m, Math.abs(p[0]), Math.abs(p[1])), 0);
46
+ if (maxAbs > 2) {
47
+ console.warn(
48
+ `[sarmal].drawCurve: control points extend to \xB1${maxAbs.toFixed(1)}, which may render off-screen. Coordinates should be in [-1, 1].`
49
+ );
50
+ }
51
+ const pts = points2.map(([x, y]) => [x, y]);
52
+ return {
53
+ name: opts?.name ?? "drawn",
54
+ fn: (t) => evaluateCatmullRom(pts, t),
55
+ period: PERIOD,
56
+ kind: "drawn"
57
+ };
58
+ }
59
+
60
+ // src/curves/artemis2.ts
61
+ var points = [
62
+ [-0.44, -0.45],
63
+ [-0.53, -0.77],
64
+ [-0.82, -0.66],
65
+ [-0.82, -0.18],
66
+ [-0.25, -0.04],
67
+ [0.16, -0.49],
68
+ [-0.03, -0.87],
69
+ [-0.68, -0.94],
70
+ [-0.95, -0.61],
71
+ [-0.87, -0],
72
+ [-0.34, 0.21],
73
+ [0.27, -0.04],
74
+ [0.87, 0.06],
75
+ [0.87, 0.57],
76
+ [0.32, 0.66],
77
+ [-0.21, -0.43],
78
+ [-0.43, -0.81],
79
+ [-0.69, -0.84],
80
+ [-0.87, -0.66],
81
+ [-0.9, -0.47],
82
+ [-0.76, -0.35]
83
+ ];
14
84
  var artemis2 = {
15
- name: "Artemis II",
16
- fn: artemis2Fn,
17
- period: TWO_PI,
85
+ ...drawCurve(points, { name: "Artemis II" }),
18
86
  speed: 0.7
19
87
  };
20
88
 
21
89
  // src/curves/astroid.ts
22
- var TWO_PI2 = Math.PI * 2;
90
+ var TWO_PI = Math.PI * 2;
23
91
  function astroidFn(t, _time, _params) {
24
92
  const c = Math.cos(t);
25
93
  const s = Math.sin(t);
@@ -31,12 +99,12 @@ function astroidFn(t, _time, _params) {
31
99
  var astroid = {
32
100
  name: "Astroid",
33
101
  fn: astroidFn,
34
- period: TWO_PI2,
102
+ period: TWO_PI,
35
103
  speed: 1.1
36
104
  };
37
105
 
38
106
  // src/curves/deltoid.ts
39
- var TWO_PI3 = Math.PI * 2;
107
+ var TWO_PI2 = Math.PI * 2;
40
108
  function deltoidFn(t, _time, _params) {
41
109
  return {
42
110
  x: 2 * Math.cos(t) + Math.cos(2 * t),
@@ -46,12 +114,12 @@ function deltoidFn(t, _time, _params) {
46
114
  var deltoid = {
47
115
  name: "Deltoid",
48
116
  fn: deltoidFn,
49
- period: TWO_PI3,
117
+ period: TWO_PI2,
50
118
  speed: 0.9
51
119
  };
52
120
 
53
121
  // src/curves/epicycloid3.ts
54
- var TWO_PI4 = Math.PI * 2;
122
+ var TWO_PI3 = Math.PI * 2;
55
123
  function epicycloid3Fn(t, _time, _params) {
56
124
  return {
57
125
  x: 4 * Math.cos(t) - Math.cos(4 * t),
@@ -61,12 +129,12 @@ function epicycloid3Fn(t, _time, _params) {
61
129
  var epicycloid3 = {
62
130
  name: "Epicycloid (n=3)",
63
131
  fn: epicycloid3Fn,
64
- period: TWO_PI4,
132
+ period: TWO_PI3,
65
133
  speed: 0.75
66
134
  };
67
135
 
68
136
  // src/curves/epitrochoid7.ts
69
- var TWO_PI5 = Math.PI * 2;
137
+ var TWO_PI4 = Math.PI * 2;
70
138
  function epitrochoid7Fn(t, _time, _params) {
71
139
  const d = 1 + 0.55 * Math.sin(t * 0.5);
72
140
  return {
@@ -84,13 +152,13 @@ function epitrochoid7SkeletonFn(t) {
84
152
  var epitrochoid7 = {
85
153
  name: "Epitrochoid",
86
154
  fn: epitrochoid7Fn,
87
- period: TWO_PI5,
155
+ period: TWO_PI4,
88
156
  speed: 1.4,
89
157
  skeletonFn: epitrochoid7SkeletonFn
90
158
  };
91
159
 
92
160
  // src/curves/lissajous32.ts
93
- var TWO_PI6 = Math.PI * 2;
161
+ var TWO_PI5 = Math.PI * 2;
94
162
  function lissajous32Fn(t, time, _params) {
95
163
  const phi = time * 0.45;
96
164
  return {
@@ -101,13 +169,13 @@ function lissajous32Fn(t, time, _params) {
101
169
  var lissajous32 = {
102
170
  name: "Lissajous 3:2",
103
171
  fn: lissajous32Fn,
104
- period: TWO_PI6,
172
+ period: TWO_PI5,
105
173
  speed: 2,
106
174
  skeleton: "live"
107
175
  };
108
176
 
109
177
  // src/curves/lissajous43.ts
110
- var TWO_PI7 = Math.PI * 2;
178
+ var TWO_PI6 = Math.PI * 2;
111
179
  function lissajous43Fn(t, time, _params) {
112
180
  const phi = time * 0.38;
113
181
  return {
@@ -118,13 +186,13 @@ function lissajous43Fn(t, time, _params) {
118
186
  var lissajous43 = {
119
187
  name: "Lissajous 4:3",
120
188
  fn: lissajous43Fn,
121
- period: TWO_PI7,
189
+ period: TWO_PI6,
122
190
  speed: 1.8,
123
191
  skeleton: "live"
124
192
  };
125
193
 
126
194
  // src/curves/lame.ts
127
- var TWO_PI8 = Math.PI * 2;
195
+ var TWO_PI7 = Math.PI * 2;
128
196
  function lameFn(t, time, _params) {
129
197
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
130
198
  const c = Math.cos(t), s = Math.sin(t);
@@ -136,13 +204,13 @@ function lameFn(t, time, _params) {
136
204
  var lame = {
137
205
  name: "Lam\xE9 Curve",
138
206
  fn: lameFn,
139
- period: TWO_PI8,
207
+ period: TWO_PI7,
140
208
  speed: 1,
141
209
  skeleton: "live"
142
210
  };
143
211
 
144
212
  // src/curves/rose3.ts
145
- var TWO_PI9 = Math.PI * 2;
213
+ var TWO_PI8 = Math.PI * 2;
146
214
  function rose3Fn(t, _time, _params) {
147
215
  const r = Math.cos(3 * t);
148
216
  return {
@@ -153,12 +221,12 @@ function rose3Fn(t, _time, _params) {
153
221
  var rose3 = {
154
222
  name: "Rose (n=3)",
155
223
  fn: rose3Fn,
156
- period: TWO_PI9,
224
+ period: TWO_PI8,
157
225
  speed: 1.15
158
226
  };
159
227
 
160
228
  // src/curves/rose5.ts
161
- var TWO_PI10 = Math.PI * 2;
229
+ var TWO_PI9 = Math.PI * 2;
162
230
  function rose5Fn(t, _time, _params) {
163
231
  const r = Math.cos(5 * t);
164
232
  return {
@@ -169,7 +237,7 @@ function rose5Fn(t, _time, _params) {
169
237
  var rose5 = {
170
238
  name: "Rose (n=5)",
171
239
  fn: rose5Fn,
172
- period: TWO_PI10,
240
+ period: TWO_PI9,
173
241
  speed: 1
174
242
  };
175
243
 
@@ -190,7 +258,7 @@ var rose52 = {
190
258
  };
191
259
 
192
260
  // src/curves/star.ts
193
- var TWO_PI11 = Math.PI * 2;
261
+ var TWO_PI10 = Math.PI * 2;
194
262
  function starFn(t, _time, _params) {
195
263
  const r = Math.abs(Math.cos(5 / 2 * t)) + 0.35 * Math.abs(Math.cos(15 / 2 * t)) + 0.15 * Math.abs(Math.cos(25 / 2 * t));
196
264
  return {
@@ -201,12 +269,12 @@ function starFn(t, _time, _params) {
201
269
  var star = {
202
270
  name: "Star",
203
271
  fn: starFn,
204
- period: TWO_PI11,
272
+ period: TWO_PI10,
205
273
  speed: 1
206
274
  };
207
275
 
208
276
  // src/curves/star4.ts
209
- var TWO_PI12 = Math.PI * 2;
277
+ var TWO_PI11 = Math.PI * 2;
210
278
  function star4Fn(t, _time, _params) {
211
279
  const r = Math.abs(Math.cos(2 * t)) + 0.35 * Math.abs(Math.cos(6 * t)) + 0.15 * Math.abs(Math.cos(10 * t));
212
280
  return {
@@ -217,12 +285,12 @@ function star4Fn(t, _time, _params) {
217
285
  var star4 = {
218
286
  name: "Star (4-arm)",
219
287
  fn: star4Fn,
220
- period: TWO_PI12,
288
+ period: TWO_PI11,
221
289
  speed: 1
222
290
  };
223
291
 
224
292
  // src/curves/star7.ts
225
- var TWO_PI13 = Math.PI * 2;
293
+ var TWO_PI12 = Math.PI * 2;
226
294
  function star7Fn(t, _time, _params) {
227
295
  const r = Math.abs(Math.cos(7 / 2 * t)) + 0.35 * Math.abs(Math.cos(21 / 2 * t)) + 0.15 * Math.abs(Math.cos(35 / 2 * t));
228
296
  return {
@@ -233,7 +301,7 @@ function star7Fn(t, _time, _params) {
233
301
  var star7 = {
234
302
  name: "Star (7-arm)",
235
303
  fn: star7Fn,
236
- period: TWO_PI13,
304
+ period: TWO_PI12,
237
305
  speed: 1
238
306
  };
239
307