@codellyson/framely 0.1.0 → 0.1.1
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/package.json +3 -2
- package/src/AbsoluteFill.tsx +50 -0
- package/src/Audio.tsx +294 -0
- package/src/Composition.tsx +378 -0
- package/src/Easing.ts +294 -0
- package/src/ErrorBoundary.tsx +136 -0
- package/src/Folder.tsx +66 -0
- package/src/Freeze.tsx +63 -0
- package/src/IFrame.tsx +100 -0
- package/src/Img.tsx +146 -0
- package/src/Loop.tsx +139 -0
- package/src/Player.tsx +594 -0
- package/src/Sequence.tsx +80 -0
- package/src/Series.tsx +181 -0
- package/src/Text.tsx +376 -0
- package/src/Video.tsx +247 -0
- package/src/__tests__/Easing.test.js +119 -0
- package/src/__tests__/interpolate.test.js +127 -0
- package/src/config.ts +406 -0
- package/src/context.tsx +241 -0
- package/src/delayRender.ts +278 -0
- package/src/getInputProps.ts +217 -0
- package/src/hooks/useDelayRender.ts +117 -0
- package/src/hooks.ts +28 -0
- package/src/index.d.ts +571 -0
- package/src/index.ts +260 -0
- package/src/interpolate.ts +160 -0
- package/src/interpolateColors.ts +368 -0
- package/src/makeTransform.ts +339 -0
- package/src/measureSpring.ts +152 -0
- package/src/noise.ts +308 -0
- package/src/preload.ts +303 -0
- package/src/registerRoot.ts +346 -0
- package/src/shapes/Circle.tsx +37 -0
- package/src/shapes/Ellipse.tsx +39 -0
- package/src/shapes/Line.tsx +37 -0
- package/src/shapes/Path.tsx +56 -0
- package/src/shapes/Polygon.tsx +39 -0
- package/src/shapes/Rect.tsx +43 -0
- package/src/shapes/Svg.tsx +39 -0
- package/src/shapes/index.ts +16 -0
- package/src/shapes/usePathLength.ts +38 -0
- package/src/staticFile.ts +117 -0
- package/src/templates/api.ts +165 -0
- package/src/templates/index.ts +7 -0
- package/src/templates/mockData.ts +271 -0
- package/src/templates/types.ts +126 -0
- package/src/transitions/TransitionSeries.tsx +399 -0
- package/src/transitions/index.ts +109 -0
- package/src/transitions/presets/fade.ts +89 -0
- package/src/transitions/presets/flip.ts +263 -0
- package/src/transitions/presets/slide.ts +154 -0
- package/src/transitions/presets/wipe.ts +195 -0
- package/src/transitions/presets/zoom.ts +183 -0
- package/src/useAudioData.ts +260 -0
- package/src/useSpring.ts +215 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for spring preset configuration values.
|
|
3
|
+
*/
|
|
4
|
+
export interface SpringPreset {
|
|
5
|
+
mass: number;
|
|
6
|
+
stiffness: number;
|
|
7
|
+
damping: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for the measureSpring function.
|
|
12
|
+
*/
|
|
13
|
+
export interface MeasureSpringOptions {
|
|
14
|
+
/** Frames per second (required) */
|
|
15
|
+
fps: number;
|
|
16
|
+
/** Controls animation speed (lower = faster). Defaults to 1. */
|
|
17
|
+
mass?: number;
|
|
18
|
+
/** Bounciness (higher = more bounce). Defaults to 100. */
|
|
19
|
+
stiffness?: number;
|
|
20
|
+
/** Deceleration (higher = less bounce). Defaults to 10. */
|
|
21
|
+
damping?: number;
|
|
22
|
+
/** How close to final value to consider "settled". Defaults to 0.001. */
|
|
23
|
+
threshold?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Measures how many frames a spring animation takes to settle.
|
|
28
|
+
*
|
|
29
|
+
* This is useful for:
|
|
30
|
+
* - Determining the duration of a Sequence containing spring animations
|
|
31
|
+
* - Setting Loop durationInFrames to match spring completion
|
|
32
|
+
* - Calculating total video duration with springs
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* const frames = measureSpring({ fps: 30 });
|
|
36
|
+
* const frames = measureSpring({ fps: 30, mass: 0.5, stiffness: 200 });
|
|
37
|
+
*
|
|
38
|
+
* @param {MeasureSpringOptions} options
|
|
39
|
+
* @param {number} options.fps - Frames per second (required)
|
|
40
|
+
* @param {number} [options.mass=1] - Controls animation speed (lower = faster)
|
|
41
|
+
* @param {number} [options.stiffness=100] - Bounciness (higher = more bounce)
|
|
42
|
+
* @param {number} [options.damping=10] - Deceleration (higher = less bounce)
|
|
43
|
+
* @param {number} [options.threshold=0.001] - How close to final value to consider "settled"
|
|
44
|
+
* @returns {number} Number of frames until the spring settles
|
|
45
|
+
*/
|
|
46
|
+
export function measureSpring(options: MeasureSpringOptions): number {
|
|
47
|
+
const {
|
|
48
|
+
fps,
|
|
49
|
+
mass = 1,
|
|
50
|
+
stiffness = 100,
|
|
51
|
+
damping = 10,
|
|
52
|
+
threshold = 0.001,
|
|
53
|
+
} = options;
|
|
54
|
+
|
|
55
|
+
if (!fps || fps <= 0) {
|
|
56
|
+
throw new Error('measureSpring requires a positive fps value');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Calculate spring physics parameters
|
|
60
|
+
const omega0: number = Math.sqrt(stiffness / mass); // Natural frequency
|
|
61
|
+
const zeta: number = damping / (2 * Math.sqrt(stiffness * mass)); // Damping ratio
|
|
62
|
+
|
|
63
|
+
// Maximum time to search (10 seconds should be enough for any reasonable spring)
|
|
64
|
+
const maxTime: number = 10;
|
|
65
|
+
const maxFrames: number = Math.ceil(maxTime * fps);
|
|
66
|
+
|
|
67
|
+
// Simulate the spring frame by frame
|
|
68
|
+
for (let frame = 1; frame <= maxFrames; frame++) {
|
|
69
|
+
const t: number = frame / fps;
|
|
70
|
+
let displacement: number;
|
|
71
|
+
|
|
72
|
+
if (zeta < 1) {
|
|
73
|
+
// Underdamped (oscillates)
|
|
74
|
+
const omegaD: number = omega0 * Math.sqrt(1 - zeta * zeta);
|
|
75
|
+
displacement = Math.exp(-zeta * omega0 * t) *
|
|
76
|
+
(Math.cos(omegaD * t) + (zeta * omega0 / omegaD) * Math.sin(omegaD * t));
|
|
77
|
+
} else if (zeta === 1) {
|
|
78
|
+
// Critically damped
|
|
79
|
+
displacement = Math.exp(-omega0 * t) * (1 + omega0 * t);
|
|
80
|
+
} else {
|
|
81
|
+
// Overdamped
|
|
82
|
+
const s1: number = -omega0 * (zeta - Math.sqrt(zeta * zeta - 1));
|
|
83
|
+
const s2: number = -omega0 * (zeta + Math.sqrt(zeta * zeta - 1));
|
|
84
|
+
displacement = (s2 * Math.exp(s1 * t) - s1 * Math.exp(s2 * t)) / (s2 - s1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if we've settled (displacement from target is below threshold)
|
|
88
|
+
if (Math.abs(displacement) < threshold) {
|
|
89
|
+
return frame;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// If we didn't settle within maxFrames, return maxFrames
|
|
94
|
+
// This shouldn't happen with reasonable spring parameters
|
|
95
|
+
return maxFrames;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Calculate the natural frequency of a spring.
|
|
100
|
+
*
|
|
101
|
+
* @param {number} stiffness
|
|
102
|
+
* @param {number} mass
|
|
103
|
+
* @returns {number} Natural frequency in radians per second
|
|
104
|
+
*/
|
|
105
|
+
export function springNaturalFrequency(stiffness: number, mass: number): number {
|
|
106
|
+
return Math.sqrt(stiffness / mass);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Calculate the damping ratio of a spring.
|
|
111
|
+
*
|
|
112
|
+
* - zeta < 1: Underdamped (oscillates)
|
|
113
|
+
* - zeta = 1: Critically damped (fastest without oscillation)
|
|
114
|
+
* - zeta > 1: Overdamped (slow, no oscillation)
|
|
115
|
+
*
|
|
116
|
+
* @param {number} damping
|
|
117
|
+
* @param {number} stiffness
|
|
118
|
+
* @param {number} mass
|
|
119
|
+
* @returns {number} Damping ratio
|
|
120
|
+
*/
|
|
121
|
+
export function springDampingRatio(damping: number, stiffness: number, mass: number): number {
|
|
122
|
+
return damping / (2 * Math.sqrt(stiffness * mass));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get spring configuration presets.
|
|
127
|
+
*
|
|
128
|
+
* Usage:
|
|
129
|
+
* const config = springPresets.gentle;
|
|
130
|
+
* spring(frame, { ...config, from: 0, to: 100 });
|
|
131
|
+
*/
|
|
132
|
+
export const springPresets: Record<string, SpringPreset> = {
|
|
133
|
+
// Quick and snappy
|
|
134
|
+
snappy: { mass: 1, stiffness: 400, damping: 30 },
|
|
135
|
+
|
|
136
|
+
// Gentle, slow movement
|
|
137
|
+
gentle: { mass: 1, stiffness: 100, damping: 15 },
|
|
138
|
+
|
|
139
|
+
// Bouncy, playful
|
|
140
|
+
bouncy: { mass: 1, stiffness: 200, damping: 10 },
|
|
141
|
+
|
|
142
|
+
// Stiff, minimal overshoot
|
|
143
|
+
stiff: { mass: 1, stiffness: 300, damping: 25 },
|
|
144
|
+
|
|
145
|
+
// Slow, heavy feeling
|
|
146
|
+
slow: { mass: 2, stiffness: 100, damping: 20 },
|
|
147
|
+
|
|
148
|
+
// Default (matches Remotion)
|
|
149
|
+
default: { mass: 1, stiffness: 100, damping: 10 },
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export default measureSpring;
|
package/src/noise.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Noise Utilities
|
|
3
|
+
*
|
|
4
|
+
* Deterministic noise and random functions for reproducible animations.
|
|
5
|
+
* All functions are pure — same inputs always produce same outputs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Options for 2D and 3D Perlin noise functions.
|
|
10
|
+
*/
|
|
11
|
+
interface NoiseOptions {
|
|
12
|
+
/** Seed for the permutation table */
|
|
13
|
+
seed?: number;
|
|
14
|
+
/** Scales the input coordinates */
|
|
15
|
+
frequency?: number;
|
|
16
|
+
/** Scales the output value */
|
|
17
|
+
amplitude?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for Fractal Brownian Motion (fBm).
|
|
22
|
+
*/
|
|
23
|
+
interface FbmOptions extends NoiseOptions {
|
|
24
|
+
/** Number of noise layers */
|
|
25
|
+
octaves?: number;
|
|
26
|
+
/** Frequency multiplier per octave */
|
|
27
|
+
lacunarity?: number;
|
|
28
|
+
/** Amplitude multiplier per octave */
|
|
29
|
+
persistence?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A 2D gradient vector [x, y] */
|
|
33
|
+
type Gradient2D = [number, number];
|
|
34
|
+
|
|
35
|
+
/** A 3D gradient vector [x, y, z] */
|
|
36
|
+
type Gradient3D = [number, number, number];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate a seeded pseudo-random number between 0 and 1.
|
|
40
|
+
*
|
|
41
|
+
* Uses a hash-based approach for single-call determinism (no state).
|
|
42
|
+
*
|
|
43
|
+
* @param {number} seed - Any number (typically frame number or index)
|
|
44
|
+
* @returns {number} Pseudo-random value in [0, 1)
|
|
45
|
+
*/
|
|
46
|
+
export function random(seed: number): number {
|
|
47
|
+
// Mulberry32 — fast, good distribution, 32-bit state
|
|
48
|
+
let t: number = (seed + 0x6d2b79f5) | 0;
|
|
49
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
50
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
51
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate a seeded random number within a range.
|
|
56
|
+
*
|
|
57
|
+
* @param {number} seed - Seed value
|
|
58
|
+
* @param {number} min - Minimum value (inclusive)
|
|
59
|
+
* @param {number} max - Maximum value (exclusive)
|
|
60
|
+
* @returns {number} Random value in [min, max)
|
|
61
|
+
*/
|
|
62
|
+
export function randomRange(seed: number, min: number, max: number): number {
|
|
63
|
+
return min + random(seed) * (max - min);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate a seeded random integer within a range.
|
|
68
|
+
*
|
|
69
|
+
* @param {number} seed - Seed value
|
|
70
|
+
* @param {number} min - Minimum value (inclusive)
|
|
71
|
+
* @param {number} max - Maximum value (inclusive)
|
|
72
|
+
* @returns {number} Random integer in [min, max]
|
|
73
|
+
*/
|
|
74
|
+
export function randomInt(seed: number, min: number, max: number): number {
|
|
75
|
+
return Math.floor(min + random(seed) * (max - min + 1));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── Permutation table for Perlin noise ──────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
function buildPermutation(seed: number): Uint8Array {
|
|
81
|
+
const perm: Uint8Array = new Uint8Array(512);
|
|
82
|
+
const base: Uint8Array = new Uint8Array(256);
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < 256; i++) {
|
|
85
|
+
base[i] = i;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Fisher-Yates shuffle with seeded random
|
|
89
|
+
for (let i = 255; i > 0; i--) {
|
|
90
|
+
const j: number = Math.floor(random(seed + i) * (i + 1));
|
|
91
|
+
const tmp: number = base[i];
|
|
92
|
+
base[i] = base[j];
|
|
93
|
+
base[j] = tmp;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Double the table to avoid index wrapping
|
|
97
|
+
for (let i = 0; i < 256; i++) {
|
|
98
|
+
perm[i] = base[i];
|
|
99
|
+
perm[i + 256] = base[i];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return perm;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Gradient vectors for 2D and 3D Perlin noise ────────────────────────────
|
|
106
|
+
|
|
107
|
+
const GRAD2: Gradient2D[] = [
|
|
108
|
+
[1, 0], [-1, 0], [0, 1], [0, -1],
|
|
109
|
+
[1, 1], [-1, 1], [1, -1], [-1, -1],
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const GRAD3: Gradient3D[] = [
|
|
113
|
+
[1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
|
|
114
|
+
[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
|
|
115
|
+
[0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1],
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
function fade(t: number): number {
|
|
119
|
+
// Improved Perlin fade: 6t^5 - 15t^4 + 10t^3
|
|
120
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function lerp(a: number, b: number, t: number): number {
|
|
124
|
+
return a + t * (b - a);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function dot2(g: Gradient2D, x: number, y: number): number {
|
|
128
|
+
return g[0] * x + g[1] * y;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function dot3(g: Gradient3D, x: number, y: number, z: number): number {
|
|
132
|
+
return g[0] * x + g[1] * y + g[2] * z;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Cache for permutation tables ────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
const permCache: Map<number, Uint8Array> = new Map();
|
|
138
|
+
|
|
139
|
+
function getPermutation(seed: number): Uint8Array {
|
|
140
|
+
if (!permCache.has(seed)) {
|
|
141
|
+
// Limit cache size
|
|
142
|
+
if (permCache.size > 32) {
|
|
143
|
+
const firstKey: number | undefined = permCache.keys().next().value;
|
|
144
|
+
if (firstKey !== undefined) {
|
|
145
|
+
permCache.delete(firstKey);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
permCache.set(seed, buildPermutation(seed));
|
|
149
|
+
}
|
|
150
|
+
return permCache.get(seed)!;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 2D Perlin noise.
|
|
155
|
+
*
|
|
156
|
+
* Returns a smooth, continuous noise value for the given coordinates.
|
|
157
|
+
* Same inputs always produce the same output.
|
|
158
|
+
*
|
|
159
|
+
* @param {number} x - X coordinate
|
|
160
|
+
* @param {number} y - Y coordinate
|
|
161
|
+
* @param {object} [options]
|
|
162
|
+
* @param {number} [options.seed=0] - Seed for the permutation table
|
|
163
|
+
* @param {number} [options.frequency=1] - Scales the input coordinates
|
|
164
|
+
* @param {number} [options.amplitude=1] - Scales the output value
|
|
165
|
+
* @returns {number} Noise value in [-amplitude, amplitude]
|
|
166
|
+
*/
|
|
167
|
+
export function noise2D(x: number, y: number, options: NoiseOptions = {}): number {
|
|
168
|
+
const { seed = 0, frequency = 1, amplitude = 1 } = options;
|
|
169
|
+
const perm: Uint8Array = getPermutation(seed);
|
|
170
|
+
|
|
171
|
+
x *= frequency;
|
|
172
|
+
y *= frequency;
|
|
173
|
+
|
|
174
|
+
// Grid cell coordinates
|
|
175
|
+
const xi: number = Math.floor(x) & 255;
|
|
176
|
+
const yi: number = Math.floor(y) & 255;
|
|
177
|
+
|
|
178
|
+
// Relative position within cell
|
|
179
|
+
const xf: number = x - Math.floor(x);
|
|
180
|
+
const yf: number = y - Math.floor(y);
|
|
181
|
+
|
|
182
|
+
// Fade curves
|
|
183
|
+
const u: number = fade(xf);
|
|
184
|
+
const v: number = fade(yf);
|
|
185
|
+
|
|
186
|
+
// Hash the 4 corners
|
|
187
|
+
const aa: number = perm[perm[xi] + yi];
|
|
188
|
+
const ab: number = perm[perm[xi] + yi + 1];
|
|
189
|
+
const ba: number = perm[perm[xi + 1] + yi];
|
|
190
|
+
const bb: number = perm[perm[xi + 1] + yi + 1];
|
|
191
|
+
|
|
192
|
+
// Gradient dot products
|
|
193
|
+
const g00: number = dot2(GRAD2[aa % 8], xf, yf);
|
|
194
|
+
const g10: number = dot2(GRAD2[ba % 8], xf - 1, yf);
|
|
195
|
+
const g01: number = dot2(GRAD2[ab % 8], xf, yf - 1);
|
|
196
|
+
const g11: number = dot2(GRAD2[bb % 8], xf - 1, yf - 1);
|
|
197
|
+
|
|
198
|
+
// Bilinear interpolation
|
|
199
|
+
const x1: number = lerp(g00, g10, u);
|
|
200
|
+
const x2: number = lerp(g01, g11, u);
|
|
201
|
+
const result: number = lerp(x1, x2, v);
|
|
202
|
+
|
|
203
|
+
return result * amplitude;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 3D Perlin noise.
|
|
208
|
+
*
|
|
209
|
+
* Useful for animated noise: use (x, y, frame * speed) as coordinates.
|
|
210
|
+
*
|
|
211
|
+
* @param {number} x - X coordinate
|
|
212
|
+
* @param {number} y - Y coordinate
|
|
213
|
+
* @param {number} z - Z coordinate (often frame * speed)
|
|
214
|
+
* @param {object} [options]
|
|
215
|
+
* @param {number} [options.seed=0] - Seed for the permutation table
|
|
216
|
+
* @param {number} [options.frequency=1] - Scales the input coordinates
|
|
217
|
+
* @param {number} [options.amplitude=1] - Scales the output value
|
|
218
|
+
* @returns {number} Noise value in [-amplitude, amplitude]
|
|
219
|
+
*/
|
|
220
|
+
export function noise3D(x: number, y: number, z: number, options: NoiseOptions = {}): number {
|
|
221
|
+
const { seed = 0, frequency = 1, amplitude = 1 } = options;
|
|
222
|
+
const perm: Uint8Array = getPermutation(seed);
|
|
223
|
+
|
|
224
|
+
x *= frequency;
|
|
225
|
+
y *= frequency;
|
|
226
|
+
z *= frequency;
|
|
227
|
+
|
|
228
|
+
const xi: number = Math.floor(x) & 255;
|
|
229
|
+
const yi: number = Math.floor(y) & 255;
|
|
230
|
+
const zi: number = Math.floor(z) & 255;
|
|
231
|
+
|
|
232
|
+
const xf: number = x - Math.floor(x);
|
|
233
|
+
const yf: number = y - Math.floor(y);
|
|
234
|
+
const zf: number = z - Math.floor(z);
|
|
235
|
+
|
|
236
|
+
const u: number = fade(xf);
|
|
237
|
+
const v: number = fade(yf);
|
|
238
|
+
const w: number = fade(zf);
|
|
239
|
+
|
|
240
|
+
// Hash the 8 corners
|
|
241
|
+
const a: number = perm[xi] + yi;
|
|
242
|
+
const aa: number = perm[a] + zi;
|
|
243
|
+
const ab: number = perm[a + 1] + zi;
|
|
244
|
+
const b: number = perm[xi + 1] + yi;
|
|
245
|
+
const ba: number = perm[b] + zi;
|
|
246
|
+
const bb: number = perm[b + 1] + zi;
|
|
247
|
+
|
|
248
|
+
const g000: number = dot3(GRAD3[perm[aa] % 12], xf, yf, zf);
|
|
249
|
+
const g100: number = dot3(GRAD3[perm[ba] % 12], xf - 1, yf, zf);
|
|
250
|
+
const g010: number = dot3(GRAD3[perm[ab] % 12], xf, yf - 1, zf);
|
|
251
|
+
const g110: number = dot3(GRAD3[perm[bb] % 12], xf - 1, yf - 1, zf);
|
|
252
|
+
const g001: number = dot3(GRAD3[perm[aa + 1] % 12], xf, yf, zf - 1);
|
|
253
|
+
const g101: number = dot3(GRAD3[perm[ba + 1] % 12], xf - 1, yf, zf - 1);
|
|
254
|
+
const g011: number = dot3(GRAD3[perm[ab + 1] % 12], xf, yf - 1, zf - 1);
|
|
255
|
+
const g111: number = dot3(GRAD3[perm[bb + 1] % 12], xf - 1, yf - 1, zf - 1);
|
|
256
|
+
|
|
257
|
+
// Trilinear interpolation
|
|
258
|
+
const x1: number = lerp(g000, g100, u);
|
|
259
|
+
const x2: number = lerp(g010, g110, u);
|
|
260
|
+
const y1: number = lerp(x1, x2, v);
|
|
261
|
+
|
|
262
|
+
const x3: number = lerp(g001, g101, u);
|
|
263
|
+
const x4: number = lerp(g011, g111, u);
|
|
264
|
+
const y2: number = lerp(x3, x4, v);
|
|
265
|
+
|
|
266
|
+
const result: number = lerp(y1, y2, w);
|
|
267
|
+
|
|
268
|
+
return result * amplitude;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Fractal Brownian Motion (fBm) — layered 2D noise for natural textures.
|
|
273
|
+
*
|
|
274
|
+
* @param {number} x - X coordinate
|
|
275
|
+
* @param {number} y - Y coordinate
|
|
276
|
+
* @param {object} [options]
|
|
277
|
+
* @param {number} [options.seed=0] - Seed
|
|
278
|
+
* @param {number} [options.octaves=4] - Number of noise layers
|
|
279
|
+
* @param {number} [options.frequency=1] - Base frequency
|
|
280
|
+
* @param {number} [options.amplitude=1] - Base amplitude
|
|
281
|
+
* @param {number} [options.lacunarity=2] - Frequency multiplier per octave
|
|
282
|
+
* @param {number} [options.persistence=0.5] - Amplitude multiplier per octave
|
|
283
|
+
* @returns {number} Noise value
|
|
284
|
+
*/
|
|
285
|
+
export function fbm2D(x: number, y: number, options: FbmOptions = {}): number {
|
|
286
|
+
const {
|
|
287
|
+
seed = 0,
|
|
288
|
+
octaves = 4,
|
|
289
|
+
frequency = 1,
|
|
290
|
+
amplitude = 1,
|
|
291
|
+
lacunarity = 2,
|
|
292
|
+
persistence = 0.5,
|
|
293
|
+
} = options;
|
|
294
|
+
|
|
295
|
+
let value: number = 0;
|
|
296
|
+
let freq: number = frequency;
|
|
297
|
+
let amp: number = amplitude;
|
|
298
|
+
|
|
299
|
+
for (let i = 0; i < octaves; i++) {
|
|
300
|
+
value += noise2D(x, y, { seed: seed + i * 1000, frequency: freq, amplitude: amp });
|
|
301
|
+
freq *= lacunarity;
|
|
302
|
+
amp *= persistence;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return value;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export default { random, randomRange, randomInt, noise2D, noise3D, fbm2D };
|