@sarmal/core 0.8.0 → 0.9.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.
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  // src/engine.ts
4
4
  var TWO_PI = Math.PI * 2;
@@ -51,7 +51,7 @@ function resolveCurve(curveDef) {
51
51
  period: curveDef.period ?? TWO_PI,
52
52
  speed: curveDef.speed ?? 1,
53
53
  skeleton: curveDef.skeleton,
54
- skeletonFn: curveDef.skeletonFn,
54
+ skeletonFn: curveDef.skeletonFn
55
55
  };
56
56
  }
57
57
  function createEngine(curveDef, trailLength = 120) {
@@ -77,7 +77,7 @@ function createEngine(curveDef, trailLength = 120) {
77
77
  actualTime += deltaTime;
78
78
  if (morphCurveB !== null && _morphAlpha !== null) {
79
79
  const a = curve.fn(t, actualTime, {});
80
- const tB = _morphStrategy === "normalized" ? (t / curve.period) * morphCurveB.period : t;
80
+ const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
81
81
  const b = morphCurveB.fn(tB, actualTime, {});
82
82
  trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
83
83
  } else {
@@ -101,14 +101,14 @@ function createEngine(curveDef, trailLength = 120) {
101
101
  trail.clear();
102
102
  },
103
103
  seek(newT, { clearTrail = false } = {}) {
104
- t = ((newT % curve.period) + curve.period) % curve.period;
104
+ t = (newT % curve.period + curve.period) % curve.period;
105
105
  if (clearTrail) {
106
106
  trail.clear();
107
107
  }
108
108
  },
109
109
  seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
110
110
  const advance = curve.speed * step;
111
- const target = ((targetT % curve.period) + curve.period) % curve.period;
111
+ const target = (targetT % curve.period + curve.period) % curve.period;
112
112
  const targetTime = target / curve.speed;
113
113
  t = target;
114
114
  actualTime = targetTime;
@@ -134,16 +134,13 @@ function createEngine(curveDef, trailLength = 120) {
134
134
  ...frozenB,
135
135
  fn: (sampleT, time, params) => {
136
136
  const a = frozenA.fn(sampleT, time, params);
137
- const tB =
138
- frozenStrategy === "normalized"
139
- ? (sampleT / frozenA.period) * frozenB.period
140
- : sampleT;
137
+ const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
141
138
  const b = frozenB.fn(tB, time, params);
142
139
  return {
143
140
  x: a.x + (b.x - a.x) * frozenAlpha,
144
- y: a.y + (b.y - a.y) * frozenAlpha,
141
+ y: a.y + (b.y - a.y) * frozenAlpha
145
142
  };
146
- },
143
+ }
147
144
  };
148
145
  }
149
146
  _morphStrategy = strategy;
@@ -156,7 +153,7 @@ function createEngine(curveDef, trailLength = 120) {
156
153
  completeMorph() {
157
154
  if (morphCurveB !== null) {
158
155
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
159
- t = (t / curve.period) * morphCurveB.period;
156
+ t = t / curve.period * morphCurveB.period;
160
157
  }
161
158
  curve = morphCurveB;
162
159
  }
@@ -168,26 +165,23 @@ function createEngine(curveDef, trailLength = 120) {
168
165
  const points = new Array(steps);
169
166
  if (morphCurveB !== null && _morphAlpha !== null) {
170
167
  for (let i = 0; i < steps; i++) {
171
- const sampleT = (i / (steps - 1)) * curve.period;
168
+ const sampleT = i / (steps - 1) * curve.period;
172
169
  const a = sampleSkeleton(curve, sampleT);
173
- const tB =
174
- _morphStrategy === "normalized"
175
- ? (sampleT / curve.period) * morphCurveB.period
176
- : sampleT;
170
+ const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
177
171
  const b = sampleSkeleton(morphCurveB, tB);
178
172
  points[i] = {
179
173
  x: a.x + (b.x - a.x) * _morphAlpha,
180
- y: a.y + (b.y - a.y) * _morphAlpha,
174
+ y: a.y + (b.y - a.y) * _morphAlpha
181
175
  };
182
176
  }
183
177
  return points;
184
178
  }
185
179
  for (let i = 0; i < steps; i++) {
186
- const sampleT = (i / (steps - 1)) * curve.period;
180
+ const sampleT = i / (steps - 1) * curve.period;
187
181
  points[i] = sampleSkeleton(curve, sampleT);
188
182
  }
189
183
  return points;
190
- },
184
+ }
191
185
  };
