@sarmal/core 0.7.0 → 0.8.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,13 +132,16 @@ 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 = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
135
+ const tB =
136
+ frozenStrategy === "normalized"
137
+ ? (sampleT / frozenA.period) * frozenB.period
138
+ : sampleT;
136
139
  const b = frozenB.fn(tB, time, params);
137
140
  return {
138
141
  x: a.x + (b.x - a.x) * frozenAlpha,
139
- y: a.y + (b.y - a.y) * frozenAlpha
142
+ y: a.y + (b.y - a.y) * frozenAlpha,
140
143
  };
141
- }
144
+ },
142
145
  };
143
146
  }
144
147
  _morphStrategy = strategy;
@@ -151,7 +154,7 @@ function createEngine(curveDef, trailLength = 120) {
151
154
  completeMorph() {
152
155
  if (morphCurveB !== null) {
153
156
  if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
154
- t = t / curve.period * morphCurveB.period;
157
+ t = (t / curve.period) * morphCurveB.period;
155
158
  }
156
159
  curve = morphCurveB;
157
160
  }
@@ -163,43 +166,74 @@ function createEngine(curveDef, trailLength = 120) {
163
166
  const points = new Array(steps);
164
167
  if (morphCurveB !== null && _morphAlpha !== null) {
165
168
  for (let i = 0; i < steps; i++) {
166
- const sampleT = i / (steps - 1) * curve.period;
169
+ const sampleT = (i / (steps - 1)) * curve.period;
167
170
  const a = sampleSkeleton(curve, sampleT);
168
- const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
171
+ const tB =
172
+ _morphStrategy === "normalized"
173
+ ? (sampleT / curve.period) * morphCurveB.period
174
+ : sampleT;
169
175
  const b = sampleSkeleton(morphCurveB, tB);
170
176
  points[i] = {
171
177
  x: a.x + (b.x - a.x) * _morphAlpha,
172
- y: a.y + (b.y - a.y) * _morphAlpha
178
+ y: a.y + (b.y - a.y) * _morphAlpha,
173
179
  };
174
180
  }
175
181
  return points;
176
182
  }
177
183
  for (let i = 0; i < steps; i++) {
178
- const sampleT = i / (steps - 1) * curve.period;
184
+ const sampleT = (i / (steps - 1)) * curve.period;
179
185
  points[i] = sampleSkeleton(curve, sampleT);
180
186
  }
181
187
  return points;
182
- }
188
+ },
183
189
  };
184
190
  }
185
191
 
186
192
  // src/renderer.ts
187
193
  var DEFAULT_MORPH_DURATION_MS = 300;
188
194
  var DEFAULT_HEAD_RADIUS = 4;
189
- var DEFAULT_GLOW_SIZE = 20;
190
195
  var DEFAULT_SKELETON_COLOR = "#ffffff";
191
196
  var DEFAULT_SKELETON_OPACITY = 0.15;
192
197
  var FIT_PADDING = 0.1;
193
- var TRAIL_BATCH_SIZE = 20;
194
198
  var TRAIL_FADE_CURVE = 1.5;
195
199
  var TRAIL_MAX_OPACITY = 0.88;
196
200
  var TRAIL_MIN_WIDTH = 0.5;
197
201
  var TRAIL_MAX_WIDTH = 2.5;
