use std::f32::consts::PI;
use smallvec::SmallVec;
use crate::core::pbrt::find_interval;
use crate::core::pbrt::Float;
use crate::core::pbrt::INV_2_PI;
pub fn catmull_rom_weights(
nodes: &[Float],
x: Float,
offset: &mut i32,
weights: &mut [Float; 4],
) -> bool {
if !(x >= *nodes.first().unwrap() && x <= *nodes.last().unwrap()) {
return false;
}
let idx: i32 = find_interval(nodes.len() as i32, |index| nodes[index as usize] <= x);
*offset = idx - 1;
assert!(idx >= 0);
let x0: Float = nodes[idx as usize];
let x1: Float = nodes[(idx + 1) as usize];
let t: Float = (x - x0) / (x1 - x0);
let t2: Float = t * t;
let t3: Float = t2 * t;
weights[1] = 2.0 as Float * t3 - 3.0 as Float * t2 + 1.0 as Float;
weights[2] = -2.0 as Float * t3 + 3.0 as Float * t2;
if idx > 0_i32 {
let w0: Float = (t3 - 2.0 as Float * t2 + t) * (x1 - x0) / (x1 - nodes[(idx - 1) as usize]);
weights[0] = -w0;
weights[2] += w0;
} else {
let w0: Float = t3 - 2.0 as Float * t2 + t;
weights[0] = 0.0 as Float;
weights[1] -= w0;
weights[2] += w0;
}
if (idx + 2) < nodes.len() as i32 {
let w3: Float = (t3 - t2) * (x1 - x0) / (nodes[(idx + 2) as usize] - x0);
weights[1] -= w3;
weights[3] = w3;
} else {
let w3: Float = t3 - t2;
weights[1] -= w3;
weights[2] += w3;
weights[3] = 0.0 as Float;
}
true
}
pub fn sample_catmull_rom_2d(
nodes1: &[Float],
nodes2: &[Float],
values: &[Float],
cdf: &[Float],
alpha: Float,
u: Float,
fval: Option<&mut Float>,
pdf: Option<&mut Float>,
) -> Float {
let size2: i32 = nodes2.len() as i32;
let mut u: Float = u;
let mut offset: i32 = 0;
let mut weights: [Float; 4] = [0.0 as Float; 4];
if !catmull_rom_weights(nodes1, alpha, &mut offset, &mut weights) {
return 0.0 as Float;
}
let interpolate = |array: &[Float], idx: i32| -> Float {
let mut value: Float = 0.0;
for (i, weight) in weights.iter().enumerate() {
if *weight != 0.0 as Float {
let index: i32 = (offset + i as i32) * size2 + idx;
assert!(index >= 0);
value += array[index as usize] * *weight;
}
}
value
};
let maximum: Float = interpolate(cdf, size2 - 1_i32);
u *= maximum;
let idx: i32 = find_interval(size2, |index| interpolate(cdf, index) <= u);
let f0: Float = interpolate(values, idx);
let f1: Float = interpolate(values, idx + 1);
assert!(idx >= 0);
let x0: Float = nodes2[idx as usize];
let x1: Float = nodes2[(idx + 1) as usize];
let width: Float = x1 - x0;
u = (u - interpolate(cdf, idx)) / width;
let d0: Float = if idx > 0_i32 {
width * (f1 - interpolate(values, idx - 1)) / (x1 - nodes2[(idx - 1) as usize])
} else {
f1 - f0
};
let d1: Float = if (idx + 2) < size2 {
width * (interpolate(values, idx + 2) - f0) / (nodes2[(idx + 2) as usize] - x0)
} else {
f1 - f0
};
let mut t = if f0 != f1 {
(f0 - (0.0 as Float)
.max(f0 * f0 + 2.0 as Float * u * (f1 - f0))
.sqrt())
/ (f0 - f1)
} else {
u / f0
};
let mut a: Float = 0.0;
let mut b: Float = 1.0;
let mut f_hat;
let mut fhat;
loop {
if !(t >= a && t <= b) {
t = 0.5 as Float * (a + b);
}
f_hat = t
* (f0
+ t * (0.5 as Float * d0
+ t * ((1.0 as Float / 3.0 as Float) * (-2.0 as Float * d0 - d1) + f1 - f0
+ t * (0.25 as Float * (d0 + d1) + 0.5 as Float * (f0 - f1)))));
fhat = f0
+ t * (d0
+ t * (-2.0 as Float * d0 - d1
+ 3.0 as Float * (f1 - f0)
+ t * (d0 + d1 + 2.0 as Float * (f0 - f1))));
if (f_hat - u).abs() < 1e-6 as Float || b - a < 1e-6 as Float {
break;
}
if (f_hat - u) < 0.0 as Float {
a = t;
} else {
b = t;
}
t -= (f_hat - u) / fhat;
}
if let Some(fval) = fval {
*fval = fhat;
}
if let Some(pdf) = pdf {
*pdf = fhat / maximum;
}
x0 + width * t
}
pub fn integrate_catmull_rom(
n: i32,
x: &[Float],
offset: usize,
values: &[Float],
cdf: &mut [Float],
) -> Float {
let mut sum: Float = 0.0;
cdf[offset] = 0.0 as Float;
for i in 0..(n - 1) as usize {
let x0: Float = x[i];
let x1: Float = x[i + 1];
let f0: Float = values[offset + i];
let f1: Float = values[offset + i + 1];
let width: Float = x1 - x0;
let d0: Float = if i > 0 {
width * (f1 - values[offset + i - 1]) / (x1 - x[i - 1])
} else {
f1 - f0
};
let d1: Float = if i + 2 < n as usize {
width * (values[offset + i + 2] - f0) / (x[i + 2] - x0)
} else {
f1 - f0
};
sum += ((d0 - d1) * (1.0 as Float / 12.0 as Float) + (f0 + f1) * 0.5 as Float) * width;
cdf[offset + i + 1] = sum;
}
sum
}
pub fn fourier(a: &SmallVec<[Float; 128]>, si: usize, m: i32, cos_phi: f64) -> Float {
let mut value: f64 = 0.0;
let mut cos_k_minus_one_phi: f64 = cos_phi;
let mut cos_k_phi: f64 = 1.0;
for k in 0..m as usize {
value += a[si + k] as f64 * cos_k_phi;
let cos_k_plus_one_phi: f64 = 2.0_f64 * cos_phi * cos_k_phi - cos_k_minus_one_phi;
cos_k_minus_one_phi = cos_k_phi;
cos_k_phi = cos_k_plus_one_phi;
}
value as Float
}
pub fn sample_fourier(
ak: &SmallVec<[Float; 128]>,
recip: &[Float],
m: i32,
u: Float,
pdf: &mut Float,
phi_ptr: &mut Float,
) -> Float {
let mut u: Float = u;
let flip: bool;
if u >= 0.5 as Float {
flip = true;
u = 1.0 as Float - 2.0 as Float * (u - 0.5 as Float);
} else {
flip = false;
u *= 2.0 as Float;
}
let mut a: f64 = 0.0;
let mut b: f64 = PI as f64;
let mut phi: f64 = (0.5_f32 * PI) as f64;
let mut cf: f64;
let mut f: f64;
loop {
let cos_phi: f64 = phi.cos();
let sin_phi: f64 = ((0.0_f64).max(1.0_f64 - cos_phi * cos_phi)).sqrt();
let mut cos_phi_prev: f64 = cos_phi;
let mut cos_phi_cur: f64 = 1.0;
let mut sin_phi_prev = -sin_phi;
let mut sin_phi_cur: f64 = 0.0;
cf = ak[0] as f64 * phi;
f = ak[0] as f64;
for k in 1..m as usize {
let sin_phi_next: f64 = (2.0_f64 * cos_phi) * sin_phi_cur - sin_phi_prev;
let cos_phi_next: f64 = (2.0_f64 * cos_phi) * cos_phi_cur - cos_phi_prev;
sin_phi_prev = sin_phi_cur;
sin_phi_cur = sin_phi_next;
cos_phi_prev = cos_phi_cur;
cos_phi_cur = cos_phi_next;
cf += (ak[k] * recip[k]) as f64 * sin_phi_next;
f += ak[k] as f64 * cos_phi_next;
}
cf -= (u * ak[0] * PI) as f64;
if cf > 0.0_f64 {
b = phi;
} else {
a = phi;
}
if cf.abs() < 1e-6_f64 || b - a < 1e-6_f64 {
break;
}
phi -= cf / f;
if !(phi > a && phi < b) {
phi = 0.5_f64 * (a + b);
}
}
if flip {
phi = 2.0_f64 * PI as f64 - phi;
}
*pdf = (INV_2_PI as f64 * f / ak[0] as f64) as Float;
*phi_ptr = phi as Float;
f as Float
}