192
186
  }
193
187
 
@@ -201,9 +195,50 @@ var TRAIL_FADE_CURVE = 1.5;
201
195
  var TRAIL_MAX_OPACITY = 0.88;
202
196
  var TRAIL_MIN_WIDTH = 0.5;
203
197
  var TRAIL_MAX_WIDTH = 2.5;
198
+ var GRADIENT = {
199
+ bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
200
+ sunset: ["#f97316", "#dc2626", "#9333ea", "#f472b6"],
201
+ ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
202
+ ice: ["#1e3a8a", "#67e8f9"],
203
+ fire: ["#7f1d1d", "#fbbf24"],
204
+ forest: ["#14532d", "#86efac"]
205
+ };
206
+ var PRESETS = {
207
+ bard: GRADIENT.bard,
208
+ sunset: GRADIENT.sunset,
209
+ ocean: GRADIENT.ocean,
210
+ ice: GRADIENT.ice,
211
+ fire: GRADIENT.fire,
212
+ forest: GRADIENT.forest
213
+ };
214
+ function hexToRgb(hex) {
215
+ const n = parseInt(hex.slice(1), 16);
216
+ return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
217
+ }
218
+ var lerpRgb = (a, b, t) => ({
219
+ r: Math.round(a.r + (b.r - a.r) * t),
220
+ g: Math.round(a.g + (b.g - a.g) * t),
221
+ b: Math.round(a.b + (b.b - a.b) * t)
222
+ });
223
+ function getPaletteColor(palette, position, timeOffset = 0) {
224
+ if (palette.length === 0) return { r: 255, g: 255, b: 255 };
225
+ if (palette.length === 1) return hexToRgb(palette[0]);
226
+ const cyclePos = (position + timeOffset) % 1;
227
+ const scaled = cyclePos * palette.length;
228
+ const idx = Math.floor(scaled);
229
+ const t = scaled - idx;
230
+ const c1 = hexToRgb(palette[idx % palette.length]);
231
+ const c2 = hexToRgb(palette[(idx + 1) % palette.length]);
232
+ return lerpRgb(c1, c2, t);
233
+ }
234
+ function resolvePalette(palette, trailStyle) {
235
+ if (Array.isArray(palette)) return palette;
236
+ if (palette && palette in PRESETS) return PRESETS[palette];
237
+ return trailStyle === "gradient-animated" ? GRADIENT.bard : GRADIENT.ice;
238
+ }
204
239
  function hexToRgbComponents(hex) {
205
240
  const n = parseInt(hex.slice(1), 16);
206
- return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
241
+ return `${n >> 16},${n >> 8 & 255},${n & 255}`;
207
242
  }