198
- var GLOW_INNER_EDGE = 0.4;
199
- var GLOW_FALLOFF_OPACITY = 0.53;
200
202
  function hexToRgbComponents(hex) {
201
203
  const n = parseInt(hex.slice(1), 16);
202
- return `${n >> 16},${n >> 8 & 255},${n & 255}`;
204
+ return `${n >> 16},${(n >> 8) & 255},${n & 255}`;
205
+ }
206
+ function computeTangent(trail, i) {
207
+ const count = trail.length;
208
+ if (count < 2) {
209
+ return { x: 1, y: 0 };
210
+ }
211
+ if (i === 0) {
212
+ const dx2 = trail[1].x - trail[0].x;
213
+ const dy2 = trail[1].y - trail[0].y;
214
+ const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
215
+ return { x: dx2 / len2, y: dy2 / len2 };
216
+ }
217
+ if (i === count - 1) {
218
+ const dx2 = trail[count - 1].x - trail[count - 2].x;
219
+ const dy2 = trail[count - 1].y - trail[count - 2].y;
220
+ const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
221
+ return { x: dx2 / len2, y: dy2 / len2 };
222
+ }
223
+ const dx = trail[i + 1].x - trail[i - 1].x;
224
+ const dy = trail[i + 1].y - trail[i - 1].y;
225
+ const len = Math.sqrt(dx * dx + dy * dy) || 1;
226
+ return { x: dx / len, y: dy / len };
227
+ }
228
+ function computeNormal(trail, i) {
229
+ const tangent = computeTangent(trail, i);
230
+ return { x: -tangent.y, y: tangent.x };
231
+ }
232
+ function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
233
+ target.style.width = `${logicalWidth}px`;
234
+ target.style.height = `${logicalHeight}px`;
235
+ target.width = logicalWidth * dpr;
236
+ target.height = logicalHeight * dpr;
203
237
  }
