@clipkit/runtime 1.0.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/LICENSE +54 -0
- package/README.md +98 -0
- package/dist/animation/easings.d.ts +9 -0
- package/dist/animation/easings.d.ts.map +1 -0
- package/dist/animation/easings.js +230 -0
- package/dist/animation/easings.js.map +1 -0
- package/dist/animation/expr.d.ts +44 -0
- package/dist/animation/expr.d.ts.map +1 -0
- package/dist/animation/expr.js +236 -0
- package/dist/animation/expr.js.map +1 -0
- package/dist/animation/keyframes.d.ts +23 -0
- package/dist/animation/keyframes.d.ts.map +1 -0
- package/dist/animation/keyframes.js +117 -0
- package/dist/animation/keyframes.js.map +1 -0
- package/dist/animation/motion-path.d.ts +18 -0
- package/dist/animation/motion-path.d.ts.map +1 -0
- package/dist/animation/motion-path.js +269 -0
- package/dist/animation/motion-path.js.map +1 -0
- package/dist/animation/noise1d.d.ts +5 -0
- package/dist/animation/noise1d.d.ts.map +1 -0
- package/dist/animation/noise1d.js +27 -0
- package/dist/animation/noise1d.js.map +1 -0
- package/dist/animation/presets.d.ts +60 -0
- package/dist/animation/presets.d.ts.map +1 -0
- package/dist/animation/presets.js +221 -0
- package/dist/animation/presets.js.map +1 -0
- package/dist/assets/cache.d.ts +18 -0
- package/dist/assets/cache.d.ts.map +1 -0
- package/dist/assets/cache.js +56 -0
- package/dist/assets/cache.js.map +1 -0
- package/dist/assets/fonts.d.ts +20 -0
- package/dist/assets/fonts.d.ts.map +1 -0
- package/dist/assets/fonts.js +127 -0
- package/dist/assets/fonts.js.map +1 -0
- package/dist/assets/loader.d.ts +18 -0
- package/dist/assets/loader.d.ts.map +1 -0
- package/dist/assets/loader.js +87 -0
- package/dist/assets/loader.js.map +1 -0
- package/dist/assets/lut.d.ts +5 -0
- package/dist/assets/lut.d.ts.map +1 -0
- package/dist/assets/lut.js +77 -0
- package/dist/assets/lut.js.map +1 -0
- package/dist/assets/media-time.d.ts +31 -0
- package/dist/assets/media-time.d.ts.map +1 -0
- package/dist/assets/media-time.js +65 -0
- package/dist/assets/media-time.js.map +1 -0
- package/dist/assets/mp4-frame-source.d.ts +44 -0
- package/dist/assets/mp4-frame-source.d.ts.map +1 -0
- package/dist/assets/mp4-frame-source.js +387 -0
- package/dist/assets/mp4-frame-source.js.map +1 -0
- package/dist/audio/encoder.d.ts +31 -0
- package/dist/audio/encoder.d.ts.map +1 -0
- package/dist/audio/encoder.js +96 -0
- package/dist/audio/encoder.js.map +1 -0
- package/dist/audio/fades.d.ts +16 -0
- package/dist/audio/fades.d.ts.map +1 -0
- package/dist/audio/fades.js +43 -0
- package/dist/audio/fades.js.map +1 -0
- package/dist/audio/limiter.d.ts +8 -0
- package/dist/audio/limiter.d.ts.map +1 -0
- package/dist/audio/limiter.js +39 -0
- package/dist/audio/limiter.js.map +1 -0
- package/dist/audio/loader.d.ts +6 -0
- package/dist/audio/loader.d.ts.map +1 -0
- package/dist/audio/loader.js +42 -0
- package/dist/audio/loader.js.map +1 -0
- package/dist/audio/mixer.d.ts +17 -0
- package/dist/audio/mixer.d.ts.map +1 -0
- package/dist/audio/mixer.js +204 -0
- package/dist/audio/mixer.js.map +1 -0
- package/dist/audio/varispeed.d.ts +24 -0
- package/dist/audio/varispeed.d.ts.map +1 -0
- package/dist/audio/varispeed.js +114 -0
- package/dist/audio/varispeed.js.map +1 -0
- package/dist/audio/wav.d.ts +6 -0
- package/dist/audio/wav.d.ts.map +1 -0
- package/dist/audio/wav.js +62 -0
- package/dist/audio/wav.js.map +1 -0
- package/dist/backend/backend.d.ts +579 -0
- package/dist/backend/backend.d.ts.map +1 -0
- package/dist/backend/backend.js +17 -0
- package/dist/backend/backend.js.map +1 -0
- package/dist/backend/webgl-backend.d.ts +97 -0
- package/dist/backend/webgl-backend.d.ts.map +1 -0
- package/dist/backend/webgl-backend.js +2142 -0
- package/dist/backend/webgl-backend.js.map +1 -0
- package/dist/backend/webgpu-backend.d.ts +121 -0
- package/dist/backend/webgpu-backend.d.ts.map +1 -0
- package/dist/backend/webgpu-backend.js +2481 -0
- package/dist/backend/webgpu-backend.js.map +1 -0
- package/dist/compositor/bitfont.d.ts +8 -0
- package/dist/compositor/bitfont.d.ts.map +1 -0
- package/dist/compositor/bitfont.js +52 -0
- package/dist/compositor/bitfont.js.map +1 -0
- package/dist/compositor/camera.d.ts +5 -0
- package/dist/compositor/camera.d.ts.map +1 -0
- package/dist/compositor/camera.js +114 -0
- package/dist/compositor/camera.js.map +1 -0
- package/dist/compositor/color.d.ts +26 -0
- package/dist/compositor/color.d.ts.map +1 -0
- package/dist/compositor/color.js +189 -0
- package/dist/compositor/color.js.map +1 -0
- package/dist/compositor/element-renderers/caption.d.ts +4 -0
- package/dist/compositor/element-renderers/caption.d.ts.map +1 -0
- package/dist/compositor/element-renderers/caption.js +376 -0
- package/dist/compositor/element-renderers/caption.js.map +1 -0
- package/dist/compositor/element-renderers/group.d.ts +12 -0
- package/dist/compositor/element-renderers/group.d.ts.map +1 -0
- package/dist/compositor/element-renderers/group.js +259 -0
- package/dist/compositor/element-renderers/group.js.map +1 -0
- package/dist/compositor/element-renderers/image.d.ts +4 -0
- package/dist/compositor/element-renderers/image.d.ts.map +1 -0
- package/dist/compositor/element-renderers/image.js +97 -0
- package/dist/compositor/element-renderers/image.js.map +1 -0
- package/dist/compositor/element-renderers/lit.d.ts +6 -0
- package/dist/compositor/element-renderers/lit.d.ts.map +1 -0
- package/dist/compositor/element-renderers/lit.js +82 -0
- package/dist/compositor/element-renderers/lit.js.map +1 -0
- package/dist/compositor/element-renderers/particles.d.ts +4 -0
- package/dist/compositor/element-renderers/particles.d.ts.map +1 -0
- package/dist/compositor/element-renderers/particles.js +212 -0
- package/dist/compositor/element-renderers/particles.js.map +1 -0
- package/dist/compositor/element-renderers/shape.d.ts +4 -0
- package/dist/compositor/element-renderers/shape.d.ts.map +1 -0
- package/dist/compositor/element-renderers/shape.js +171 -0
- package/dist/compositor/element-renderers/shape.js.map +1 -0
- package/dist/compositor/element-renderers/svg.d.ts +4 -0
- package/dist/compositor/element-renderers/svg.d.ts.map +1 -0
- package/dist/compositor/element-renderers/svg.js +210 -0
- package/dist/compositor/element-renderers/svg.js.map +1 -0
- package/dist/compositor/element-renderers/text.d.ts +25 -0
- package/dist/compositor/element-renderers/text.d.ts.map +1 -0
- package/dist/compositor/element-renderers/text.js +1358 -0
- package/dist/compositor/element-renderers/text.js.map +1 -0
- package/dist/compositor/element-renderers/video.d.ts +12 -0
- package/dist/compositor/element-renderers/video.d.ts.map +1 -0
- package/dist/compositor/element-renderers/video.js +109 -0
- package/dist/compositor/element-renderers/video.js.map +1 -0
- package/dist/compositor/fit.d.ts +18 -0
- package/dist/compositor/fit.d.ts.map +1 -0
- package/dist/compositor/fit.js +106 -0
- package/dist/compositor/fit.js.map +1 -0
- package/dist/compositor/lighting.d.ts +63 -0
- package/dist/compositor/lighting.d.ts.map +1 -0
- package/dist/compositor/lighting.js +141 -0
- package/dist/compositor/lighting.js.map +1 -0
- package/dist/compositor/mat4.d.ts +88 -0
- package/dist/compositor/mat4.d.ts.map +1 -0
- package/dist/compositor/mat4.js +245 -0
- package/dist/compositor/mat4.js.map +1 -0
- package/dist/compositor/project.d.ts +24 -0
- package/dist/compositor/project.d.ts.map +1 -0
- package/dist/compositor/project.js +105 -0
- package/dist/compositor/project.js.map +1 -0
- package/dist/compositor/render-context.d.ts +194 -0
- package/dist/compositor/render-context.d.ts.map +1 -0
- package/dist/compositor/render-context.js +10 -0
- package/dist/compositor/render-context.js.map +1 -0
- package/dist/compositor/resolve.d.ts +80 -0
- package/dist/compositor/resolve.d.ts.map +1 -0
- package/dist/compositor/resolve.js +276 -0
- package/dist/compositor/resolve.js.map +1 -0
- package/dist/compositor/scene.d.ts +10 -0
- package/dist/compositor/scene.d.ts.map +1 -0
- package/dist/compositor/scene.js +658 -0
- package/dist/compositor/scene.js.map +1 -0
- package/dist/compositor/transform.d.ts +73 -0
- package/dist/compositor/transform.d.ts.map +1 -0
- package/dist/compositor/transform.js +229 -0
- package/dist/compositor/transform.js.map +1 -0
- package/dist/compositor/unit.d.ts +27 -0
- package/dist/compositor/unit.d.ts.map +1 -0
- package/dist/compositor/unit.js +74 -0
- package/dist/compositor/unit.js.map +1 -0
- package/dist/encoder/exporter.d.ts +95 -0
- package/dist/encoder/exporter.d.ts.map +1 -0
- package/dist/encoder/exporter.js +341 -0
- package/dist/encoder/exporter.js.map +1 -0
- package/dist/encoder/index.d.ts +3 -0
- package/dist/encoder/index.d.ts.map +1 -0
- package/dist/encoder/index.js +2 -0
- package/dist/encoder/index.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/logger.js.map +1 -0
- package/dist/runtime.d.ts +216 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1012 -0
- package/dist/runtime.js.map +1 -0
- package/dist/svg/morph.d.ts +6 -0
- package/dist/svg/morph.d.ts.map +1 -0
- package/dist/svg/morph.js +62 -0
- package/dist/svg/morph.js.map +1 -0
- package/dist/svg/svg-renderer.d.ts +18 -0
- package/dist/svg/svg-renderer.d.ts.map +1 -0
- package/dist/svg/svg-renderer.js +142 -0
- package/dist/svg/svg-renderer.js.map +1 -0
- package/dist/text/caption-chunk.d.ts +17 -0
- package/dist/text/caption-chunk.d.ts.map +1 -0
- package/dist/text/caption-chunk.js +76 -0
- package/dist/text/caption-chunk.js.map +1 -0
- package/dist/text/font-atlas.d.ts +63 -0
- package/dist/text/font-atlas.d.ts.map +1 -0
- package/dist/text/font-atlas.js +225 -0
- package/dist/text/font-atlas.js.map +1 -0
- package/dist/text/measure.d.ts +38 -0
- package/dist/text/measure.d.ts.map +1 -0
- package/dist/text/measure.js +164 -0
- package/dist/text/measure.js.map +1 -0
- package/dist/text/text-animation.d.ts +52 -0
- package/dist/text/text-animation.d.ts.map +1 -0
- package/dist/text/text-animation.js +133 -0
- package/dist/text/text-animation.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Particles renderer — Clipkit extension.
|
|
2
|
+
//
|
|
3
|
+
// Pure-function-of-time simulation: every particle's position, rotation,
|
|
4
|
+
// size, and color is deterministically derived from (element id, particle
|
|
5
|
+
// index, age). No state carried frame-to-frame, so seeking backward and
|
|
6
|
+
// rendering arbitrary frames works without resimulation.
|
|
7
|
+
//
|
|
8
|
+
// Each frame:
|
|
9
|
+
// 1. Enumerate which particle indices are currently alive.
|
|
10
|
+
// 2. For each, run simulateParticle(n, age) → {x, y, rotation, size, color, alpha}.
|
|
11
|
+
// 3. Emit one backend.drawShape per particle.
|
|
12
|
+
//
|
|
13
|
+
// Active-window enumeration is O(maxAlive) per frame regardless of total
|
|
14
|
+
// emission count — for continuous mode the alive window slides forward in
|
|
15
|
+
// time, so we only iterate ceil(lifetime × rate) indices.
|
|
16
|
+
import { parseColor } from '../color.js';
|
|
17
|
+
import { mat4ApplyToPoint, mat4Multiply, mat4PlaneAt, mat4Rotation, quadWorldTransform } from '../mat4.js';
|
|
18
|
+
import { resolveLength } from '../unit.js';
|
|
19
|
+
import { applyAnimation, resolve3D } from '../resolve.js';
|
|
20
|
+
import { applyEasing } from '../../animation/easings.js';
|
|
21
|
+
export function renderParticlesElement(element, ctx) {
|
|
22
|
+
const { canvas, backend } = ctx;
|
|
23
|
+
const elementStart = ctx.timeOffset + numberOr(element.time, 0);
|
|
24
|
+
const localTime = ctx.time - elementStart;
|
|
25
|
+
if (localTime < 0)
|
|
26
|
+
return;
|
|
27
|
+
// Emitter position (animatable like any other element).
|
|
28
|
+
const emitterX = applyAnimation(element, 'x', resolveLength(element.x, canvas.width, canvas), ctx);
|
|
29
|
+
const emitterY = applyAnimation(element, 'y', resolveLength(element.y, canvas.height, canvas), ctx);
|
|
30
|
+
// Per-particle depth (CKP/1.0, §5.7.3): vz along the emitter plane's
|
|
31
|
+
// normal. Authoring either field puts the element on the matrix path
|
|
32
|
+
// (like element `z`, depth is invisible without perspective).
|
|
33
|
+
const zVelocity = numberOr(element.z_velocity, 0);
|
|
34
|
+
const zSpread = numberOr(element.z_spread, 0);
|
|
35
|
+
const hasZDynamics = zVelocity !== 0 || zSpread !== 0;
|
|
36
|
+
// CKP/1.0 3D (§4.4): the whole particle field tilts as one plane
|
|
37
|
+
// anchored at the emitter.
|
|
38
|
+
const t3d = resolve3D(element, ctx);
|
|
39
|
+
const planeChain = t3d !== null || !ctx.modelMatrix.aff || hasZDynamics
|
|
40
|
+
? mat4Multiply(ctx.modelMatrix, mat4PlaneAt(emitterX, emitterY, t3d?.z ?? 0, 0, t3d?.yRot ?? 0, t3d?.xRot ?? 0))
|
|
41
|
+
: null;
|
|
42
|
+
// Config with defaults.
|
|
43
|
+
const rate = Math.max(1, numberOr(element.rate, 60));
|
|
44
|
+
const lifetime = Math.max(0.01, numberOr(element.lifetime, 1.5));
|
|
45
|
+
const velocity = numberOr(element.velocity, 300);
|
|
46
|
+
const spread = numberOr(element.spread, 360);
|
|
47
|
+
const direction = numberOr(element.direction, -90);
|
|
48
|
+
const gravity = numberOr(element.gravity, 600);
|
|
49
|
+
const size = Math.max(1, numberOr(element.size, 12));
|
|
50
|
+
const sizeVariation = clamp01(numberOr(element.size_variation, 0.4));
|
|
51
|
+
const rotSpeed = numberOr(element.rotation_speed, 360);
|
|
52
|
+
const burst = element.burst === true;
|
|
53
|
+
const burstCount = Math.max(1, Math.floor(numberOr(element.burst_count, 80)));
|
|
54
|
+
const fadeAt = clamp01(numberOr(element.fade_at, 0.7));
|
|
55
|
+
const isCircle = element.particle_shape === 'circle';
|
|
56
|
+
// Convergence mode setup.
|
|
57
|
+
const targetPoints = Array.isArray(element.target_points) ? element.target_points : null;
|
|
58
|
+
const convergenceEasing = element.convergence_easing ?? 'ease-out-quart';
|
|
59
|
+
const scatterRadius = numberOr(element.scatter_radius, Math.max(canvas.width, canvas.height));
|
|
60
|
+
const colors = normalizeColors(element.color);
|
|
61
|
+
const elementOpacity = clamp01(applyAnimation(element, 'opacity', numberOr(element.opacity, 1), ctx) * ctx.opacityFactor);
|
|
62
|
+
if (elementOpacity <= 0)
|
|
63
|
+
return;
|
|
64
|
+
// Stable seed per element so the particle field is deterministic across frames.
|
|
65
|
+
const seedBase = hashString(typeof element.id === 'string' ? element.id : 'particles');
|
|
66
|
+
// Enumerate active particle indices.
|
|
67
|
+
// Burst mode: all particles share spawnTime=0, age=localTime.
|
|
68
|
+
// Continuous mode: particle n spawns at n/rate; alive when 0 ≤ age < lifetime.
|
|
69
|
+
let firstN;
|
|
70
|
+
let lastN;
|
|
71
|
+
if (burst) {
|
|
72
|
+
if (localTime >= lifetime)
|
|
73
|
+
return;
|
|
74
|
+
firstN = 0;
|
|
75
|
+
lastN = burstCount - 1;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
firstN = Math.max(0, Math.ceil((localTime - lifetime) * rate));
|
|
79
|
+
lastN = Math.floor(localTime * rate);
|
|
80
|
+
}
|
|
81
|
+
for (let n = firstN; n <= lastN; n++) {
|
|
82
|
+
const spawnTime = burst ? 0 : n / rate;
|
|
83
|
+
const age = localTime - spawnTime;
|
|
84
|
+
if (age < 0 || age >= lifetime)
|
|
85
|
+
continue;
|
|
86
|
+
// 6 independent random values per particle.
|
|
87
|
+
const rng = mulberry32(seedBase + n * 0x9e3779b9);
|
|
88
|
+
const r1 = rng(); // angle within spread / scatter x
|
|
89
|
+
const r2 = rng(); // speed jitter / scatter y
|
|
90
|
+
const r3 = rng(); // rotation direction
|
|
91
|
+
const r4 = rng(); // size
|
|
92
|
+
const r5 = rng(); // color index
|
|
93
|
+
const r6 = rng(); // z-velocity spread (§5.7.3)
|
|
94
|
+
let px;
|
|
95
|
+
let py;
|
|
96
|
+
if (targetPoints && targetPoints.length > 0) {
|
|
97
|
+
// ── Convergence mode ────────────────────────────────────────────
|
|
98
|
+
// Random scattered start position (in a disk of radius scatterRadius
|
|
99
|
+
// centered on the emitter), animating to its assigned target point.
|
|
100
|
+
const startAngle = r1 * Math.PI * 2;
|
|
101
|
+
const startDist = scatterRadius * (0.5 + 0.5 * r2); // 50%..100% radius
|
|
102
|
+
const startX = emitterX + Math.cos(startAngle) * startDist;
|
|
103
|
+
const startY = emitterY + Math.sin(startAngle) * startDist;
|
|
104
|
+
const target = targetPoints[n % targetPoints.length];
|
|
105
|
+
const progress = clamp01(age / lifetime);
|
|
106
|
+
const eased = applyEasing(convergenceEasing, progress);
|
|
107
|
+
px = startX + (target[0] - startX) * eased;
|
|
108
|
+
py = startY + (target[1] - startY) * eased;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// ── Ballistic emission (default) ────────────────────────────────
|
|
112
|
+
const angleDeg = direction + (r1 - 0.5) * spread;
|
|
113
|
+
const angleRad = (angleDeg * Math.PI) / 180;
|
|
114
|
+
const speed = velocity * (0.7 + 0.6 * r2); // 70%..130%
|
|
115
|
+
const vx = Math.cos(angleRad) * speed;
|
|
116
|
+
const vy = Math.sin(angleRad) * speed;
|
|
117
|
+
px = emitterX + vx * age;
|
|
118
|
+
py = emitterY + vy * age + 0.5 * gravity * age * age;
|
|
119
|
+
}
|
|
120
|
+
// Depth along the plane normal: vz = z_velocity + (r6 − 0.5) ×
|
|
121
|
+
// z_spread, pz = vz · age. No z gravity (gravity stays in-plane y).
|
|
122
|
+
// Applies in convergence mode too (orthogonal, no special case).
|
|
123
|
+
const pz = hasZDynamics ? (zVelocity + (r6 - 0.5) * zSpread) * age : 0;
|
|
124
|
+
// Rotation accumulates linearly; sign randomized per particle.
|
|
125
|
+
const rot = rotSpeed * age * (r3 < 0.5 ? -1 : 1);
|
|
126
|
+
// Size — anchored at `size`, scaled down by up to sizeVariation.
|
|
127
|
+
const partSize = size * (1 - sizeVariation + sizeVariation * r4);
|
|
128
|
+
// Color — pick from the palette.
|
|
129
|
+
const color = colors[Math.floor(r5 * colors.length) % colors.length];
|
|
130
|
+
// Fade — full alpha until fade_at, then linear ramp to 0.
|
|
131
|
+
const lifeProg = age / lifetime;
|
|
132
|
+
let alpha = elementOpacity;
|
|
133
|
+
if (lifeProg > fadeAt) {
|
|
134
|
+
alpha *= (1 - lifeProg) / (1 - fadeAt);
|
|
135
|
+
}
|
|
136
|
+
if (alpha <= 0)
|
|
137
|
+
continue;
|
|
138
|
+
// Apply group transform stack: each particle's center is translated
|
|
139
|
+
// through the model matrix, rotation is summed, opacity multiplied.
|
|
140
|
+
// Under 3D the particle field lives in the emitter's plane: local
|
|
141
|
+
// coords stay local and each quad projects through planeChain.
|
|
142
|
+
const [worldX, worldY] = planeChain ? [px, py] : mat4ApplyToPoint(ctx.modelMatrix, px, py);
|
|
143
|
+
const totalRot = planeChain ? rot : rot + mat4Rotation(ctx.modelMatrix);
|
|
144
|
+
// Premultiplied RGBA. Group's opacity factor was already folded
|
|
145
|
+
// into elementOpacity at the top of this loop's caller.
|
|
146
|
+
const c = parseColor(color);
|
|
147
|
+
const ca = c[3] * alpha;
|
|
148
|
+
const premul = [
|
|
149
|
+
c[0] * ca,
|
|
150
|
+
c[1] * ca,
|
|
151
|
+
c[2] * ca,
|
|
152
|
+
ca,
|
|
153
|
+
];
|
|
154
|
+
backend.drawShape({
|
|
155
|
+
cx: worldX,
|
|
156
|
+
cy: worldY,
|
|
157
|
+
width: partSize,
|
|
158
|
+
height: partSize,
|
|
159
|
+
rotation: totalRot,
|
|
160
|
+
transform: planeChain
|
|
161
|
+
? quadWorldTransform(planeChain, px, py, partSize, partSize, rot, 0, 0, pz !== 0 ? { xRot: 0, yRot: 0, z: pz } : null)
|
|
162
|
+
: undefined,
|
|
163
|
+
color: premul,
|
|
164
|
+
shape: isCircle ? 'ellipse' : 'rectangle',
|
|
165
|
+
cornerRadius: isCircle ? 0 : 0,
|
|
166
|
+
blend: element.blend_mode,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ── helpers ────────────────────────────────────────────────────────────────
|
|
171
|
+
function numberOr(value, fallback) {
|
|
172
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
173
|
+
return value;
|
|
174
|
+
if (typeof value === 'string') {
|
|
175
|
+
const n = parseFloat(value);
|
|
176
|
+
if (Number.isFinite(n))
|
|
177
|
+
return n;
|
|
178
|
+
}
|
|
179
|
+
return fallback;
|
|
180
|
+
}
|
|
181
|
+
function clamp01(n) {
|
|
182
|
+
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
183
|
+
}
|
|
184
|
+
function normalizeColors(c) {
|
|
185
|
+
if (!c)
|
|
186
|
+
return ['#ffffff'];
|
|
187
|
+
if (Array.isArray(c))
|
|
188
|
+
return c.length > 0 ? c : ['#ffffff'];
|
|
189
|
+
return [c];
|
|
190
|
+
}
|
|
191
|
+
// FNV-1a string hash — fast, decent dispersion, no allocations.
|
|
192
|
+
function hashString(s) {
|
|
193
|
+
let h = 0x811c9dc5;
|
|
194
|
+
for (let i = 0; i < s.length; i++) {
|
|
195
|
+
h ^= s.charCodeAt(i);
|
|
196
|
+
h = Math.imul(h, 0x01000193);
|
|
197
|
+
}
|
|
198
|
+
return h >>> 0;
|
|
199
|
+
}
|
|
200
|
+
// Mulberry32 — small, fast, good-enough PRNG. Returns a thunk that yields
|
|
201
|
+
// successive [0, 1) values from a single seed.
|
|
202
|
+
function mulberry32(seed) {
|
|
203
|
+
let a = seed >>> 0;
|
|
204
|
+
return () => {
|
|
205
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
206
|
+
let t = a;
|
|
207
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
208
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
209
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=particles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"particles.js","sourceRoot":"","sources":["../../../src/compositor/element-renderers/particles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../../../src/compositor/element-renderers/shape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkC,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAStF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CAuJlF"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { parseColor, parseColorPremultiplied } from '../color.js';
|
|
2
|
+
import { applyModelTransform, quadWorldTransform } from '../mat4.js';
|
|
3
|
+
import { resolveAnchor, resolveLength } from '../unit.js';
|
|
4
|
+
import { anchorToCenter } from '../transform.js';
|
|
5
|
+
import { applyAnimation, applyAspectRatio, resolve3D, resolveColorProperty, resolveScalePair, resolveSkewPair } from '../resolve.js';
|
|
6
|
+
import { resolveMaterial } from '../lighting.js';
|
|
7
|
+
import { buildLitParams } from './lit.js';
|
|
8
|
+
export function renderShapeElement(element, ctx) {
|
|
9
|
+
const { canvas, backend } = ctx;
|
|
10
|
+
const x = applyAnimation(element, 'x', resolveLength(element.x, canvas.width, canvas), ctx);
|
|
11
|
+
const y = applyAnimation(element, 'y', resolveLength(element.y, canvas.height, canvas), ctx);
|
|
12
|
+
const { sx, sy } = resolveScalePair(element, ctx);
|
|
13
|
+
const box = applyAspectRatio(element, applyAnimation(element, 'width', resolveLength(element.width, canvas.width, canvas, 100), ctx), applyAnimation(element, 'height', resolveLength(element.height, canvas.height, canvas, 100), ctx));
|
|
14
|
+
const width = sx * box.width;
|
|
15
|
+
const height = sy * box.height;
|
|
16
|
+
const rotation = applyAnimation(element, 'rotation', numberOr((element.rotation ?? element.z_rotation), 0), ctx);
|
|
17
|
+
const { skewX, skewY } = resolveSkewPair(element, ctx);
|
|
18
|
+
const opacity01 = applyAnimation(element, 'opacity', numberOr(element.opacity, 1), ctx);
|
|
19
|
+
const xAnchor = resolveAnchor(element.x_anchor);
|
|
20
|
+
const yAnchor = resolveAnchor(element.y_anchor);
|
|
21
|
+
const { cx, cy } = anchorToCenter(x, y, width, height, xAnchor, yAnchor);
|
|
22
|
+
// CKP/1.0 3D (§4.4): own 3D fields, or an un-flattened 3D ancestor,
|
|
23
|
+
// route through the full-matrix hand-off. Everything else stays on
|
|
24
|
+
// the byte-stable 2D decomposition.
|
|
25
|
+
const t3d = resolve3D(element, ctx);
|
|
26
|
+
const matrixPath = t3d !== null || !ctx.modelMatrix.aff;
|
|
27
|
+
// Apply the group transform stack (no-op when not nested in a group).
|
|
28
|
+
// Under the matrix path only opacity01 is meaningful (it is matrix-
|
|
29
|
+
// independent); the spatial fields are superseded by `transform`.
|
|
30
|
+
const w = applyModelTransform(ctx.modelMatrix, ctx.opacityFactor, cx, cy, rotation, opacity01, width, height);
|
|
31
|
+
const transform = matrixPath
|
|
32
|
+
? quadWorldTransform(ctx.modelMatrix, cx, cy, width, height, rotation, skewX, skewY, t3d)
|
|
33
|
+
: undefined;
|
|
34
|
+
// Color: parse hex → straight RGBA, multiply alpha by opacity, premultiply.
|
|
35
|
+
// (parseColorPremultiplied premultiplies by the parsed alpha — we apply
|
|
36
|
+
// element-level opacity by multiplying the alpha channel before that.)
|
|
37
|
+
// fill_color is animatable via color-valued keyframe_animations.
|
|
38
|
+
const fillColor = resolveColorProperty(element, 'fill_color', typeof element.fill_color === 'string' ? element.fill_color : undefined, ctx);
|
|
39
|
+
const straight = parseColorPremultipliedWithOpacity(fillColor, w.opacity01);
|
|
40
|
+
// Border radius is in PIXELS — the backend now does corner SDF in pixel
|
|
41
|
+
// space (so corners stay circular on non-square rects) and clamps to a
|
|
42
|
+
// safe maximum internally. Animatable via keyframe_animations (no keyframes
|
|
43
|
+
// ⇒ applyAnimation returns the static value, so static shapes are unchanged).
|
|
44
|
+
const cornerRadius = applyAnimation(element, 'border_radius', numberOr(element.border_radius, 0), ctx);
|
|
45
|
+
// Primitive kind: rectangle (default) or ellipse. Arbitrary geometry takes
|
|
46
|
+
// the `paths` form and never reaches this SDF renderer.
|
|
47
|
+
const shapeName = (typeof element.shape === 'string' && element.shape) || 'rectangle';
|
|
48
|
+
const isEllipse = shapeName.toLowerCase() === 'ellipse';
|
|
49
|
+
// Parse gradient if present. Hex stops → premultiplied RGBA; angle deg → rad.
|
|
50
|
+
const gradient = element.gradient
|
|
51
|
+
? compileGradient(element.gradient, w.opacity01)
|
|
52
|
+
: undefined;
|
|
53
|
+
// Stroke (border) — the SDF in SHAPE_FS paints the stroke band
|
|
54
|
+
// directly, so semi-transparent fills no longer let the stroke color
|
|
55
|
+
// bleed through the interior. We just pass through to the backend.
|
|
56
|
+
const strokeColorStr = resolveColorProperty(element, 'stroke_color', typeof element.stroke_color === 'string' ? element.stroke_color : undefined, ctx) ?? null;
|
|
57
|
+
const strokeWidth = applyAnimation(element, 'stroke_width', numberOr(element.stroke_width, 0), ctx);
|
|
58
|
+
const strokePremul = strokeColorStr && strokeWidth > 0
|
|
59
|
+
? parseColorPremultipliedWithOpacity(strokeColorStr, w.opacity01)
|
|
60
|
+
: undefined;
|
|
61
|
+
// Draw the drop shadow (if any) BEFORE the shape so the shape paints
|
|
62
|
+
// over the inside-of-SDF region. Shadow opacity scales with the
|
|
63
|
+
// element's overall opacity so animating the shape's opacity fades
|
|
64
|
+
// the shadow alongside it.
|
|
65
|
+
if (element.shadow && typeof element.shadow.color === 'string') {
|
|
66
|
+
const shadowPremul = parseColorPremultipliedWithOpacity(element.shadow.color, w.opacity01);
|
|
67
|
+
const offsetX = numberOr(element.shadow.offset_x, 0);
|
|
68
|
+
const offsetY = numberOr(element.shadow.offset_y, 0);
|
|
69
|
+
const blur = Math.max(0, numberOr(element.shadow.blur, 0));
|
|
70
|
+
backend.drawShapeShadow({
|
|
71
|
+
cx: w.cx,
|
|
72
|
+
cy: w.cy,
|
|
73
|
+
width: matrixPath ? width : w.width,
|
|
74
|
+
height: matrixPath ? height : w.height,
|
|
75
|
+
rotation: w.rotation,
|
|
76
|
+
skewX,
|
|
77
|
+
skewY,
|
|
78
|
+
// Under 3D the EXPANDED quad foreshortens with the element while
|
|
79
|
+
// the offset translates in the PARENT plane — consistent with 2D,
|
|
80
|
+
// where a rotated shape's shadow offset stays screen-aligned.
|
|
81
|
+
transform: matrixPath
|
|
82
|
+
? quadWorldTransform(ctx.modelMatrix, cx + offsetX, cy + offsetY, width + blur * 2, height + blur * 2, rotation, skewX, skewY, t3d)
|
|
83
|
+
: undefined,
|
|
84
|
+
cornerRadius,
|
|
85
|
+
shape: isEllipse ? 'ellipse' : 'rectangle',
|
|
86
|
+
offsetX,
|
|
87
|
+
offsetY,
|
|
88
|
+
blur,
|
|
89
|
+
color: shadowPremul,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// §4.8 PBR lighting: when the shape carries a material and the scene
|
|
93
|
+
// has lights, build the lit payload (world-space normal + position via
|
|
94
|
+
// the camera-FREE world matrix, straight-alpha albedo). gradient fills
|
|
95
|
+
// aren't lit in Phase 1.
|
|
96
|
+
const material = !gradient ? resolveMaterial(element, ctx.time) : null;
|
|
97
|
+
const opacityFactor = Math.max(0, Math.min(1, w.opacity01));
|
|
98
|
+
const lit = material
|
|
99
|
+
? buildLitParams(ctx, quadWorldTransform(ctx.worldMatrix, cx, cy, width, height, rotation, skewX, skewY, t3d), material, straightWithOpacity(fillColor, opacityFactor), strokeColorStr && strokeWidth > 0 ? straightWithOpacity(strokeColorStr, opacityFactor) : undefined)
|
|
100
|
+
: null;
|
|
101
|
+
backend.drawShape({
|
|
102
|
+
cx: w.cx,
|
|
103
|
+
cy: w.cy,
|
|
104
|
+
// Matrix path: local dims (the SDF works in element pixel space and
|
|
105
|
+
// the projection lives in `transform`); 2D path: decomposed dims.
|
|
106
|
+
width: matrixPath ? width : w.width,
|
|
107
|
+
height: matrixPath ? height : w.height,
|
|
108
|
+
rotation: w.rotation,
|
|
109
|
+
skewX,
|
|
110
|
+
skewY,
|
|
111
|
+
transform,
|
|
112
|
+
color: straight,
|
|
113
|
+
gradient,
|
|
114
|
+
cornerRadius,
|
|
115
|
+
shape: isEllipse ? 'ellipse' : 'rectangle',
|
|
116
|
+
strokeColor: strokePremul,
|
|
117
|
+
strokeWidth: strokePremul ? strokeWidth : 0,
|
|
118
|
+
blend: element.blend_mode,
|
|
119
|
+
lit: lit ?? undefined,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function straightWithOpacity(hex, opacityFactor) {
|
|
123
|
+
const c = parseColor(typeof hex === 'string' ? hex : '#ffffff');
|
|
124
|
+
return [c[0], c[1], c[2], c[3] * opacityFactor];
|
|
125
|
+
}
|
|
126
|
+
function compileGradient(g, opacity01) {
|
|
127
|
+
const opacityFactor = Math.max(0, Math.min(1, opacity01));
|
|
128
|
+
const stops = g.stops.slice(0, 4).map((s) => {
|
|
129
|
+
const c = parseColorPremultiplied(s.color);
|
|
130
|
+
return {
|
|
131
|
+
offset: Math.max(0, Math.min(1, s.offset)),
|
|
132
|
+
color: [
|
|
133
|
+
c[0] * opacityFactor,
|
|
134
|
+
c[1] * opacityFactor,
|
|
135
|
+
c[2] * opacityFactor,
|
|
136
|
+
c[3] * opacityFactor,
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
if (g.type === 'linear') {
|
|
141
|
+
// CSS `linear-gradient(θ)`: 0° = to top, clockwise (90° = to right,
|
|
142
|
+
// 180° = to bottom). Default 180 (to bottom). The shader projects
|
|
143
|
+
// centered UV onto (cos, sin) of its angle, whose basis is 0° = +x
|
|
144
|
+
// (right), 90° = +y (down) — so map CSS θ → shader (θ − 90).
|
|
145
|
+
const angleDeg = typeof g.angle === 'number' ? g.angle : 180;
|
|
146
|
+
return { type: 'linear', angle: ((angleDeg - 90) * Math.PI) / 180, stops };
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
type: 'radial',
|
|
150
|
+
cx: typeof g.cx === 'number' ? g.cx : 0.5,
|
|
151
|
+
cy: typeof g.cy === 'number' ? g.cy : 0.5,
|
|
152
|
+
radius: typeof g.radius === 'number' ? g.radius : 0.5,
|
|
153
|
+
stops,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function numberOr(value, fallback) {
|
|
157
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
158
|
+
return value;
|
|
159
|
+
if (typeof value === 'string') {
|
|
160
|
+
const n = parseFloat(value);
|
|
161
|
+
if (Number.isFinite(n))
|
|
162
|
+
return n;
|
|
163
|
+
}
|
|
164
|
+
return fallback;
|
|
165
|
+
}
|
|
166
|
+
function parseColorPremultipliedWithOpacity(hex, opacity01) {
|
|
167
|
+
const c = parseColorPremultiplied(typeof hex === 'string' ? hex : '#ffffff');
|
|
168
|
+
const opacityFactor = Math.max(0, Math.min(1, opacity01));
|
|
169
|
+
return [c[0] * opacityFactor, c[1] * opacityFactor, c[2] * opacityFactor, c[3] * opacityFactor];
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/compositor/element-renderers/shape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../../../src/compositor/element-renderers/svg.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAY,YAAY,EAAQ,MAAM,mBAAmB,CAAC;AAUtE,OAAO,KAAK,EACV,aAAa,EAGd,MAAM,sBAAsB,CAAC;AAU9B,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CAqG/E"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// SVG element renderer.
|
|
2
|
+
//
|
|
3
|
+
// Each frame:
|
|
4
|
+
// 1. Resolve per-path stroke_progress values (animatable via keyframes).
|
|
5
|
+
// 2. Get or create a cached OffscreenCanvas + Texture sized to the viewBox
|
|
6
|
+
// at a 2× supersampling factor.
|
|
7
|
+
// 3. Rasterize the paths into the OffscreenCanvas.
|
|
8
|
+
// 4. Upload the canvas contents into the texture.
|
|
9
|
+
// 5. Draw as a textured quad at the element's animated transform.
|
|
10
|
+
import { interpolateKeyframes } from '../../animation/keyframes.js';
|
|
11
|
+
import { isExpr, evalExpr } from '../../animation/expr.js';
|
|
12
|
+
import { applyEasing } from '../../animation/easings.js';
|
|
13
|
+
import { morphD } from '../../svg/morph.js';
|
|
14
|
+
import { rasterizeSvgElement } from '../../svg/svg-renderer.js';
|
|
15
|
+
import { applyModelTransform, quadWorldTransform } from '../mat4.js';
|
|
16
|
+
import { resolveAnchor, resolveLength } from '../unit.js';
|
|
17
|
+
import { anchorToCenter } from '../transform.js';
|
|
18
|
+
import { applyAnimation, applyAspectRatio, resolve3D, resolveScalePair, resolveSkewPair } from '../resolve.js';
|
|
19
|
+
const SUPERSAMPLE = 2;
|
|
20
|
+
// Cap the rasterization canvas. Going past this wastes memory + GPU
|
|
21
|
+
// without visible improvement at typical canvas sizes.
|
|
22
|
+
const MAX_RASTER = 2048;
|
|
23
|
+
// Renders the PATH form of a `shape` (an element carrying `paths`) — rasterized
|
|
24
|
+
// to a cached OffscreenCanvas and drawn as a textured quad. The primitive form
|
|
25
|
+
// (rectangle/ellipse, no `paths`) goes through renderShapeElement instead.
|
|
26
|
+
export function renderPathShape(element, ctx) {
|
|
27
|
+
const { canvas, backend, svgRasters } = ctx;
|
|
28
|
+
const paths = element.paths;
|
|
29
|
+
if (!paths || paths.length === 0)
|
|
30
|
+
return;
|
|
31
|
+
const viewBox = element.view_box ?? [0, 0, 100, 100];
|
|
32
|
+
const [, , vbW, vbH] = viewBox;
|
|
33
|
+
const x = applyAnimation(element, 'x', resolveLength(element.x, canvas.width, canvas), ctx);
|
|
34
|
+
const y = applyAnimation(element, 'y', resolveLength(element.y, canvas.height, canvas), ctx);
|
|
35
|
+
const { sx, sy } = resolveScalePair(element, ctx);
|
|
36
|
+
const box = applyAspectRatio(element, applyAnimation(element, 'width', resolveLength(element.width, canvas.width, canvas, vbW), ctx), applyAnimation(element, 'height', resolveLength(element.height, canvas.height, canvas, vbH), ctx));
|
|
37
|
+
const width = sx * box.width;
|
|
38
|
+
const height = sy * box.height;
|
|
39
|
+
const rotation = applyAnimation(element, 'rotation', numberOr(element.rotation ?? element.z_rotation, 0), ctx);
|
|
40
|
+
const opacity01 = applyAnimation(element, 'opacity', numberOr(element.opacity, 1), ctx);
|
|
41
|
+
const xAnchor = resolveAnchor(element.x_anchor);
|
|
42
|
+
const yAnchor = resolveAnchor(element.y_anchor);
|
|
43
|
+
const { cx, cy } = anchorToCenter(x, y, width, height, xAnchor, yAnchor);
|
|
44
|
+
const w = applyModelTransform(ctx.modelMatrix, ctx.opacityFactor, cx, cy, rotation, opacity01, width, height);
|
|
45
|
+
const opacity = clamp01(w.opacity01);
|
|
46
|
+
if (opacity <= 0 || w.width <= 0 || w.height <= 0)
|
|
47
|
+
return;
|
|
48
|
+
// Resolve per-path trim windows at this frame (§5.6.1) —
|
|
49
|
+
// stroke_progress is sugar for [0, progress, 0].
|
|
50
|
+
const elementStart = ctx.timeOffset + numberOr(element.time, 0);
|
|
51
|
+
const localTime = ctx.time - elementStart;
|
|
52
|
+
const trims = paths.flatMap((p) => resolveTrim(p, localTime));
|
|
53
|
+
const ds = paths.map((p) => resolveD(p.d, localTime));
|
|
54
|
+
// Get or create the per-element raster target.
|
|
55
|
+
//
|
|
56
|
+
// The canvas resolution drives clarity at display. If we used the viewBox
|
|
57
|
+
// dimensions alone (e.g. 20×20 for an SVG icon), an element drawn at
|
|
58
|
+
// 320×320 px would upscale the texture 16× and look blurry. So we size
|
|
59
|
+
// the canvas to max(viewBox, display) × supersample, capped to avoid
|
|
60
|
+
// blowing up GPU memory.
|
|
61
|
+
const targetRasterW = Math.min(MAX_RASTER, Math.max(1, Math.ceil(Math.max(vbW, width) * SUPERSAMPLE)));
|
|
62
|
+
const targetRasterH = Math.min(MAX_RASTER, Math.max(1, Math.ceil(Math.max(vbH, height) * SUPERSAMPLE)));
|
|
63
|
+
const cacheKey = typeof element.id === 'string' ? element.id : `__svg_${JSON.stringify(viewBox)}`;
|
|
64
|
+
let target = svgRasters.get(cacheKey);
|
|
65
|
+
if (!target || target.canvas.width !== targetRasterW || target.canvas.height !== targetRasterH) {
|
|
66
|
+
const off = new OffscreenCanvas(targetRasterW, targetRasterH);
|
|
67
|
+
const offCtx = off.getContext('2d');
|
|
68
|
+
if (!offCtx)
|
|
69
|
+
return;
|
|
70
|
+
// We have to initialize the texture with the canvas to pin its size.
|
|
71
|
+
const texture = backend.createTexture(off);
|
|
72
|
+
target = { canvas: off, ctx: offCtx, texture };
|
|
73
|
+
svgRasters.set(cacheKey, target);
|
|
74
|
+
}
|
|
75
|
+
// Memoize the raster + upload — skip both when nothing about the
|
|
76
|
+
// rendered pixels has changed since the last frame. For a complex
|
|
77
|
+
// SVG (US map: 50+ paths, 2048×2048 raster, 16MB texture upload),
|
|
78
|
+
// this is the difference between ~50ms per frame and ~0ms while the
|
|
79
|
+
// element is sitting on screen post-animation. Stroke-progress
|
|
80
|
+
// animations still re-raster every frame because their progresses
|
|
81
|
+
// genuinely change frame-to-frame; the moment they settle at 1.0
|
|
82
|
+
// the cache kicks in.
|
|
83
|
+
const nextSignature = {
|
|
84
|
+
trims,
|
|
85
|
+
ds,
|
|
86
|
+
pathsRef: paths,
|
|
87
|
+
gradientsRef: element.gradients,
|
|
88
|
+
rasterW: targetRasterW,
|
|
89
|
+
rasterH: targetRasterH,
|
|
90
|
+
};
|
|
91
|
+
if (!signaturesEqual(target.lastSignature, nextSignature)) {
|
|
92
|
+
rasterizeSvgElement(element, trims, ds, target);
|
|
93
|
+
backend.updateTexture(target.texture, target.canvas);
|
|
94
|
+
target.lastSignature = nextSignature;
|
|
95
|
+
}
|
|
96
|
+
// Draw the rasterized SVG as a textured quad. Tint by opacity (premul).
|
|
97
|
+
// CKP/1.0 3D (§4.4): full-matrix hand-off when 3D is in play.
|
|
98
|
+
const t3d = resolve3D(element, ctx);
|
|
99
|
+
const matrixPath = t3d !== null || !ctx.modelMatrix.aff;
|
|
100
|
+
const { skewX, skewY } = resolveSkewPair(element, ctx);
|
|
101
|
+
backend.drawTexturedQuad({
|
|
102
|
+
cx: w.cx,
|
|
103
|
+
cy: w.cy,
|
|
104
|
+
width: matrixPath ? width : w.width,
|
|
105
|
+
height: matrixPath ? height : w.height,
|
|
106
|
+
rotation: w.rotation,
|
|
107
|
+
skewX,
|
|
108
|
+
skewY,
|
|
109
|
+
transform: matrixPath
|
|
110
|
+
? quadWorldTransform(ctx.modelMatrix, cx, cy, width, height, rotation, skewX, skewY, t3d)
|
|
111
|
+
: undefined,
|
|
112
|
+
texture: target.texture,
|
|
113
|
+
tint: [opacity, opacity, opacity, opacity],
|
|
114
|
+
blend: element.blend_mode,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function resolveTrim(p, localTime) {
|
|
118
|
+
const rp = (v) => {
|
|
119
|
+
if (v === undefined)
|
|
120
|
+
return undefined;
|
|
121
|
+
if (typeof v === 'number')
|
|
122
|
+
return v;
|
|
123
|
+
if (isExpr(v))
|
|
124
|
+
return evalExpr(v, { t: localTime, dur: 0, i: 0, n: 1, value: 0 });
|
|
125
|
+
if (Array.isArray(v))
|
|
126
|
+
return interpolateKeyframes(v, localTime);
|
|
127
|
+
return undefined;
|
|
128
|
+
};
|
|
129
|
+
const hasTrim = p.trim_start !== undefined || p.trim_end !== undefined || p.trim_offset !== undefined;
|
|
130
|
+
if (hasTrim) {
|
|
131
|
+
return [
|
|
132
|
+
clamp01p(rp(p.trim_start) ?? 0),
|
|
133
|
+
clamp01p(rp(p.trim_end) ?? 1),
|
|
134
|
+
rp(p.trim_offset) ?? 0,
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
return [0, clamp01p(rp(p.stroke_progress) ?? 1), 0];
|
|
138
|
+
}
|
|
139
|
+
function clamp01p(v) {
|
|
140
|
+
return Math.max(0, Math.min(1, v));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Resolve a path's d at element-local time: a plain string passes
|
|
144
|
+
* through; keyframed d-strings MORPH between compatible command lists
|
|
145
|
+
* (§5.6.2) and SNAP at the destination keyframe otherwise.
|
|
146
|
+
*/
|
|
147
|
+
function resolveD(d, localTime) {
|
|
148
|
+
if (typeof d === 'string')
|
|
149
|
+
return d;
|
|
150
|
+
if (!Array.isArray(d) || d.length === 0)
|
|
151
|
+
return '';
|
|
152
|
+
const tv = (k) => typeof k.time === 'number' ? k.time : parseFloat(String(k.time)) || 0;
|
|
153
|
+
const first = d[0];
|
|
154
|
+
const last = d[d.length - 1];
|
|
155
|
+
if (localTime <= tv(first))
|
|
156
|
+
return String(first.value);
|
|
157
|
+
if (localTime >= tv(last))
|
|
158
|
+
return String(last.value);
|
|
159
|
+
for (let i = 0; i < d.length - 1; i++) {
|
|
160
|
+
const a = d[i];
|
|
161
|
+
const b = d[i + 1];
|
|
162
|
+
const at = tv(a);
|
|
163
|
+
const bt = tv(b);
|
|
164
|
+
if (localTime >= at && localTime <= bt) {
|
|
165
|
+
const span = bt - at;
|
|
166
|
+
const u = applyEasing(b.easing, span > 0 ? (localTime - at) / span : 1);
|
|
167
|
+
const morphed = morphD(String(a.value), String(b.value), u);
|
|
168
|
+
return morphed ?? String(a.value);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return String(last.value);
|
|
172
|
+
}
|
|
173
|
+
function numberOr(value, fallback) {
|
|
174
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
175
|
+
return value;
|
|
176
|
+
if (typeof value === 'string') {
|
|
177
|
+
const n = parseFloat(value);
|
|
178
|
+
if (Number.isFinite(n))
|
|
179
|
+
return n;
|
|
180
|
+
}
|
|
181
|
+
return fallback;
|
|
182
|
+
}
|
|
183
|
+
function clamp01(n) {
|
|
184
|
+
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
185
|
+
}
|
|
186
|
+
function signaturesEqual(prev, next) {
|
|
187
|
+
if (!prev)
|
|
188
|
+
return false;
|
|
189
|
+
if (prev.pathsRef !== next.pathsRef)
|
|
190
|
+
return false;
|
|
191
|
+
if (prev.gradientsRef !== next.gradientsRef)
|
|
192
|
+
return false;
|
|
193
|
+
if (prev.rasterW !== next.rasterW || prev.rasterH !== next.rasterH) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
if (prev.trims.length !== next.trims.length)
|
|
197
|
+
return false;
|
|
198
|
+
for (let i = 0; i < prev.trims.length; i++) {
|
|
199
|
+
if (prev.trims[i] !== next.trims[i])
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
if (prev.ds.length !== next.ds.length)
|
|
203
|
+
return false;
|
|
204
|
+
for (let i = 0; i < prev.ds.length; i++) {
|
|
205
|
+
if (prev.ds[i] !== next.ds[i])
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=svg.js.map
|