@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
84 lines (69 loc) • 3.05 kB
JavaScript
import { vec2 } from "./lorenz_mie_coefs.js";
/**
* Computes the normalized Mie phase function value for a specific angle.
*
* The phase function P(theta) is normalized such that the integral over 4pi solid angle is 4pi.
* This implementation uses the recurrence relations for angular functions pi_n and tau_n as described by Bohren & Huffman.
*
* @param {Float32Array} ab - The flat array of a_n, b_n coefficients from LorenzMie_coefs.
* @param {number} C_sca total scattering cross-section for normalization
* @param {number} wavelength - Wavelength of light in vacuum (in meters).
* @param {vec2} n_med - Complex refractive index of the medium.
* @param {number} cos_theta - Cosine of the angle between incoming light and observation angle.
* @returns {number} The normalized phase function value P(theta).
*/
export function compute_mie_phase(
ab,
C_sca,
n_med,
wavelength,
cos_theta,
) {
// If there is effectively no scattering, return isotropic phase (1.0)
if (C_sca < 1e-30) return 1.0;
const M = ab.length / 4;
// Initialize sums as scalars (faster than array access)
// S1 = Real + Imaginary parts
let S1_r = 0.0;
let S1_i = 0.0;
// S2 = Real + Imaginary parts
let S2_r = 0.0;
let S2_i = 0.0;
// Angular function recurrence initialization
let pi_prev = 0.0; // pi_0 = 0
let pi_cur = 1.0; // pi_1 = 1
for (let n = 1; n < M; ++n) {
// 1. Load Coefficients directly (Zero Allocation)
const idx = 4 * n;
const an_r = ab[idx];
const an_i = ab[idx + 1];
const bn_r = ab[idx + 2];
const bn_i = ab[idx + 3];
// 2. Angular Recurrence
// tau_n = n * cos_theta * pi_n - (n + 1) * pi_{n-1}
const tau_cur = n * cos_theta * pi_cur - (n + 1) * pi_prev;
// 3. Update Sums
// factor = (2n + 1) / (n(n + 1))
const factor = (2 * n + 1) / (n * (n + 1));
// S1 += factor * (an * pi + bn * tau)
S1_r += factor * (an_r * pi_cur + bn_r * tau_cur);
S1_i += factor * (an_i * pi_cur + bn_i * tau_cur);
// S2 += factor * (an * tau + bn * pi)
S2_r += factor * (an_r * tau_cur + bn_r * pi_cur);
S2_i += factor * (an_i * tau_cur + bn_i * pi_cur);
// 4. Prepare next iteration
// pi_{n+1} = ((2n + 1) * cos * pi_n - (n + 1) * pi_{n-1}) / n
const pi_next = ((2 * n + 1) * cos_theta * pi_cur - (n + 1) * pi_prev) / n;
pi_prev = pi_cur;
pi_cur = pi_next;
}
// Calculate Intensity: (|S1|^2 + |S2|^2) / 2
const S1_sq = S1_r * S1_r + S1_i * S1_i;
const S2_sq = S2_r * S2_r + S2_i * S2_i;
const intensity = 0.5 * (S1_sq + S2_sq);
// Wavenumber in medium k = 2pi * Re(n_med) / lambda
const k_med = (2.0 * Math.PI * n_med[0]) / wavelength;
const k2 = k_med * k_med;
// Normalization: P(theta) = 4pi * I / (k^2 * C_sca)
return (4.0 * Math.PI * intensity) / (k2 * C_sca);
}