208
243
  function computeTangent(trail, i) {
209
244
  const count = trail.length;
@@ -248,8 +283,10 @@ function createRenderer(options) {
248
283
  skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
249
284
  trailColor: options.trailColor ?? "#ffffff",
250
285
  headColor: options.headColor ?? "#ffffff",
251
- headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS,
286
+ headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
252
287
  };
288
+ const trailStyle = options.trailStyle ?? "default";
289
+ const palette = resolvePalette(options.palette, trailStyle);
253
290
  const trailRgb = hexToRgbComponents(opts.trailColor);
254
291
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
255
292
  function setupCanvas() {
@@ -275,13 +312,11 @@ function createRenderer(options) {
275
312
  let morphResolve = null;
276
313
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
277
314
  let morphAlpha = 0;
315
+ let gradientAnimTime = 0;
278
316
  function computeBoundaries(pts) {
279
317
  if (pts.length === 0) return null;
280
318
  const first = pts[0];
281
- let minX = first.x,
282
- maxX = first.x,
283
- minY = first.y,
284
- maxY = first.y;
319
+ let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
285
320
  for (const p of pts) {
286
321
  if (p.x < minX) minX = p.x;
287
322
  if (p.x > maxX) maxX = p.x;
@@ -298,7 +333,7 @@ function createRenderer(options) {
298
333
  return {
299
334
  scale: s,
300
335
  offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
301
- offsetY: (logicalHeight - boundsHeight) / 2 - minY * s,
336
+ offsetY: (logicalHeight - boundsHeight) / 2 - minY * s
302
337
  };
303
338
  }
304
339
  function calculateBoundaries() {
@@ -386,7 +421,13 @@ function createRenderer(options) {
386
421
  const l1y = next.y * scale + offsetY + n1.y * halfW1;
387
422
  const r1x = next.x * scale + offsetX - n1.x * halfW1;
388
423
  const r1y = next.y * scale + offsetY - n1.y * halfW1;
389
- ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
424
+ if (trailStyle === "default") {
425
+ ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
426
+ } else {
427
+ const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
428
+ const color = getPaletteColor(palette, progress, timeOffset);
429
+ ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${alpha})`;
430
+ }
390
431
  ctx.beginPath();
391
432
  ctx.moveTo(l0x, l0y);
392
433
  ctx.lineTo(l1x, l1y);
@@ -411,6 +452,9 @@ function createRenderer(options) {
411
452
  const now = performance.now();
412
453
  const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
413
454
  lastTime = now;
455
+ if (trailStyle === "gradient-animated") {
456
+ gradientAnimTime += deltaTime * 1e3;
457
+ }
414
458
  if (engine.morphAlpha !== null) {
415
459
  morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
416
460
  engine.setMorphAlpha(morphAlpha);
@@ -495,36 +539,33 @@ function createRenderer(options) {
495
539
  return new Promise((resolve) => {
496
540
  morphResolve = resolve;
497
541
  });
498
- },
542
+ }
499
543
  };
500
544
  }
501
545
 
502
546
  // src/curves.ts
503
547
  var TWO_PI2 = Math.PI * 2;
504
548
  function artemis2(t, _time, _params) {
505
- const a = 0.35,
506
- b = 0.15,
507
- ox = 0.175;
508
- const s = Math.sin(t),
509
- c = Math.cos(t);
549
+ const a = 0.35, b = 0.15, ox = 0.175;
550
+ const s = Math.sin(t), c = Math.cos(t);
510
551
  const denom = 1 + s * s;
511
552
  return {
512
- x: (c * (1 + a * c)) / denom - ox,
513
- y: (s * c * (1 + b * c)) / denom,
553
+ x: c * (1 + a * c) / denom - ox,
554
+ y: s * c * (1 + b * c) / denom
514
555
  };
515
556
  }
516
557
  function epitrochoid7(t, _time, _params) {
517
558
  const d = 1 + 0.55 * Math.sin(t * 0.5);
518
559
  return {
519
560
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
520
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
561
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
521
562
  };
522
563
  }
523
564
  function epitrochoid7Skeleton(t) {
524
565
  const d = 1.275;
525
566
  return {
526
567
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
527
- y: 7 * Math.sin(t) - d * Math.sin(7 * t),
568
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t)
528
569
  };
529
570
  }
530
571
  function astroid(t, _time, _params) {
@@ -532,56 +573,55 @@ function astroid(t, _time, _params) {
532
573
  const s = Math.sin(t);
533
574
  return {
534
575
  x: c * c * c,
535
- y: s * s * s,
576
+ y: s * s * s
536
577
  };
537
578
  }
538
579
  function deltoid(t, _time, _params) {
539
580
  return {
540
581
  x: 2 * Math.cos(t) + Math.cos(2 * t),
541
- y: 2 * Math.sin(t) - Math.sin(2 * t),
582
+ y: 2 * Math.sin(t) - Math.sin(2 * t)
542
583
  };
543
584
  }
544
585
  function rose5(t, _time, _params) {
545
586
  const r = Math.cos(5 * t);
546
587
  return {
547
588
  x: r * Math.cos(t),
548
- y: r * Math.sin(t),
589
+ y: r * Math.sin(t)
549
590
  };
550
591
  }
551
592
  function rose3(t, _time, _params) {
552
593
  const r = Math.cos(3 * t);
553
594
  return {
554
595
  x: r * Math.cos(t),
555
- y: r * Math.sin(t),
596
+ y: r * Math.sin(t)
556
597
  };
557
598
  }
558
599
  function lissajous32(t, time, _params) {
559
600
  const phi = time * 0.45;
560
601
  return {
561
602
  x: Math.sin(3 * t + phi),
562
- y: Math.sin(2 * t),
603
+ y: Math.sin(2 * t)
563
604
  };
564
605
  }
565
606
  function lissajous43(t, time, _params) {
566
607
  const phi = time * 0.38;
567
608
  return {
568
609
  x: Math.sin(4 * t + phi),
569
- y: Math.sin(3 * t),
610
+ y: Math.sin(3 * t)
570
611
  };
571
612
  }
572
613
  function epicycloid3(t, _time, _params) {
573
614
  return {
574
615
  x: 4 * Math.cos(t) - Math.cos(4 * t),
575
- y: 4 * Math.sin(t) - Math.sin(4 * t),
616
+ y: 4 * Math.sin(t) - Math.sin(4 * t)
576
617
  };
577
618
  }
578
619
  function lame(t, time, _params) {
579
620
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
580
- const c = Math.cos(t),
581
- s = Math.sin(t);
621
+ const c = Math.cos(t), s = Math.sin(t);
582
622
  return {
583
623
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
584
- y: Math.sign(s) * Math.pow(Math.abs(s), p),
624
+ y: Math.sign(s) * Math.pow(Math.abs(s), p)
585
625
  };
586
626
  }
587
627
  var curves = {
@@ -589,66 +629,66 @@ var curves = {
589
629
  name: "Artemis II",
590
630
  fn: artemis2,
591
631
  period: TWO_PI2,
592
- speed: 0.7,
632
+ speed: 0.7
593
633
  },
594
634
  epitrochoid7: {
595
635
  name: "Epitrochoid",
596
636
  fn: epitrochoid7,
597
637
  period: TWO_PI2,
598
638
  speed: 1.4,
599
- skeletonFn: epitrochoid7Skeleton,
639
+ skeletonFn: epitrochoid7Skeleton
600
640
  },
601
641
  astroid: {
602
642
  name: "Astroid",
603
643
  fn: astroid,
604
644
  period: TWO_PI2,
605
- speed: 1.1,
645
+ speed: 1.1
606
646
  },
607
647
  deltoid: {
608
648
  name: "Deltoid",
609
649
  fn: deltoid,
610
650
  period: TWO_PI2,
611
- speed: 0.9,
651
+ speed: 0.9
612
652
  },
613
653
  rose5: {
614
654
  name: "Rose (n=5)",
615
655
  fn: rose5,
616
656
  period: TWO_PI2,
617
- speed: 1,
657
+ speed: 1
618
658
  },
619
659
  rose3: {
620
660
  name: "Rose (n=3)",
621
661
  fn: rose3,
622
662
  period: TWO_PI2,
623
- speed: 1.15,
663
+ speed: 1.15
624
664
  },
625
665
  lissajous32: {
626
666
  name: "Lissajous 3:2",
627
667
  fn: lissajous32,
628
668
  period: TWO_PI2,
629
669
  speed: 2,
630
- skeleton: "live",
670
+ skeleton: "live"
631
671
  },
632
672
  lissajous43: {
633
673
  name: "Lissajous 4:3",
634
674
  fn: lissajous43,
635
675
  period: TWO_PI2,
636
676
  speed: 1.8,
637
- skeleton: "live",
677
+ skeleton: "live"
638
678
  },
639
679
  epicycloid3: {
640
680
  name: "Epicycloid (n=3)",
641
681
  fn: epicycloid3,
642
682
  period: TWO_PI2,
643
- speed: 0.75,
683
+ speed: 0.75
644
684
  },
645
685
  lame: {
646
686
  name: "Lam\xE9 Curve",
647
687
  fn: lame,
648
688
  period: TWO_PI2,
649
689
  speed: 1,
650
- skeleton: "live",
651
- },
690
+ skeleton: "live"
691
+ }
652
692
  };
653
693
 
654
694
  // src/index.ts
@@ -671,12 +711,11 @@ function init() {
671
711
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
672
712
  }
673
713
  const sarmal = createSarmal(canvas, curveDef, {
674
- ...(canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor }),
675
- ...(canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor }),
676
- ...(canvas.dataset.headColor && { headColor: canvas.dataset.headColor }),
677
- ...(canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) }),
678
- ...(canvas.dataset.glowSize && { glowSize: parseInt(canvas.dataset.glowSize, 10) }),
679
- ...(canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }),
714
+ ...canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor },
715
+ ...canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor },
716
+ ...canvas.dataset.headColor && { headColor: canvas.dataset.headColor },
717
+ ...canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) },
718
+ ...canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }
680
719
  });
681
720
  sarmal.start();
682
721
  });
@@ -687,4 +726,4 @@ if (document.readyState === "loading") {
687
726
  init();
688
727
  }
689
728
  //# sourceMappingURL=auto-init.cjs.map
690
- //# sourceMappingURL=auto-init.cjs.map
729
+ //# sourceMappingURL=auto-init.cjs.map