@sarmal/core 0.2.1 → 0.4.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 +7 -0
- package/dist/auto-init.cjs +129 -29
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.js +129 -29
- package/dist/auto-init.js.map +1 -1
- package/dist/index.cjs +355 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -7
- package/dist/index.d.ts +83 -7
- package/dist/index.js +354 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var CircularBuffer = class {
|
|
|
9
9
|
this.data = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));
|
|
10
10
|
this.result = Array.from({ length: capacity }, () => ({ x: 0, y: 0 }));
|
|
11
11
|
}
|
|
12
|
-
/** Mutates
|
|
12
|
+
/** Mutates in-place */
|
|
13
13
|
push(x, y) {
|
|
14
14
|
const slot = this.data[this.head];
|
|
15
15
|
slot.x = x;
|
|
@@ -20,9 +20,9 @@ var CircularBuffer = class {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Copies ordered points into the pre-allocated result buffer and returns it
|
|
24
|
-
* The same array reference is returned every call
|
|
25
|
-
*
|
|
23
|
+
* Copies ordered points into the pre-allocated result buffer and returns it
|
|
24
|
+
* Note: The *same* array reference is returned every call,
|
|
25
|
+
* so `result.length` is also always `capacity`
|
|
26
26
|
*/
|
|
27
27
|
toArray() {
|
|
28
28
|
const start = this.count < this.capacity ? 0 : this.head;
|
|
@@ -68,6 +68,30 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
68
68
|
actualTime = 0;
|
|
69
69
|
trail.clear();
|
|
70
70
|
},
|
|
71
|
+
seek(newT, { clearTrail = false } = {}) {
|
|
72
|
+
t = (newT % curve.period + curve.period) % curve.period;
|
|
73
|
+
if (clearTrail) {
|
|
74
|
+
trail.clear();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
seekWithTrail(targetT, { wrap = false } = {}) {
|
|
78
|
+
const STEP = 1 / 60;
|
|
79
|
+
const advance = curve.speed * STEP;
|
|
80
|
+
const target = (targetT % curve.period + curve.period) % curve.period;
|
|
81
|
+
const targetTime = target / curve.speed;
|
|
82
|
+
t = target;
|
|
83
|
+
actualTime = targetTime;
|
|
84
|
+
trail.clear();
|
|
85
|
+
const pointsFromStart = Math.floor(target / advance) + 1;
|
|
86
|
+
const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
|
|
87
|
+
for (let step = count - 1; step >= 0; step--) {
|
|
88
|
+
const sampleT = target - step * advance;
|
|
89
|
+
const wrappedT = sampleT < 0 ? sampleT + curve.period : sampleT;
|
|
90
|
+
const time = targetTime - step * STEP;
|
|
91
|
+
const point = curve.fn(wrappedT, time, {});
|
|
92
|
+
trail.push(point.x, point.y);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
71
95
|
getSarmalSkeleton() {
|
|
72
96
|
const steps = Math.ceil(curve.period * POINTS_PER_PERIOD_UNIT);
|
|
73
97
|
const points = new Array(steps);
|
|
@@ -131,10 +155,18 @@ function createRenderer(options) {
|
|
|
131
155
|
const first = skeleton[0];
|
|
132
156
|
let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
|
|
133
157
|
for (const p of skeleton) {
|
|
134
|
-
if (p.x < minX)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (p.
|
|
158
|
+
if (p.x < minX) {
|
|
159
|
+
minX = p.x;
|
|
160
|
+
}
|
|
161
|
+
if (p.x > maxX) {
|
|
162
|
+
maxX = p.x;
|
|
163
|
+
}
|
|
164
|
+
if (p.y < minY) {
|
|
165
|
+
minY = p.y;
|
|
166
|
+
}
|
|
167
|
+
if (p.y > maxY) {
|
|
168
|
+
maxY = p.y;
|
|
169
|
+
}
|
|
138
170
|
}
|
|
139
171
|
const width = maxX - minX;
|
|
140
172
|
const height = maxY - minY;
|
|
@@ -151,38 +183,42 @@ function createRenderer(options) {
|
|
|
151
183
|
function buildSkeletonCanvas() {
|
|
152
184
|
if (skeleton.length < 2) return;
|
|
153
185
|
skeletonCanvas = new OffscreenCanvas(canvas.width, canvas.height);
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
186
|
+
const skeletonCtx = skeletonCanvas.getContext("2d");
|
|
187
|
+
skeletonCtx.strokeStyle = `rgba(${hexToRgbComponents(opts.skeletonColor)},${DEFAULT_SKELETON_OPACITY})`;
|
|
188
|
+
skeletonCtx.lineWidth = 1.5;
|
|
189
|
+
skeletonCtx.beginPath();
|
|
158
190
|
const first = skeleton[0];
|
|
159
|
-
|
|
191
|
+
skeletonCtx.moveTo(first.x * scale + offsetX, first.y * scale + offsetY);
|
|
160
192
|
for (let i = 1; i < skeleton.length; i++) {
|
|
161
193
|
const p = skeleton[i];
|
|
162
|
-
|
|
194
|
+
skeletonCtx.lineTo(p.x * scale + offsetX, p.y * scale + offsetY);
|
|
163
195
|
}
|
|
164
|
-
|
|
196
|
+
skeletonCtx.stroke();
|
|
165
197
|
}
|
|
166
198
|
function drawSkeleton() {
|
|
167
|
-
if (!skeletonCanvas)
|
|
199
|
+
if (!skeletonCanvas) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
168
202
|
ctx.drawImage(skeletonCanvas, 0, 0);
|
|
169
203
|
}
|
|
170
204
|
function drawTrail() {
|
|
171
|
-
if (trailCount < 2)
|
|
205
|
+
if (trailCount < 2) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
172
208
|
ctx.lineJoin = "round";
|
|
173
209
|
ctx.lineCap = "round";
|
|
174
|
-
for (let
|
|
175
|
-
const bEnd = Math.min(
|
|
176
|
-
const progress = (
|
|
210
|
+
for (let batchIndex = 0; batchIndex < trailCount - 1; batchIndex += TRAIL_BATCH_SIZE) {
|
|
211
|
+
const bEnd = Math.min(batchIndex + TRAIL_BATCH_SIZE, trailCount - 1);
|
|
212
|
+
const progress = (batchIndex + bEnd) / 2 / (trailCount - 1);
|
|
177
213
|
const alpha = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
178
214
|
const lineWidth = TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH);
|
|
179
215
|
ctx.beginPath();
|
|
180
|
-
for (let i =
|
|
181
|
-
const
|
|
182
|
-
if (i ===
|
|
183
|
-
ctx.moveTo(
|
|
216
|
+
for (let i = batchIndex; i <= bEnd; i++) {
|
|
217
|
+
const point = trail[i];
|
|
218
|
+
if (i === batchIndex) {
|
|
219
|
+
ctx.moveTo(point.x * scale + offsetX, point.y * scale + offsetY);
|
|
184
220
|
} else {
|
|
185
|
-
ctx.lineTo(
|
|
221
|
+
ctx.lineTo(point.x * scale + offsetX, point.y * scale + offsetY);
|
|
186
222
|
}
|
|
187
223
|
}
|
|
188
224
|
ctx.strokeStyle = `rgba(${trailRgb},${alpha})`;
|
|
@@ -191,7 +227,9 @@ function createRenderer(options) {
|
|
|
191
227
|
}
|
|
192
228
|
}
|
|
193
229
|
function drawHead() {
|
|
194
|
-
if (!head)
|
|
230
|
+
if (!head) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
195
233
|
const x = head.x * scale + offsetX;
|
|
196
234
|
const y = head.y * scale + offsetY;
|
|
197
235
|
const gradient = ctx.createRadialGradient(x, y, 0, x, y, opts.glowSize);
|
|
@@ -209,7 +247,7 @@ function createRenderer(options) {
|
|
|
209
247
|
}
|
|
210
248
|
function render() {
|
|
211
249
|
const now = performance.now();
|
|
212
|
-
const deltaTime = (now - lastTime) / 1e3;
|
|
250
|
+
const deltaTime = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
213
251
|
lastTime = now;
|
|
214
252
|
trail = engine.tick(deltaTime);
|
|
215
253
|
trailCount = engine.trailCount;
|
|
@@ -225,12 +263,16 @@ function createRenderer(options) {
|
|
|
225
263
|
buildSkeletonCanvas();
|
|
226
264
|
return {
|
|
227
265
|
start() {
|
|
228
|
-
if (animationId !== null)
|
|
266
|
+
if (animationId !== null) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
229
269
|
lastTime = performance.now();
|
|
230
270
|
render();
|
|
231
271
|
},
|
|
232
272
|
stop() {
|
|
233
|
-
if (animationId === null)
|
|
273
|
+
if (animationId === null) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
234
276
|
cancelAnimationFrame(animationId);
|
|
235
277
|
animationId = null;
|
|
236
278
|
},
|
|
@@ -244,10 +286,240 @@ function createRenderer(options) {
|
|
|
244
286
|
cancelAnimationFrame(animationId);
|
|
245
287
|
animationId = null;
|
|
246
288
|
}
|
|
289
|
+
},
|
|
290
|
+
seek(t, options2) {
|
|
291
|
+
engine.seek(t, options2);
|
|
292
|
+
},
|
|
293
|
+
seekWithTrail(t) {
|
|
294
|
+
engine.seekWithTrail(t);
|
|
247
295
|
}
|
|
248
296
|
};
|
|
249
297
|
}
|
|
250
298
|
|
|
299
|
+
// src/renderer-svg.ts
|
|
300
|
+
var TRAIL_BATCH_COUNT = 12;
|
|
301
|
+
var TRAIL_FADE_CURVE2 = 1.5;
|
|
302
|
+
var TRAIL_MAX_OPACITY2 = 0.88;
|
|
303
|
+
var TRAIL_MIN_WIDTH2 = 0.5;
|
|
304
|
+
var TRAIL_MAX_WIDTH2 = 2.5;
|
|
305
|
+
var DEFAULT_SKELETON_OPACITY2 = 0.15;
|
|
306
|
+
var DEFAULT_GLOW_INNER_STOP = 0.4;
|
|
307
|
+
var DEFAULT_GLOW_FALLOFF_OPACITY = 0.53;
|
|
308
|
+
var FIT_PADDING2 = 0.1;
|
|
309
|
+
var instanceCount = 0;
|
|
310
|
+
function el(tag) {
|
|
311
|
+
return document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
312
|
+
}
|
|
313
|
+
function createSVGRenderer(options) {
|
|
314
|
+
const { container, engine } = options;
|
|
315
|
+
const opts = {
|
|
316
|
+
skeletonColor: options.skeletonColor ?? "#ffffff",
|
|
317
|
+
trailColor: options.trailColor ?? "#ffffff",
|
|
318
|
+
headColor: options.headColor ?? "#ffffff",
|
|
319
|
+
headRadius: options.headRadius ?? 4,
|
|
320
|
+
glowSize: options.glowSize ?? 20,
|
|
321
|
+
ariaLabel: options.ariaLabel ?? "Loading"
|
|
322
|
+
};
|
|
323
|
+
const uid = ++instanceCount;
|
|
324
|
+
const gradientId = `sarmal-glow-${uid}`;
|
|
325
|
+
const rect = container.getBoundingClientRect();
|
|
326
|
+
const width = rect.width || 200;
|
|
327
|
+
const height = rect.height || 200;
|
|
328
|
+
const svg = el("svg");
|
|
329
|
+
svg.setAttribute("width", String(width));
|
|
330
|
+
svg.setAttribute("height", String(height));
|
|
331
|
+
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
332
|
+
svg.setAttribute("role", "img");
|
|
333
|
+
svg.setAttribute("aria-label", opts.ariaLabel);
|
|
334
|
+
const titleEl = el("title");
|
|
335
|
+
titleEl.textContent = opts.ariaLabel;
|
|
336
|
+
svg.appendChild(titleEl);
|
|
337
|
+
const defs = el("defs");
|
|
338
|
+
const gradient = el("radialGradient");
|
|
339
|
+
gradient.id = gradientId;
|
|
340
|
+
gradient.setAttribute("cx", "50%");
|
|
341
|
+
gradient.setAttribute("cy", "50%");
|
|
342
|
+
gradient.setAttribute("r", "50%");
|
|
343
|
+
const stop0 = el("stop");
|
|
344
|
+
stop0.setAttribute("offset", "0%");
|
|
345
|
+
stop0.setAttribute("stop-color", opts.headColor);
|
|
346
|
+
stop0.setAttribute("stop-opacity", "1");
|
|
347
|
+
const stopMid = el("stop");
|
|
348
|
+
stopMid.setAttribute("offset", `${DEFAULT_GLOW_INNER_STOP * 100}%`);
|
|
349
|
+
stopMid.setAttribute("stop-color", opts.headColor);
|
|
350
|
+
stopMid.setAttribute("stop-opacity", String(DEFAULT_GLOW_FALLOFF_OPACITY));
|
|
351
|
+
const stop1 = el("stop");
|
|
352
|
+
stop1.setAttribute("offset", "100%");
|
|
353
|
+
stop1.setAttribute("stop-color", opts.headColor);
|
|
354
|
+
stop1.setAttribute("stop-opacity", "0");
|
|
355
|
+
gradient.append(stop0, stopMid, stop1);
|
|
356
|
+
defs.appendChild(gradient);
|
|
357
|
+
svg.appendChild(defs);
|
|
358
|
+
const skeletonPath = el("path");
|
|
359
|
+
skeletonPath.setAttribute("fill", "none");
|
|
360
|
+
skeletonPath.setAttribute("stroke", opts.skeletonColor);
|
|
361
|
+
skeletonPath.setAttribute("stroke-opacity", String(DEFAULT_SKELETON_OPACITY2));
|
|
362
|
+
skeletonPath.setAttribute("stroke-width", "1.5");
|
|
363
|
+
svg.appendChild(skeletonPath);
|
|
364
|
+
const trailPaths = [];
|
|
365
|
+
for (let i = 0; i < TRAIL_BATCH_COUNT; i++) {
|
|
366
|
+
const path = el("path");
|
|
367
|
+
path.setAttribute("fill", "none");
|
|
368
|
+
path.setAttribute("stroke", opts.trailColor);
|
|
369
|
+
path.setAttribute("stroke-linecap", "round");
|
|
370
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
371
|
+
svg.appendChild(path);
|
|
372
|
+
trailPaths.push(path);
|
|
373
|
+
}
|
|
374
|
+
const glowCircle = el("circle");
|
|
375
|
+
glowCircle.setAttribute("fill", `url(#${gradientId})`);
|
|
376
|
+
glowCircle.setAttribute("r", String(opts.glowSize));
|
|
377
|
+
svg.appendChild(glowCircle);
|
|
378
|
+
const headCircle = el("circle");
|
|
379
|
+
headCircle.setAttribute("fill", opts.headColor);
|
|
380
|
+
headCircle.setAttribute("r", String(opts.headRadius));
|
|
381
|
+
svg.appendChild(headCircle);
|
|
382
|
+
container.appendChild(svg);
|
|
383
|
+
let scale = 1;
|
|
384
|
+
let offsetX = 0;
|
|
385
|
+
let offsetY = 0;
|
|
386
|
+
function calculateBoundaries(skeleton2) {
|
|
387
|
+
if (skeleton2.length === 0) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const first = skeleton2[0];
|
|
391
|
+
let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
|
|
392
|
+
for (const p of skeleton2) {
|
|
393
|
+
if (p.x < minX) {
|
|
394
|
+
minX = p.x;
|
|
395
|
+
}
|
|
396
|
+
if (p.x > maxX) {
|
|
397
|
+
maxX = p.x;
|
|
398
|
+
}
|
|
399
|
+
if (p.y < minY) {
|
|
400
|
+
minY = p.y;
|
|
401
|
+
}
|
|
402
|
+
if (p.y > maxY) {
|
|
403
|
+
maxY = p.y;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const w = maxX - minX;
|
|
407
|
+
const h = maxY - minY;
|
|
408
|
+
const scaleX = width / (w * (1 + FIT_PADDING2 * 2));
|
|
409
|
+
const scaleY = height / (h * (1 + FIT_PADDING2 * 2));
|
|
410
|
+
scale = Math.min(scaleX, scaleY);
|
|
411
|
+
offsetX = (width - w * scale) / 2 - minX * scale;
|
|
412
|
+
offsetY = (height - h * scale) / 2 - minY * scale;
|
|
413
|
+
}
|
|
414
|
+
function px(p) {
|
|
415
|
+
return (p.x * scale + offsetX).toFixed(2);
|
|
416
|
+
}
|
|
417
|
+
function py(p) {
|
|
418
|
+
return (p.y * scale + offsetY).toFixed(2);
|
|
419
|
+
}
|
|
420
|
+
const skeleton = engine.getSarmalSkeleton();
|
|
421
|
+
calculateBoundaries(skeleton);
|
|
422
|
+
if (skeleton.length >= 2) {
|
|
423
|
+
let d = `M${px(skeleton[0])} ${py(skeleton[0])}`;
|
|
424
|
+
for (let i = 1; i < skeleton.length; i++) {
|
|
425
|
+
d += ` L${px(skeleton[i])} ${py(skeleton[i])}`;
|
|
426
|
+
}
|
|
427
|
+
d += " Z";
|
|
428
|
+
skeletonPath.setAttribute("d", d);
|
|
429
|
+
}
|
|
430
|
+
function updateTrail(trail, trailCount) {
|
|
431
|
+
if (trailCount < 2) {
|
|
432
|
+
for (const p of trailPaths) {
|
|
433
|
+
p.setAttribute("d", "");
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const batchSize = Math.ceil(trailCount / TRAIL_BATCH_COUNT);
|
|
438
|
+
for (let b = 0; b < TRAIL_BATCH_COUNT; b++) {
|
|
439
|
+
const start = b * batchSize;
|
|
440
|
+
const end = Math.min(start + batchSize, trailCount - 1);
|
|
441
|
+
if (start >= trailCount - 1) {
|
|
442
|
+
trailPaths[b].setAttribute("d", "");
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const progress = (start + end) / 2 / (trailCount - 1);
|
|
446
|
+
const opacity = Math.pow(progress, TRAIL_FADE_CURVE2) * TRAIL_MAX_OPACITY2;
|
|
447
|
+
const strokeWidth = TRAIL_MIN_WIDTH2 + progress * (TRAIL_MAX_WIDTH2 - TRAIL_MIN_WIDTH2);
|
|
448
|
+
let d = `M${px(trail[start])} ${py(trail[start])}`;
|
|
449
|
+
for (let i = start + 1; i <= end; i++) {
|
|
450
|
+
d += ` L${px(trail[i])} ${py(trail[i])}`;
|
|
451
|
+
}
|
|
452
|
+
trailPaths[b].setAttribute("d", d);
|
|
453
|
+
trailPaths[b].setAttribute("stroke-opacity", opacity.toFixed(3));
|
|
454
|
+
trailPaths[b].setAttribute("stroke-width", strokeWidth.toFixed(2));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function updateHead(trail, trailCount) {
|
|
458
|
+
if (trailCount === 0) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const head = trail[trailCount - 1];
|
|
462
|
+
const x = px(head);
|
|
463
|
+
const y = py(head);
|
|
464
|
+
glowCircle.setAttribute("cx", x);
|
|
465
|
+
glowCircle.setAttribute("cy", y);
|
|
466
|
+
headCircle.setAttribute("cx", x);
|
|
467
|
+
headCircle.setAttribute("cy", y);
|
|
468
|
+
}
|
|
469
|
+
let animationId = null;
|
|
470
|
+
let lastTime = 0;
|
|
471
|
+
const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
472
|
+
function renderFrame() {
|
|
473
|
+
const now = performance.now();
|
|
474
|
+
const dt = Math.min((now - lastTime) / 1e3, 1 / 30);
|
|
475
|
+
lastTime = now;
|
|
476
|
+
const trail = engine.tick(dt);
|
|
477
|
+
const trailCount = engine.trailCount;
|
|
478
|
+
updateTrail(trail, trailCount);
|
|
479
|
+
updateHead(trail, trailCount);
|
|
480
|
+
if (!prefersReducedMotion) {
|
|
481
|
+
animationId = requestAnimationFrame(renderFrame);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
start() {
|
|
486
|
+
if (animationId !== null) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
lastTime = performance.now();
|
|
490
|
+
renderFrame();
|
|
491
|
+
},
|
|
492
|
+
stop() {
|
|
493
|
+
if (animationId === null) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
cancelAnimationFrame(animationId);
|
|
497
|
+
animationId = null;
|
|
498
|
+
},
|
|
499
|
+
reset() {
|
|
500
|
+
engine.reset();
|
|
501
|
+
},
|
|
502
|
+
destroy() {
|
|
503
|
+
if (animationId !== null) {
|
|
504
|
+
cancelAnimationFrame(animationId);
|
|
505
|
+
animationId = null;
|
|
506
|
+
}
|
|
507
|
+
svg.remove();
|
|
508
|
+
},
|
|
509
|
+
seek(t, options2) {
|
|
510
|
+
engine.seek(t, options2);
|
|
511
|
+
},
|
|
512
|
+
seekWithTrail(t) {
|
|
513
|
+
engine.seekWithTrail(t);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function createSarmalSVG(container, curveDef, options) {
|
|
518
|
+
const { trailLength, ...rendererOpts } = options ?? {};
|
|
519
|
+
const engine = createEngine(curveDef, trailLength);
|
|
520
|
+
return createSVGRenderer({ container, engine, ...rendererOpts });
|
|
521
|
+
}
|
|
522
|
+
|
|
251
523
|
// src/curves.ts
|
|
252
524
|
var TWO_PI2 = Math.PI * 2;
|
|
253
525
|
function artemis2(t, _time, _params) {
|
|
@@ -294,6 +566,34 @@ function rose3(t, _time, _params) {
|
|
|
294
566
|
y: r * Math.sin(t)
|
|
295
567
|
};
|
|
296
568
|
}
|
|
569
|
+
function lissajous32(t, time, _params) {
|
|
570
|
+
const phi = time * 0.45;
|
|
571
|
+
return {
|
|
572
|
+
x: Math.sin(3 * t + phi),
|
|
573
|
+
y: Math.sin(2 * t)
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function lissajous43(t, time, _params) {
|
|
577
|
+
const phi = time * 0.38;
|
|
578
|
+
return {
|
|
579
|
+
x: Math.sin(4 * t + phi),
|
|
580
|
+
y: Math.sin(3 * t)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function epicycloid3(t, _time, _params) {
|
|
584
|
+
return {
|
|
585
|
+
x: 4 * Math.cos(t) - Math.cos(4 * t),
|
|
586
|
+
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function lame(t, time, _params) {
|
|
590
|
+
const p = 1.75 + 1.25 * Math.sin(time * 0.48);
|
|
591
|
+
const c = Math.cos(t), s = Math.sin(t);
|
|
592
|
+
return {
|
|
593
|
+
x: Math.sign(c) * Math.pow(Math.abs(c), p),
|
|
594
|
+
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
297
597
|
var curves = {
|
|
298
598
|
artemis2: {
|
|
299
599
|
name: "Artemis II",
|
|
@@ -330,6 +630,30 @@ var curves = {
|
|
|
330
630
|
fn: rose3,
|
|
331
631
|
period: TWO_PI2,
|
|
332
632
|
speed: 1.15
|
|
633
|
+
},
|
|
634
|
+
lissajous32: {
|
|
635
|
+
name: "Lissajous 3:2",
|
|
636
|
+
fn: lissajous32,
|
|
637
|
+
period: TWO_PI2,
|
|
638
|
+
speed: 2
|
|
639
|
+
},
|
|
640
|
+
lissajous43: {
|
|
641
|
+
name: "Lissajous 4:3",
|
|
642
|
+
fn: lissajous43,
|
|
643
|
+
period: TWO_PI2,
|
|
644
|
+
speed: 1.8
|
|
645
|
+
},
|
|
646
|
+
epicycloid3: {
|
|
647
|
+
name: "Epicycloid (n=3)",
|
|
648
|
+
fn: epicycloid3,
|
|
649
|
+
period: TWO_PI2,
|
|
650
|
+
speed: 0.75
|
|
651
|
+
},
|
|
652
|
+
lame: {
|
|
653
|
+
name: "Lam\xE9 Curve",
|
|
654
|
+
fn: lame,
|
|
655
|
+
period: TWO_PI2,
|
|
656
|
+
speed: 1
|
|
333
657
|
}
|
|
334
658
|
};
|
|
335
659
|
|
|
@@ -340,6 +664,6 @@ function createSarmal(canvas, curveDef, options) {
|
|
|
340
664
|
return createRenderer({ canvas, engine, ...rendererOpts });
|
|
341
665
|
}
|
|
342
666
|
|
|
343
|
-
export { createEngine, createRenderer, createSarmal, curves };
|
|
667
|
+
export { createEngine, createRenderer, createSVGRenderer, createSarmal, createSarmalSVG, curves };
|
|
344
668
|
//# sourceMappingURL=index.js.map
|
|
345
669
|
//# sourceMappingURL=index.js.map
|