@woosh/meep-engine 2.131.3 → 2.131.5
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 +1 -1
- package/src/core/collection/queue/Deque.d.ts.map +1 -1
- package/src/core/collection/queue/Deque.js +17 -12
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD.d.ts +1 -0
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD.d.ts.map +1 -1
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD.js +1 -0
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.d.ts +39 -0
- package/src/core/math/physics/mie/MIE_PARTICLES_STANDARD_PRECOMPUTED.js +235 -214
- package/src/core/math/physics/mie/compute_lorenz_mie_optical_properties.d.ts.map +1 -1
- package/src/core/math/physics/mie/compute_lorenz_mie_optical_properties.js +3 -339
- package/src/core/math/physics/mie/compute_mie_particle_properties_rgb.js +1 -1
- package/src/core/math/physics/mie/compute_mie_phase.d.ts +16 -0
- package/src/core/math/physics/mie/compute_mie_phase.d.ts.map +1 -0
- package/src/core/math/physics/mie/compute_mie_phase.js +84 -0
- package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts +29 -0
- package/src/core/math/physics/mie/lorenz_mie_coefs.d.ts.map +1 -0
- package/src/core/math/physics/mie/lorenz_mie_coefs.js +233 -0
- package/src/core/math/physics/mie/mie_ab_to_optical_properties.d.ts +14 -0
- package/src/core/math/physics/mie/mie_ab_to_optical_properties.d.ts.map +1 -0
- package/src/core/math/physics/mie/mie_ab_to_optical_properties.js +109 -0
- package/src/engine/animation/curve/AnimationCurve.d.ts +18 -3
- package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
- package/src/engine/animation/curve/AnimationCurve.js +69 -36
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { assert } from "../../../assert.js";
|
|
2
|
+
import { v2_length } from "../../../geom/vec2/v2_length.js";
|
|
3
|
+
import { complex_add } from "../../complex/complex_add.js";
|
|
4
|
+
import { complex_div } from "../../complex/complex_div.js";
|
|
5
|
+
import { complex_mul } from "../../complex/complex_mul.js";
|
|
6
|
+
import { complex_sub } from "../../complex/complex_sub.js";
|
|
7
|
+
|
|
8
|
+
export function vec2(x, y) {
|
|
9
|
+
return new Float64Array([x, y]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function vec4(x, y, z, w) {
|
|
13
|
+
return new Float64Array([x, y, z, w]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function cadd(a, b) {
|
|
17
|
+
const r = vec2(0, 0);
|
|
18
|
+
|
|
19
|
+
complex_add(r, a, b);
|
|
20
|
+
|
|
21
|
+
return r;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function csub(a, b) {
|
|
25
|
+
const r = vec2(0, 0);
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
complex_sub(r, a, b);
|
|
29
|
+
|
|
30
|
+
return r;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function cmult(a, b) {
|
|
34
|
+
const r = vec2(0, 0);
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
complex_mul(r, a, b);
|
|
38
|
+
|
|
39
|
+
return r;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function cdiv(a, b) {
|
|
43
|
+
const r = vec2(0, 0);
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
complex_div(r, a, b);
|
|
47
|
+
|
|
48
|
+
return r;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Number of terms to include in infinite sums (truncation)
|
|
52
|
+
function terms_to_sum(z) {
|
|
53
|
+
const size = v2_length(z[0], z[1]);
|
|
54
|
+
return Math.ceil(size + 4.3 * Math.cbrt(size) + 1.0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Downward recurrence for A
|
|
58
|
+
function A_all_n(z, M) {
|
|
59
|
+
const A = new Array(M + 2);
|
|
60
|
+
A[M + 1] = vec2(0.0, 0.0);
|
|
61
|
+
for (let n = M; n >= 0; --n) {
|
|
62
|
+
const tmp = cdiv(vec2(n + 1.0, 0.0), z);
|
|
63
|
+
A[n] = csub(tmp, cdiv(vec2(1.0, 0.0), cadd(tmp, A[n + 1])));
|
|
64
|
+
}
|
|
65
|
+
return A;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Upward recurrences for B, psi_zeta, and R
|
|
69
|
+
function psi_zeta(n, z, A, old_B, old_psi_zeta) {
|
|
70
|
+
if (n > 0) {
|
|
71
|
+
const n_z = cdiv(vec2(n, 0.0), z);
|
|
72
|
+
const tmp = cmult(csub(n_z, A[n - 1]), csub(n_z, old_B));
|
|
73
|
+
old_psi_zeta = cmult(old_psi_zeta, tmp);
|
|
74
|
+
}
|
|
75
|
+
return old_psi_zeta;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Calculates the B_n(z) coefficient for term 'n' via upward recurrence.
|
|
80
|
+
* This is a helper function for LorenzMie_ab.
|
|
81
|
+
*
|
|
82
|
+
* @param {number} n - The current recurrence term index (n >= 0).
|
|
83
|
+
* @param {vec2} z - The complex size parameter (x or y).
|
|
84
|
+
* @param {Array<vec2>} A - Pre-computed log-derivatives A_n(z).
|
|
85
|
+
* @param {vec2} old_B - The previous B coefficient (B_{n-1}).
|
|
86
|
+
* @param {vec2} old_psi_zeta - The previous psi_zeta product term.
|
|
87
|
+
* @returns {vec2} The new B_n coefficient (complex number).
|
|
88
|
+
*/
|
|
89
|
+
function B(n, z, A, old_B, old_psi_zeta) {
|
|
90
|
+
|
|
91
|
+
if (n > 0) {
|
|
92
|
+
old_B.psi_zeta = psi_zeta(n, z, A, old_B, old_psi_zeta);
|
|
93
|
+
old_B = cadd(A[n], cdiv(vec2(0.0, 1.0), old_B.psi_zeta));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return old_B;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Calculates the R_n(z) coefficient (ratio psi_n / zeta_n) for term 'n'.
|
|
101
|
+
* This is a helper function for LorenzMie_ab.
|
|
102
|
+
*
|
|
103
|
+
* @param {number} n - The current recurrence term index (n >= 0).
|
|
104
|
+
* @param {vec2} z - The complex size parameter (x).
|
|
105
|
+
* @param {Array<vec2>} A - Pre-computed log-derivatives A_n(z).
|
|
106
|
+
* @param {vec2} B_n - The current B_n(z) coefficient.
|
|
107
|
+
* @param {vec2} old_R - The previous R coefficient (R_{n-1}).
|
|
108
|
+
* @returns {vec2} The new R_n coefficient (complex number).
|
|
109
|
+
*/
|
|
110
|
+
function R(n, z, A, B_n, old_R) {
|
|
111
|
+
if (n > 0) {
|
|
112
|
+
const n_z = cdiv(vec2(n, 0.0), z);
|
|
113
|
+
const tmp = cdiv(cadd(B_n, n_z), cadd(A[n], n_z));
|
|
114
|
+
old_R = cmult(old_R, tmp);
|
|
115
|
+
}
|
|
116
|
+
return old_R;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Calculates the Lorenz-Mie scattering coefficients a_n and b_n for a single term 'n'.
|
|
121
|
+
* This implements the formulas from Figure 3 of Frisvad et al. 2007.
|
|
122
|
+
*
|
|
123
|
+
* @param {number} n - The current recurrence term index (n >= 0).
|
|
124
|
+
* @param {number} size - Unitless size parameter x_vac = 2*pi*r / lambda_vac.
|
|
125
|
+
* @param {vec2} n_p - Complex refractive index of the particle.
|
|
126
|
+
* @param {vec2} n_med - Complex refractive index of the medium.
|
|
127
|
+
* @param {Array<vec2>} A_p - Pre-computed log-derivatives A_n(y) for the particle.
|
|
128
|
+
* @param {Array<vec2>} A_med - Pre-computed log-derivatives A_n(x) for the medium.
|
|
129
|
+
* @param {vec2} old_B - The previous B_{n-1}(x) coefficient.
|
|
130
|
+
* @param {vec2} old_R - The previous R_{n-1}(x) coefficient.
|
|
131
|
+
* @param {vec2} old_psi_zeta - The previous psi_zeta product.
|
|
132
|
+
* @returns {vec4} A vec4 [Re(a_n), Im(a_n), Re(b_n), Im(b_n)] with state properties
|
|
133
|
+
* (.old_B, .old_R, .old_psi_zeta) attached for the next iteration.
|
|
134
|
+
*/
|
|
135
|
+
function LorenzMie_ab(n, size, n_p, n_med, A_p, A_med, old_B, old_R, old_psi_zeta) {
|
|
136
|
+
|
|
137
|
+
const x = vec2(size * n_med[0], size * n_med[1]);
|
|
138
|
+
const B_n = B(n, x, A_med, old_B, old_psi_zeta);
|
|
139
|
+
const R_n = R(n, x, A_med, B_n, old_R);
|
|
140
|
+
const n_med_A_p = cmult(n_med, A_p[n]);
|
|
141
|
+
const n_p_A_med = cmult(n_p, A_med[n]);
|
|
142
|
+
const n_p_A_p = cmult(n_p, A_p[n]);
|
|
143
|
+
const n_med_A_med = cmult(n_med, A_med[n]);
|
|
144
|
+
const n_p_B_n = cmult(n_p, B_n);
|
|
145
|
+
const n_med_B_n = cmult(n_med, B_n);
|
|
146
|
+
const a = cmult(R_n, cdiv(csub(n_med_A_p, n_p_A_med), csub(n_med_A_p, n_p_B_n)));
|
|
147
|
+
const b = cmult(R_n, cdiv(csub(n_p_A_p, n_med_A_med), csub(n_p_A_p, n_med_B_n)));
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
const ab = vec4(a[0], a[1], b[0], b[1]);
|
|
151
|
+
|
|
152
|
+
ab.old_psi_zeta = n > 0 ? old_B.psi_zeta : old_psi_zeta;
|
|
153
|
+
ab.old_B = B_n;
|
|
154
|
+
ab.old_R = R_n;
|
|
155
|
+
|
|
156
|
+
return ab;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The following source code implements Lorenz-Mie theory using the formulas presented in the SIGGRAPH 2007 paper
|
|
161
|
+
*
|
|
162
|
+
* Computing the Scattering Properties of Participating
|
|
163
|
+
* Media Using Lorenz-Mie Theory
|
|
164
|
+
*
|
|
165
|
+
* By authors: Jeppe Revall Frisvad
|
|
166
|
+
* Niels Joergen Christensen
|
|
167
|
+
* Henrik Wann Jensen
|
|
168
|
+
*
|
|
169
|
+
* Code written by Jeppe Revall Frisvad, 2017.
|
|
170
|
+
* Copyright (c) Jeppe Revall Frisvad 2017
|
|
171
|
+
*
|
|
172
|
+
* Permission is granted to anyone to use this code as
|
|
173
|
+
* software for any purpose, including commercial applications.
|
|
174
|
+
* However, the software is provided 'as-is' without any warranty.
|
|
175
|
+
*
|
|
176
|
+
* @param {number} radius_m - Particle radius (in meters).
|
|
177
|
+
* @param {number} wavelength_m - Wavelength of light in vacuum (in meters).
|
|
178
|
+
* @param {vec2} n_p - Complex refractive index of the particle
|
|
179
|
+
* @param {vec2} n_med - Complex refractive index of the medium (e.g., [1.0, 0.0] for air).
|
|
180
|
+
* @returns {Float64Array} Mie coefficients a_n and b_n in form [a_n0.r, a_n0.i, b_n0.r, b_n0.i, ... a_nN.r, a_nN.i, b_nN.r, b_nN.i]
|
|
181
|
+
*/
|
|
182
|
+
export function lorenz_mie_coefs(
|
|
183
|
+
wavelength_m,
|
|
184
|
+
radius_m,
|
|
185
|
+
n_p,
|
|
186
|
+
n_med
|
|
187
|
+
) {
|
|
188
|
+
|
|
189
|
+
assert.isNumber(wavelength_m, 'wavelength_m');
|
|
190
|
+
assert.isFinite(wavelength_m, 'wavelength_m');
|
|
191
|
+
assert.greaterThan(wavelength_m, 0, 'wavelength_m');
|
|
192
|
+
|
|
193
|
+
assert.isNumber(radius_m, 'radius_m');
|
|
194
|
+
assert.isFinite(radius_m, 'radius_m');
|
|
195
|
+
assert.greaterThan(radius_m, 0, 'radius_m');
|
|
196
|
+
|
|
197
|
+
const psize = 2.0 * Math.PI * radius_m / wavelength_m;
|
|
198
|
+
|
|
199
|
+
const x = vec2(psize * n_med[0], psize * n_med[1]);
|
|
200
|
+
const y = vec2(psize * n_p[0], psize * n_p[1]);
|
|
201
|
+
const M = terms_to_sum(x);
|
|
202
|
+
const A_med = A_all_n(x, M);
|
|
203
|
+
const A_p = A_all_n(y, M);
|
|
204
|
+
|
|
205
|
+
const e_term = Math.exp(2.0 * x[1]);
|
|
206
|
+
|
|
207
|
+
let prev_psi_zeta = vec2(0.5 * (1.0 - Math.cos(2.0 * x[0]) / e_term), -0.5 * Math.sin(2.0 * x[0]) / e_term);
|
|
208
|
+
let prev_B = vec2(0.0, 1.0);
|
|
209
|
+
let prev_R = vec2(0.5 * (1.0 - Math.cos(2.0 * x[0]) * e_term), 0.5 * Math.sin(2.0 * x[0]) * e_term);
|
|
210
|
+
|
|
211
|
+
const ab = new Float64Array(M * 4);
|
|
212
|
+
|
|
213
|
+
for (let n = 0; n < M; ++n) {
|
|
214
|
+
|
|
215
|
+
const ab_n = LorenzMie_ab(
|
|
216
|
+
n, psize, n_p, n_med, A_p, A_med,
|
|
217
|
+
prev_B, prev_R, prev_psi_zeta
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
prev_psi_zeta = ab_n.old_psi_zeta;
|
|
221
|
+
prev_B = ab_n.old_B;
|
|
222
|
+
prev_R = ab_n.old_R;
|
|
223
|
+
|
|
224
|
+
// plug the data in
|
|
225
|
+
ab[4 * n] = ab_n[0];
|
|
226
|
+
ab[4 * n + 1] = ab_n[1];
|
|
227
|
+
ab[4 * n + 2] = ab_n[2];
|
|
228
|
+
ab[4 * n + 3] = ab_n[3];
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return ab;
|
|
233
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the final optical properties by summing the Mie coefficients.
|
|
3
|
+
* This function implements Equations (2) and (3) from the 2007 paper
|
|
4
|
+
* "Computing the Scattering Properties of Participating Media..."
|
|
5
|
+
*
|
|
6
|
+
* @param {Float32Array} ab - The flat array of a_n, b_n coefficients from LorenzMie_coefs.
|
|
7
|
+
* @param {number} radius - Particle radius (in meters).
|
|
8
|
+
* @param {number} wavelength - Wavelength of light in vacuum (in meters).
|
|
9
|
+
* @param {vec2} n_med - Complex refractive index of the medium (e.g., [1.0, 0.0] for air).
|
|
10
|
+
* @returns {object} An object with Q_e, Q_s, albedo, C_ext, and C_sca.
|
|
11
|
+
*/
|
|
12
|
+
export function mie_ab_to_optical_properties(ab: Float32Array, wavelength: number, radius: number, n_med: typeof vec2): object;
|
|
13
|
+
import { vec2 } from "./lorenz_mie_coefs.js";
|
|
14
|
+
//# sourceMappingURL=mie_ab_to_optical_properties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mie_ab_to_optical_properties.d.ts","sourceRoot":"","sources":["../../../../../../src/core/math/physics/mie/mie_ab_to_optical_properties.js"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,iDANW,YAAY,cAEZ,MAAM,UADN,MAAM,uBAGJ,MAAM,CAgGlB;qBA3GuC,uBAAuB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { v2_dot } from "../../../geom/vec2/v2_dot.js";
|
|
2
|
+
import { cadd, cdiv, cmult, vec2 } from "./lorenz_mie_coefs.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Calculates the final optical properties by summing the Mie coefficients.
|
|
6
|
+
* This function implements Equations (2) and (3) from the 2007 paper
|
|
7
|
+
* "Computing the Scattering Properties of Participating Media..."
|
|
8
|
+
*
|
|
9
|
+
* @param {Float32Array} ab - The flat array of a_n, b_n coefficients from LorenzMie_coefs.
|
|
10
|
+
* @param {number} radius - Particle radius (in meters).
|
|
11
|
+
* @param {number} wavelength - Wavelength of light in vacuum (in meters).
|
|
12
|
+
* @param {vec2} n_med - Complex refractive index of the medium (e.g., [1.0, 0.0] for air).
|
|
13
|
+
* @returns {object} An object with Q_e, Q_s, albedo, C_ext, and C_sca.
|
|
14
|
+
*/
|
|
15
|
+
export function mie_ab_to_optical_properties(
|
|
16
|
+
ab,
|
|
17
|
+
wavelength,
|
|
18
|
+
radius,
|
|
19
|
+
n_med
|
|
20
|
+
) {
|
|
21
|
+
const M = ab.length / 4;
|
|
22
|
+
|
|
23
|
+
let sum_t = vec2(0, 0); // Complex sum for Ct
|
|
24
|
+
let sum_s = 0; // Real sum for Cs
|
|
25
|
+
let sum_g = 0; // Real sum for Asymmetry (g)
|
|
26
|
+
|
|
27
|
+
for (let n = 1; n < M; ++n) {
|
|
28
|
+
// The 'n' in the paper's formulas
|
|
29
|
+
const multiplier = 2 * n + 1;
|
|
30
|
+
|
|
31
|
+
// Get an and bn from your flat array
|
|
32
|
+
const an = vec2(ab[4 * n], ab[4 * n + 1]);
|
|
33
|
+
const bn = vec2(ab[4 * n + 2], ab[4 * n + 3]);
|
|
34
|
+
|
|
35
|
+
// 1. For Extinction (Ct)
|
|
36
|
+
const an_plus_bn = cadd(an, bn);
|
|
37
|
+
// You need complex math functions cadd, cdiv
|
|
38
|
+
const n_med_sq = cmult(n_med, n_med);
|
|
39
|
+
const term_t = cdiv(an_plus_bn, n_med_sq);
|
|
40
|
+
sum_t = cadd(sum_t, cmult(vec2(multiplier, 0), term_t));
|
|
41
|
+
|
|
42
|
+
// 2. For Scattering (Cs)
|
|
43
|
+
const an_mag_sq = an[0] * an[0] + an[1] * an[1];
|
|
44
|
+
const bn_mag_sq = bn[0] * bn[0] + bn[1] * bn[1];
|
|
45
|
+
sum_s += multiplier * (an_mag_sq + bn_mag_sq);
|
|
46
|
+
|
|
47
|
+
// 3. Asymmetry Accumulation (New)
|
|
48
|
+
// Term A: Interference of an and bn
|
|
49
|
+
// Formula: (2n+1) / (n(n+1)) * Re(an * bn*)
|
|
50
|
+
const factor_A = (2 * n + 1) / (n * (n + 1));
|
|
51
|
+
sum_g += factor_A * v2_dot(an[0], an[1], bn[0], bn[1]);
|
|
52
|
+
|
|
53
|
+
// Term B: Interference of n and n+1
|
|
54
|
+
// Formula: (n(n+2)) / (n+1) * Re(an * an+1* + bn * bn+1*)
|
|
55
|
+
if (n < M - 1) {
|
|
56
|
+
const n_next = n + 1;
|
|
57
|
+
const an1_r = ab[4 * n_next];
|
|
58
|
+
const an1_i = ab[4 * n_next + 1];
|
|
59
|
+
const bn1_r = ab[4 * n_next + 2];
|
|
60
|
+
const bn1_i = ab[4 * n_next + 3];
|
|
61
|
+
|
|
62
|
+
const factor_B = (n * (n + 2)) / (n + 1);
|
|
63
|
+
|
|
64
|
+
const term_an_an1 = v2_dot(an[0], an[1], an1_r, an1_i);
|
|
65
|
+
const term_bn_bn1 = v2_dot(bn[0], bn[1], bn1_r, bn1_i);
|
|
66
|
+
|
|
67
|
+
sum_g += factor_B * (term_an_an1 + term_bn_bn1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Final Calculations ---
|
|
72
|
+
|
|
73
|
+
// Asymmetry Parameter (g)
|
|
74
|
+
// g = (4/x^2 * sum_g) / (2/x^2 * sum_s) => 2 * sum_g / sum_s
|
|
75
|
+
let g = 0;
|
|
76
|
+
if (sum_s > 1e-12) {
|
|
77
|
+
g = (2 * sum_g) / sum_s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Extinction (Ct from Eq. 22)
|
|
81
|
+
const C_t = (Math.pow(wavelength, 2) / (2 * Math.PI)) * sum_t[0]; //
|
|
82
|
+
|
|
83
|
+
// Scattering (Cs from Eq. 23)
|
|
84
|
+
const alpha = (4 * Math.PI * radius * n_med[1]) / wavelength;
|
|
85
|
+
let gamma;
|
|
86
|
+
if (Math.abs(alpha) < 1e-10) {
|
|
87
|
+
// avoid division by 0
|
|
88
|
+
gamma = 1;
|
|
89
|
+
} else {
|
|
90
|
+
gamma = (2 * (1 + (alpha - 1) * Math.exp(alpha))) / Math.pow(alpha, 2);
|
|
91
|
+
}
|
|
92
|
+
const n_med_mag_sq = n_med[0] * n_med[0] + n_med[1] * n_med[1];
|
|
93
|
+
const C_s_factor = (Math.pow(wavelength, 2) * Math.exp(-alpha)) / (2 * Math.PI * gamma * n_med_mag_sq);
|
|
94
|
+
const C_s = C_s_factor * sum_s;
|
|
95
|
+
|
|
96
|
+
// Bulk Coefficients (assuming number density N)
|
|
97
|
+
const sigma_a_med = (4 * Math.PI * n_med[1]) / wavelength;
|
|
98
|
+
|
|
99
|
+
// To get bulk coefficients sigma_t and sigma_s, use the following formulae, where N is numeric density (particles per cubic meter):
|
|
100
|
+
// sigma_t = sigma_a_med + C_t * N;
|
|
101
|
+
// sigma_s = C_s * N;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
sigma_a_med,
|
|
105
|
+
C_ext: C_t,
|
|
106
|
+
C_sca: C_s,
|
|
107
|
+
g, // Anisotropy parameter
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -12,10 +12,17 @@
|
|
|
12
12
|
*
|
|
13
13
|
* jump_curve.evaluate(0.1); // what is the height at time 0.1?
|
|
14
14
|
*
|
|
15
|
+
* @example
|
|
16
|
+
* const curve = AnimationCurve.easeInOut();
|
|
17
|
+
*
|
|
18
|
+
* sprite.transparency = curve.evaluate(time); // smoothly animate transparency of the sprite
|
|
19
|
+
*
|
|
20
|
+
* @implements Iterable<Keyframe>
|
|
21
|
+
*
|
|
15
22
|
* @author Alex Goldring
|
|
16
23
|
* @copyright Company Named Limited (c) 2025
|
|
17
24
|
*/
|
|
18
|
-
export class AnimationCurve {
|
|
25
|
+
export class AnimationCurve implements Iterable<Keyframe> {
|
|
19
26
|
/**
|
|
20
27
|
* Utility constructor
|
|
21
28
|
* @param {Keyframe[]} keys
|
|
@@ -25,6 +32,7 @@ export class AnimationCurve {
|
|
|
25
32
|
/**
|
|
26
33
|
* S-shaped curve that starts slowly, ramps up and flattens out again.
|
|
27
34
|
* Useful for pleasing transitions where exit and entry should not be abrupt.
|
|
35
|
+
*
|
|
28
36
|
* @param {number} [timeStart]
|
|
29
37
|
* @param {number} [valueStart]
|
|
30
38
|
* @param {number} [timeEnd]
|
|
@@ -71,7 +79,7 @@ export class AnimationCurve {
|
|
|
71
79
|
/**
|
|
72
80
|
*
|
|
73
81
|
* @param {Keyframe} key
|
|
74
|
-
* @returns {boolean}
|
|
82
|
+
* @returns {boolean} true if the key was removed, false if the key was not found
|
|
75
83
|
*/
|
|
76
84
|
remove(key: Keyframe): boolean;
|
|
77
85
|
/**
|
|
@@ -108,10 +116,12 @@ export class AnimationCurve {
|
|
|
108
116
|
get duration(): number;
|
|
109
117
|
/**
|
|
110
118
|
* Returns index of a key that is just before or at the time specified.
|
|
119
|
+
* Useful for insertion and evaluation logic.
|
|
120
|
+
* Note: if time is past the end of last key - index of the last key will be returned instead
|
|
111
121
|
* @param {number} time
|
|
112
122
|
* @returns {number} index of the key
|
|
113
123
|
*/
|
|
114
|
-
|
|
124
|
+
getKeyIndexLow(time: number): number;
|
|
115
125
|
/**
|
|
116
126
|
* Evaluate interpolated value across the curve at a given time.
|
|
117
127
|
* @param {number} time time in seconds
|
|
@@ -164,6 +174,11 @@ export class AnimationCurve {
|
|
|
164
174
|
* @type {boolean}
|
|
165
175
|
*/
|
|
166
176
|
readonly isAnimationCurve: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* @deprecated use `getKeyIndexLow` instead
|
|
179
|
+
*/
|
|
180
|
+
getKeyIndexByTime: (time: number) => number;
|
|
181
|
+
[Symbol.iterator](): Generator<Keyframe, void, unknown>;
|
|
167
182
|
}
|
|
168
183
|
import { Keyframe } from "./Keyframe.js";
|
|
169
184
|
//# sourceMappingURL=AnimationCurve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationCurve.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/curve/AnimationCurve.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AnimationCurve.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/curve/AnimationCurve.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,gDALwB,QAAQ;IAqZ5B;;;;OAIG;IACH,kBAHW,QAAQ,EAAE,GACR,cAAc,CAQ1B;IAED;;;;;;;;;OASG;IACH,6BANW,MAAM,eACN,MAAM,YACN,MAAM,aACN,MAAM,GACL,cAAc,CAUzB;IAED;;;;;;OAMG;IACH,4BALW,MAAM,YACN,MAAM,UACN,MAAM,GACL,cAAc,CAOzB;IAED;;;;;;;OAOG;IACH,0BANW,MAAM,eACN,MAAM,YACN,MAAM,aACN,MAAM,GACL,cAAc,CAezB;IAndD;;;;;OAKG;IACH,eAFU,QAAQ,EAAE,CAEV;IAEV;;;;;OAKG;IACH,SAHW,QAAQ,GACN,MAAM,CAuClB;IAED;;;OAGG;IACH,cAFW,QAAQ,EAAE,QAUpB;IAED;;;;OAIG;IACH,YAHW,QAAQ,GACN,OAAO,CAYnB;IAED;;OAEG;IACH,cAEC;IAGD;;;OAGG;IACH,WAFY,OAAO,CAIlB;IAED;;;OAGG;IACH,qBAEC;IAED;;;;OAIG;IACH,yBAUC;IAED;;;;OAIG;IACH,uBAUC;IAED;;;;OAIG;IACH,uBAcC;IAED;;;;;;OAMG;IACH,qBAHW,MAAM,GACJ,MAAM,CAmDlB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACL,MAAM,CAoCjB;IAED;;;;OAIG;IACH,qBAFW,MAAM,QAmChB;IAED;;;;OAIG;IACH,sBAHW,MAAM,UACN,MAAM,QAWhB;IAED,0BAKC;IAGD;;;OAGG;IACH,YAFW,cAAc,QAIxB;IAED;;;OAGG;IACH,SAFY,cAAc,CAQzB;IAED;;;;OAIG;IACH,cAHW,cAAc,GACb,OAAO,CAIlB;IAED;;;OAGG;IACH,QAFY,MAAM,CAIjB;IAED;;MAIC;IAED;;aAkBC;IAoFL;;;;OAIG;IACH,2BAFU,OAAO,CAEwB;IAGzC;;OAEG;IACH,0BA3Te,MAAM,KACJ,MAAM,CA0TmB;IA7FtC,wDAQC;CAuEJ;yBA/ewB,eAAe"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { assert } from "../../../core/assert.js";
|
|
2
|
-
import { binarySearchHighIndex } from "../../../core/collection/array/binarySearchHighIndex.js";
|
|
3
2
|
import { computeHashArray } from "../../../core/collection/array/computeHashArray.js";
|
|
4
3
|
import { isArrayEqual } from "../../../core/collection/array/isArrayEqual.js";
|
|
5
4
|
import { lerp } from "../../../core/math/lerp.js";
|
|
@@ -9,16 +8,6 @@ import { invokeObjectToJSON } from "../../../core/model/object/invokeObjectToJSO
|
|
|
9
8
|
import { evaluate_two_key_curve } from "./evaluate_two_key_curve.js";
|
|
10
9
|
import { Keyframe } from "./Keyframe.js";
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {number} time
|
|
15
|
-
* @param {Keyframe} keyframe
|
|
16
|
-
* @return {number}
|
|
17
|
-
*/
|
|
18
|
-
function compareKeyframeToTime(time, keyframe) {
|
|
19
|
-
return time - keyframe.time;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
11
|
/**
|
|
23
12
|
* Describes change of a numeric value over time.
|
|
24
13
|
* Values are stored in {@link Keyframe}s, interpolation is defined by tangents on {@link Keyframe}s.
|
|
@@ -33,6 +22,13 @@ function compareKeyframeToTime(time, keyframe) {
|
|
|
33
22
|
*
|
|
34
23
|
* jump_curve.evaluate(0.1); // what is the height at time 0.1?
|
|
35
24
|
*
|
|
25
|
+
* @example
|
|
26
|
+
* const curve = AnimationCurve.easeInOut();
|
|
27
|
+
*
|
|
28
|
+
* sprite.transparency = curve.evaluate(time); // smoothly animate transparency of the sprite
|
|
29
|
+
*
|
|
30
|
+
* @implements Iterable<Keyframe>
|
|
31
|
+
*
|
|
36
32
|
* @author Alex Goldring
|
|
37
33
|
* @copyright Company Named Limited (c) 2025
|
|
38
34
|
*/
|
|
@@ -61,6 +57,8 @@ export class AnimationCurve {
|
|
|
61
57
|
const key_count = keys.length;
|
|
62
58
|
const last_key_index = key_count - 1;
|
|
63
59
|
|
|
60
|
+
// Optimization: if the curve is empty or the new key is chronologically
|
|
61
|
+
// after the last key, we can simply push it to the end.
|
|
64
62
|
if (
|
|
65
63
|
last_key_index < 0
|
|
66
64
|
|| keys[last_key_index].time <= key.time
|
|
@@ -71,17 +69,21 @@ export class AnimationCurve {
|
|
|
71
69
|
|
|
72
70
|
return key_count;
|
|
73
71
|
|
|
74
|
-
} else {
|
|
72
|
+
} else if (key_count > 0 && keys[0].time > key.time) {
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
// TODO make use of this.getKeyIndexByTime instead
|
|
78
|
-
const i = binarySearchHighIndex(keys, key.time, compareKeyframeToTime, 0, last_key_index);
|
|
74
|
+
keys.unshift(key);
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
keys.splice(i, 0, key);
|
|
82
|
-
|
|
83
|
-
return i;
|
|
76
|
+
return 0;
|
|
84
77
|
}
|
|
78
|
+
|
|
79
|
+
// figure out the right place to insert the key
|
|
80
|
+
const i = this.getKeyIndexLow(key.time) + 1;
|
|
81
|
+
|
|
82
|
+
// insert key at the right place
|
|
83
|
+
keys.splice(i, 0, key);
|
|
84
|
+
|
|
85
|
+
return i;
|
|
86
|
+
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
/**
|
|
@@ -101,7 +103,7 @@ export class AnimationCurve {
|
|
|
101
103
|
/**
|
|
102
104
|
*
|
|
103
105
|
* @param {Keyframe} key
|
|
104
|
-
* @returns {boolean}
|
|
106
|
+
* @returns {boolean} true if the key was removed, false if the key was not found
|
|
105
107
|
*/
|
|
106
108
|
remove(key) {
|
|
107
109
|
const i = this.keys.indexOf(key);
|
|
@@ -196,21 +198,37 @@ export class AnimationCurve {
|
|
|
196
198
|
|
|
197
199
|
/**
|
|
198
200
|
* Returns index of a key that is just before or at the time specified.
|
|
201
|
+
* Useful for insertion and evaluation logic.
|
|
202
|
+
* Note: if time is past the end of last key - index of the last key will be returned instead
|
|
199
203
|
* @param {number} time
|
|
200
204
|
* @returns {number} index of the key
|
|
201
205
|
*/
|
|
202
|
-
|
|
206
|
+
getKeyIndexLow(time) {
|
|
207
|
+
assert.isNumber(time, 'time');
|
|
208
|
+
assert.notNaN(time, 'time');
|
|
209
|
+
|
|
203
210
|
const keys = this.keys;
|
|
204
211
|
const key_count = keys.length;
|
|
205
212
|
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
if (key_count === 0) {
|
|
214
|
+
// no keys
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
208
217
|
|
|
209
218
|
if (time <= keys[0].time) {
|
|
210
219
|
// before start
|
|
211
220
|
return 0;
|
|
212
221
|
}
|
|
213
222
|
|
|
223
|
+
if (time >= keys[key_count - 1].time) {
|
|
224
|
+
// after the end
|
|
225
|
+
return key_count - 1;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let found_index = 0;
|
|
229
|
+
let i0 = 0;
|
|
230
|
+
let i1 = key_count - 1;
|
|
231
|
+
|
|
214
232
|
// binary search
|
|
215
233
|
while (i0 <= i1) {
|
|
216
234
|
const pivot = (i0 + i1) >>> 1;
|
|
@@ -218,27 +236,22 @@ export class AnimationCurve {
|
|
|
218
236
|
const key = keys[pivot];
|
|
219
237
|
const key_time = key.time;
|
|
220
238
|
|
|
221
|
-
if (key_time
|
|
239
|
+
if (key_time <= time) {
|
|
240
|
+
found_index = pivot;
|
|
222
241
|
i0 = pivot + 1;
|
|
223
242
|
} else if (key_time > time) {
|
|
224
243
|
i1 = pivot - 1;
|
|
225
|
-
} else {
|
|
226
|
-
i0 = pivot;
|
|
227
|
-
break;
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
|
|
231
|
-
|
|
232
|
-
// swap
|
|
233
|
-
i0 = i1;
|
|
234
|
-
}
|
|
247
|
+
assert.lessThanOrEqual(keys[found_index].time, time, 'keys[found_index].time >= time');
|
|
235
248
|
|
|
236
249
|
// fast-forward to last matching frame if there are multiple matches
|
|
237
|
-
while (
|
|
238
|
-
|
|
250
|
+
while (found_index + 2 < key_count && keys[found_index + 1].time === time) {
|
|
251
|
+
found_index++;
|
|
239
252
|
}
|
|
240
253
|
|
|
241
|
-
return
|
|
254
|
+
return found_index;
|
|
242
255
|
}
|
|
243
256
|
|
|
244
257
|
/**
|
|
@@ -262,7 +275,7 @@ export class AnimationCurve {
|
|
|
262
275
|
return keys[0].value;
|
|
263
276
|
}
|
|
264
277
|
|
|
265
|
-
const i = this.
|
|
278
|
+
const i = this.getKeyIndexLow(time);
|
|
266
279
|
|
|
267
280
|
if (i >= key_count - 1) {
|
|
268
281
|
// past last keyframe
|
|
@@ -400,12 +413,25 @@ export class AnimationCurve {
|
|
|
400
413
|
|
|
401
414
|
keyframe.fromJSON(keys[i]);
|
|
402
415
|
|
|
416
|
+
// The 'add' sorts the keys incrementally, we can do this faster if we assume the input is already sorted.
|
|
417
|
+
// But to be safe we don't make that assumption
|
|
418
|
+
|
|
403
419
|
this.add(keyframe);
|
|
404
420
|
|
|
405
421
|
}
|
|
406
422
|
|
|
407
423
|
}
|
|
408
424
|
|
|
425
|
+
* [Symbol.iterator]() {
|
|
426
|
+
|
|
427
|
+
for (const key of this.keys) {
|
|
428
|
+
|
|
429
|
+
yield key;
|
|
430
|
+
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
}
|
|
434
|
+
|
|
409
435
|
/**
|
|
410
436
|
* Utility constructor
|
|
411
437
|
* @param {Keyframe[]} keys
|
|
@@ -422,6 +448,7 @@ export class AnimationCurve {
|
|
|
422
448
|
/**
|
|
423
449
|
* S-shaped curve that starts slowly, ramps up and flattens out again.
|
|
424
450
|
* Useful for pleasing transitions where exit and entry should not be abrupt.
|
|
451
|
+
*
|
|
425
452
|
* @param {number} [timeStart]
|
|
426
453
|
* @param {number} [valueStart]
|
|
427
454
|
* @param {number} [timeEnd]
|
|
@@ -482,4 +509,10 @@ export class AnimationCurve {
|
|
|
482
509
|
* @readonly
|
|
483
510
|
* @type {boolean}
|
|
484
511
|
*/
|
|
485
|
-
AnimationCurve.prototype.isAnimationCurve = true;
|
|
512
|
+
AnimationCurve.prototype.isAnimationCurve = true;
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* @deprecated use `getKeyIndexLow` instead
|
|
517
|
+
*/
|
|
518
|
+
AnimationCurve.prototype.getKeyIndexByTime = AnimationCurve.prototype.getKeyIndexLow;
|