UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

84 lines (69 loc) 3.05 kB
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); }