@sarmal/core 0.9.1 → 0.9.10
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.cjs +196 -157
- package/dist/auto-init.cjs.map +1 -1
- package/dist/auto-init.d.cts +2 -1
- package/dist/auto-init.d.ts +2 -1
- package/dist/auto-init.js +195 -156
- package/dist/auto-init.js.map +1 -1
- package/dist/curves/artemis2.cjs +7 -10
- package/dist/curves/artemis2.d.cts +1 -1
- package/dist/curves/artemis2.d.ts +1 -1
- package/dist/curves/artemis2.js +6 -9
- package/dist/curves/astroid.cjs +4 -4
- package/dist/curves/astroid.d.cts +1 -1
- package/dist/curves/astroid.d.ts +1 -1
- package/dist/curves/astroid.js +3 -3
- package/dist/curves/deltoid.cjs +4 -4
- package/dist/curves/deltoid.d.cts +1 -1
- package/dist/curves/deltoid.d.ts +1 -1
- package/dist/curves/deltoid.js +3 -3
- package/dist/curves/epicycloid3.cjs +4 -4
- package/dist/curves/epicycloid3.d.cts +1 -1
- package/dist/curves/epicycloid3.d.ts +1 -1
- package/dist/curves/epicycloid3.js +3 -3
- package/dist/curves/epitrochoid7.cjs +5 -5
- package/dist/curves/epitrochoid7.d.cts +1 -1
- package/dist/curves/epitrochoid7.d.ts +1 -1
- package/dist/curves/epitrochoid7.js +4 -4
- package/dist/curves/index.cjs +28 -32
- package/dist/curves/index.d.cts +11 -11
- package/dist/curves/index.d.ts +11 -11
- package/dist/curves/index.js +28 -44
- package/dist/curves/lame.cjs +5 -6
- package/dist/curves/lame.d.cts +1 -1
- package/dist/curves/lame.d.ts +1 -1
- package/dist/curves/lame.js +4 -5
- package/dist/curves/lissajous32.cjs +4 -4
- package/dist/curves/lissajous32.d.cts +1 -1
- package/dist/curves/lissajous32.d.ts +1 -1
- package/dist/curves/lissajous32.js +3 -3
- package/dist/curves/lissajous43.cjs +4 -4
- package/dist/curves/lissajous43.d.cts +1 -1
- package/dist/curves/lissajous43.d.ts +1 -1
- package/dist/curves/lissajous43.js +3 -3
- package/dist/curves/rose3.cjs +4 -4
- package/dist/curves/rose3.d.cts +1 -1
- package/dist/curves/rose3.d.ts +1 -1
- package/dist/curves/rose3.js +3 -3
- package/dist/curves/rose5.cjs +4 -4
- package/dist/curves/rose5.d.cts +1 -1
- package/dist/curves/rose5.d.ts +1 -1
- package/dist/curves/rose5.js +3 -3
- package/dist/index.cjs +233 -265
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -64
- package/dist/index.d.ts +31 -64
- package/dist/index.js +233 -282
- package/dist/index.js.map +1 -1
- package/dist/types-cR2xOewv.d.cts +223 -0
- package/dist/types-cR2xOewv.d.ts +223 -0
- package/package.json +5 -3
- package/dist/types-DX8VfIVK.d.cts +0 -226
- package/dist/types-DX8VfIVK.d.ts +0 -226
package/dist/auto-init.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// src/engine.ts
|
|
2
2
|
var TWO_PI = Math.PI * 2;
|
|
3
3
|
var POINTS_PER_PERIOD_UNIT = 50;
|
|
4
|
+
function lerp(start, end, t) {
|
|
5
|
+
return start + (end - start) * t;
|
|
6
|
+
}
|
|
7
|
+
var EMPTY_PARAMS = {};
|
|
4
8
|
var CircularBuffer = class {
|
|
5
9
|
constructor(capacity) {
|
|
6
10
|
this.head = 0;
|
|
@@ -43,16 +47,29 @@ var CircularBuffer = class {
|
|
|
43
47
|
}
|
|
44
48
|
};
|
|
45
49
|
function resolveCurve(curveDef) {
|
|
50
|
+
const period = curveDef.period ?? TWO_PI;
|
|
51
|
+
if (!Number.isFinite(period) || period <= 0) {
|
|
52
|
+
throw new RangeError(`[sarmal] period must be a positive finite number, got ${period}`);
|
|
53
|
+
}
|
|
54
|
+
const speed = curveDef.speed ?? 1;
|
|
55
|
+
if (!Number.isFinite(speed)) {
|
|
56
|
+
throw new RangeError(`[sarmal] speed must be a finite number, got ${speed}`);
|
|
57
|
+
}
|
|
46
58
|
return {
|
|
47
59
|
name: curveDef.name,
|
|
48
60
|
fn: curveDef.fn,
|
|
49
|
-
period
|
|
50
|
-
speed
|
|
61
|
+
period,
|
|
62
|
+
speed,
|
|
51
63
|
skeleton: curveDef.skeleton,
|
|
52
|
-
skeletonFn: curveDef.skeletonFn
|
|
64
|
+
skeletonFn: curveDef.skeletonFn
|
|
53
65
|
};
|
|
54
66
|
}
|
|
55
67
|
function createEngine(curveDef, trailLength = 120) {
|
|
68
|
+
if (!Number.isFinite(trailLength) || trailLength <= 0) {
|
|
69
|
+
throw new RangeError(
|
|
70
|
+
`[sarmal] trailLength must be a positive finite number, got ${trailLength}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
56
73
|
let curve = resolveCurve(curveDef);
|
|
57
74
|
const trail = new CircularBuffer(trailLength);
|
|
58
75
|
let t = 0;
|
|
@@ -65,21 +82,25 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
65
82
|
return c.skeletonFn(sampleT);
|
|
66
83
|
}
|
|
67
84
|
if (c.skeleton === "live") {
|
|
68
|
-
return c.fn(sampleT, actualTime,
|
|
85
|
+
return c.fn(sampleT, actualTime, EMPTY_PARAMS);
|
|
69
86
|
}
|
|
70
|
-
return c.fn(sampleT, 0,
|
|
87
|
+
return c.fn(sampleT, 0, EMPTY_PARAMS);
|
|
71
88
|
}
|
|
72
89
|
return {
|
|
73
90
|
tick(deltaTime) {
|
|
74
|
-
|
|
91
|
+
let effectiveSpeed = curve.speed;
|
|
92
|
+
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
93
|
+
effectiveSpeed = lerp(curve.speed, morphCurveB.speed, _morphAlpha);
|
|
94
|
+
}
|
|
95
|
+
t = (t + effectiveSpeed * deltaTime) % curve.period;
|
|
75
96
|
actualTime += deltaTime;
|
|
76
97
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
77
|
-
const a = curve.fn(t, actualTime,
|
|
78
|
-
const tB = _morphStrategy === "normalized" ?
|
|
79
|
-
const b = morphCurveB.fn(tB, actualTime,
|
|
98
|
+
const a = curve.fn(t, actualTime, EMPTY_PARAMS);
|
|
99
|
+
const tB = _morphStrategy === "normalized" ? t / curve.period * morphCurveB.period : t;
|
|
100
|
+
const b = morphCurveB.fn(tB, actualTime, EMPTY_PARAMS);
|
|
80
101
|
trail.push(a.x + (b.x - a.x) * _morphAlpha, a.y + (b.y - a.y) * _morphAlpha);
|
|
81
102
|
} else {
|
|
82
|
-
const point = curve.fn(t, actualTime,
|
|
103
|
+
const point = curve.fn(t, actualTime, EMPTY_PARAMS);
|
|
83
104
|
trail.push(point.x, point.y);
|
|
84
105
|
}
|
|
85
106
|
return trail.toArray();
|
|
@@ -99,14 +120,14 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
99
120
|
trail.clear();
|
|
100
121
|
},
|
|
101
122
|
seek(newT, { clearTrail = false } = {}) {
|
|
102
|
-
t = (
|
|
123
|
+
t = (newT % curve.period + curve.period) % curve.period;
|
|
103
124
|
if (clearTrail) {
|
|
104
125
|
trail.clear();
|
|
105
126
|
}
|
|
106
127
|
},
|
|
107
128
|
seekWithTrail(targetT, { wrap = false, step = curve.period / trailLength } = {}) {
|
|
108
129
|
const advance = curve.speed * step;
|
|
109
|
-
const target = (
|
|
130
|
+
const target = (targetT % curve.period + curve.period) % curve.period;
|
|
110
131
|
const targetTime = target / curve.speed;
|
|
111
132
|
t = target;
|
|
112
133
|
actualTime = targetTime;
|
|
@@ -115,9 +136,9 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
115
136
|
const count = wrap ? trailLength : Math.min(trailLength, pointsFromStart);
|
|
116
137
|
for (let i = count - 1; i >= 0; i--) {
|
|
117
138
|
const sampleT = target - i * advance;
|
|
118
|
-
const wrappedT = sampleT
|
|
139
|
+
const wrappedT = (sampleT % curve.period + curve.period) % curve.period;
|
|
119
140
|
const time = targetTime - i * step;
|
|
120
|
-
const point = curve.fn(wrappedT, time,
|
|
141
|
+
const point = curve.fn(wrappedT, time, EMPTY_PARAMS);
|
|
121
142
|
trail.push(point.x, point.y);
|
|
122
143
|
}
|
|
123
144
|
},
|
|
@@ -132,16 +153,13 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
132
153
|
...frozenB,
|
|
133
154
|
fn: (sampleT, time, params) => {
|
|
134
155
|
const a = frozenA.fn(sampleT, time, params);
|
|
135
|
-
const tB =
|
|
136
|
-
frozenStrategy === "normalized"
|
|
137
|
-
? (sampleT / frozenA.period) * frozenB.period
|
|
138
|
-
: sampleT;
|
|
156
|
+
const tB = frozenStrategy === "normalized" ? sampleT / frozenA.period * frozenB.period : sampleT;
|
|
139
157
|
const b = frozenB.fn(tB, time, params);
|
|
140
158
|
return {
|
|
141
159
|
x: a.x + (b.x - a.x) * frozenAlpha,
|
|
142
|
-
y: a.y + (b.y - a.y) * frozenAlpha
|
|
160
|
+
y: a.y + (b.y - a.y) * frozenAlpha
|
|
143
161
|
};
|
|
144
|
-
}
|
|
162
|
+
}
|
|
145
163
|
};
|
|
146
164
|
}
|
|
147
165
|
_morphStrategy = strategy;
|
|
@@ -154,7 +172,7 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
154
172
|
completeMorph() {
|
|
155
173
|
if (morphCurveB !== null) {
|
|
156
174
|
if (_morphStrategy === "normalized" && curve.period !== morphCurveB.period) {
|
|
157
|
-
t =
|
|
175
|
+
t = t / curve.period * morphCurveB.period;
|
|
158
176
|
}
|
|
159
177
|
curve = morphCurveB;
|
|
160
178
|
}
|
|
@@ -166,46 +184,132 @@ function createEngine(curveDef, trailLength = 120) {
|
|
|
166
184
|
const points = new Array(steps);
|
|
167
185
|
if (morphCurveB !== null && _morphAlpha !== null) {
|
|
168
186
|
for (let i = 0; i < steps; i++) {
|
|
169
|
-
const sampleT =
|
|
187
|
+
const sampleT = i / (steps - 1) * curve.period;
|
|
170
188
|
const a = sampleSkeleton(curve, sampleT);
|
|
171
|
-
const tB =
|
|
172
|
-
_morphStrategy === "normalized"
|
|
173
|
-
? (sampleT / curve.period) * morphCurveB.period
|
|
174
|
-
: sampleT;
|
|
189
|
+
const tB = _morphStrategy === "normalized" ? sampleT / curve.period * morphCurveB.period : sampleT;
|
|
175
190
|
const b = sampleSkeleton(morphCurveB, tB);
|
|
176
191
|
points[i] = {
|
|
177
192
|
x: a.x + (b.x - a.x) * _morphAlpha,
|
|
178
|
-
y: a.y + (b.y - a.y) * _morphAlpha
|
|
193
|
+
y: a.y + (b.y - a.y) * _morphAlpha
|
|
179
194
|
};
|
|
180
195
|
}
|
|
181
196
|
return points;
|
|
182
197
|
}
|
|
183
198
|
for (let i = 0; i < steps; i++) {
|
|
184
|
-
const sampleT =
|
|
199
|
+
const sampleT = i / (steps - 1) * curve.period;
|
|
185
200
|
points[i] = sampleSkeleton(curve, sampleT);
|
|
186
201
|
}
|
|
187
202
|
return points;
|
|
188
|
-
}
|
|
203
|
+
}
|
|
189
204
|
};
|
|
190
205
|
}
|
|
191
206
|
|
|
192
|
-
// src/renderer.ts
|
|
207
|
+
// src/renderer-shared.ts
|
|
193
208
|
var DEFAULT_MORPH_DURATION_MS = 300;
|
|
194
|
-
var DEFAULT_HEAD_RADIUS = 4;
|
|
195
|
-
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
196
209
|
var DEFAULT_SKELETON_OPACITY = 0.15;
|
|
197
210
|
var FIT_PADDING = 0.1;
|
|
198
211
|
var TRAIL_FADE_CURVE = 1.5;
|
|
199
212
|
var TRAIL_MAX_OPACITY = 0.88;
|
|
200
213
|
var TRAIL_MIN_WIDTH = 0.5;
|
|
201
214
|
var TRAIL_MAX_WIDTH = 2.5;
|
|
215
|
+
function computeTangent(trail, i) {
|
|
216
|
+
const count = trail.length;
|
|
217
|
+
if (count < 2) {
|
|
218
|
+
return { x: 1, y: 0 };
|
|
219
|
+
}
|
|
220
|
+
if (i === 0) {
|
|
221
|
+
const dx2 = trail[1].x - trail[0].x;
|
|
222
|
+
const dy2 = trail[1].y - trail[0].y;
|
|
223
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
224
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
225
|
+
}
|
|
226
|
+
if (i === count - 1) {
|
|
227
|
+
const dx2 = trail[count - 1].x - trail[count - 2].x;
|
|
228
|
+
const dy2 = trail[count - 1].y - trail[count - 2].y;
|
|
229
|
+
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
230
|
+
return { x: dx2 / len2, y: dy2 / len2 };
|
|
231
|
+
}
|
|
232
|
+
const dx = trail[i + 1].x - trail[i - 1].x;
|
|
233
|
+
const dy = trail[i + 1].y - trail[i - 1].y;
|
|
234
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
235
|
+
return { x: dx / len, y: dy / len };
|
|
236
|
+
}
|
|
237
|
+
function computeNormal(trail, i) {
|
|
238
|
+
const tangent = computeTangent(trail, i);
|
|
239
|
+
return { x: -tangent.y, y: tangent.x };
|
|
240
|
+
}
|
|
241
|
+
function computeTrailQuad(trail, i, trailCount, toX, toY) {
|
|
242
|
+
const progress = i / (trailCount - 1);
|
|
243
|
+
const nextProgress = (i + 1) / (trailCount - 1);
|
|
244
|
+
const opacity = Math.pow(progress, TRAIL_FADE_CURVE) * TRAIL_MAX_OPACITY;
|
|
245
|
+
const w0 = (TRAIL_MIN_WIDTH + progress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH)) / 2;
|
|
246
|
+
const w1 = (TRAIL_MIN_WIDTH + nextProgress * (TRAIL_MAX_WIDTH - TRAIL_MIN_WIDTH)) / 2;
|
|
247
|
+
const curr = trail[i];
|
|
248
|
+
const next = trail[i + 1];
|
|
249
|
+
const n0 = computeNormal(trail, i);
|
|
250
|
+
const n1 = computeNormal(trail, i + 1);
|
|
251
|
+
const cx = toX(curr);
|
|
252
|
+
const cy = toY(curr);
|
|
253
|
+
const nx = toX(next);
|
|
254
|
+
const ny = toY(next);
|
|
255
|
+
return {
|
|
256
|
+
l0x: cx + n0.x * w0,
|
|
257
|
+
l0y: cy + n0.y * w0,
|
|
258
|
+
r0x: cx - n0.x * w0,
|
|
259
|
+
r0y: cy - n0.y * w0,
|
|
260
|
+
l1x: nx + n1.x * w1,
|
|
261
|
+
l1y: ny + n1.y * w1,
|
|
262
|
+
r1x: nx - n1.x * w1,
|
|
263
|
+
r1y: ny - n1.y * w1,
|
|
264
|
+
opacity,
|
|
265
|
+
progress
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function computeBoundaries(pts, logicalWidth, logicalHeight) {
|
|
269
|
+
if (pts.length === 0) return null;
|
|
270
|
+
const first = pts[0];
|
|
271
|
+
let minX = first.x, maxX = first.x, minY = first.y, maxY = first.y;
|
|
272
|
+
for (const p of pts) {
|
|
273
|
+
if (p.x < minX) {
|
|
274
|
+
minX = p.x;
|
|
275
|
+
}
|
|
276
|
+
if (p.x > maxX) {
|
|
277
|
+
maxX = p.x;
|
|
278
|
+
}
|
|
279
|
+
if (p.y < minY) {
|
|
280
|
+
minY = p.y;
|
|
281
|
+
}
|
|
282
|
+
if (p.y > maxY) {
|
|
283
|
+
maxY = p.y;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const w = maxX - minX;
|
|
287
|
+
const h = maxY - minY;
|
|
288
|
+
if (w === 0 && h === 0) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"[sarmal] Degenerate curve: all skeleton points are identical. Check that your curve fn returns distinct points for different values of t."
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const scaleX = logicalWidth / (w * (1 + FIT_PADDING * 2));
|
|
294
|
+
const scaleY = logicalHeight / (h * (1 + FIT_PADDING * 2));
|
|
295
|
+
const scale = Math.min(scaleX, scaleY);
|
|
296
|
+
return {
|
|
297
|
+
scale,
|
|
298
|
+
offsetX: (logicalWidth - w * scale) / 2 - minX * scale,
|
|
299
|
+
offsetY: (logicalHeight - h * scale) / 2 - minY * scale
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/renderer.ts
|
|
304
|
+
var DEFAULT_HEAD_RADIUS = 4;
|
|
305
|
+
var DEFAULT_SKELETON_COLOR = "#ffffff";
|
|
202
306
|
var GRADIENT = {
|
|
203
307
|
bard: ["#a855f7", "#3b82f6", "#14b8a6", "#ec4899"],
|
|
204
308
|
sunset: ["#f97316", "#dc2626", "#9333ea", "#f472b6"],
|
|
205
309
|
ocean: ["#1e3a8a", "#06b6d4", "#22d3ee", "#e0f2fe"],
|
|
206
310
|
ice: ["#1e3a8a", "#67e8f9"],
|
|
207
311
|
fire: ["#7f1d1d", "#fbbf24"],
|
|
208
|
-
forest: ["#14532d", "#86efac"]
|
|
312
|
+
forest: ["#14532d", "#86efac"]
|
|
209
313
|
};
|
|
210
314
|
var PRESETS = {
|
|
211
315
|
bard: GRADIENT.bard,
|
|
@@ -213,16 +317,16 @@ var PRESETS = {
|
|
|
213
317
|
ocean: GRADIENT.ocean,
|
|
214
318
|
ice: GRADIENT.ice,
|
|
215
319
|
fire: GRADIENT.fire,
|
|
216
|
-
forest: GRADIENT.forest
|
|
320
|
+
forest: GRADIENT.forest
|
|
217
321
|
};
|
|
218
322
|
function hexToRgb(hex) {
|
|
219
323
|
const n = parseInt(hex.slice(1), 16);
|
|
220
|
-
return { r: n >> 16, g:
|
|
324
|
+
return { r: n >> 16, g: n >> 8 & 255, b: n & 255 };
|
|
221
325
|
}
|
|
222
326
|
var lerpRgb = (a, b, t) => ({
|
|
223
327
|
r: Math.round(a.r + (b.r - a.r) * t),
|
|
224
328
|
g: Math.round(a.g + (b.g - a.g) * t),
|
|
225
|
-
b: Math.round(a.b + (b.b - a.b) * t)
|
|
329
|
+
b: Math.round(a.b + (b.b - a.b) * t)
|
|
226
330
|
});
|
|
227
331
|
function getPaletteColor(palette, position, timeOffset = 0) {
|
|
228
332
|
if (palette.length === 0) return { r: 255, g: 255, b: 255 };
|
|
@@ -242,33 +346,7 @@ function resolvePalette(palette, trailStyle) {
|
|
|
242
346
|
}
|
|
243
347
|
function hexToRgbComponents(hex) {
|
|
244
348
|
const n = parseInt(hex.slice(1), 16);
|
|
245
|
-
return `${n >> 16},${
|
|
246
|
-
}
|
|
247
|
-
function computeTangent(trail, i) {
|
|
248
|
-
const count = trail.length;
|
|
249
|
-
if (count < 2) {
|
|
250
|
-
return { x: 1, y: 0 };
|
|
251
|
-
}
|
|
252
|
-
if (i === 0) {
|
|
253
|
-
const dx2 = trail[1].x - trail[0].x;
|
|
254
|
-
const dy2 = trail[1].y - trail[0].y;
|
|
255
|
-
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
256
|
-
return { x: dx2 / len2, y: dy2 / len2 };
|
|
257
|
-
}
|
|
258
|
-
if (i === count - 1) {
|
|
259
|
-
const dx2 = trail[count - 1].x - trail[count - 2].x;
|
|
260
|
-
const dy2 = trail[count - 1].y - trail[count - 2].y;
|
|
261
|
-
const len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2) || 1;
|
|
262
|
-
return { x: dx2 / len2, y: dy2 / len2 };
|
|
263
|
-
}
|
|
264
|
-
const dx = trail[i + 1].x - trail[i - 1].x;
|
|
265
|
-
const dy = trail[i + 1].y - trail[i - 1].y;
|
|
266
|
-
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
267
|
-
return { x: dx / len, y: dy / len };
|
|
268
|
-
}
|
|
269
|
-
function computeNormal(trail, i) {
|
|
270
|
-
const tangent = computeTangent(trail, i);
|
|
271
|
-
return { x: -tangent.y, y: tangent.x };
|
|
349
|
+
return `${n >> 16},${n >> 8 & 255},${n & 255}`;
|
|
272
350
|
}
|
|
273
351
|
function applyDprSizing(target, logicalWidth, logicalHeight, dpr) {
|
|
274
352
|
target.style.width = `${logicalWidth}px`;
|
|
@@ -287,7 +365,7 @@ function createRenderer(options) {
|
|
|
287
365
|
skeletonColor: options.skeletonColor ?? DEFAULT_SKELETON_COLOR,
|
|
288
366
|
trailColor: options.trailColor ?? "#ffffff",
|
|
289
367
|
headColor: options.headColor ?? "#ffffff",
|
|
290
|
-
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
|
|
368
|
+
headRadius: options.headRadius ?? DEFAULT_HEAD_RADIUS
|
|
291
369
|
};
|
|
292
370
|
const trailStyle = options.trailStyle ?? "default";
|
|
293
371
|
const palette = resolvePalette(options.palette, trailStyle);
|
|
@@ -317,34 +395,8 @@ function createRenderer(options) {
|
|
|
317
395
|
let morphDurationMs = DEFAULT_MORPH_DURATION_MS;
|
|
318
396
|
let morphAlpha = 0;
|
|
319
397
|
let gradientAnimTime = 0;
|
|
320
|
-
function computeBoundaries(pts) {
|
|
321
|
-
if (pts.length === 0) return null;
|
|
322
|
-
const first = pts[0];
|
|
323
|
-
let minX = first.x,
|
|
324
|
-
maxX = first.x,
|
|
325
|
-
minY = first.y,
|
|
326
|
-
maxY = first.y;
|
|
327
|
-
for (const p of pts) {
|
|
328
|
-
if (p.x < minX) minX = p.x;
|
|
329
|
-
if (p.x > maxX) maxX = p.x;
|
|
330
|
-
if (p.y < minY) minY = p.y;
|
|
331
|
-
if (p.y > maxY) maxY = p.y;
|
|
332
|
-
}
|
|
333
|
-
const width = maxX - minX;
|
|
334
|
-
const height = maxY - minY;
|
|
335
|
-
const scaleX = logicalWidth / (width * (1 + FIT_PADDING * 2));
|
|
336
|
-
const scaleY = logicalHeight / (height * (1 + FIT_PADDING * 2));
|
|
337
|
-
const s = Math.min(scaleX, scaleY);
|
|
338
|
-
const boundsWidth = width * s;
|
|
339
|
-
const boundsHeight = height * s;
|
|
340
|
-
return {
|
|
341
|
-
scale: s,
|
|
342
|
-
offsetX: (logicalWidth - boundsWidth) / 2 - minX * s,
|
|
343
|
-
offsetY: (logicalHeight - boundsHeight) / 2 - minY * s,
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
398
|
function calculateBoundaries() {
|
|
347
|
-
const b = computeBoundaries(skeleton);
|
|
399
|
+
const b = computeBoundaries(skeleton, logicalWidth, logicalHeight);
|
|
348
400
|
if (b) {
|
|
349
401
|
scale = b.scale;
|
|
350
402
|
offsetX = b.offsetX;
|
|
@@ -408,32 +460,22 @@ function createRenderer(options) {
|
|
|
408
460
|
if (trailCount < 2) {
|
|
409
461
|
return;
|
|
410
462
|
}
|
|
463
|
+
const toX = (p) => p.x * scale + offsetX;
|
|
464
|
+
const toY = (p) => p.y * scale + offsetY;
|
|
411
465
|
for (let i = 0; i < trailCount - 1; i++) {
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const n0 = computeNormal(trail, i);
|
|
420
|
-
const n1 = computeNormal(trail, i + 1);
|
|
421
|
-
const halfW0 = width / 2;
|
|
422
|
-
const halfW1 = nextWidth / 2;
|
|
423
|
-
const l0x = curr.x * scale + offsetX + n0.x * halfW0;
|
|
424
|
-
const l0y = curr.y * scale + offsetY + n0.y * halfW0;
|
|
425
|
-
const r0x = curr.x * scale + offsetX - n0.x * halfW0;
|
|
426
|
-
const r0y = curr.y * scale + offsetY - n0.y * halfW0;
|
|
427
|
-
const l1x = next.x * scale + offsetX + n1.x * halfW1;
|
|
428
|
-
const l1y = next.y * scale + offsetY + n1.y * halfW1;
|
|
429
|
-
const r1x = next.x * scale + offsetX - n1.x * halfW1;
|
|
430
|
-
const r1y = next.y * scale + offsetY - n1.y * halfW1;
|
|
466
|
+
const { l0x, l0y, r0x, r0y, l1x, l1y, r1x, r1y, opacity, progress } = computeTrailQuad(
|
|
467
|
+
trail,
|
|
468
|
+
i,
|
|
469
|
+
trailCount,
|
|
470
|
+
toX,
|
|
471
|
+
toY
|
|
472
|
+
);
|
|
431
473
|
if (trailStyle === "default") {
|
|
432
|
-
ctx.fillStyle = `rgba(${trailRgb},${
|
|
474
|
+
ctx.fillStyle = `rgba(${trailRgb},${opacity})`;
|
|
433
475
|
} else {
|
|
434
476
|
const timeOffset = trailStyle === "gradient-animated" ? gradientAnimTime * 5e-4 : 0;
|
|
435
477
|
const color = getPaletteColor(palette, progress, timeOffset);
|
|
436
|
-
ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${
|
|
478
|
+
ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${opacity})`;
|
|
437
479
|
}
|
|
438
480
|
ctx.beginPath();
|
|
439
481
|
ctx.moveTo(l0x, l0y);
|
|
@@ -466,7 +508,7 @@ function createRenderer(options) {
|
|
|
466
508
|
morphAlpha = Math.min(1, morphAlpha + deltaTime / (morphDurationMs / 1e3));
|
|
467
509
|
engine.setMorphAlpha(morphAlpha);
|
|
468
510
|
const interpolatedSkeleton = engine.getSarmalSkeleton();
|
|
469
|
-
const bounds = computeBoundaries(interpolatedSkeleton);
|
|
511
|
+
const bounds = computeBoundaries(interpolatedSkeleton, logicalWidth, logicalHeight);
|
|
470
512
|
if (bounds) {
|
|
471
513
|
scale = bounds.scale;
|
|
472
514
|
offsetX = bounds.offsetX;
|
|
@@ -546,29 +588,26 @@ function createRenderer(options) {
|
|
|
546
588
|
return new Promise((resolve) => {
|
|
547
589
|
morphResolve = resolve;
|
|
548
590
|
});
|
|
549
|
-
}
|
|
591
|
+
}
|
|
550
592
|
};
|
|
551
593
|
}
|
|
552
594
|
|
|
553
595
|
// src/curves/artemis2.ts
|
|
554
596
|
var TWO_PI2 = Math.PI * 2;
|
|
555
597
|
function artemis2Fn(t, _time, _params) {
|
|
556
|
-
const a = 0.35,
|
|
557
|
-
|
|
558
|
-
ox = 0.175;
|
|
559
|
-
const s = Math.sin(t),
|
|
560
|
-
c = Math.cos(t);
|
|
598
|
+
const a = 0.35, b = 0.15, ox = 0.175;
|
|
599
|
+
const s = Math.sin(t), c = Math.cos(t);
|
|
561
600
|
const denom = 1 + s * s;
|
|
562
601
|
return {
|
|
563
|
-
x:
|
|
564
|
-
y:
|
|
602
|
+
x: c * (1 + a * c) / denom - ox,
|
|
603
|
+
y: s * c * (1 + b * c) / denom
|
|
565
604
|
};
|
|
566
605
|
}
|
|
567
606
|
var artemis2 = {
|
|
568
607
|
name: "Artemis II",
|
|
569
608
|
fn: artemis2Fn,
|
|
570
609
|
period: TWO_PI2,
|
|
571
|
-
speed: 0.7
|
|
610
|
+
speed: 0.7
|
|
572
611
|
};
|
|
573
612
|
|
|
574
613
|
// src/curves/astroid.ts
|
|
@@ -578,14 +617,14 @@ function astroidFn(t, _time, _params) {
|
|
|
578
617
|
const s = Math.sin(t);
|
|
579
618
|
return {
|
|
580
619
|
x: c * c * c,
|
|
581
|
-
y: s * s * s
|
|
620
|
+
y: s * s * s
|
|
582
621
|
};
|
|
583
622
|
}
|
|
584
623
|
var astroid = {
|
|
585
624
|
name: "Astroid",
|
|
586
625
|
fn: astroidFn,
|
|
587
626
|
period: TWO_PI3,
|
|
588
|
-
speed: 1.1
|
|
627
|
+
speed: 1.1
|
|
589
628
|
};
|
|
590
629
|
|
|
591
630
|
// src/curves/deltoid.ts
|
|
@@ -593,14 +632,14 @@ var TWO_PI4 = Math.PI * 2;
|
|
|
593
632
|
function deltoidFn(t, _time, _params) {
|
|
594
633
|
return {
|
|
595
634
|
x: 2 * Math.cos(t) + Math.cos(2 * t),
|
|
596
|
-
y: 2 * Math.sin(t) - Math.sin(2 * t)
|
|
635
|
+
y: 2 * Math.sin(t) - Math.sin(2 * t)
|
|
597
636
|
};
|
|
598
637
|
}
|
|
599
638
|
var deltoid = {
|
|
600
639
|
name: "Deltoid",
|
|
601
640
|
fn: deltoidFn,
|
|
602
641
|
period: TWO_PI4,
|
|
603
|
-
speed: 0.9
|
|
642
|
+
speed: 0.9
|
|
604
643
|
};
|
|
605
644
|
|
|
606
645
|
// src/curves/epicycloid3.ts
|
|
@@ -608,14 +647,14 @@ var TWO_PI5 = Math.PI * 2;
|
|
|
608
647
|
function epicycloid3Fn(t, _time, _params) {
|
|
609
648
|
return {
|
|
610
649
|
x: 4 * Math.cos(t) - Math.cos(4 * t),
|
|
611
|
-
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
650
|
+
y: 4 * Math.sin(t) - Math.sin(4 * t)
|
|
612
651
|
};
|
|
613
652
|
}
|
|
614
653
|
var epicycloid3 = {
|
|
615
654
|
name: "Epicycloid (n=3)",
|
|
616
655
|
fn: epicycloid3Fn,
|
|
617
656
|
period: TWO_PI5,
|
|
618
|
-
speed: 0.75
|
|
657
|
+
speed: 0.75
|
|
619
658
|
};
|
|
620
659
|
|
|
621
660
|
// src/curves/epitrochoid7.ts
|
|
@@ -624,14 +663,14 @@ function epitrochoid7Fn(t, _time, _params) {
|
|
|
624
663
|
const d = 1 + 0.55 * Math.sin(t * 0.5);
|
|
625
664
|
return {
|
|
626
665
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
627
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
666
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
628
667
|
};
|
|
629
668
|
}
|
|
630
669
|
function epitrochoid7SkeletonFn(t) {
|
|
631
670
|
const d = 1.275;
|
|
632
671
|
return {
|
|
633
672
|
x: 7 * Math.cos(t) - d * Math.cos(7 * t),
|
|
634
|
-
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
673
|
+
y: 7 * Math.sin(t) - d * Math.sin(7 * t)
|
|
635
674
|
};
|
|
636
675
|
}
|
|
637
676
|
var epitrochoid7 = {
|
|
@@ -639,7 +678,7 @@ var epitrochoid7 = {
|
|
|
639
678
|
fn: epitrochoid7Fn,
|
|
640
679
|
period: TWO_PI6,
|
|
641
680
|
speed: 1.4,
|
|
642
|
-
skeletonFn: epitrochoid7SkeletonFn
|
|
681
|
+
skeletonFn: epitrochoid7SkeletonFn
|
|
643
682
|
};
|
|
644
683
|
|
|
645
684
|
// src/curves/lissajous32.ts
|
|
@@ -648,7 +687,7 @@ function lissajous32Fn(t, time, _params) {
|
|
|
648
687
|
const phi = time * 0.45;
|
|
649
688
|
return {
|
|
650
689
|
x: Math.sin(3 * t + phi),
|
|
651
|
-
y: Math.sin(2 * t)
|
|
690
|
+
y: Math.sin(2 * t)
|
|
652
691
|
};
|
|
653
692
|
}
|
|
654
693
|
var lissajous32 = {
|
|
@@ -656,7 +695,7 @@ var lissajous32 = {
|
|
|
656
695
|
fn: lissajous32Fn,
|
|
657
696
|
period: TWO_PI7,
|
|
658
697
|
speed: 2,
|
|
659
|
-
skeleton: "live"
|
|
698
|
+
skeleton: "live"
|
|
660
699
|
};
|
|
661
700
|
|
|
662
701
|
// src/curves/lissajous43.ts
|
|
@@ -665,7 +704,7 @@ function lissajous43Fn(t, time, _params) {
|
|
|
665
704
|
const phi = time * 0.38;
|
|
666
705
|
return {
|
|
667
706
|
x: Math.sin(4 * t + phi),
|
|
668
|
-
y: Math.sin(3 * t)
|
|
707
|
+
y: Math.sin(3 * t)
|
|
669
708
|
};
|
|
670
709
|
}
|
|
671
710
|
var lissajous43 = {
|
|
@@ -673,18 +712,17 @@ var lissajous43 = {
|
|
|
673
712
|
fn: lissajous43Fn,
|
|
674
713
|
period: TWO_PI8,
|
|
675
714
|
speed: 1.8,
|
|
676
|
-
skeleton: "live"
|
|
715
|
+
skeleton: "live"
|
|
677
716
|
};
|
|
678
717
|
|
|
679
718
|
// src/curves/lame.ts
|
|
680
719
|
var TWO_PI9 = Math.PI * 2;
|
|
681
720
|
function lameFn(t, time, _params) {
|
|
682
721
|
const p = 1.75 + 1.25 * Math.sin(time * 0.48);
|
|
683
|
-
const c = Math.cos(t),
|
|
684
|
-
s = Math.sin(t);
|
|
722
|
+
const c = Math.cos(t), s = Math.sin(t);
|
|
685
723
|
return {
|
|
686
724
|
x: Math.sign(c) * Math.pow(Math.abs(c), p),
|
|
687
|
-
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
725
|
+
y: Math.sign(s) * Math.pow(Math.abs(s), p)
|
|
688
726
|
};
|
|
689
727
|
}
|
|
690
728
|
var lame = {
|
|
@@ -692,7 +730,7 @@ var lame = {
|
|
|
692
730
|
fn: lameFn,
|
|
693
731
|
period: TWO_PI9,
|
|
694
732
|
speed: 1,
|
|
695
|
-
skeleton: "live"
|
|
733
|
+
skeleton: "live"
|
|
696
734
|
};
|
|
697
735
|
|
|
698
736
|
// src/curves/rose3.ts
|
|
@@ -701,14 +739,14 @@ function rose3Fn(t, _time, _params) {
|
|
|
701
739
|
const r = Math.cos(3 * t);
|
|
702
740
|
return {
|
|
703
741
|
x: r * Math.cos(t),
|
|
704
|
-
y: r * Math.sin(t)
|
|
742
|
+
y: r * Math.sin(t)
|
|
705
743
|
};
|
|
706
744
|
}
|
|
707
745
|
var rose3 = {
|
|
708
746
|
name: "Rose (n=3)",
|
|
709
747
|
fn: rose3Fn,
|
|
710
748
|
period: TWO_PI10,
|
|
711
|
-
speed: 1.15
|
|
749
|
+
speed: 1.15
|
|
712
750
|
};
|
|
713
751
|
|
|
714
752
|
// src/curves/rose5.ts
|
|
@@ -717,14 +755,14 @@ function rose5Fn(t, _time, _params) {
|
|
|
717
755
|
const r = Math.cos(5 * t);
|
|
718
756
|
return {
|
|
719
757
|
x: r * Math.cos(t),
|
|
720
|
-
y: r * Math.sin(t)
|
|
758
|
+
y: r * Math.sin(t)
|
|
721
759
|
};
|
|
722
760
|
}
|
|
723
761
|
var rose5 = {
|
|
724
762
|
name: "Rose (n=5)",
|
|
725
763
|
fn: rose5Fn,
|
|
726
764
|
period: TWO_PI11,
|
|
727
|
-
speed: 1
|
|
765
|
+
speed: 1
|
|
728
766
|
};
|
|
729
767
|
|
|
730
768
|
// src/curves/index.ts
|
|
@@ -738,7 +776,7 @@ var curves = {
|
|
|
738
776
|
lissajous32,
|
|
739
777
|
lissajous43,
|
|
740
778
|
epicycloid3,
|
|
741
|
-
lame
|
|
779
|
+
lame
|
|
742
780
|
};
|
|
743
781
|
|
|
744
782
|
// src/index.ts
|
|
@@ -755,7 +793,8 @@ function parsePalette(value) {
|
|
|
755
793
|
if (Array.isArray(parsed)) {
|
|
756
794
|
return parsed;
|
|
757
795
|
}
|
|
758
|
-
} catch {
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
759
798
|
return value;
|
|
760
799
|
}
|
|
761
800
|
function init() {
|
|
@@ -763,22 +802,22 @@ function init() {
|
|
|
763
802
|
canvases.forEach((canvas) => {
|
|
764
803
|
const curveName = canvas.getAttribute("data-sarmal");
|
|
765
804
|
if (curveName == null) {
|
|
766
|
-
return console.warn("[sarmal] curveName
|
|
805
|
+
return console.warn("[sarmal] curveName is required");
|
|
767
806
|
}
|
|
768
807
|
const curveDef = curves[curveName];
|
|
769
808
|
if (!curveDef) {
|
|
770
809
|
return console.error(`[sarmal] "${curveName}" is not a valid curve name`);
|
|
771
810
|
}
|
|
772
811
|
const sarmal = createSarmal(canvas, curveDef, {
|
|
773
|
-
...
|
|
774
|
-
...
|
|
775
|
-
...
|
|
776
|
-
...
|
|
777
|
-
...
|
|
778
|
-
...
|
|
779
|
-
trailStyle: canvas.dataset.trailStyle
|
|
780
|
-
}
|
|
781
|
-
...
|
|
812
|
+
...canvas.dataset.trailColor && { trailColor: canvas.dataset.trailColor },
|
|
813
|
+
...canvas.dataset.skeletonColor && { skeletonColor: canvas.dataset.skeletonColor },
|
|
814
|
+
...canvas.dataset.headColor && { headColor: canvas.dataset.headColor },
|
|
815
|
+
...canvas.dataset.headRadius && { headRadius: parseFloat(canvas.dataset.headRadius) },
|
|
816
|
+
...canvas.dataset.trailLength && { trailLength: parseInt(canvas.dataset.trailLength, 10) },
|
|
817
|
+
...canvas.dataset.trailStyle && {
|
|
818
|
+
trailStyle: canvas.dataset.trailStyle
|
|
819
|
+
},
|
|
820
|
+
...canvas.dataset.palette && { palette: parsePalette(canvas.dataset.palette) }
|
|
782
821
|
});
|
|
783
822
|
sarmal.start();
|
|
784
823
|
});
|
|
@@ -791,4 +830,4 @@ if (document.readyState === "loading") {
|
|
|
791
830
|
requestAnimationFrame(init);
|
|
792
831
|
}
|
|
793
832
|
//# sourceMappingURL=auto-init.js.map
|
|
794
|
-
//# sourceMappingURL=auto-init.js.map
|
|
833
|
+
//# sourceMappingURL=auto-init.js.map
|