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