204
238
  function createRenderer(options) {
205
239
  const canvas = options.canvas;
@@ -213,10 +247,19 @@ function createRenderer(options) {
213
247
  trailColor: options.trailColor ?? "#ffffff",
214
248
  headColor: options.headColor ?? "#ffffff",
215
249
  headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS,
216
- glowSize: options.glowSize ?? DEFAULT_GLOW_SIZE
217
250
  };
218
251
  const trailRgb = hexToRgbComponents(opts.trailColor);
219
- const headRgbFalloff = `rgba(${hexToRgbComponents(opts.headColor)},${GLOW_FALLOFF_OPACITY})`;
252
+ const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
253
+ function setupCanvas() {
254
+ const rect = canvas.getBoundingClientRect();
255
+ const lw = rect.width || 200;
256
+ const lh = rect.height || 200;
257
+ applyDprSizing(canvas, lw, lh, dpr);
258
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
259
+ }
260
+ setupCanvas();
261
+ let logicalWidth = canvas.width / dpr;
262
+ let logicalHeight = canvas.height / dpr;
220
263
  let skeleton = [];
221
264
  let skeletonCanvas = null;
222
265
  let trail = [];
@@ -230,12 +273,13 @@ function createRenderer(options) {
230
273
  let morphResolve = null;
231
274
  let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
232
275
  let morphAlpha = 0;
233
- let morphBoundsA = null;
234
- let morphBoundsB = null;
235
276
  function computeBoundaries(pts) {
236
277
  if (pts.length === 0) return null;
237
278
  const first = pts[0];
238
- let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
279
+ let minX = first.x,
280
+ maxX = first.x,
281
+ minY = first.y,
282
+ maxY = first.y;
239
283
  for (const p of pts) {
240
284
  if (p.x < minX) minX = p.x;
241
285
  if (p.x > maxX) maxX = p.x;
@@ -244,17 +288,15 @@ function createRenderer(options) {
244
288
  }
245
289
  const width = maxX - minX;
246
290
  const height = maxY - minY;
247
- const canvasWidth = canvas.width;
248
- const canvasHeight = canvas.height;
249
- const scaleX = canvasWidth / (width * (1 + FIT_PADDING * 2));
250
- const scaleY = canvasHeight / (height * (1 + FIT_PADDING * 2));
291
+ const scaleX = logicalWidth / (width * (1 + FIT_PADDING * 2));
292
+ const scaleY = logicalHeight / (height * (1 + FIT_PADDING * 2));
251
293
  const s = Math.min(scaleX, scaleY);
252
294
  const boundsWidth = width * s;
253
295
  const boundsHeight = height * s;
254
296
  return {
255
297
  scale: s,
256
- offsetX: (canvasWidth - boundsWidth) / 2 - minX * s,
257
- offsetY: (canvasHeight - boundsHeight) / 2 - minY * s
298
+ offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
299
+ offsetY: (logicalHeight - boundsHeight) / 2 - minY * s,
258
300
  };
259
301
  }
260
302
  function calculateBoundaries() {
@@ -269,6 +311,7 @@ function createRenderer(options) {
269
311
  if (skeleton.length < 2) return;
270
312
  skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
271
313
  const skeletonCtx = skeletonCanvas.getContext("2d");
314
+ skeletonCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
272
315
  skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
273
316
  skeletonCtx.lineWidth = 1.5;
274
317
  skeletonCtx.beginPath();
@@ -314,32 +357,41 @@ function createRenderer(options) {
314
357
  }
315
358
  ctx.stroke();
316
359
  } else if (skeletonCanvas) {
317
- ctx.drawImage(skeletonCanvas, 0, 0);
360
+ ctx.drawImage(skeletonCanvas, 0, 0, logicalWidth, logicalHeight);
318
361
  }
319
362
  }
320
363
  function drawTrail() {
321
364
  if (trailCount < 2) {
322
365
  return;
323
366
  }
324
- ctx.lineJoin = "round";
325
- ctx.lineCap = "round";
326
- for (let batchIndex = 0; batchIndex < trailCount - 1; batchIndex += TRAIL_BATCH_SIZE) {
327
- const bEnd = Math.min(batchIndex + TRAIL_BATCH_SIZE, trailCount - 1);
328
- const progress = (batchIndex + bEnd) / 2 / (trailCount - 1);
367
+ for (let i = 0; i < trailCount - 1; i++) {
368
+ const progress = i / (trailCount - 1);
369
+ const nextProgress = (i + 1) / (trailCount - 1);
329
370
  const alpha = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
330
- const lineWidth = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
371
+ const width = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
372
+ const nextWidth = TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
373
+ const curr = trail[i];
374
+ const next = trail[i + 1];
375
+ const n0 = computeNormal(trail, i);
376
+ const n1 = computeNormal(trail, i + 1);
377
+ const halfW0 = width / 2;
378
+ const halfW1 = nextWidth / 2;
379
+ const l0x = curr.x * scale + offsetX + n0.x * halfW0;
380
+ const l0y = curr.y * scale + offsetY + n0.y * halfW0;
381
+ const r0x = curr.x * scale + offsetX - n0.x * halfW0;
382
+ const r0y = curr.y * scale + offsetY - n0.y * halfW0;
383
+ const l1x = next.x * scale + offsetX + n1.x * halfW1;
384
+ const l1y = next.y * scale + offsetY + n1.y * halfW1;
385
+ const r1x = next.x * scale + offsetX - n1.x * halfW1;
386
+ const r1y = next.y * scale + offsetY - n1.y * halfW1;
387
+ ctx.fillStyle = `rgba(${trailRgb},${alpha})`;
331
388
  ctx.beginPath();
332
- for (let i = batchIndex; i <= bEnd; i++) {
333
- const point = trail[i];
334
- if (i === batchIndex) {
335
- ctx.moveTo(point.x * scale + offsetX, point.y * scale + offsetY);
336
- } else {
337
- ctx.lineTo(point.x * scale + offsetX, point.y * scale + offsetY);
338
- }
339
- }
340
- ctx.strokeStyle = `rgba(${trailRgb},${alpha})`;
341
- ctx.lineWidth = lineWidth;
342
- ctx.stroke();
389
+ ctx.moveTo(l0x, l0y);
390
+ ctx.lineTo(l1x, l1y);
391
+ ctx.lineTo(r1x, r1y);
392
+ ctx.lineTo(r0x, r0y);
393
+ ctx.closePath();
394
+ ctx.fill();
343
395
  }
344
396
  }
345
397
  function drawHead() {
@@ -348,14 +400,6 @@ function createRenderer(options) {
348
400
  }
349
401
  const x = head.x * scale + offsetX;
350
402
  const y = head.y * scale + offsetY;
351
- const gradient = ctx.createRadialGradient(x, y, 0, x, y, opts.glowSize);
352
- gradient.addColorStop(0, opts.headColor);
353
- gradient.addColorStop(GLOW_INNER_EDGE, headRgbFalloff);
354
- gradient.addColorStop(1, "transparent");
355
- ctx.fillStyle = gradient;
356
- ctx.beginPath();
357
- ctx.arc(x, y, opts.glowSize, 0, Math.PI * 2);
358
- ctx.fill();
359
403
  ctx.fillStyle = opts.headColor;
360
404
  ctx.beginPath();
361
405
  ctx.arc(x, y, opts.headRadius, 0, Math.PI * 2);
@@ -368,20 +412,18 @@ function createRenderer(options) {
368
412
  if (engine.morphAlpha !== null) {
369
413
  morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
370
414
  engine.setMorphAlpha(morphAlpha);
371
- if (morphBoundsA && morphBoundsB) {
372
- const a = morphBoundsA;
373
- const b = morphBoundsB;
374
- scale = a.scale + (b.scale - a.scale) * morphAlpha;
375
- offsetX = a.offsetX + (b.offsetX - a.offsetX) * morphAlpha;
376
- offsetY = a.offsetY + (b.offsetY - a.offsetY) * morphAlpha;
415
+ const interpolatedSkeleton = engine.getSarmalSkeleton();
416
+ const bounds = computeBoundaries(interpolatedSkeleton);
417
+ if (bounds) {
418
+ scale = bounds.scale;
419
+ offsetX = bounds.offsetX;
420
+ offsetY = bounds.offsetY;
377
421
  }
378
422
  if (morphAlpha >= 1) {
379
423
  engine.completeMorph();
380
424
  morphResolve?.();
381
425
  morphResolve = null;
382
426
  morphAlpha = 0;
383
- morphBoundsA = null;
384
- morphBoundsB = null;
385
427
  skeleton = engine.getSarmalSkeleton();
386
428
  if (!engine.isLiveSkeleton) {
387
429
  buildSkeletonCanvas();
@@ -391,7 +433,7 @@ function createRenderer(options) {
391
433
  trail = engine.tick(deltaTime);
392
434
  trailCount = engine.trailCount;
393
435
  head = trailCount > 0 ? trail[trailCount - 1] : null;
394
- ctx.clearRect(0, 0, canvas.width, canvas.height);
436
+ ctx.clearRect(0, 0, logicalWidth, logicalHeight);
395
437
  if (engine.isLiveSkeleton && engine.morphAlpha === null) {
396
438
  skeleton = engine.getSarmalSkeleton();
397
439
  calculateBoundaries();
@@ -439,57 +481,48 @@ function createRenderer(options) {
439
481
  engine.seekWithTrail(t);
440
482
  },
441
483
  morphTo(target, options2) {
442
- const interruptBounds = morphResolve !== null ? { scale, offsetX, offsetY } : null;
443
484
  if (morphResolve !== null) {
444
485
  engine.completeMorph();
445
486
  morphResolve();
446
487
  morphResolve = null;
447
488
  morphAlpha = 0;
448
- morphBoundsA = null;
449
- morphBoundsB = null;
450
489
  }
451
490
  morphDurationMs = options2?.duration ?? DEFAULT_MORPH_DURATION_MS;
452
491
  morphAlpha = 0;
453
- morphBoundsA = interruptBounds ?? computeBoundaries(engine.getSarmalSkeleton()) ?? { scale, offsetX, offsetY };
454
492
  engine.startMorph(target, options2?.morphStrategy);
455
- const period = target.period ?? Math.PI * 2;
456
- const samples = Math.max(50, Math.round(period * 20));
457
- const skeletonFn = target.skeletonFn ?? ((t) => target.fn(t, 0, {}));
458
- const skeletonB = Array.from(
459
- { length: samples + 1 },
460
- (_, i) => skeletonFn(i / samples * period)
461
- );
462
- morphBoundsB = computeBoundaries(skeletonB) ?? { scale, offsetX, offsetY };
463
493
  return new Promise((resolve) => {
464
494
  morphResolve = resolve;
465
495
  });
466
- }
496
+ },
467
497
  };
468
498
  }
469
499
 
470
500
  // src/curves.ts
471
501
  var TWO_PI2 = Math.PI * 2;
472
502
  function artemis2(t, _time, _params) {
473
- const a = 0.35, b = 0.15, ox = 0.175;
474
- const s = Math.sin(t), c = Math.cos(t);
503
+ const a = 0.35,
504
+ b = 0.15,
505
+ ox = 0.175;
506
+ const s = Math.sin(t),
507
+ c = Math.cos(t);
475
508
  const denom = 1 + s * s;
476
509
  return {
477
- x: c * (1 + a * c) / denom - ox,
478
- y: s * c * (1 + b * c) / denom
510
+ x: (c * (1 + a * c)) / denom - ox,
511
+ y: (s * c * (1 + b * c)) / denom,
479
512
  };
480
513
  }
481
514
  function epitrochoid7(t, _time, _params) {
482
515
  const d = 1 + 0.55 * Math.sin(t * 0.5);
483
516
  return {
484
517
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
485
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
518
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
486
519
  };
487
520
  }
488
521
  function epitrochoid7Skeleton(t) {
489
522
  const d = 1.275;
490
523
  return {
491
524
  x: 7 * Math.cos(t) - d * Math.cos(7 * t),
492
- y: 7 * Math.sin(t) - d * Math.sin(7 * t)
525
+ y: 7 * Math.sin(t) - d * Math.sin(7 * t),
493
526
  };
494
527
  }
495
528
  function astroid(t, _time, _params) {
@@ -497,55 +530,56 @@ function astroid(t, _time, _params) {
497
530
  const s = Math.sin(t);
498
531
  return {
499
532
  x: c * c * c,
500
- y: s * s * s
533
+ y: s * s * s,
501
534
  };
502
535
  }
503
536
  function deltoid(t, _time, _params) {
504
537
  return {
505
538
  x: 2 * Math.cos(t) + Math.cos(2 * t),
506
- y: 2 * Math.sin(t) - Math.sin(2 * t)
539
+ y: 2 * Math.sin(t) - Math.sin(2 * t),
507
540
  };
508
541
  }
509
542
  function rose5(t, _time, _params) {
510
543
  const r = Math.cos(5 * t);
511
544
  return {
512
545
  x: r * Math.cos(t),
513
- y: r * Math.sin(t)
546
+ y: r * Math.sin(t),
514
547
  };
515
548
  }
516
549
  function rose3(t, _time, _params) {
517
550
  const r = Math.cos(3 * t);
518
551
  return {
519
552
  x: r * Math.cos(t),
520
- y: r * Math.sin(t)
553
+ y: r * Math.sin(t),
521
554
  };
522
555
  }
523
556
  function lissajous32(t, time, _params) {
524
557
  const phi = time * 0.45;
525
558
  return {
526
559
  x: Math.sin(3 * t + phi),
527
- y: Math.sin(2 * t)
560
+ y: Math.sin(2 * t),
528
561
  };
529
562
  }
530
563
  function lissajous43(t, time, _params) {
531
564
  const phi = time * 0.38;
532
565
  return {
533
566
  x: Math.sin(4 * t + phi),
534
- y: Math.sin(3 * t)
567
+ y: Math.sin(3 * t),
535
568
  };
536
569
  }
537
570
  function epicycloid3(t, _time, _params) {
538
571
  return {
539
572
  x: 4 * Math.cos(t) - Math.cos(4 * t),
540
- y: 4 * Math.sin(t) - Math.sin(4 * t)
573
+ y: 4 * Math.sin(t) - Math.sin(4 * t),
541
574
  };
542
575
  }
543
576
  function lame(t, time, _params) {
544
577
  const p = 1.75 + 1.25 * Math.sin(time * 0.48);
545
- const c = Math.cos(t), s = Math.sin(t);
578
+ const c = Math.cos(t),
579
+ s = Math.sin(t);
546
580
  return {
547
581
  x: Math.sign(c) * Math.pow(Math.abs(c), p),
548
- y: Math.sign(s) * Math.pow(Math.abs(s), p)
582
+ y: Math.sign(s) * Math.pow(Math.abs(s), p),
549
583
  };
550
584
  }
551
585
  var curves = {
@@ -553,66 +587,66 @@ var curves = {
553
587
  name: "Artemis II",
554
588
  fn: artemis2,
555
589
  period: TWO_PI2,
556
- speed: 0.7
590
+ speed: 0.7,
557
591
  },
558
592
  epitrochoid7: {
559
593
  name: "Epitrochoid",
560
594
  fn: epitrochoid7,
561
595
  period: TWO_PI2,
562
596
  speed: 1.4,
563
- skeletonFn: epitrochoid7Skeleton
597
+ skeletonFn: epitrochoid7Skeleton,
564
598
  },
565
599
  astroid: {
566
600
  name: "Astroid",
567
601
  fn: astroid,
568
602
  period: TWO_PI2,
569
- speed: 1.1
603
+ speed: 1.1,
570
604
  },
571
605
  deltoid: {
572
606
  name: "Deltoid",
573
607
  fn: deltoid,
574
608
  period: TWO_PI2,
575
- speed: 0.9
609
+ speed: 0.9,
576
610
  },
577
611
  rose5: {
578
612
  name: "Rose (n=5)",
579
613
  fn: rose5,
580
614
  period: TWO_PI2,
581
- speed: 1
615
+ speed: 1,
582
616
  },
583
617
  rose3: {
584
618
  name: "Rose (n=3)",
585
619
  fn: rose3,
586
620
  period: TWO_PI2,
587
- speed: 1.15
621
+ speed: 1.15,
588
622
  },
589
623
  lissajous32: {
590
624
  name: "Lissajous 3:2",
591
625
  fn: lissajous32,
592
626
  period: TWO_PI2,
593
627
  speed: 2,
594
- skeleton: "live"
628
+ skeleton: "live",
595
629
  },
596
630
  lissajous43: {
597
631
  name: "Lissajous 4:3",
598
632
  fn: lissajous43,
599
633
  period: TWO_PI2,
600
634
  speed: 1.8,
601
- skeleton: "live"
635
+ skeleton: "live",
602
636
  },
603
637
  epicycloid3: {
604
638
  name: "Epicycloid (n=3)",
605
639
  fn: epicycloid3,
606
640
  period: TWO_PI2,
607
- speed: 0.75
641
+ speed: 0.75,
608
642
  },
609
643
  lame: {
610
644
  name: "Lam\xE9 Curve",
611
645
  fn: lame,
612
646
  period: TWO_PI2,
613
647
  speed: 1,
614
- skeleton: "live"
615
- }
648
+ skeleton: "live",
649
+ },
616
650
  };
617
651
 
618
652
  // src/index.ts
@@ -635,12 +669,12 @@ function init() {
635
669
  return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
636
670
  }
637
671
  const sarmal = createSarmal(canvas, curveDef, {
638
- ...canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor },
639
- ...canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor },
640
- ...canvas.dataset.headColor && { headColor: canvas.dataset.headColor },
641
- ...canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) },
642
- ...canvas.dataset.glowSize && { glowSize: parseInt(canvas.dataset.glowSize, 10) },
643
- ...canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) }
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) }),
644
678
  });
645
679
  sarmal.start();
646
680
  });
@@ -651,4 +685,4 @@ if (document.readyState === "loading") {
651
685
  init();
652
686
  }
653
687
  //# sourceMappingURL=auto-init.js.map
654
- //# sourceMappingURL=auto-init.js.map
688
+ //# sourceMappingURL=auto-init.js.map