diff --git a/Cargo.toml b/Cargo.toml index 20457b5..53cc07e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] +bitflags = "2.10.0" +bumpalo = "3.19.0" num-traits = "0.2.19" once_cell = "1.21.3" +rand = "0.9.2" diff --git a/src/camera/mod.rs b/src/camera/mod.rs new file mode 100644 index 0000000..c0e0c0b --- /dev/null +++ b/src/camera/mod.rs @@ -0,0 +1,211 @@ +mod perspective; +mod orthographic; +mod spherical; +mod realistic; + +pub use perspective::PerspectiveCamera; +pub use orthographic::OrthographicCamera; +pub use spherical::SphericalCamera; +pub use realistic::RealisticCamera; + +use crate::core::film::Film; +use crate::core::medium::Medium; +use crate::core::pbrt::{Float, lerp, RenderingCoordinateSystem}; +use crate::core::sampler::CameraSample; +use crate::utils::geometry::{Ray, RayDifferential, Vector3f, Point3f, Normal3f, Dot, Normed}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; +use crate::utils::transform::{AnimatedTransform, Transform}; + +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct CameraRay { + pub ray: Ray, + pub weight: SampledSpectrum, +} + +pub struct CameraTransform { + render_from_camera: AnimatedTransform, + world_from_render: Transform, +} + +impl CameraTransform { + pub fn from_world(world_from_camera: AnimatedTransform, rendering_space: RenderingCoordinateSystem) -> Self { + let world_from_render = match rendering_space { + RenderingCoordinateSystem::Camera => { + let t_mid = (world_from_camera.start_time + world_from_camera.end_time) / 2.; + world_from_camera.interpolate(t_mid) + } + RenderingCoordinateSystem::CameraWorld => { + let t_mid = (world_from_camera.start_time + world_from_camera.end_time) / 2.; + let p_camera = world_from_camera.apply_point(Point3f::default(), t_mid); + Transform::translate(p_camera - Point3f::new(0., 0., 0.)) + } + RenderingCoordinateSystem::World => Transform::identity(), + }; + let render_from_world = world_from_render.inverse(); + let rfc = [render_from_world * world_from_camera.start_transform, render_from_world * world_from_camera.end_transform]; + let render_from_camera = AnimatedTransform::new(&rfc[0], world_from_camera.start_time, &rfc[1], world_from_camera.end_time).expect("Render wrong"); + Self { render_from_camera, world_from_render } + + } + + pub fn render_from_camera(&self, p: Point3f, time: Float) -> Point3f { + self.render_from_camera.apply_point(p, time) + } + + pub fn render_from_camera_vector(&self, v: Vector3f, time: Float) -> Vector3f { + self.render_from_camera.apply_vector(v, time) + } + + pub fn render_from_camera_ray(&self, r: &Ray, t_max: &mut Option) -> Ray { + self.render_from_camera.apply_ray(r, t_max) + } + + pub fn camera_from_render(&self, p: Point3f, time: Float) -> Point3f { + self.render_from_camera.apply_inverse_point(p, time) + } + + pub fn camera_from_render_vector(&self, v: Vector3f, time: Float) -> Vector3f { + self.render_from_camera.apply_inverse_vector(v, time) + } + + pub fn camera_from_render_normal(&self, n: Normal3f, time: Float) -> Normal3f { + self.render_from_camera.apply_inverse_normal(n, time) + } +} + +pub struct CameraBase { + pub camera_transform: CameraTransform, + pub shutter_open: Float, + pub shutter_close: Float, + pub film: Film, + pub medium: Option>, + pub min_pos_differential_x: Vector3f, + pub min_pos_differential_y: Vector3f, + pub min_dir_differential_x: Vector3f, + pub min_dir_differential_y: Vector3f, +} + +pub trait CameraTrait { + fn base(&self) -> &CameraBase; + fn generate_ray(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option; + fn generate_ray_differential(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option { + let mut central_cam_ray = match self.generate_ray(sample, lambda) { + Some(cr) => cr, + None => return None, + }; + + let mut rd = RayDifferential::default(); + let mut rx_found = false; + let mut ry_found = false; + + for eps in [0.05, -0.05] { + let mut s_shift = sample; + s_shift.p_film[0] += eps; + + if let Some(rx_cam_ray) = self.generate_ray(s_shift, lambda) { + rd.rx_origin = central_cam_ray.ray.o + (rx_cam_ray.ray.o - central_cam_ray.ray.o) / eps; + rd.rx_direction = central_cam_ray.ray.d + (rx_cam_ray.ray.d - central_cam_ray.ray.d) / eps; + rx_found = true; + break; + } + } + + for eps in [0.05, -0.05] { + let mut s_shift = sample; + s_shift.p_film[1] += eps; + + if let Some(ry_cam_ray) = self.generate_ray(s_shift, lambda) { + rd.ry_origin = central_cam_ray.ray.o + (ry_cam_ray.ray.o - central_cam_ray.ray.o) / eps; + rd.ry_direction = central_cam_ray.ray.d + (ry_cam_ray.ray.d - central_cam_ray.ray.d) / eps; + ry_found = true; + break + } + } + + if rx_found && ry_found { + central_cam_ray.ray.differential = Some(rd); + } + + Some(central_cam_ray) + } + + fn sample_time(&self, u: Float) -> Float { + lerp(u, self.base().shutter_open, self.base().shutter_close) + } + + fn approximate_dp_dxy(&self, p: Point3f, n: Normal3f, time: Float, samples_per_pixel: i32, dpdx: &mut Vector3f, dpdy: &mut Vector3f) { + let p_camera = self.base().camera_transform.camera_from_render(p, time); + let p_camera_vec = p_camera - Point3f::new(0., 0., 0.); + let down_z_from_camera = Transform::rotate_from_to(p_camera_vec.normalize(), Vector3f::new(1., 0., 0.)); + let p_down_z = down_z_from_camera.apply_to_point(p_camera); + let n_down_z = down_z_from_camera.apply_to_normal(self.base().camera_transform.camera_from_render_normal(n, time)); + let d = n_down_z.z() * p_down_z.z(); + let x_ray = Ray::new(Point3f::new(0., 0., 0.) + self.base().min_pos_differential_x, + Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_x, + None, None); + let y_ray = Ray::new(Point3f::new(0., 0., 0.) + self.base().min_pos_differential_y, + Vector3f::new(0., 0., 1.) + self.base().min_dir_differential_y, + None, None); + let tx = -(n_down_z.dot(y_ray.o)) / n_down_z.dot(x_ray.d); + let ty = -(n_down_z.dot(x_ray.o) - d) / n_down_z.dot(y_ray.d); + let px = x_ray.evaluate(tx); + let py = y_ray.evaluate(ty); + let spp_scale = 0.125_f32.max((samples_per_pixel as Float).sqrt()); + *dpdx = spp_scale * self.base().camera_transform.render_from_camera_vector(down_z_from_camera.apply_inverse_vector(px - p_down_z), time); + *dpdy = spp_scale * self.base().camera_transform.render_from_camera_vector(down_z_from_camera.apply_inverse_vector(py - p_down_z), time); + } + + fn render_from_camera(&self, r: &Ray, t_max: &mut Option) -> Ray { + self.base().camera_transform.render_from_camera_ray(r, t_max) + } + +} + +pub enum Camera { + Perspective(PerspectiveCamera), + Orthographic(OrthographicCamera), + Spherical(SphericalCamera), + Realistic(RealisticCamera), +} +impl CameraTrait for Camera { + fn base(&self) -> &CameraBase { + match self { + Camera::Perspective(c) => c.base(), + Camera::Orthographic(c) => c.base(), + Camera::Spherical(c) => c.base(), + Camera::Realistic(c) => c.base(), + } + } + + fn generate_ray(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option { + match self { + Camera::Perspective(c) => c.generate_ray(sample, lambda), + Camera::Orthographic(c) => c.generate_ray(sample, lambda), + Camera::Spherical(c) => c.generate_ray(sample, lambda), + Camera::Realistic(c) => c.generate_ray(sample, lambda), + } + } + + fn generate_ray_differential(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option { + match self { + Camera::Perspective(c) => c.generate_ray_differential(sample, lambda), + Camera::Orthographic(c) => c.generate_ray_differential(sample, lambda), + Camera::Spherical(c) => c.generate_ray_differential(sample, lambda), + Camera::Realistic(c) => c.generate_ray_differential(sample, lambda), + } + } +} + +pub struct LensElementInterface { + pub curvature_radius: Float, + pub thickness: Float, + pub eta: Float, + pub aperture_radius: Float, +} + +struct ExitPupilSample { + pub p_pupil: Point3f, + pub pdf: Float, +} diff --git a/src/camera/orthographic.rs b/src/camera/orthographic.rs new file mode 100644 index 0000000..7560021 --- /dev/null +++ b/src/camera/orthographic.rs @@ -0,0 +1,96 @@ +use super::{CameraBase, CameraRay, CameraTrait}; +use crate::core::sampler::CameraSample; +use crate::core::pbrt::{Float, sample_uniform_disk_concentric}; +use crate::core::film::FilmTrait; +use crate::utils::transform::Transform; +use crate::utils::geometry::{Bounds2f, Point3f, Vector3f, Ray, RayDifferential, Normed}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; + +pub struct OrthographicCamera { + pub base: CameraBase, + pub screen_from_camera: Transform, + pub camera_from_raster: Transform, + pub raster_from_screen: Transform, + pub screen_from_raster: Transform, + pub lens_radius: Float, + pub focal_distance: Float, + pub dx_camera: Vector3f, + pub dy_camera: Vector3f, +} + +impl OrthographicCamera { + pub fn new(base: CameraBase, screen_window: Bounds2f, lens_radius: Float, focal_distance: Float) -> Self { + let ndc_from_screen: Transform = Transform::scale( + 1./(screen_window.p_max.x() - screen_window.p_min.x()), + 1. / (screen_window.p_max.y() - screen_window.p_min.y()), 1.) * + Transform::translate(Vector3f::new(-screen_window.p_min.x(), -screen_window.p_max.y(), 0.)); + let raster_from_ndc = Transform::scale(base.film.full_resolution().x() as Float, -base.film.full_resolution().y() as Float, 1.); + let raster_from_screen = raster_from_ndc * ndc_from_screen; + let screen_from_raster = raster_from_screen.inverse(); + let screen_from_camera = Transform::orthographic(0., 1.); + let camera_from_raster = screen_from_camera.inverse() * screen_from_raster; + let dx_camera = camera_from_raster.apply_to_vector(Vector3f::new(1., 0., 0.)); + let dy_camera = camera_from_raster.apply_to_vector(Vector3f::new(0., 1., 0.)); + let mut base_ortho = base; + base_ortho.min_dir_differential_x = Vector3f::new(0., 0., 0.); + base_ortho.min_dir_differential_y = Vector3f::new(0., 0., 0.); + base_ortho.min_pos_differential_x = dx_camera; + base_ortho.min_pos_differential_y = dy_camera; + Self { base: base_ortho, screen_from_camera, camera_from_raster, raster_from_screen, screen_from_raster, lens_radius, focal_distance, dx_camera, dy_camera } + } +} + + +impl CameraTrait for OrthographicCamera { + fn base(&self) -> &CameraBase { + &self.base + } + fn generate_ray(&self, sample: CameraSample, _lambda: &SampledWavelengths) -> Option { + // Compute raster and camera sample positions + let p_film = Point3f::new(sample.p_film.x(), sample.p_film.y(), 0.); + let p_camera = self.camera_from_raster.apply_to_point(p_film); + + let mut ray = Ray::new(p_camera, Vector3f::new(0., 0., 1.), Some(self.sample_time(sample.time)), self.base().medium.clone()); + // Modify ray for depth of field + if self.lens_radius > 0. { + // Sample point on lens + let p_lens = self.lens_radius * sample_uniform_disk_concentric(sample.p_lens); + + // Compute point on plane of focus + let ft = self.focal_distance / ray.d.z(); + let p_focus = ray.evaluate(ft); + + // Update ray for effect of lens + ray.o = Point3f::new(p_lens.x(), p_lens.y(), 0.); + ray.d = (p_focus - ray.o).normalize(); + } + + let camera_ray = self.render_from_camera(&ray, &mut None); + + Some(CameraRay{ray: camera_ray, weight: SampledSpectrum::default()}) + } + + fn generate_ray_differential(&self, sample: CameraSample, lambda: &SampledWavelengths) -> Option { + let mut central_cam_ray = match self.generate_ray(sample, lambda) { + Some(cr) => cr, + None => return None, + }; + + let mut rd = RayDifferential::default(); + if self.lens_radius > 0.0 { + return self.generate_ray_differential(sample, lambda); + } else { + let time = self.sample_time(sample.time); + let world_dx = self.base.camera_transform.render_from_camera_vector(self.dx_camera, time); + let world_dy = self.base.camera_transform.render_from_camera_vector(self.dy_camera, time); + + rd.rx_origin = central_cam_ray.ray.o + world_dx; + rd.ry_origin = central_cam_ray.ray.o + world_dy; + + rd.rx_direction = central_cam_ray.ray.d; + rd.ry_direction = central_cam_ray.ray.d; + } + central_cam_ray.ray.differential = Some(rd); + Some(central_cam_ray) + } +} diff --git a/src/camera/perspective.rs b/src/camera/perspective.rs new file mode 100644 index 0000000..e27d99d --- /dev/null +++ b/src/camera/perspective.rs @@ -0,0 +1,82 @@ +use super::{CameraRay, CameraBase, CameraTrait}; +use crate::camera; +use crate::core::film::FilmTrait; +use crate::core::filter::FilterTrait; +use crate::core::pbrt::{Float, sample_uniform_disk_concentric}; +use crate::core::sampler::CameraSample; +use crate::utils::geometry::{Point2f, Point3f, Bounds2f, Vector3f, Normed, Ray, RayDifferential}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; +use crate::utils::transform::Transform; + +pub struct PerspectiveCamera { + pub base: CameraBase, + pub screen_from_camera: Transform, + pub camera_from_raster: Transform, + pub raster_from_screen: Transform, + pub screen_from_raster: Transform, + pub lens_radius: Float, + pub focal_distance: Float, + pub dx_camera: Vector3f, + pub dy_camera: Vector3f, + pub cos_total_width: Float, +} + +impl PerspectiveCamera { + pub fn new(base: CameraBase, screen_from_camera: &Transform, screen_window: Bounds2f, lens_radius: Float, focal_distance: Float) -> Self { + let ndc_from_screen: Transform = Transform::scale( + 1./(screen_window.p_max.x() - screen_window.p_min.x()), + 1. / (screen_window.p_max.y() - screen_window.p_min.y()), 1.) * + Transform::translate(Vector3f::new(-screen_window.p_min.x(), -screen_window.p_max.y(), 0.)); + let raster_from_ndc = Transform::scale(base.film.full_resolution().x() as Float, -base.film.full_resolution().y() as Float, 1.); + let raster_from_screen = raster_from_ndc * ndc_from_screen; + let screen_from_raster = raster_from_screen.inverse(); + let camera_from_raster = screen_from_camera.inverse() * screen_from_raster; + let dx_camera = camera_from_raster.apply_to_point(Point3f::new(1., 0., 0.)) - camera_from_raster.apply_to_point(Point3f::new(0., 0., 0.)); + let dy_camera = camera_from_raster.apply_to_point(Point3f::new(0., 1., 0.)) - camera_from_raster.apply_to_point(Point3f::new(0., 0., 0.)); + let radius = base.film.get_filter().radius(); + let p_corner = Point3f::new(-radius.x(), -radius.y(), 0.); + let w_corner_camera = (camera_from_raster.apply_to_point(p_corner) - Point3f::new(0., 0., 0.)).normalize(); + let cos_total_width = w_corner_camera.z(); + Self { base, + screen_from_camera: *screen_from_camera, + camera_from_raster, + raster_from_screen, + screen_from_raster, + lens_radius, + focal_distance, + dx_camera, + dy_camera, + cos_total_width, + } + } +} + +impl CameraTrait for PerspectiveCamera { + fn base(&self) -> &CameraBase { + &self.base + } + fn generate_ray(&self, sample: CameraSample, _lambda: &SampledWavelengths) -> Option { + // Compute raster and camera sample positions + let p_film = Point3f::new(sample.p_film.x(), sample.p_film.y(), 0.); + let p_camera = self.camera_from_raster.apply_to_point(p_film); + let p_vector = p_camera - Point3f::new(0., 0., 0.); + + let mut r = Ray::new(Point3f::new(0., 0., 0.), p_vector.normalize(), Some(self.sample_time(sample.time)), self.base().medium.clone()); + // Modify ray for depth of field + if self.lens_radius > 0. { + // Sample point on lens + let p_lens = self.lens_radius * sample_uniform_disk_concentric(sample.p_lens); + + // Compute point on plane of focus + let ft = self.focal_distance / r.d.z(); + let p_focus = r.evaluate(ft); + + // Update ray for effect of lens + r.o = Point3f::new(p_lens.x(), p_lens.y(), 0.); + r.d = (p_focus - r.o).normalize(); + } + + let ray = self.render_from_camera(&r, &mut None); + Some(CameraRay{ray, weight: SampledSpectrum::default()}) + } +} diff --git a/src/camera/realistic.rs b/src/camera/realistic.rs new file mode 100644 index 0000000..427f165 --- /dev/null +++ b/src/camera/realistic.rs @@ -0,0 +1,261 @@ +use super::{CameraBase, LensElementInterface, CameraRay, CameraTrait, ExitPupilSample}; +use crate::core::film::FilmTrait; +use crate::core::pbrt::{Float, square, lerp}; +use crate::core::sampler::CameraSample; +use crate::utils::math::quadratic; +use crate::utils::geometry::{Bounds2f, Point2f, Point3f, Point2i, Normal3f, Vector2i, Vector3f, Ray, Normed, Dot}; +use crate::utils::scattering::refract; +use crate::utils::spectrum::{SampledWavelengths, SampledSpectrum}; + +pub struct RealisticCamera { + base: CameraBase, + focus_distance: Float, + set_aperture_diameter: Float, + // aperture_image: Image, + element_interface: Vec, + physical_extent: Bounds2f, + exit_pupil_bounds: Vec, +} + +impl RealisticCamera { + pub fn new(&self, + base: CameraBase, + lens_params: Vec, + focus_distance: Float, + set_aperture_diameter: Float, + ) -> Self { + + let aspect = base.film.full_resolution().x() as Float / base.film.full_resolution().y() as Float; + let diagonal = base.film.diagonal(); + let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt(); + let y = x * aspect; + let physical_extent = Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.)); + let mut element_interface: Vec = Vec::new(); + + for i in (0..lens_params.len()).step_by(4) { + let curvature_radius = lens_params[i] / 1000.0; + let thickness = lens_params[i + 1] / 1000.0; + let eta = lens_params[i + 2]; + let mut aperture_diameter = lens_params[i + 3] / 1000.0; + + if curvature_radius == 0.0 { + aperture_diameter /= 1000.0; + if set_aperture_diameter > aperture_diameter { + println!("Aperture is larger than possible") + } else { + aperture_diameter = set_aperture_diameter; + } + } + let el_int = LensElementInterface { curvature_radius, thickness, eta, aperture_radius: aperture_diameter / 2.0 }; + element_interface.push(el_int); + } + + let mut exit_pupil_bounds: Vec = Vec::new(); + + let n_samples = 64; + for i in 0..64 { + let r0 = i as Float / 64. * base.film.diagonal() / 2.; + let r1 = (i + 1) as Float / n_samples as Float * base.film.diagonal() / 2.; + exit_pupil_bounds[i] = self.bound_exit_pupil(r0, r1); + } + + Self { base, focus_distance, element_interface, physical_extent, set_aperture_diameter, exit_pupil_bounds } + } + + pub fn lens_rear_z(&self) -> Float { self.element_interface.last().unwrap().thickness } + + pub fn lens_front_z(&self) -> Float { + let mut z_sum = 0.; + for element in &self.element_interface { + z_sum += element.thickness; + } + z_sum + } + + pub fn rear_element_radius(&self) -> Float { self.element_interface.last().unwrap().aperture_radius } + + pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f { + let mut pupil_bounds = Bounds2f::default(); + let n_samples = 1024 * 1024; + let rear_radius = self.rear_element_radius(); + let proj_rear_bounds = Bounds2f::from_points(Point2f::new(-1.5 * rear_radius, -1.5 * rear_radius), + Point2f::new(1.5 * rear_radius, 1.5 * rear_radius)); + + let radical_inverse = |x: i32, _y: i64| x as Float; + let lens_rear_z = || 1. ; + let trace_lenses_from_film = |_ray: Ray, _place: Option| true; + for i in 0..n_samples { + // Find location of sample points on $x$ segment and rear lens element + // + let p_film = Point3f::new(lerp((i as Float + 0.5) / n_samples as Float, film_x_0, film_x_1), 0., 0.); + let u: [Float; 2] = [radical_inverse(0, i), radical_inverse(1, i)]; + let p_rear = Point3f::new(lerp(u[0], proj_rear_bounds.p_min.x(), proj_rear_bounds.p_max.x()), + lerp(u[1], proj_rear_bounds.p_min.y(), proj_rear_bounds.p_max.y()), + lens_rear_z()); + + // Expand pupil bounds if ray makes it through the lens system + if !pupil_bounds.contains(Point2f::new(p_rear.x(), p_rear.y())) && + trace_lenses_from_film(Ray::new(p_film, p_rear - p_film, None, None), None) { + pupil_bounds = pupil_bounds.union_point(Point2f::new(p_rear.x(), p_rear.y())); + } + } + + // Return degenerate bounds if no rays made it through the lens system + if pupil_bounds.is_degenerate() { + print!("Unable to find exit pupil in x = {},{} on film.", film_x_0, film_x_1); + return pupil_bounds; + } + + pupil_bounds.expand(2. * proj_rear_bounds.diagonal().norm() / (n_samples as Float).sqrt()) + } + + pub fn intersect_spherical_element(radius: Float, z_center: Float, ray: &Ray) -> Option<(Float, Normal3f)> { + let o = ray.o - Vector3f::new(0.0, 0.0, z_center); + + let a = ray.d.norm_squared(); + let b = 2.0 * ray.d.dot(o); + let c = Vector3f::from(o).norm_squared() - radius * radius; + + let (t0, t1) = quadratic(a, b, c)?; + + let use_closer_t = (ray.d.z() > 0.0) ^ (radius < 0.0); + let t = if use_closer_t { t0.min(t1) } else { t0.max(t1) }; + + // Intersection is behind the ray's origin. + if t < 0.0 { + return None; + } + + let p_hit_relative = o + ray.d * t; + // Ensures the normal points towards the incident ray. + let n = Normal3f::from(Vector3f::from(p_hit_relative)).normalize().face_forward(-ray.d); + + Some((t, n)) + } + + pub fn trace_lenses_from_film(&self, r_camera: &Ray) -> Option<(Float, Ray)> { + let mut element_z = 0.; + let weight = 1.; + // Transform _r_camera_ from camera to lens system space + let mut r_lens = Ray::new(Point3f::new(r_camera.o.x(), r_camera.o.y(), -r_camera.o.z()), + Vector3f::new(r_camera.d.x(), r_camera.d.y(), -r_camera.d.z()), Some(r_camera.time), None); + + for i in (0..self.element_interface.len() - 1).rev() { + let element: &LensElementInterface = &self.element_interface[i]; + // Update ray from film accounting for interaction with _element_ + element_z -= element.thickness; + + let is_stop = element.curvature_radius == 0.; + let t: Float; + let mut n = Normal3f::default(); + if is_stop { + // Compute _t_ at plane of aperture stop + t = (element_z - r_lens.o.z()) / r_lens.d.z(); + } else { + // Intersect ray with element to compute _t_ and _n_ + let radius = element.curvature_radius; + let z_center = element_z + element.curvature_radius; + if let Some((intersect_t, intersect_n)) = RealisticCamera::intersect_spherical_element(radius, z_center, &r_lens) { + t = intersect_t; + n = intersect_n; + } else { + return None; // Ray missed the element + } + } + + if t < 0. { + return None; + } + + // Test intersection point against element aperture + let p_hit = r_lens.evaluate(t); + if square(p_hit.x()) + square(p_hit.y()) > square(element.aperture_radius) { + return None; + } + + r_lens.o = p_hit; + + // Update ray path for element interface interaction + if !is_stop { + let eta_i = element.eta; + let eta_t = if i > 0 && self.element_interface[i - 1].eta != 0. { self.element_interface[i - 1].eta } else { 1. }; + let wi = -r_lens.d.normalize(); + let eta_ratio = eta_t / eta_i; + + // Handle refraction idiomatically + if let Some((wt, _final_eta)) = refract(wi, n, eta_ratio) { + r_lens.d = wt; + } else { + // Total internal reflection occurred + return None; + } + } + } + + // Transform lens system space ray back to camera space + let r_out = Ray::new(Point3f::new(r_lens.o.x(), r_lens.o.y(), -r_lens.o.z()), + Vector3f::new(r_lens.d.x(), r_lens.d.y(), -r_lens.d.z()), Some(r_lens.time), None); + + Some((weight, r_out)) + } + + pub fn sample_exit_pupil(&self, p_film: Point2f, u_lens: Point2f) -> Option { + // Find exit pupil bound for sample distance from film center + let r_film = (square(p_film.x()) + square(p_film.y())).sqrt(); + let mut r_index = (r_film / (self.base.film.diagonal() / 2.)) as usize * self.exit_pupil_bounds.len(); + r_index = (self.exit_pupil_bounds.len() - 1).min(r_index); + + let pupil_bounds = self.exit_pupil_bounds[r_index]; + if pupil_bounds.is_degenerate() { + return None; + } + + // Generate sample point inside exit pupil bound + let p_lens = pupil_bounds.lerp(u_lens); + let pdf = 1. / pupil_bounds.area(); + + // Return sample point rotated by angle of _pFilm_ with $+x$ axis + let sin_theta = if r_film != 0. { p_film.y() / r_film } else { 0. }; + let cos_theta = if r_film != 0. { p_film.x() / r_film } else { 1. }; + let p_pupil = Point3f::new(cos_theta * p_lens.x() - sin_theta * p_lens.y(), + sin_theta * p_lens.x() + cos_theta * p_lens.y(), self.lens_rear_z()); + + Some(ExitPupilSample{p_pupil, pdf}) + } +} + +impl CameraTrait for RealisticCamera { + fn base(&self) -> &CameraBase { + &self.base + } + + fn generate_ray(&self, sample: CameraSample, _lambda: &SampledWavelengths) -> Option { + // Find point on film, _pFilm_, corresponding to _sample.pFilm_ + let s = Point2f::new(sample.p_film.x() / self.base.film.full_resolution().x() as Float, + sample.p_film.y() / self.base.film.full_resolution().y() as Float); + let p_film2 = self.physical_extent.lerp(s); + let p_film = Point3f::new(-p_film2.x(), p_film2.y(), 0.); + + // Trace ray from _pFilm_ through lens system + let eps = self.sample_exit_pupil(Point2f::new(p_film.x(), p_film.y()), sample.p_lens)?; + + let p_pupil = Point3f::new(0., 0., 0.); + let r_film = Ray::new(p_film, p_pupil - p_film, None, None); + let (weight, mut ray) = self.trace_lenses_from_film(&r_film)?; + if weight == 0. { + return None + } + + // Finish initialization of _RealisticCamera_ ray + ray.time = self.sample_time(sample.time); + ray.medium = self.base.medium.clone(); + ray = self.render_from_camera(&ray, &mut None); + ray.d = ray.d.normalize(); + + // Compute weighting for _RealisticCamera_ ray + let cos_theta = r_film.d.normalize().z(); + let final_weight = weight * cos_theta.powf(4.) / (eps.pdf as Float * square(self.lens_rear_z())); + + Some(CameraRay{ray, weight: SampledSpectrum::new(final_weight)}) + } +} diff --git a/src/camera/spherical.rs b/src/camera/spherical.rs new file mode 100644 index 0000000..ba473c4 --- /dev/null +++ b/src/camera/spherical.rs @@ -0,0 +1,51 @@ +use super::{CameraRay, CameraTrait, CameraBase}; +use crate::utils::geometry::{Bounds2f, Point2f, Point3f, Vector3f, Ray, spherical_direction}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; +use crate::utils::math::{wrap_equal_area_square, equal_area_square_to_sphere}; +use crate::core::film::FilmTrait; +use crate::core::pbrt::{Float, PI}; +use crate::core::sampler::CameraSample; + +#[derive(PartialEq)] +pub struct EquiRectangularMapping; + +#[derive(PartialEq)] +pub enum Mapping { + EquiRectangular(EquiRectangularMapping), +} + +pub struct SphericalCamera { + pub base: CameraBase, + pub screen: Bounds2f, + pub lens_radius: Float, + pub focal_distance: Float, + pub mapping: Mapping, +} + +impl CameraTrait for SphericalCamera { + fn base(&self) -> &CameraBase { + &self.base + } + + fn generate_ray(&self, sample: CameraSample, _lamdba: &SampledWavelengths) -> Option { + // Compute spherical camera ray direction + let mut uv = Point2f::new(sample.p_film.x() / self.base().film.full_resolution().x() as Float, + sample.p_film.y() / self.base().film.full_resolution().y() as Float); + let dir: Vector3f; + if self.mapping == Mapping::EquiRectangular(EquiRectangularMapping) { + // Compute ray direction using equirectangular mapping + let theta = PI * uv[1]; + let phi = 2. * PI * uv[0]; + dir = spherical_direction(theta.sin(), theta.cos(), phi); + + } else { + // Compute ray direction using equal area mapping + uv = wrap_equal_area_square(&mut uv); + dir = equal_area_square_to_sphere(uv); + } + std::mem::swap(&mut dir.y(), &mut dir.z()); + + let ray = Ray::new(Point3f::new(0., 0., 0.), dir, Some(self.sample_time(sample.time)), self.base().medium.clone()); + Some(CameraRay{ray: self.render_from_camera(&ray, &mut None), weight: SampledSpectrum::default() }) + } +} diff --git a/src/core/bxdf.rs b/src/core/bxdf.rs new file mode 100644 index 0000000..894fce6 --- /dev/null +++ b/src/core/bxdf.rs @@ -0,0 +1,213 @@ +use bitflags::bitflags; +use std::fmt; +use std::ops::Not; + +use crate::utils::geometry::{Point2f, Vector3f, abs_cos_theta}; +use crate::utils::spectrum::SampledSpectrum; +use crate::core::pbrt::{Float, PI}; +use crate::utils::sampling::{uniform_hemisphere_pdf, sample_uniform_hemisphere}; + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct BxDFReflTransFlags: u8 { + const REFLECTION = 1 << 0; + const TRANSMISSION = 1 << 1; + const ALL = Self::REFLECTION.bits() | Self::TRANSMISSION.bits(); + } +} + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct BxDFFlags: u8 { + const REFLECTION = 1 << 0; + const TRANSMISSION = 1 << 1; + const DIFFUSE = 1 << 2; + const GLOSSY = 1 << 3; + const SPECULAR = 1 << 4; + + // Composite BxDFFlags definitions + const DIFFUSE_REFLECTION = Self::DIFFUSE.bits() | Self::REFLECTION.bits(); + const DIFFUSE_TRANSMISSION = Self::DIFFUSE.bits() | Self::TRANSMISSION.bits(); + const GLOSSY_REFLECTION = Self::GLOSSY.bits() | Self::REFLECTION.bits(); + const GLOSSY_TRANSMISSION = Self::GLOSSY.bits() | Self::TRANSMISSION.bits(); + const SPECULAR_REFLECTION = Self::SPECULAR.bits() | Self::REFLECTION.bits(); + const SPECULAR_TRANSMISSION = Self::SPECULAR.bits() | Self::TRANSMISSION.bits(); + + const SCATTERING = Self::DIFFUSE.bits() | Self::GLOSSY.bits() | Self::SPECULAR.bits(); + const ALL = Self::REFLECTION.bits() | Self::TRANSMISSION.bits() | Self::SCATTERING.bits(); + } +} + +impl BxDFFlags { + #[inline] + pub fn is_reflective(&self) -> bool { self.contains(Self::REFLECTION) } + #[inline] + pub fn is_transmissive(&self) -> bool { self.contains(Self::TRANSMISSION) } + #[inline] + pub fn is_diffuse(&self) -> bool { self.contains(Self::DIFFUSE) } + #[inline] + pub fn is_glossy(&self) -> bool { self.contains(Self::GLOSSY) } + #[inline] + pub fn is_specular(&self) -> bool { self.contains(Self::SPECULAR) } + #[inline] + pub fn is_non_specular(&self) -> bool { self.intersects(Self::DIFFUSE | Self::GLOSSY) } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TransportMode { + Radiance, + Importance, +} + +impl Not for TransportMode { + type Output = Self; + fn not(self) -> Self::Output { + match self { + TransportMode::Radiance => TransportMode::Importance, + TransportMode::Importance => TransportMode::Radiance, + } + } +} + +#[derive(Debug, Clone)] +pub struct BSDFSample { + pub f: SampledSpectrum, + pub wi: Vector3f, + pub pdf: Float, + pub flags: BxDFFlags, + pub eta: Float, + pub pdf_is_proportional: bool, +} + +impl BSDFSample { + pub fn new( + f: SampledSpectrum, wi: Vector3f, pdf: Float, flags: BxDFFlags, + eta: Float, pdf_is_proportional: bool + ) -> Self { + Self { f, wi, pdf, flags, eta, pdf_is_proportional } + } + + pub fn default() -> Self { + Self { + f: SampledSpectrum::default(), + wi: Vector3f::default(), + pdf: 0.0, + flags: BxDFFlags::empty(), + eta: 1.0, + pdf_is_proportional: false, + } + } + + #[inline] pub fn is_reflective(&self) -> bool { self.flags.is_reflective() } + #[inline] pub fn is_transmissive(&self) -> bool { self.flags.is_transmissive() } + #[inline] pub fn is_diffuse(&self) -> bool { self.flags.is_diffuse() } + #[inline] pub fn is_glossy(&self) -> bool { self.flags.is_glossy() } + #[inline] pub fn is_specular(&self) -> bool { self.flags.is_specular() } +} + +pub struct DiffuseBxDF; +pub struct DiffuseTransmissionBxDF; +pub struct DielectricBxDF; +pub struct ThinDielectricBxDF; +pub struct CoatedDiffuseBxDF; +pub struct CoatedConductorBxDF; +pub struct HairBxDF; +pub struct MeasuredBxDF; +pub struct ConductorBxDF; + +pub trait BxDFTrait { + fn flags(&self) -> BxDFFlags; + + fn f(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode) -> SampledSpectrum; + + fn sample_f( + &self, wo: Vector3f, uc: Float, u: Point2f, mode: Option, + sample_flags: Option + ) -> Option; + + fn pdf( + &self, wo: Vector3f, wi: Vector3f, mode: TransportMode, + sample_flags: BxDFReflTransFlags + ) -> Float; + + fn rho_wo(&self, wo: Vector3f, uc: &[Float], u2: &[Point2f]) -> SampledSpectrum { + let mut r = SampledSpectrum::new(0.); + for i in 0..uc.len() { + if let Some(bs) = self.sample_f(wo, uc[i], u2[i], Some(TransportMode::Radiance), Some(BxDFReflTransFlags::ALL)) { + r += bs.f * abs_cos_theta(bs.wi) / bs.pdf; + } + } + r / uc.len() as Float + } + + fn rho_u(&self, u1: &[Point2f], uc: &[Float], u2: &[Point2f]) -> SampledSpectrum { + let mut r = SampledSpectrum::new(0.); + for i in 0..uc.len() { + let wo = sample_uniform_hemisphere(u1[i]); + if wo.z() == 0. { + continue + } + let pdfo = uniform_hemisphere_pdf(); + if let Some(bs) = self.sample_f(wo, uc[i], u2[i], Some(TransportMode::Radiance), Some(BxDFReflTransFlags::ALL)) { + r += bs.f * abs_cos_theta(bs.wi) * abs_cos_theta(wo) / (pdfo * bs.pdf); + } + } + r / (PI * uc.len() as Float) + } + + fn regularize(&mut self) { + todo!(); + } +} + +pub enum BxDF { + Diffuse(DiffuseBxDF), + DiffuseTransmission(DiffuseTransmissionBxDF), + Dielectric(DielectricBxDF), + // ThinDielectric(ThinDielectricBxDF), + // Hair(HairBxDF), + // Measured(MeasuredBxDF), + // Conductor(ConductorBxDF), +} + +impl BxDFTrait for BxDF { + fn flags(&self) -> BxDFFlags { + match self { + BxDF::Diffuse(b) => b.flags(), + BxDF::DiffuseTransmission(b) => b.flags(), + BxDF::Dielectric(b) => b.flags(), + } + } + + fn f(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode) -> SampledSpectrum { + match self { + BxDF::Diffuse(b) => b.f(wo, wi, mode), + BxDF::DiffuseTransmission(b) => b.f(wo, wi, mode), + BxDF::Dielectric(b) => b.f(wo, wi, mode), + } + } + + fn sample_f(&self, wo: Vector3f, uc: Float, u: Point2f, mode: TransportMode, sample_flags: BxDFReflTransFlags) -> Option { + match self { + BxDF::Diffuse(b) => b.sample_f(wo, uc, u, mode, sample_flags), + BxDF::DiffuseTransmission(b) => b.sample_f(wo, uc, u, mode, sample_flags), + BxDF::Dielectric(b) => b.sample_f(wo, uc, u, mode, sample_flags), + } + } + + fn pdf(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode, sample_flags: BxDFReflTransFlags) -> Float { + match self { + BxDF::Diffuse(b) => b.pdf(wo, wi, mode, sample_flags), + BxDF::DiffuseTransmission(b) => b.pdf(wo, wi, mode, sample_flags), + BxDF::Dielectric(b) => b.pdf(wo, wi, mode, sample_flags), + } + } + + fn regularize(&mut self) { + match self { + BxDF::Diffuse(b) => b.regularize(), + BxDF::DiffuseTransmission(b) => b.regularize(), + BxDF::Dielectric(b) => b.regularize(), + } + } +} diff --git a/src/core/camera.rs b/src/core/camera.rs deleted file mode 100644 index eddd38c..0000000 --- a/src/core/camera.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::core::film::Film; -use crate::core::geometry::{Point2f, Point3f, Ray, RayDifferential}; -use crate::core::medium::Medium; -use crate::core::pbrt::{Float, RenderingCoordinateSystem}; -use crate::core::spectrum::SampledSpectrum; -use crate::core::transform::{Transform, AnimatedTransform}; - -pub enum Camera { - Perspective(PerspectiveCamera), - Ortographic(OrtographicCamera), - Spherical(SphericalCamera), - Realistic(RealisticCamera), -} - -impl Camera { - pub fn create(&self, name: str, medium: Medium, camera_transform: CameraTransform, film: Film) -> Float { - match self { - Camera::Perspective(s) => s.create(name, medium, camera_transform, film), - Camera::Ortographic(s) => s.create(name, medium, camera_transform, film), - Camera::Spherical(s) => s.create(name, medium, camera_transform, film), - Camera::Realistic(s) => s.create(name, medium, camera_transform, film), - } - } -} - -pub struct CameraSample { - p_film: Point2f, - p_lens: Point2f, - time: Float, - filter_weight: Float, -} - -pub struct CameraRay { - ray: Ray, - weight: SampledSpectrum, -} - -pub struct CameraDifferential { - ray: RayDifferential, - weight: SampledSpectrum, -} - -pub struct CameraTransform { - render_from_camera: AnimatedTransform, - world_from_render: Transform, -} - -impl CameraTransform { - pub fn from_world(world_from_camera: AnimatedTransform, rendering_space: RenderingCoordinateSystem) -> Self { - let world_from_render = match rendering_space { - RenderingCoordinateSystem::Camera => { - let t_mid = (world_from_camera.start_time + world_from_camera.end_time) / 2.; - world_from_camera.interpolate(t_mid); - } - RenderingCoordinateSystem::CameraWorld => { - let t_mid = (world_from_camera.start_time + world_from_camera.end_time) / 2.; - let p_camera = world_from_camera.apply_point(Point3f::default(), t_mid); - Transform::translate(p_camera.to_vec()); - } - RenderingCoordinateSystem::World => Transform::identity(), - }; - } - - pub fn render_from_camera(&self, p: Point3f, time: Float) -> Point3f { - self.render_from_camera.apply_point(p, time) - } - - pub fn camera_from_render(&self, p: Point3f, time: Float) -> Point3f { - self.render_from_camera.apply_inverse_point(p, time) - } -} - -pub struct PerspectiveCamera; -pub struct OrtographicCamera; -pub struct SphericalCamera; -pub struct RealisticCamera; diff --git a/src/core/cie.rs b/src/core/cie.rs new file mode 100644 index 0000000..0d20057 --- /dev/null +++ b/src/core/cie.rs @@ -0,0 +1,1862 @@ +use crate::core::pbrt::Float; + +pub const CIE_SAMPLES: usize = 471; +pub const CIE_X: [Float; CIE_SAMPLES] = [ + 0.0001299000, 0.0001458470, 0.0001638021, 0.0001840037, 0.0002066902, + 0.0002321000, 0.0002607280, 0.0002930750, 0.0003293880, 0.0003699140, + 0.0004149000, 0.0004641587, 0.0005189860, 0.0005818540, 0.0006552347, + 0.0007416000, 0.0008450296, 0.0009645268, 0.001094949, 0.001231154, + 0.001368000, 0.001502050, 0.001642328, 0.001802382, 0.001995757, + 0.002236000, 0.002535385, 0.002892603, 0.003300829, 0.003753236, + 0.004243000, 0.004762389, 0.005330048, 0.005978712, 0.006741117, + 0.007650000, 0.008751373, 0.01002888, 0.01142170, 0.01286901, + 0.01431000, 0.01570443, 0.01714744, 0.01878122, 0.02074801, + 0.02319000, 0.02620736, 0.02978248, 0.03388092, 0.03846824, + 0.04351000, 0.04899560, 0.05502260, 0.06171880, 0.06921200, + 0.07763000, 0.08695811, 0.09717672, 0.1084063, 0.1207672, + 0.1343800, 0.1493582, 0.1653957, 0.1819831, 0.1986110, + 0.2147700, 0.2301868, 0.2448797, 0.2587773, 0.2718079, + 0.2839000, 0.2949438, 0.3048965, 0.3137873, 0.3216454, + 0.3285000, 0.3343513, 0.3392101, 0.3431213, 0.3461296, + 0.3482800, 0.3495999, 0.3501474, 0.3500130, 0.3492870, + 0.3480600, 0.3463733, 0.3442624, 0.3418088, 0.3390941, + 0.3362000, 0.3331977, 0.3300411, 0.3266357, 0.3228868, + 0.3187000, 0.3140251, 0.3088840, 0.3032904, 0.2972579, + 0.2908000, 0.2839701, 0.2767214, 0.2689178, 0.2604227, + 0.2511000, 0.2408475, 0.2298512, 0.2184072, 0.2068115, + 0.1953600, 0.1842136, 0.1733273, 0.1626881, 0.1522833, + 0.1421000, 0.1321786, 0.1225696, 0.1132752, 0.1042979, + 0.09564000, 0.08729955, 0.07930804, 0.07171776, 0.06458099, + 0.05795001, 0.05186211, 0.04628152, 0.04115088, 0.03641283, + 0.03201000, 0.02791720, 0.02414440, 0.02068700, 0.01754040, + 0.01470000, 0.01216179, 0.009919960, 0.007967240, 0.006296346, + 0.004900000, 0.003777173, 0.002945320, 0.002424880, 0.002236293, + 0.002400000, 0.002925520, 0.003836560, 0.005174840, 0.006982080, + 0.009300000, 0.01214949, 0.01553588, 0.01947752, 0.02399277, + 0.02910000, 0.03481485, 0.04112016, 0.04798504, 0.05537861, + 0.06327000, 0.07163501, 0.08046224, 0.08973996, 0.09945645, + 0.1096000, 0.1201674, 0.1311145, 0.1423679, 0.1538542, + 0.1655000, 0.1772571, 0.1891400, 0.2011694, 0.2133658, + 0.2257499, 0.2383209, 0.2510668, 0.2639922, 0.2771017, + 0.2904000, 0.3038912, 0.3175726, 0.3314384, 0.3454828, + 0.3597000, 0.3740839, 0.3886396, 0.4033784, 0.4183115, + 0.4334499, 0.4487953, 0.4643360, 0.4800640, 0.4959713, + 0.5120501, 0.5282959, 0.5446916, 0.5612094, 0.5778215, + 0.5945000, 0.6112209, 0.6279758, 0.6447602, 0.6615697, + 0.6784000, 0.6952392, 0.7120586, 0.7288284, 0.7455188, + 0.7621000, 0.7785432, 0.7948256, 0.8109264, 0.8268248, + 0.8425000, 0.8579325, 0.8730816, 0.8878944, 0.9023181, + 0.9163000, 0.9297995, 0.9427984, 0.9552776, 0.9672179, + 0.9786000, 0.9893856, 0.9995488, 1.0090892, 1.0180064, + 1.0263000, 1.0339827, 1.0409860, 1.0471880, 1.0524667, + 1.0567000, 1.0597944, 1.0617992, 1.0628068, 1.0629096, + 1.0622000, 1.0607352, 1.0584436, 1.0552244, 1.0509768, + 1.0456000, 1.0390369, 1.0313608, 1.0226662, 1.0130477, + 1.0026000, 0.9913675, 0.9793314, 0.9664916, 0.9528479, + 0.9384000, 0.9231940, 0.9072440, 0.8905020, 0.8729200, + 0.8544499, 0.8350840, 0.8149460, 0.7941860, 0.7729540, + 0.7514000, 0.7295836, 0.7075888, 0.6856022, 0.6638104, + 0.6424000, 0.6215149, 0.6011138, 0.5811052, 0.5613977, + 0.5419000, 0.5225995, 0.5035464, 0.4847436, 0.4661939, + 0.4479000, 0.4298613, 0.4120980, 0.3946440, 0.3775333, + 0.3608000, 0.3444563, 0.3285168, 0.3130192, 0.2980011, + 0.2835000, 0.2695448, 0.2561184, 0.2431896, 0.2307272, + 0.2187000, 0.2070971, 0.1959232, 0.1851708, 0.1748323, + 0.1649000, 0.1553667, 0.1462300, 0.1374900, 0.1291467, + 0.1212000, 0.1136397, 0.1064650, 0.09969044, 0.09333061, + 0.08740000, 0.08190096, 0.07680428, 0.07207712, 0.06768664, + 0.06360000, 0.05980685, 0.05628216, 0.05297104, 0.04981861, + 0.04677000, 0.04378405, 0.04087536, 0.03807264, 0.03540461, + 0.03290000, 0.03056419, 0.02838056, 0.02634484, 0.02445275, + 0.02270000, 0.02108429, 0.01959988, 0.01823732, 0.01698717, + 0.01584000, 0.01479064, 0.01383132, 0.01294868, 0.01212920, + 0.01135916, 0.01062935, 0.009938846, 0.009288422, 0.008678854, + 0.008110916, 0.007582388, 0.007088746, 0.006627313, 0.006195408, + 0.005790346, 0.005409826, 0.005052583, 0.004717512, 0.004403507, + 0.004109457, 0.003833913, 0.003575748, 0.003334342, 0.003109075, + 0.002899327, 0.002704348, 0.002523020, 0.002354168, 0.002196616, + 0.002049190, 0.001910960, 0.001781438, 0.001660110, 0.001546459, + 0.001439971, 0.001340042, 0.001246275, 0.001158471, 0.001076430, + 0.0009999493, 0.0009287358, 0.0008624332, 0.0008007503, 0.0007433960, + 0.0006900786, 0.0006405156, 0.0005945021, 0.0005518646, 0.0005124290, + 0.0004760213, 0.0004424536, 0.0004115117, 0.0003829814, 0.0003566491, + 0.0003323011, 0.0003097586, 0.0002888871, 0.0002695394, 0.0002515682, + 0.0002348261, 0.0002191710, 0.0002045258, 0.0001908405, 0.0001780654, + 0.0001661505, 0.0001550236, 0.0001446219, 0.0001349098, 0.0001258520, + 0.0001174130, 0.0001095515, 0.0001022245, 0.00009539445, 0.00008902390, + 0.00008307527, 0.00007751269, 0.00007231304, 0.00006745778, 0.00006292844, + 0.00005870652, 0.00005477028, 0.00005109918, 0.00004767654, 0.00004448567, + 0.00004150994, 0.00003873324, 0.00003614203, 0.00003372352, 0.00003146487, + 0.00002935326, 0.00002737573, 0.00002552433, 0.00002379376, 0.00002217870, + 0.00002067383, 0.00001927226, 0.00001796640, 0.00001674991, 0.00001561648, + 0.00001455977, 0.00001357387, 0.00001265436, 0.00001179723, 0.00001099844, + 0.00001025398, 0.000009559646, 0.000008912044, 0.000008308358, 0.000007745769, + 0.000007221456, 0.000006732475, 0.000006276423, 0.000005851304, 0.000005455118, + 0.000005085868, 0.000004741466, 0.000004420236, 0.000004120783, 0.000003841716, + 0.000003581652, 0.000003339127, 0.000003112949, 0.000002902121, 0.000002705645, + 0.000002522525, 0.000002351726, 0.000002192415, 0.000002043902, 0.000001905497, + 0.000001776509, 0.000001656215, 0.000001544022, 0.000001439440, 0.000001341977, + 0.000001251141]; + +pub const CIE_Y: [Float; CIE_SAMPLES] = [ + // CIE Y function values + 0.000003917000, 0.000004393581, 0.000004929604, 0.000005532136, 0.000006208245, + 0.000006965000, 0.000007813219, 0.000008767336, 0.000009839844, 0.00001104323, + 0.00001239000, 0.00001388641, 0.00001555728, 0.00001744296, 0.00001958375, + 0.00002202000, 0.00002483965, 0.00002804126, 0.00003153104, 0.00003521521, + 0.00003900000, 0.00004282640, 0.00004691460, 0.00005158960, 0.00005717640, + 0.00006400000, 0.00007234421, 0.00008221224, 0.00009350816, 0.0001061361, + 0.0001200000, 0.0001349840, 0.0001514920, 0.0001702080, 0.0001918160, + 0.0002170000, 0.0002469067, 0.0002812400, 0.0003185200, 0.0003572667, + 0.0003960000, 0.0004337147, 0.0004730240, 0.0005178760, 0.0005722187, + 0.0006400000, 0.0007245600, 0.0008255000, 0.0009411600, 0.001069880, + 0.001210000, 0.001362091, 0.001530752, 0.001720368, 0.001935323, + 0.002180000, 0.002454800, 0.002764000, 0.003117800, 0.003526400, + 0.004000000, 0.004546240, 0.005159320, 0.005829280, 0.006546160, + 0.007300000, 0.008086507, 0.008908720, 0.009767680, 0.01066443, + 0.01160000, 0.01257317, 0.01358272, 0.01462968, 0.01571509, + 0.01684000, 0.01800736, 0.01921448, 0.02045392, 0.02171824, + 0.02300000, 0.02429461, 0.02561024, 0.02695857, 0.02835125, + 0.02980000, 0.03131083, 0.03288368, 0.03452112, 0.03622571, + 0.03800000, 0.03984667, 0.04176800, 0.04376600, 0.04584267, + 0.04800000, 0.05024368, 0.05257304, 0.05498056, 0.05745872, + 0.06000000, 0.06260197, 0.06527752, 0.06804208, 0.07091109, + 0.07390000, 0.07701600, 0.08026640, 0.08366680, 0.08723280, + 0.09098000, 0.09491755, 0.09904584, 0.1033674, 0.1078846, + 0.1126000, 0.1175320, 0.1226744, 0.1279928, 0.1334528, + 0.1390200, 0.1446764, 0.1504693, 0.1564619, 0.1627177, + 0.1693000, 0.1762431, 0.1835581, 0.1912735, 0.1994180, + 0.2080200, 0.2171199, 0.2267345, 0.2368571, 0.2474812, + 0.2586000, 0.2701849, 0.2822939, 0.2950505, 0.3085780, + 0.3230000, 0.3384021, 0.3546858, 0.3716986, 0.3892875, + 0.4073000, 0.4256299, 0.4443096, 0.4633944, 0.4829395, + 0.5030000, 0.5235693, 0.5445120, 0.5656900, 0.5869653, + 0.6082000, 0.6293456, 0.6503068, 0.6708752, 0.6908424, + 0.7100000, 0.7281852, 0.7454636, 0.7619694, 0.7778368, + 0.7932000, 0.8081104, 0.8224962, 0.8363068, 0.8494916, + 0.8620000, 0.8738108, 0.8849624, 0.8954936, 0.9054432, + 0.9148501, 0.9237348, 0.9320924, 0.9399226, 0.9472252, + 0.9540000, 0.9602561, 0.9660074, 0.9712606, 0.9760225, + 0.9803000, 0.9840924, 0.9874812, 0.9903128, 0.9928116, + 0.9949501, 0.9967108, 0.9980983, 0.9991120, 0.9997482, + 1.0000000, 0.9998567, 0.9993046, 0.9983255, 0.9968987, + 0.9950000, 0.9926005, 0.9897426, 0.9864444, 0.9827241, + 0.9786000, 0.9740837, 0.9691712, 0.9638568, 0.9581349, + 0.9520000, 0.9454504, 0.9384992, 0.9311628, 0.9234576, + 0.9154000, 0.9070064, 0.8982772, 0.8892048, 0.8797816, + 0.8700000, 0.8598613, 0.8493920, 0.8386220, 0.8275813, + 0.8163000, 0.8047947, 0.7930820, 0.7811920, 0.7691547, + 0.7570000, 0.7447541, 0.7324224, 0.7200036, 0.7074965, + 0.6949000, 0.6822192, 0.6694716, 0.6566744, 0.6438448, + 0.6310000, 0.6181555, 0.6053144, 0.5924756, 0.5796379, + 0.5668000, 0.5539611, 0.5411372, 0.5283528, 0.5156323, + 0.5030000, 0.4904688, 0.4780304, 0.4656776, 0.4534032, + 0.4412000, 0.4290800, 0.4170360, 0.4050320, 0.3930320, + 0.3810000, 0.3689184, 0.3568272, 0.3447768, 0.3328176, + 0.3210000, 0.3093381, 0.2978504, 0.2865936, 0.2756245, + 0.2650000, 0.2547632, 0.2448896, 0.2353344, 0.2260528, + 0.2170000, 0.2081616, 0.1995488, 0.1911552, 0.1829744, + 0.1750000, 0.1672235, 0.1596464, 0.1522776, 0.1451259, + 0.1382000, 0.1315003, 0.1250248, 0.1187792, 0.1127691, + 0.1070000, 0.1014762, 0.09618864, 0.09112296, 0.08626485, + 0.08160000, 0.07712064, 0.07282552, 0.06871008, 0.06476976, + 0.06100000, 0.05739621, 0.05395504, 0.05067376, 0.04754965, + 0.04458000, 0.04175872, 0.03908496, 0.03656384, 0.03420048, + 0.03200000, 0.02996261, 0.02807664, 0.02632936, 0.02470805, + 0.02320000, 0.02180077, 0.02050112, 0.01928108, 0.01812069, + 0.01700000, 0.01590379, 0.01483718, 0.01381068, 0.01283478, + 0.01192000, 0.01106831, 0.01027339, 0.009533311, 0.008846157, + 0.008210000, 0.007623781, 0.007085424, 0.006591476, 0.006138485, + 0.005723000, 0.005343059, 0.004995796, 0.004676404, 0.004380075, + 0.004102000, 0.003838453, 0.003589099, 0.003354219, 0.003134093, + 0.002929000, 0.002738139, 0.002559876, 0.002393244, 0.002237275, + 0.002091000, 0.001953587, 0.001824580, 0.001703580, 0.001590187, + 0.001484000, 0.001384496, 0.001291268, 0.001204092, 0.001122744, + 0.001047000, 0.0009765896, 0.0009111088, 0.0008501332, 0.0007932384, + 0.0007400000, 0.0006900827, 0.0006433100, 0.0005994960, 0.0005584547, + 0.0005200000, 0.0004839136, 0.0004500528, 0.0004183452, 0.0003887184, + 0.0003611000, 0.0003353835, 0.0003114404, 0.0002891656, 0.0002684539, + 0.0002492000, 0.0002313019, 0.0002146856, 0.0001992884, 0.0001850475, + 0.0001719000, 0.0001597781, 0.0001486044, 0.0001383016, 0.0001287925, + 0.0001200000, 0.0001118595, 0.0001043224, 0.00009733560, 0.00009084587, + 0.00008480000, 0.00007914667, 0.00007385800, 0.00006891600, 0.00006430267, + 0.00006000000, 0.00005598187, 0.00005222560, 0.00004871840, 0.00004544747, + 0.00004240000, 0.00003956104, 0.00003691512, 0.00003444868, 0.00003214816, + 0.00003000000, 0.00002799125, 0.00002611356, 0.00002436024, 0.00002272461, + 0.00002120000, 0.00001977855, 0.00001845285, 0.00001721687, 0.00001606459, + 0.00001499000, 0.00001398728, 0.00001305155, 0.00001217818, 0.00001136254, + 0.00001060000, 0.000009885877, 0.000009217304, 0.000008592362, 0.000008009133, + 0.000007465700, 0.000006959567, 0.000006487995, 0.000006048699, 0.000005639396, + 0.000005257800, 0.000004901771, 0.000004569720, 0.000004260194, 0.000003971739, + 0.000003702900, 0.000003452163, 0.000003218302, 0.000003000300, 0.000002797139, + 0.000002607800, 0.000002431220, 0.000002266531, 0.000002113013, 0.000001969943, + 0.000001836600, 0.000001712230, 0.000001596228, 0.000001488090, 0.000001387314, + 0.000001293400, 0.000001205820, 0.000001124143, 0.000001048009, 0.0000009770578, + 0.0000009109300, 0.0000008492513, 0.0000007917212, 0.0000007380904, 0.0000006881098, + 0.0000006415300, 0.0000005980895, 0.0000005575746, 0.0000005198080, 0.0000004846123, + 0.0000004518100]; + +pub const CIE_Z: [Float; CIE_SAMPLES] = [ + // CIE Z function values + 0.0006061000, 0.0006808792, 0.0007651456, 0.0008600124, 0.0009665928, + 0.001086000, 0.001220586, 0.001372729, 0.001543579, 0.001734286, + 0.001946000, 0.002177777, 0.002435809, 0.002731953, 0.003078064, + 0.003486000, 0.003975227, 0.004540880, 0.005158320, 0.005802907, + 0.006450001, 0.007083216, 0.007745488, 0.008501152, 0.009414544, + 0.01054999, 0.01196580, 0.01365587, 0.01558805, 0.01773015, + 0.02005001, 0.02251136, 0.02520288, 0.02827972, 0.03189704, + 0.03621000, 0.04143771, 0.04750372, 0.05411988, 0.06099803, + 0.06785001, 0.07448632, 0.08136156, 0.08915364, 0.09854048, + 0.1102000, 0.1246133, 0.1417017, 0.1613035, 0.1832568, + 0.2074000, 0.2336921, 0.2626114, 0.2947746, 0.3307985, + 0.3713000, 0.4162091, 0.4654642, 0.5196948, 0.5795303, + 0.6456000, 0.7184838, 0.7967133, 0.8778459, 0.9594390, + 1.0390501, 1.1153673, 1.1884971, 1.2581233, 1.3239296, + 1.3856000, 1.4426352, 1.4948035, 1.5421903, 1.5848807, + 1.6229600, 1.6564048, 1.6852959, 1.7098745, 1.7303821, + 1.7470600, 1.7600446, 1.7696233, 1.7762637, 1.7804334, + 1.7826000, 1.7829682, 1.7816998, 1.7791982, 1.7758671, + 1.7721100, 1.7682589, 1.7640390, 1.7589438, 1.7524663, + 1.7441000, 1.7335595, 1.7208581, 1.7059369, 1.6887372, + 1.6692000, 1.6475287, 1.6234127, 1.5960223, 1.5645280, + 1.5281000, 1.4861114, 1.4395215, 1.3898799, 1.3387362, + 1.2876400, 1.2374223, 1.1878243, 1.1387611, 1.0901480, + 1.0419000, 0.9941976, 0.9473473, 0.9014531, 0.8566193, + 0.8129501, 0.7705173, 0.7294448, 0.6899136, 0.6521049, + 0.6162000, 0.5823286, 0.5504162, 0.5203376, 0.4919673, + 0.4651800, 0.4399246, 0.4161836, 0.3938822, 0.3729459, + 0.3533000, 0.3348578, 0.3175521, 0.3013375, 0.2861686, + 0.2720000, 0.2588171, 0.2464838, 0.2347718, 0.2234533, + 0.2123000, 0.2011692, 0.1901196, 0.1792254, 0.1685608, + 0.1582000, 0.1481383, 0.1383758, 0.1289942, 0.1200751, + 0.1117000, 0.1039048, 0.09666748, 0.08998272, 0.08384531, + 0.07824999, 0.07320899, 0.06867816, 0.06456784, 0.06078835, + 0.05725001, 0.05390435, 0.05074664, 0.04775276, 0.04489859, + 0.04216000, 0.03950728, 0.03693564, 0.03445836, 0.03208872, + 0.02984000, 0.02771181, 0.02569444, 0.02378716, 0.02198925, + 0.02030000, 0.01871805, 0.01724036, 0.01586364, 0.01458461, + 0.01340000, 0.01230723, 0.01130188, 0.01037792, 0.009529306, + 0.008749999, 0.008035200, 0.007381600, 0.006785400, 0.006242800, + 0.005749999, 0.005303600, 0.004899800, 0.004534200, 0.004202400, + 0.003900000, 0.003623200, 0.003370600, 0.003141400, 0.002934800, + 0.002749999, 0.002585200, 0.002438600, 0.002309400, 0.002196800, + 0.002100000, 0.002017733, 0.001948200, 0.001889800, 0.001840933, + 0.001800000, 0.001766267, 0.001737800, 0.001711200, 0.001683067, + 0.001650001, 0.001610133, 0.001564400, 0.001513600, 0.001458533, + 0.001400000, 0.001336667, 0.001270000, 0.001205000, 0.001146667, + 0.001100000, 0.001068800, 0.001049400, 0.001035600, 0.001021200, + 0.001000000, 0.0009686400, 0.0009299200, 0.0008868800, 0.0008425600, + 0.0008000000, 0.0007609600, 0.0007236800, 0.0006859200, 0.0006454400, + 0.0006000000, 0.0005478667, 0.0004916000, 0.0004354000, 0.0003834667, + 0.0003400000, 0.0003072533, 0.0002831600, 0.0002654400, 0.0002518133, + 0.0002400000, 0.0002295467, 0.0002206400, 0.0002119600, 0.0002021867, + 0.0001900000, 0.0001742133, 0.0001556400, 0.0001359600, 0.0001168533, + 0.0001000000, 0.00008613333, 0.00007460000, 0.00006500000, 0.00005693333, + 0.00004999999, 0.00004416000, 0.00003948000, 0.00003572000, 0.00003264000, + 0.00003000000, 0.00002765333, 0.00002556000, 0.00002364000, 0.00002181333, + 0.00002000000, 0.00001813333, 0.00001620000, 0.00001420000, 0.00001213333, + 0.00001000000, 0.000007733333, 0.000005400000, 0.000003200000, 0.000001333333, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000]; + +pub const CIE_LAMBDA: [i32; CIE_SAMPLES] = [ + 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, + 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, + 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, + 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, + 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, + 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, + 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, + 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, + 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, + 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, + 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, + 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, + 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, + 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, + 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, + 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, + 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, + 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, + 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, + 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, + 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830]; + +pub const CIE_ILLUM_A: [Float; 214] = [ + 300.000000, 0.930483, 305.000000, 1.128210, 310.000000, 1.357690, 315.000000, + 1.622190, 320.000000, 1.925080, 325.000000, 2.269800, 330.000000, 2.659810, + 335.000000, 3.098610, 340.000000, 3.589680, 345.000000, 4.136480, 350.000000, + 4.742380, 355.000000, 5.410700, 360.000000, 6.144620, 365.000000, 6.947200, + 370.000000, 7.821350, 375.000000, 8.769800, 380.000000, 9.795100, 385.000000, + 10.899600, 390.000000, 12.085300, 395.000000, 13.354300, 400.000000, 14.708000, + 405.000000, 16.148001, 410.000000, 17.675301, 415.000000, 19.290701, 420.000000, + 20.995001, 425.000000, 22.788300, 430.000000, 24.670900, 435.000000, 26.642500, + 440.000000, 28.702700, 445.000000, 30.850800, 450.000000, 33.085899, 455.000000, + 35.406799, 460.000000, 37.812099, 465.000000, 40.300201, 470.000000, 42.869301, + 475.000000, 45.517399, 480.000000, 48.242298, 485.000000, 51.041801, 490.000000, + 53.913200, 495.000000, 56.853901, 500.000000, 59.861099, 505.000000, 62.931999, + 510.000000, 66.063499, 515.000000, 69.252502, 520.000000, 72.495903, 525.000000, + 75.790298, 530.000000, 79.132599, 535.000000, 82.519302, 540.000000, 85.946999, + 545.000000, 89.412399, 550.000000, 92.912003, 555.000000, 96.442299, 560.000000, + 100.000000, 565.000000, 103.582001, 570.000000, 107.183998, 575.000000, 110.803001, + 580.000000, 114.435997, 585.000000, 118.080002, 590.000000, 121.731003, 595.000000, + 125.386002, 600.000000, 129.042999, 605.000000, 132.697006, 610.000000, 136.345993, + 615.000000, 139.988007, 620.000000, 143.617996, 625.000000, 147.235001, 630.000000, + 150.835999, 635.000000, 154.417999, 640.000000, 157.979004, 645.000000, 161.516006, + 650.000000, 165.028000, 655.000000, 168.509995, 660.000000, 171.962997, 665.000000, + 175.382996, 670.000000, 178.768997, 675.000000, 182.117996, 680.000000, 185.429001, + 685.000000, 188.701004, 690.000000, 191.931000, 695.000000, 195.117996, 700.000000, + 198.261002, 705.000000, 201.358994, 710.000000, 204.408997, 715.000000, 207.410995, + 720.000000, 210.365005, 725.000000, 213.268005, 730.000000, 216.119995, 735.000000, + 218.919998, 740.000000, 221.667007, 745.000000, 224.360992, 750.000000, 227.000000, + 755.000000, 229.585007, 760.000000, 232.115005, 765.000000, 234.589005, 770.000000, + 237.007996, 775.000000, 239.369995, 780.000000, 241.675003, 785.000000, 243.923996, + 790.000000, 246.115997, 795.000000, 248.251007, 800.000000, 250.328995, 805.000000, + 252.350006, 810.000000, 254.313995, 815.000000, 256.221008, 820.000000, 258.071014, + 825.000000, 259.864990, 830.000000, 261.601990, +]; + +// CIE Illuminant D S basis functions +const N_CIES: usize = 107; + +pub const CIE_S_LAMBDA: [Float; N_CIES] = [ + 300.000000, 305.000000, 310.000000, 315.000000, 320.000000, 325.000000, 330.000000, + 335.000000, 340.000000, 345.000000, 350.000000, 355.000000, 360.000000, 365.000000, + 370.000000, 375.000000, 380.000000, 385.000000, 390.000000, 395.000000, 400.000000, + 405.000000, 410.000000, 415.000000, 420.000000, 425.000000, 430.000000, 435.000000, + 440.000000, 445.000000, 450.000000, 455.000000, 460.000000, 465.000000, 470.000000, + 475.000000, 480.000000, 485.000000, 490.000000, 495.000000, 500.000000, 505.000000, + 510.000000, 515.000000, 520.000000, 525.000000, 530.000000, 535.000000, 540.000000, + 545.000000, 550.000000, 555.000000, 560.000000, 565.000000, 570.000000, 575.000000, + 580.000000, 585.000000, 590.000000, 595.000000, 600.000000, 605.000000, 610.000000, + 615.000000, 620.000000, 625.000000, 630.000000, 635.000000, 640.000000, 645.000000, + 650.000000, 655.000000, 660.000000, 665.000000, 670.000000, 675.000000, 680.000000, + 685.000000, 690.000000, 695.000000, 700.000000, 705.000000, 710.000000, 715.000000, + 720.000000, 725.000000, 730.000000, 735.000000, 740.000000, 745.000000, 750.000000, + 755.000000, 760.000000, 765.000000, 770.000000, 775.000000, 780.000000, 785.000000, + 790.000000, 795.000000, 800.000000, 805.000000, 810.000000, 815.000000, 820.000000, + 825.000000, 830.000000]; + +pub const CIE_S0: [Float; N_CIES] = [ + 0.040000, 3.020000, 6.000000, 17.800000, 29.600000, 42.450000, 55.300000, + 56.300000, 57.300000, 59.550000, 61.800000, 61.650000, 61.500000, 65.150000, + 68.800000, 66.100000, 63.400000, 64.600000, 65.800000, 80.300000, 94.800000, + 99.800000, 104.800000, 105.350000, 105.900000, 101.350000, 96.800000, 105.350000, + 113.900000, 119.750000, 125.600000, 125.550000, 125.500000, 123.400000, 121.300000, + 121.300000, 121.300000, 117.400000, 113.500000, 113.300000, 113.100000, 111.950000, + 110.800000, 108.650000, 106.500000, 107.650000, 108.800000, 107.050000, 105.300000, + 104.850000, 104.400000, 102.200000, 100.000000, 98.000000, 96.000000, 95.550000, + 95.100000, 92.100000, 89.100000, 89.800000, 90.500000, 90.400000, 90.300000, + 89.350000, 88.400000, 86.200000, 84.000000, 84.550000, 85.100000, 83.500000, + 81.900000, 82.250000, 82.600000, 83.750000, 84.900000, 83.100000, 81.300000, + 76.600000, 71.900000, 73.100000, 74.300000, 75.350000, 76.400000, 69.850000, + 63.300000, 67.500000, 71.700000, 74.350000, 77.000000, 71.100000, 65.200000, + 56.450000, 47.700000, 58.150000, 68.600000, 66.800000, 65.000000, 65.500000, + 66.000000, 63.500000, 61.000000, 57.150000, 53.300000, 56.100000, 58.900000, + 60.400000, 61.900000]; + +pub const CIE_S1: [Float; N_CIES] = [ + 0.020000, 2.260000, 4.500000, 13.450000, 22.400000, 32.200000, 42.000000, + 41.300000, 40.600000, 41.100000, 41.600000, 39.800000, 38.000000, 40.200000, + 42.400000, 40.450000, 38.500000, 36.750000, 35.000000, 39.200000, 43.400000, + 44.850000, 46.300000, 45.100000, 43.900000, 40.500000, 37.100000, 36.900000, + 36.700000, 36.300000, 35.900000, 34.250000, 32.600000, 30.250000, 27.900000, + 26.100000, 24.300000, 22.200000, 20.100000, 18.150000, 16.200000, 14.700000, + 13.200000, 10.900000, 8.600000, 7.350000, 6.100000, 5.150000, 4.200000, + 3.050000, 1.900000, 0.950000, -0.000000, -0.800000, -1.600000, -2.550000, + -3.500000, -3.500000, -3.500000, -4.650000, -5.800000, -6.500000, -7.200000, + -7.900000, -8.600000, -9.050000, -9.500000, -10.200000, -10.900000, -10.800000, + -10.700000, -11.350000, -12.000000, -13.000000, -14.000000, -13.800000, -13.600000, + -12.800000, -12.000000, -12.650000, -13.300000, -13.100000, -12.900000, -11.750000, + -10.600000, -11.100000, -11.600000, -11.900000, -12.200000, -11.200000, -10.200000, + -9.000000, -7.800000, -9.500000, -11.200000, -10.800000, -10.400000, -10.500000, + -10.600000, -10.150000, -9.700000, -9.000000, -8.300000, -8.800000, -9.300000, + -9.550000, -9.800000]; + +pub const CIE_S2: [Float; N_CIES] = [ + 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 6.250000, 8.500000, + 8.150000, 7.800000, 7.250000, 6.700000, 6.000000, 5.300000, 5.700000, + 6.100000, 4.550000, 3.000000, 2.100000, 1.200000, 0.050000, -1.100000, + -0.800000, -0.500000, -0.600000, -0.700000, -0.950000, -1.200000, -1.900000, + -2.600000, -2.750000, -2.900000, -2.850000, -2.800000, -2.700000, -2.600000, + -2.600000, -2.600000, -2.200000, -1.800000, -1.650000, -1.500000, -1.400000, + -1.300000, -1.250000, -1.200000, -1.100000, -1.000000, -0.750000, -0.500000, + -0.400000, -0.300000, -0.150000, 0.000000, 0.100000, 0.200000, 0.350000, + 0.500000, 1.300000, 2.100000, 2.650000, 3.200000, 3.650000, 4.100000, + 4.400000, 4.700000, 4.900000, 5.100000, 5.900000, 6.700000, 7.000000, + 7.300000, 7.950000, 8.600000, 9.200000, 9.800000, 10.000000, 10.200000, + 9.250000, 8.300000, 8.950000, 9.600000, 9.050000, 8.500000, 7.750000, + 7.000000, 7.300000, 7.600000, 7.800000, 8.000000, 7.350000, 6.700000, + 5.950000, 5.200000, 6.300000, 7.400000, 7.100000, 6.800000, 6.900000, + 7.000000, 6.700000, 6.400000, 5.950000, 5.500000, 5.800000, 6.100000, + 6.300000, 6.500000]; + +pub const CIE_ILLUM_D5000: [Float; 214] = [ + 300.000000, 0.019200, 305.000000, 1.036600, 310.000000, 2.054000, 315.000000, + 4.913000, 320.000000, 7.772000, 325.000000, 11.255700, 330.000000, 14.739500, + 335.000000, 16.339001, 340.000000, 17.938601, 345.000000, 19.466700, 350.000000, + 20.994900, 355.000000, 22.459999, 360.000000, 23.925100, 365.000000, 25.433901, + 370.000000, 26.942699, 375.000000, 25.701799, 380.000000, 24.461000, 385.000000, + 27.150700, 390.000000, 29.840401, 395.000000, 39.550301, 400.000000, 49.664001, + 405.000000, 53.155998, 410.000000, 56.647999, 415.000000, 58.445999, 420.000000, + 60.243999, 425.000000, 59.230000, 430.000000, 58.216000, 435.000000, 66.973999, + 440.000000, 75.732002, 445.000000, 81.998001, 450.000000, 88.264000, 455.000000, + 89.930000, 460.000000, 91.596001, 465.000000, 91.940002, 470.000000, 92.283997, + 475.000000, 94.155998, 480.000000, 96.028000, 485.000000, 94.311996, 490.000000, + 92.596001, 495.000000, 94.424004, 500.000000, 96.251999, 505.000000, 96.662003, + 510.000000, 97.071999, 515.000000, 97.314003, 520.000000, 97.556000, 525.000000, + 100.005997, 530.000000, 102.456001, 535.000000, 101.694000, 540.000000, 100.931999, + 545.000000, 101.678001, 550.000000, 102.424004, 555.000000, 101.211998, 560.000000, + 100.000000, 565.000000, 98.036697, 570.000000, 96.073402, 575.000000, 95.678398, + 580.000000, 95.283501, 585.000000, 92.577103, 590.000000, 89.870697, 595.000000, + 90.772499, 600.000000, 91.674400, 605.000000, 91.739502, 610.000000, 91.804703, + 615.000000, 90.964798, 620.000000, 90.124901, 625.000000, 87.998299, 630.000000, + 85.871696, 635.000000, 86.715302, 640.000000, 87.558899, 645.000000, 86.069000, + 650.000000, 84.579102, 655.000000, 85.167603, 660.000000, 85.756203, 665.000000, + 87.126404, 670.000000, 88.496597, 675.000000, 86.769997, 680.000000, 85.043404, + 685.000000, 79.994698, 690.000000, 74.946098, 695.000000, 76.384598, 700.000000, + 77.823196, 705.000000, 78.671303, 710.000000, 79.519501, 715.000000, 72.694199, + 720.000000, 65.869003, 725.000000, 70.179100, 730.000000, 74.489197, 735.000000, + 77.212601, 740.000000, 79.935997, 745.000000, 73.797401, 750.000000, 67.658897, + 755.000000, 58.633598, 760.000000, 49.608398, 765.000000, 60.462101, 770.000000, + 71.315804, 775.000000, 69.405701, 780.000000, 67.495598, 785.000000, 68.032303, + 790.000000, 68.569000, 795.000000, 65.958900, 800.000000, 63.348801, 805.000000, + 59.333599, 810.000000, 55.318501, 815.000000, 58.228600, 820.000000, 61.138699, + 825.000000, 62.712101, 830.000000, 64.285500, +]; + +// Via https://gist.github.com/aforsythe/4df4e5377853df76a5a83a3c001c7eeb +// with the critial bugfix: +// < cct = 6000 +// -- +// > cct = 6000. +pub const ACES_ILLUM_D60: [Float; 214] = [ + 300., 0.02928, 305., 1.28964, 310., 2.55, 315., 9.0338, 320., 15.5176, + 325., 21.94705, 330., 28.3765, 335., 29.93335, 340., 31.4902, 345., 33.75765, + 350., 36.0251, 355., 37.2032, 360., 38.3813, 365., 40.6445, 370., 42.9077, + 375., 42.05735, 380., 41.207, 385., 43.8121, 390., 46.4172, 395., 59.26285, + 400., 72.1085, 405., 76.1756, 410., 80.2427, 415., 81.4878, 420., 82.7329, + 425., 80.13505, 430., 77.5372, 435., 86.5577, 440., 95.5782, 445., 101.72045, + 450., 107.8627, 455., 108.67115, 460., 109.4796, 465., 108.5873, 470., 107.695, + 475., 108.6598, 480., 109.6246, 485., 106.6426, 490., 103.6606, 495., 104.42795, + 500., 105.1953, 505., 104.7974, 510., 104.3995, 515., 103.45635, 520., 102.5132, + 525., 104.2813, 530., 106.0494, 535., 104.67885, 540., 103.3083, 545., 103.4228, + 550., 103.5373, 555., 101.76865, 560., 100.0, 565., 98.3769, 570., 96.7538, + 575., 96.73515, 580., 96.7165, 585., 93.3013, 590., 89.8861, 595., 90.91705, + 600., 91.948, 605., 91.98965, 610., 92.0313, 615., 91.3008, 620., 90.5703, + 625., 88.5077, 630., 86.4451, 635., 86.9551, 640., 87.4651, 645., 85.6558, + 650., 83.8465, 655., 84.20755, 660., 84.5686, 665., 85.9432, 670., 87.3178, + 675., 85.3068, 680., 83.2958, 685., 78.66005, 690., 74.0243, 695., 75.23535, + 700., 76.4464, 705., 77.67465, 710., 78.9029, 715., 72.12575, 720., 65.3486, + 725., 69.6609, 730., 73.9732, 735., 76.6802, 740., 79.3872, 745., 73.28855, + 750., 67.1899, 755., 58.18595, 760., 49.182, 765., 59.9723, 770., 70.7626, + 775., 68.9039, 780., 67.0452, 785., 67.5469, 790., 68.0486, 795., 65.4631, + 800., 62.8776, 805., 58.88595, 810., 54.8943, 815., 57.8066, 820., 60.7189, + 825., 62.2491, 830., 63.7793, +]; + +pub const CIE_ILLUM_D6500: [Float; 214] = [ + 300.000000, 0.034100, 305.000000, 1.664300, 310.000000, 3.294500, 315.000000, + 11.765200, 320.000000, 20.236000, 325.000000, 28.644699, 330.000000, 37.053501, + 335.000000, 38.501099, 340.000000, 39.948799, 345.000000, 42.430199, 350.000000, + 44.911701, 355.000000, 45.775002, 360.000000, 46.638302, 365.000000, 49.363701, + 370.000000, 52.089100, 375.000000, 51.032299, 380.000000, 49.975498, 385.000000, + 52.311798, 390.000000, 54.648201, 395.000000, 68.701500, 400.000000, 82.754898, + 405.000000, 87.120399, 410.000000, 91.486000, 415.000000, 92.458900, 420.000000, + 93.431801, 425.000000, 90.056999, 430.000000, 86.682297, 435.000000, 95.773598, + 440.000000, 104.864998, 445.000000, 110.935997, 450.000000, 117.008003, 455.000000, + 117.410004, 460.000000, 117.811996, 465.000000, 116.335999, 470.000000, 114.861000, + 475.000000, 115.391998, 480.000000, 115.922997, 485.000000, 112.366997, 490.000000, + 108.810997, 495.000000, 109.082001, 500.000000, 109.353996, 505.000000, 108.578003, + 510.000000, 107.802002, 515.000000, 106.295998, 520.000000, 104.790001, 525.000000, + 106.238998, 530.000000, 107.689003, 535.000000, 106.046997, 540.000000, 104.404999, + 545.000000, 104.224998, 550.000000, 104.045998, 555.000000, 102.023003, 560.000000, + 100.000000, 565.000000, 98.167099, 570.000000, 96.334198, 575.000000, 96.061096, + 580.000000, 95.788002, 585.000000, 92.236801, 590.000000, 88.685600, 595.000000, + 89.345901, 600.000000, 90.006203, 605.000000, 89.802597, 610.000000, 89.599098, + 615.000000, 88.648903, 620.000000, 87.698700, 625.000000, 85.493599, 630.000000, + 83.288597, 635.000000, 83.493896, 640.000000, 83.699203, 645.000000, 81.862999, + 650.000000, 80.026802, 655.000000, 80.120697, 660.000000, 80.214600, 665.000000, + 81.246201, 670.000000, 82.277802, 675.000000, 80.280998, 680.000000, 78.284203, + 685.000000, 74.002701, 690.000000, 69.721298, 695.000000, 70.665199, 700.000000, + 71.609100, 705.000000, 72.978996, 710.000000, 74.348999, 715.000000, 67.976501, + 720.000000, 61.604000, 725.000000, 65.744797, 730.000000, 69.885597, 735.000000, + 72.486298, 740.000000, 75.086998, 745.000000, 69.339798, 750.000000, 63.592701, + 755.000000, 55.005402, 760.000000, 46.418201, 765.000000, 56.611801, 770.000000, + 66.805397, 775.000000, 65.094101, 780.000000, 63.382801, 785.000000, 63.843399, + 790.000000, 64.304001, 795.000000, 61.877899, 800.000000, 59.451900, 805.000000, + 55.705399, 810.000000, 51.959000, 815.000000, 54.699799, 820.000000, 57.440601, + 825.000000, 58.876499, 830.000000, 60.312500, +]; + +pub const CIE_ILLUM_F1: [Float; 162] = [ + 380.000000, 1.870000, 385.000000, 2.360000, 390.000000, 2.940000, 395.000000, + 3.470000, 400.000000, 5.170000, 405.000000, 19.490000, 410.000000, 6.130000, + 415.000000, 6.240000, 420.000000, 7.010000, 425.000000, 7.790000, 430.000000, + 8.560000, 435.000000, 43.669998, 440.000000, 16.940001, 445.000000, 10.720000, + 450.000000, 11.350000, 455.000000, 11.890000, 460.000000, 12.370000, 465.000000, + 12.750000, 470.000000, 13.000000, 475.000000, 13.150000, 480.000000, 13.230000, + 485.000000, 13.170000, 490.000000, 13.130000, 495.000000, 12.850000, 500.000000, + 12.520000, 505.000000, 12.200000, 510.000000, 11.830000, 515.000000, 11.500000, + 520.000000, 11.220000, 525.000000, 11.050000, 530.000000, 11.030000, 535.000000, + 11.180000, 540.000000, 11.530000, 545.000000, 27.740000, 550.000000, 17.049999, + 555.000000, 13.550000, 560.000000, 14.330000, 565.000000, 15.010000, 570.000000, + 15.520000, 575.000000, 18.290001, 580.000000, 19.549999, 585.000000, 15.480000, + 590.000000, 14.910000, 595.000000, 14.150000, 600.000000, 13.220000, 605.000000, + 12.190000, 610.000000, 11.120000, 615.000000, 10.030000, 620.000000, 8.950000, + 625.000000, 7.960000, 630.000000, 7.020000, 635.000000, 6.200000, 640.000000, + 5.420000, 645.000000, 4.730000, 650.000000, 4.150000, 655.000000, 3.640000, + 660.000000, 3.200000, 665.000000, 2.810000, 670.000000, 2.470000, 675.000000, + 2.180000, 680.000000, 1.930000, 685.000000, 1.720000, 690.000000, 1.670000, + 695.000000, 1.430000, 700.000000, 1.290000, 705.000000, 1.190000, 710.000000, + 1.080000, 715.000000, 0.960000, 720.000000, 0.880000, 725.000000, 0.810000, + 730.000000, 0.770000, 735.000000, 0.750000, 740.000000, 0.730000, 745.000000, + 0.680000, 750.000000, 0.690000, 755.000000, 0.640000, 760.000000, 0.680000, + 765.000000, 0.690000, 770.000000, 0.610000, 775.000000, 0.520000, 780.000000, + 0.430000, +]; + +pub const CIE_ILLUM_F2: [Float; 162] = [ + 380.000000, 0.820000, 385.000000, 1.020000, 390.000000, 1.260000, 395.000000, + 1.440000, 400.000000, 2.570000, 405.000000, 14.360000, 410.000000, 2.700000, + 415.000000, 2.450000, 420.000000, 2.730000, 425.000000, 3.000000, 430.000000, + 3.280000, 435.000000, 31.850000, 440.000000, 9.470000, 445.000000, 4.020000, + 450.000000, 4.250000, 455.000000, 4.440000, 460.000000, 4.590000, 465.000000, + 4.720000, 470.000000, 4.800000, 475.000000, 4.860000, 480.000000, 4.870000, + 485.000000, 4.850000, 490.000000, 4.880000, 495.000000, 4.770000, 500.000000, + 4.670000, 505.000000, 4.620000, 510.000000, 4.620000, 515.000000, 4.730000, + 520.000000, 4.990000, 525.000000, 5.480000, 530.000000, 6.250000, 535.000000, + 7.340000, 540.000000, 8.780000, 545.000000, 23.820000, 550.000000, 16.139999, + 555.000000, 14.590000, 560.000000, 16.629999, 565.000000, 18.490000, 570.000000, + 19.950001, 575.000000, 23.110001, 580.000000, 24.690001, 585.000000, 21.410000, + 590.000000, 20.850000, 595.000000, 19.930000, 600.000000, 18.670000, 605.000000, + 17.219999, 610.000000, 15.650000, 615.000000, 14.040000, 620.000000, 12.450000, + 625.000000, 10.950000, 630.000000, 9.510000, 635.000000, 8.270000, 640.000000, + 7.110000, 645.000000, 6.090000, 650.000000, 5.220000, 655.000000, 4.450000, + 660.000000, 3.800000, 665.000000, 3.230000, 670.000000, 2.750000, 675.000000, + 2.330000, 680.000000, 1.990000, 685.000000, 1.700000, 690.000000, 1.550000, + 695.000000, 1.270000, 700.000000, 1.090000, 705.000000, 0.960000, 710.000000, + 0.830000, 715.000000, 0.710000, 720.000000, 0.620000, 725.000000, 0.540000, + 730.000000, 0.490000, 735.000000, 0.460000, 740.000000, 0.430000, 745.000000, + 0.390000, 750.000000, 0.390000, 755.000000, 0.350000, 760.000000, 0.380000, + 765.000000, 0.390000, 770.000000, 0.330000, 775.000000, 0.280000, 780.000000, + 0.210000, +]; + +pub const CIE_ILLUM_F4: [Float; 162] = [ + 380.000000, 0.570000, 385.000000, 0.700000, 390.000000, 0.870000, 395.000000, + 0.980000, 400.000000, 2.010000, 405.000000, 13.750000, 410.000000, 1.950000, + 415.000000, 1.590000, 420.000000, 1.760000, 425.000000, 1.930000, 430.000000, + 2.100000, 435.000000, 30.280001, 440.000000, 8.030000, 445.000000, 2.550000, + 450.000000, 2.700000, 455.000000, 2.820000, 460.000000, 2.910000, 465.000000, + 2.990000, 470.000000, 3.040000, 475.000000, 3.080000, 480.000000, 3.090000, + 485.000000, 3.090000, 490.000000, 3.140000, 495.000000, 3.060000, 500.000000, + 3.000000, 505.000000, 2.980000, 510.000000, 3.010000, 515.000000, 3.140000, + 520.000000, 3.410000, 525.000000, 3.900000, 530.000000, 4.690000, 535.000000, + 5.810000, 540.000000, 7.320000, 545.000000, 22.590000, 550.000000, 15.110000, + 555.000000, 13.880000, 560.000000, 16.330000, 565.000000, 18.680000, 570.000000, + 20.639999, 575.000000, 24.280001, 580.000000, 26.260000, 585.000000, 23.280001, + 590.000000, 22.940001, 595.000000, 22.139999, 600.000000, 20.910000, 605.000000, + 19.430000, 610.000000, 17.740000, 615.000000, 16.000000, 620.000000, 14.420000, + 625.000000, 12.560000, 630.000000, 10.930000, 635.000000, 9.520000, 640.000000, + 8.180000, 645.000000, 7.010000, 650.000000, 6.000000, 655.000000, 5.110000, + 660.000000, 4.360000, 665.000000, 3.690000, 670.000000, 3.130000, 675.000000, + 2.640000, 680.000000, 2.240000, 685.000000, 1.910000, 690.000000, 1.700000, + 695.000000, 1.390000, 700.000000, 1.180000, 705.000000, 1.030000, 710.000000, + 0.880000, 715.000000, 0.740000, 720.000000, 0.640000, 725.000000, 0.540000, + 730.000000, 0.490000, 735.000000, 0.460000, 740.000000, 0.420000, 745.000000, + 0.370000, 750.000000, 0.370000, 755.000000, 0.330000, 760.000000, 0.350000, + 765.000000, 0.360000, 770.000000, 0.310000, 775.000000, 0.260000, 780.000000, + 0.190000, +]; + +pub const CIE_ILLUM_F5: [Float; 162] = [ + 380.000000, 1.870000, 385.000000, 2.350000, 390.000000, 2.920000, 395.000000, + 3.450000, 400.000000, 5.100000, 405.000000, 18.910000, 410.000000, 6.000000, + 415.000000, 6.110000, 420.000000, 6.850000, 425.000000, 7.580000, 430.000000, + 8.310000, 435.000000, 40.759998, 440.000000, 16.059999, 445.000000, 10.320000, + 450.000000, 10.910000, 455.000000, 11.400000, 460.000000, 11.830000, 465.000000, + 12.170000, 470.000000, 12.400000, 475.000000, 12.540000, 480.000000, 12.580000, + 485.000000, 12.520000, 490.000000, 12.470000, 495.000000, 12.200000, 500.000000, + 11.890000, 505.000000, 11.610000, 510.000000, 11.330000, 515.000000, 11.100000, + 520.000000, 10.960000, 525.000000, 10.970000, 530.000000, 11.160000, 535.000000, + 11.540000, 540.000000, 12.120000, 545.000000, 27.780001, 550.000000, 17.730000, + 555.000000, 14.470000, 560.000000, 15.200000, 565.000000, 15.770000, 570.000000, + 16.100000, 575.000000, 18.540001, 580.000000, 19.500000, 585.000000, 15.390000, + 590.000000, 14.640000, 595.000000, 13.720000, 600.000000, 12.690000, 605.000000, + 11.570000, 610.000000, 10.450000, 615.000000, 9.350000, 620.000000, 8.290000, + 625.000000, 7.320000, 630.000000, 6.410000, 635.000000, 5.630000, 640.000000, + 4.900000, 645.000000, 4.260000, 650.000000, 3.720000, 655.000000, 3.250000, + 660.000000, 2.830000, 665.000000, 2.490000, 670.000000, 2.190000, 675.000000, + 1.930000, 680.000000, 1.710000, 685.000000, 1.520000, 690.000000, 1.430000, + 695.000000, 1.260000, 700.000000, 1.130000, 705.000000, 1.050000, 710.000000, + 0.960000, 715.000000, 0.850000, 720.000000, 0.780000, 725.000000, 0.720000, + 730.000000, 0.680000, 735.000000, 0.670000, 740.000000, 0.650000, 745.000000, + 0.610000, 750.000000, 0.620000, 755.000000, 0.590000, 760.000000, 0.620000, + 765.000000, 0.640000, 770.000000, 0.550000, 775.000000, 0.470000, 780.000000, + 0.400000, +]; + +pub const CIE_ILLUM_F6: [Float; 162] = [ + 380.000000, 1.050000, 385.000000, 1.310000, 390.000000, 1.630000, 395.000000, + 1.900000, 400.000000, 3.110000, 405.000000, 14.800000, 410.000000, 3.430000, + 415.000000, 3.300000, 420.000000, 3.680000, 425.000000, 4.070000, 430.000000, + 4.450000, 435.000000, 32.610001, 440.000000, 10.740000, 445.000000, 5.480000, + 450.000000, 5.780000, 455.000000, 6.030000, 460.000000, 6.250000, 465.000000, + 6.410000, 470.000000, 6.520000, 475.000000, 6.580000, 480.000000, 6.590000, + 485.000000, 6.560000, 490.000000, 6.560000, 495.000000, 6.420000, 500.000000, + 6.280000, 505.000000, 6.200000, 510.000000, 6.190000, 515.000000, 6.300000, + 520.000000, 6.600000, 525.000000, 7.120000, 530.000000, 7.940000, 535.000000, + 9.070000, 540.000000, 10.490000, 545.000000, 25.219999, 550.000000, 17.459999, + 555.000000, 15.630000, 560.000000, 17.219999, 565.000000, 18.530001, 570.000000, + 19.430000, 575.000000, 21.969999, 580.000000, 23.010000, 585.000000, 19.410000, + 590.000000, 18.559999, 595.000000, 17.420000, 600.000000, 16.090000, 605.000000, + 14.640000, 610.000000, 13.150000, 615.000000, 11.680000, 620.000000, 10.250000, + 625.000000, 8.960000, 630.000000, 7.740000, 635.000000, 6.690000, 640.000000, + 5.710000, 645.000000, 4.870000, 650.000000, 4.160000, 655.000000, 3.550000, + 660.000000, 3.020000, 665.000000, 2.570000, 670.000000, 2.200000, 675.000000, + 1.870000, 680.000000, 1.600000, 685.000000, 1.370000, 690.000000, 1.290000, + 695.000000, 1.050000, 700.000000, 0.910000, 705.000000, 0.810000, 710.000000, + 0.710000, 715.000000, 0.610000, 720.000000, 0.540000, 725.000000, 0.480000, + 730.000000, 0.440000, 735.000000, 0.430000, 740.000000, 0.400000, 745.000000, + 0.370000, 750.000000, 0.380000, 755.000000, 0.350000, 760.000000, 0.390000, + 765.000000, 0.410000, 770.000000, 0.330000, 775.000000, 0.260000, 780.000000, + 0.210000, +]; + +pub const CIE_ILLUM_F7: [Float; 162] = [ + 380.000000, 2.560000, 385.000000, 3.180000, 390.000000, 3.840000, 395.000000, + 4.530000, 400.000000, 6.150000, 405.000000, 19.370001, 410.000000, 7.370000, + 415.000000, 7.050000, 420.000000, 7.710000, 425.000000, 8.410000, 430.000000, + 9.150000, 435.000000, 44.139999, 440.000000, 17.520000, 445.000000, 11.350000, + 450.000000, 12.000000, 455.000000, 12.580000, 460.000000, 13.080000, 465.000000, + 13.450000, 470.000000, 13.710000, 475.000000, 13.880000, 480.000000, 13.950000, + 485.000000, 13.930000, 490.000000, 13.820000, 495.000000, 13.640000, 500.000000, + 13.430000, 505.000000, 13.250000, 510.000000, 13.080000, 515.000000, 12.930000, + 520.000000, 12.780000, 525.000000, 12.600000, 530.000000, 12.440000, 535.000000, + 12.330000, 540.000000, 12.260000, 545.000000, 29.520000, 550.000000, 17.049999, + 555.000000, 12.440000, 560.000000, 12.580000, 565.000000, 12.720000, 570.000000, + 12.830000, 575.000000, 15.460000, 580.000000, 16.750000, 585.000000, 12.830000, + 590.000000, 12.670000, 595.000000, 12.450000, 600.000000, 12.190000, 605.000000, + 11.890000, 610.000000, 11.600000, 615.000000, 11.350000, 620.000000, 11.120000, + 625.000000, 10.950000, 630.000000, 10.760000, 635.000000, 10.420000, 640.000000, + 10.110000, 645.000000, 10.040000, 650.000000, 10.020000, 655.000000, 10.110000, + 660.000000, 9.870000, 665.000000, 8.650000, 670.000000, 7.270000, 675.000000, + 6.440000, 680.000000, 5.830000, 685.000000, 5.410000, 690.000000, 5.040000, + 695.000000, 4.570000, 700.000000, 4.120000, 705.000000, 3.770000, 710.000000, + 3.460000, 715.000000, 3.080000, 720.000000, 2.730000, 725.000000, 2.470000, + 730.000000, 2.250000, 735.000000, 2.060000, 740.000000, 1.900000, 745.000000, + 1.750000, 750.000000, 1.620000, 755.000000, 1.540000, 760.000000, 1.450000, + 765.000000, 1.320000, 770.000000, 1.170000, 775.000000, 0.990000, 780.000000, + 0.810000, +]; + +pub const CIE_ILLUM_F8: [Float; 162] = [ + 380.000000, 1.210000, 385.000000, 1.500000, 390.000000, 1.810000, 395.000000, + 2.130000, 400.000000, 3.170000, 405.000000, 13.080000, 410.000000, 3.830000, + 415.000000, 3.450000, 420.000000, 3.860000, 425.000000, 4.420000, 430.000000, + 5.090000, 435.000000, 34.099998, 440.000000, 12.420000, 445.000000, 7.680000, + 450.000000, 8.600000, 455.000000, 9.460000, 460.000000, 10.240000, 465.000000, + 10.840000, 470.000000, 11.330000, 475.000000, 11.710000, 480.000000, 11.980000, + 485.000000, 12.170000, 490.000000, 12.280000, 495.000000, 12.320000, 500.000000, + 12.350000, 505.000000, 12.440000, 510.000000, 12.550000, 515.000000, 12.680000, + 520.000000, 12.770000, 525.000000, 12.720000, 530.000000, 12.600000, 535.000000, + 12.430000, 540.000000, 12.220000, 545.000000, 28.959999, 550.000000, 16.510000, + 555.000000, 11.790000, 560.000000, 11.760000, 565.000000, 11.770000, 570.000000, + 11.840000, 575.000000, 14.610000, 580.000000, 16.110001, 585.000000, 12.340000, + 590.000000, 12.530000, 595.000000, 12.720000, 600.000000, 12.920000, 605.000000, + 13.120000, 610.000000, 13.340000, 615.000000, 13.610000, 620.000000, 13.870000, + 625.000000, 14.070000, 630.000000, 14.200000, 635.000000, 14.160000, 640.000000, + 14.130000, 645.000000, 14.340000, 650.000000, 14.500000, 655.000000, 14.460000, + 660.000000, 14.000000, 665.000000, 12.580000, 670.000000, 10.990000, 675.000000, + 9.980000, 680.000000, 9.220000, 685.000000, 8.620000, 690.000000, 8.070000, + 695.000000, 7.390000, 700.000000, 6.710000, 705.000000, 6.160000, 710.000000, + 5.630000, 715.000000, 5.030000, 720.000000, 4.460000, 725.000000, 4.020000, + 730.000000, 3.660000, 735.000000, 3.360000, 740.000000, 3.090000, 745.000000, + 2.850000, 750.000000, 2.650000, 755.000000, 2.510000, 760.000000, 2.370000, + 765.000000, 2.150000, 770.000000, 1.890000, 775.000000, 1.610000, 780.000000, + 1.320000, +]; + +pub const CIE_ILLUM_F9: [Float; 162] = [ + 380.000000, 0.900000, 385.000000, 1.120000, 390.000000, 1.360000, 395.000000, + 1.600000, 400.000000, 2.590000, 405.000000, 12.800000, 410.000000, 3.050000, + 415.000000, 2.560000, 420.000000, 2.860000, 425.000000, 3.300000, 430.000000, + 3.820000, 435.000000, 32.619999, 440.000000, 10.770000, 445.000000, 5.840000, + 450.000000, 6.570000, 455.000000, 7.250000, 460.000000, 7.860000, 465.000000, + 8.350000, 470.000000, 8.750000, 475.000000, 9.060000, 480.000000, 9.310000, + 485.000000, 9.480000, 490.000000, 9.610000, 495.000000, 9.680000, 500.000000, + 9.740000, 505.000000, 9.880000, 510.000000, 10.040000, 515.000000, 10.260000, + 520.000000, 10.480000, 525.000000, 10.630000, 530.000000, 10.760000, 535.000000, + 10.960000, 540.000000, 11.180000, 545.000000, 27.709999, 550.000000, 16.290001, + 555.000000, 12.280000, 560.000000, 12.740000, 565.000000, 13.210000, 570.000000, + 13.650000, 575.000000, 16.570000, 580.000000, 18.139999, 585.000000, 14.550000, + 590.000000, 14.650000, 595.000000, 14.660000, 600.000000, 14.610000, 605.000000, + 14.500000, 610.000000, 14.390000, 615.000000, 14.400000, 620.000000, 14.470000, + 625.000000, 14.620000, 630.000000, 14.720000, 635.000000, 14.550000, 640.000000, + 14.400000, 645.000000, 14.580000, 650.000000, 14.880000, 655.000000, 15.510000, + 660.000000, 15.470000, 665.000000, 13.200000, 670.000000, 10.570000, 675.000000, + 9.180000, 680.000000, 8.250000, 685.000000, 7.570000, 690.000000, 7.030000, + 695.000000, 6.350000, 700.000000, 5.720000, 705.000000, 5.250000, 710.000000, + 4.800000, 715.000000, 4.290000, 720.000000, 3.800000, 725.000000, 3.430000, + 730.000000, 3.120000, 735.000000, 2.860000, 740.000000, 2.640000, 745.000000, + 2.430000, 750.000000, 2.260000, 755.000000, 2.140000, 760.000000, 2.020000, + 765.000000, 1.830000, 770.000000, 1.610000, 775.000000, 1.380000, 780.000000, + 1.120000, +]; + +pub const CIE_ILLUM_F10: [Float; 162] = [ + 380.000000, 1.110000, 385.000000, 0.630000, 390.000000, 0.620000, 395.000000, + 0.570000, 400.000000, 1.480000, 405.000000, 12.160000, 410.000000, 2.120000, + 415.000000, 2.700000, 420.000000, 3.740000, 425.000000, 5.140000, 430.000000, + 6.750000, 435.000000, 34.389999, 440.000000, 14.860000, 445.000000, 10.400000, + 450.000000, 10.760000, 455.000000, 10.670000, 460.000000, 10.110000, 465.000000, + 9.270000, 470.000000, 8.290000, 475.000000, 7.290000, 480.000000, 7.910000, + 485.000000, 16.639999, 490.000000, 16.730000, 495.000000, 10.440000, 500.000000, + 5.940000, 505.000000, 3.340000, 510.000000, 2.350000, 515.000000, 1.880000, + 520.000000, 1.590000, 525.000000, 1.470000, 530.000000, 1.800000, 535.000000, + 5.710000, 540.000000, 40.980000, 545.000000, 73.690002, 550.000000, 33.610001, + 555.000000, 8.240000, 560.000000, 3.380000, 565.000000, 2.470000, 570.000000, + 2.140000, 575.000000, 4.860000, 580.000000, 11.450000, 585.000000, 14.790000, + 590.000000, 12.160000, 595.000000, 8.970000, 600.000000, 6.520000, 605.000000, + 8.810000, 610.000000, 44.119999, 615.000000, 34.549999, 620.000000, 12.090000, + 625.000000, 12.150000, 630.000000, 10.520000, 635.000000, 4.430000, 640.000000, + 1.950000, 645.000000, 2.190000, 650.000000, 3.190000, 655.000000, 2.770000, + 660.000000, 2.290000, 665.000000, 2.000000, 670.000000, 1.520000, 675.000000, + 1.350000, 680.000000, 1.470000, 685.000000, 1.790000, 690.000000, 1.740000, + 695.000000, 1.020000, 700.000000, 1.140000, 705.000000, 3.320000, 710.000000, + 4.490000, 715.000000, 2.050000, 720.000000, 0.490000, 725.000000, 0.240000, + 730.000000, 0.210000, 735.000000, 0.210000, 740.000000, 0.240000, 745.000000, + 0.240000, 750.000000, 0.210000, 755.000000, 0.170000, 760.000000, 0.210000, + 765.000000, 0.220000, 770.000000, 0.170000, 775.000000, 0.120000, 780.000000, + 0.090000, +]; + +pub const CIE_ILLUM_F11: [Float; 162] = [ + 380.000000, 0.910000, 385.000000, 0.630000, 390.000000, 0.460000, 395.000000, + 0.370000, 400.000000, 1.290000, 405.000000, 12.680000, 410.000000, 1.590000, + 415.000000, 1.790000, 420.000000, 2.460000, 425.000000, 3.330000, 430.000000, + 4.490000, 435.000000, 33.939999, 440.000000, 12.130000, 445.000000, 6.950000, + 450.000000, 7.190000, 455.000000, 7.120000, 460.000000, 6.720000, 465.000000, + 6.130000, 470.000000, 5.460000, 475.000000, 4.790000, 480.000000, 5.660000, + 485.000000, 14.290000, 490.000000, 14.960000, 495.000000, 8.970000, 500.000000, + 4.720000, 505.000000, 2.330000, 510.000000, 1.470000, 515.000000, 1.100000, + 520.000000, 0.890000, 525.000000, 0.830000, 530.000000, 1.180000, 535.000000, + 4.900000, 540.000000, 39.590000, 545.000000, 72.839996, 550.000000, 32.610001, + 555.000000, 7.520000, 560.000000, 2.830000, 565.000000, 1.960000, 570.000000, + 1.670000, 575.000000, 4.430000, 580.000000, 11.280000, 585.000000, 14.760000, + 590.000000, 12.730000, 595.000000, 9.740000, 600.000000, 7.330000, 605.000000, + 9.720000, 610.000000, 55.270000, 615.000000, 42.580002, 620.000000, 13.180000, + 625.000000, 13.160000, 630.000000, 12.260000, 635.000000, 5.110000, 640.000000, + 2.070000, 645.000000, 2.340000, 650.000000, 3.580000, 655.000000, 3.010000, + 660.000000, 2.480000, 665.000000, 2.140000, 670.000000, 1.540000, 675.000000, + 1.330000, 680.000000, 1.460000, 685.000000, 1.940000, 690.000000, 2.000000, + 695.000000, 1.200000, 700.000000, 1.350000, 705.000000, 4.100000, 710.000000, + 5.580000, 715.000000, 2.510000, 720.000000, 0.570000, 725.000000, 0.270000, + 730.000000, 0.230000, 735.000000, 0.210000, 740.000000, 0.240000, 745.000000, + 0.240000, 750.000000, 0.200000, 755.000000, 0.240000, 760.000000, 0.320000, + 765.000000, 0.260000, 770.000000, 0.160000, 775.000000, 0.120000, 780.000000, + 0.090000, +]; + +pub const CIE_ILLUM_F12: [Float; 162] = [ + 380.000000, 0.960000, 385.000000, 0.640000, 390.000000, 0.450000, 395.000000, + 0.330000, 400.000000, 1.190000, 405.000000, 12.480000, 410.000000, 1.120000, + 415.000000, 0.940000, 420.000000, 1.080000, 425.000000, 1.370000, 430.000000, + 1.780000, 435.000000, 29.049999, 440.000000, 7.900000, 445.000000, 2.650000, + 450.000000, 2.710000, 455.000000, 2.650000, 460.000000, 2.490000, 465.000000, + 2.330000, 470.000000, 2.100000, 475.000000, 1.910000, 480.000000, 3.010000, + 485.000000, 10.830000, 490.000000, 11.880000, 495.000000, 6.880000, 500.000000, + 3.430000, 505.000000, 1.490000, 510.000000, 0.920000, 515.000000, 0.710000, + 520.000000, 0.600000, 525.000000, 0.630000, 530.000000, 1.100000, 535.000000, + 4.560000, 540.000000, 34.400002, 545.000000, 65.400002, 550.000000, 29.480000, + 555.000000, 7.160000, 560.000000, 3.080000, 565.000000, 2.470000, 570.000000, + 2.270000, 575.000000, 5.090000, 580.000000, 11.960000, 585.000000, 15.320000, + 590.000000, 14.270000, 595.000000, 11.860000, 600.000000, 9.280000, 605.000000, + 12.310000, 610.000000, 68.529999, 615.000000, 53.020000, 620.000000, 14.670000, + 625.000000, 14.380000, 630.000000, 14.710000, 635.000000, 6.460000, 640.000000, + 2.570000, 645.000000, 2.750000, 650.000000, 4.180000, 655.000000, 3.440000, + 660.000000, 2.810000, 665.000000, 2.420000, 670.000000, 1.640000, 675.000000, + 1.360000, 680.000000, 1.490000, 685.000000, 2.140000, 690.000000, 2.340000, + 695.000000, 1.420000, 700.000000, 1.610000, 705.000000, 5.040000, 710.000000, + 6.980000, 715.000000, 3.190000, 720.000000, 0.710000, 725.000000, 0.300000, + 730.000000, 0.260000, 735.000000, 0.230000, 740.000000, 0.280000, 745.000000, + 0.280000, 750.000000, 0.210000, 755.000000, 0.170000, 760.000000, 0.210000, + 765.000000, 0.190000, 770.000000, 0.150000, 775.000000, 0.100000, 780.000000, + 0.050000, +]; + +pub const AG_ETA: [Float; 112] = [ + 298.757050, 1.519000, 302.400421, 1.496000, 306.133759, 1.432500, 309.960449, + 1.323000, 313.884003, 1.142062, 317.908142, 0.932000, 322.036835, 0.719062, + 326.274139, 0.526000, 330.624481, 0.388125, 335.092377, 0.294000, 339.682678, + 0.253313, 344.400482, 0.238000, 349.251221, 0.221438, 354.240509, 0.209000, + 359.374420, 0.194813, 364.659332, 0.186000, 370.102020, 0.192063, 375.709625, + 0.200000, 381.489777, 0.198063, 387.450562, 0.192000, 393.600555, 0.182000, + 399.948975, 0.173000, 406.505493, 0.172625, 413.280579, 0.173000, 420.285339, + 0.166688, 427.531647, 0.160000, 435.032196, 0.158500, 442.800629, 0.157000, + 450.851562, 0.151063, 459.200653, 0.144000, 467.864838, 0.137313, 476.862213, + 0.132000, 486.212463, 0.130250, 495.936707, 0.130000, 506.057861, 0.129938, + 516.600769, 0.130000, 527.592224, 0.130063, 539.061646, 0.129000, 551.040771, + 0.124375, 563.564453, 0.120000, 576.670593, 0.119313, 590.400818, 0.121000, + 604.800842, 0.125500, 619.920898, 0.131000, 635.816284, 0.136125, 652.548279, + 0.140000, 670.184753, 0.140063, 688.800964, 0.140000, 708.481018, 0.144313, + 729.318665, 0.148000, 751.419250, 0.145875, 774.901123, 0.143000, 799.897949, + 0.142563, 826.561157, 0.145000, 855.063293, 0.151938, 885.601257, 0.163000, +]; + +const AG_K: [Float; 112] = [ + 298.757050, 1.080000, 302.400421, 0.882000, 306.133759, 0.761063, 309.960449, + 0.647000, 313.884003, 0.550875, 317.908142, 0.504000, 322.036835, 0.554375, + 326.274139, 0.663000, 330.624481, 0.818563, 335.092377, 0.986000, 339.682678, + 1.120687, 344.400482, 1.240000, 349.251221, 1.345250, 354.240509, 1.440000, + 359.374420, 1.533750, 364.659332, 1.610000, 370.102020, 1.641875, 375.709625, + 1.670000, 381.489777, 1.735000, 387.450562, 1.810000, 393.600555, 1.878750, + 399.948975, 1.950000, 406.505493, 2.029375, 413.280579, 2.110000, 420.285339, + 2.186250, 427.531647, 2.260000, 435.032196, 2.329375, 442.800629, 2.400000, + 450.851562, 2.478750, 459.200653, 2.560000, 467.864838, 2.640000, 476.862213, + 2.720000, 486.212463, 2.798125, 495.936707, 2.880000, 506.057861, 2.973750, + 516.600769, 3.070000, 527.592224, 3.159375, 539.061646, 3.250000, 551.040771, + 3.348125, 563.564453, 3.450000, 576.670593, 3.553750, 590.400818, 3.660000, + 604.800842, 3.766250, 619.920898, 3.880000, 635.816284, 4.010625, 652.548279, + 4.150000, 670.184753, 4.293125, 688.800964, 4.440000, 708.481018, 4.586250, + 729.318665, 4.740000, 751.419250, 4.908125, 774.901123, 5.090000, 799.897949, + 5.288750, 826.561157, 5.500000, 855.063293, 5.720624, 885.601257, 5.950000, +]; + +pub const AL_ETA: [Float; 112] = [ + 298.757050, 0.273375, 302.400421, 0.280000, 306.133759, 0.286813, 309.960449, + 0.294000, 313.884003, 0.301875, 317.908142, 0.310000, 322.036835, 0.317875, + 326.274139, 0.326000, 330.624481, 0.334750, 335.092377, 0.344000, 339.682678, + 0.353813, 344.400482, 0.364000, 349.251221, 0.374375, 354.240509, 0.385000, + 359.374420, 0.395750, 364.659332, 0.407000, 370.102020, 0.419125, 375.709625, + 0.432000, 381.489777, 0.445688, 387.450562, 0.460000, 393.600555, 0.474688, + 399.948975, 0.490000, 406.505493, 0.506188, 413.280579, 0.523000, 420.285339, + 0.540063, 427.531647, 0.558000, 435.032196, 0.577313, 442.800629, 0.598000, + 450.851562, 0.620313, 459.200653, 0.644000, 467.864838, 0.668625, 476.862213, + 0.695000, 486.212463, 0.723750, 495.936707, 0.755000, 506.057861, 0.789000, + 516.600769, 0.826000, 527.592224, 0.867000, 539.061646, 0.912000, 551.040771, + 0.963000, 563.564453, 1.020000, 576.670593, 1.080000, 590.400818, 1.150000, + 604.800842, 1.220000, 619.920898, 1.300000, 635.816284, 1.390000, 652.548279, + 1.490000, 670.184753, 1.600000, 688.800964, 1.740000, 708.481018, 1.910000, + 729.318665, 2.140000, 751.419250, 2.410000, 774.901123, 2.630000, 799.897949, + 2.800000, 826.561157, 2.740000, 855.063293, 2.580000, 885.601257, 2.240000, +]; + +pub const AL_K: [Float; 112] = [ + 298.757050, 3.593750, 302.400421, 3.640000, 306.133759, 3.689375, 309.960449, + 3.740000, 313.884003, 3.789375, 317.908142, 3.840000, 322.036835, 3.894375, + 326.274139, 3.950000, 330.624481, 4.005000, 335.092377, 4.060000, 339.682678, + 4.113750, 344.400482, 4.170000, 349.251221, 4.233750, 354.240509, 4.300000, + 359.374420, 4.365000, 364.659332, 4.430000, 370.102020, 4.493750, 375.709625, + 4.560000, 381.489777, 4.633750, 387.450562, 4.710000, 393.600555, 4.784375, + 399.948975, 4.860000, 406.505493, 4.938125, 413.280579, 5.020000, 420.285339, + 5.108750, 427.531647, 5.200000, 435.032196, 5.290000, 442.800629, 5.380000, + 450.851562, 5.480000, 459.200653, 5.580000, 467.864838, 5.690000, 476.862213, + 5.800000, 486.212463, 5.915000, 495.936707, 6.030000, 506.057861, 6.150000, + 516.600769, 6.280000, 527.592224, 6.420000, 539.061646, 6.550000, 551.040771, + 6.700000, 563.564453, 6.850000, 576.670593, 7.000000, 590.400818, 7.150000, + 604.800842, 7.310000, 619.920898, 7.480000, 635.816284, 7.650000, 652.548279, + 7.820000, 670.184753, 8.010000, 688.800964, 8.210000, 708.481018, 8.390000, + 729.318665, 8.570000, 751.419250, 8.620000, 774.901123, 8.600000, 799.897949, + 8.450000, 826.561157, 8.310000, 855.063293, 8.210000, 885.601257, 8.210000, +]; + +pub const AU_ETA: [Float; 112] = [ + 298.757050, 1.795000, 302.400421, 1.812000, 306.133759, 1.822625, 309.960449, + 1.830000, 313.884003, 1.837125, 317.908142, 1.840000, 322.036835, 1.834250, + 326.274139, 1.824000, 330.624481, 1.812000, 335.092377, 1.798000, 339.682678, + 1.782000, 344.400482, 1.766000, 349.251221, 1.752500, 354.240509, 1.740000, + 359.374420, 1.727625, 364.659332, 1.716000, 370.102020, 1.705875, 375.709625, + 1.696000, 381.489777, 1.684750, 387.450562, 1.674000, 393.600555, 1.666000, + 399.948975, 1.658000, 406.505493, 1.647250, 413.280579, 1.636000, 420.285339, + 1.628000, 427.531647, 1.616000, 435.032196, 1.596250, 442.800629, 1.562000, + 450.851562, 1.502125, 459.200653, 1.426000, 467.864838, 1.345875, 476.862213, + 1.242000, 486.212463, 1.086750, 495.936707, 0.916000, 506.057861, 0.754500, + 516.600769, 0.608000, 527.592224, 0.491750, 539.061646, 0.402000, 551.040771, + 0.345500, 563.564453, 0.306000, 576.670593, 0.267625, 590.400818, 0.236000, + 604.800842, 0.212375, 619.920898, 0.194000, 635.816284, 0.177750, 652.548279, + 0.166000, 670.184753, 0.161000, 688.800964, 0.160000, 708.481018, 0.160875, + 729.318665, 0.164000, 751.419250, 0.169500, 774.901123, 0.176000, 799.897949, + 0.181375, 826.561157, 0.188000, 855.063293, 0.198125, 885.601257, 0.210000, +]; + +pub const AU_K: [Float; 112] = [ + 298.757050, 1.920375, 302.400421, 1.920000, 306.133759, 1.918875, 309.960449, + 1.916000, 313.884003, 1.911375, 317.908142, 1.904000, 322.036835, 1.891375, + 326.274139, 1.878000, 330.624481, 1.868250, 335.092377, 1.860000, 339.682678, + 1.851750, 344.400482, 1.846000, 349.251221, 1.845250, 354.240509, 1.848000, + 359.374420, 1.852375, 364.659332, 1.862000, 370.102020, 1.883000, 375.709625, + 1.906000, 381.489777, 1.922500, 387.450562, 1.936000, 393.600555, 1.947750, + 399.948975, 1.956000, 406.505493, 1.959375, 413.280579, 1.958000, 420.285339, + 1.951375, 427.531647, 1.940000, 435.032196, 1.924500, 442.800629, 1.904000, + 450.851562, 1.875875, 459.200653, 1.846000, 467.864838, 1.814625, 476.862213, + 1.796000, 486.212463, 1.797375, 495.936707, 1.840000, 506.057861, 1.956500, + 516.600769, 2.120000, 527.592224, 2.326250, 539.061646, 2.540000, 551.040771, + 2.730625, 563.564453, 2.880000, 576.670593, 2.940625, 590.400818, 2.970000, + 604.800842, 3.015000, 619.920898, 3.060000, 635.816284, 3.070000, 652.548279, + 3.150000, 670.184753, 3.445812, 688.800964, 3.800000, 708.481018, 4.087687, + 729.318665, 4.357000, 751.419250, 4.610188, 774.901123, 4.860000, 799.897949, + 5.125813, 826.561157, 5.390000, 855.063293, 5.631250, 885.601257, 5.880000, +]; + +pub const CU_ETA: [Float; 112] = [ + 298.757050, 1.400313, 302.400421, 1.380000, 306.133759, 1.358438, 309.960449, + 1.340000, 313.884003, 1.329063, 317.908142, 1.325000, 322.036835, 1.332500, + 326.274139, 1.340000, 330.624481, 1.334375, 335.092377, 1.325000, 339.682678, + 1.317812, 344.400482, 1.310000, 349.251221, 1.300313, 354.240509, 1.290000, + 359.374420, 1.281563, 364.659332, 1.270000, 370.102020, 1.249062, 375.709625, + 1.225000, 381.489777, 1.200000, 387.450562, 1.180000, 393.600555, 1.174375, + 399.948975, 1.175000, 406.505493, 1.177500, 413.280579, 1.180000, 420.285339, + 1.178125, 427.531647, 1.175000, 435.032196, 1.172812, 442.800629, 1.170000, + 450.851562, 1.165312, 459.200653, 1.160000, 467.864838, 1.155312, 476.862213, + 1.150000, 486.212463, 1.142812, 495.936707, 1.135000, 506.057861, 1.131562, + 516.600769, 1.120000, 527.592224, 1.092437, 539.061646, 1.040000, 551.040771, + 0.950375, 563.564453, 0.826000, 576.670593, 0.645875, 590.400818, 0.468000, + 604.800842, 0.351250, 619.920898, 0.272000, 635.816284, 0.230813, 652.548279, + 0.214000, 670.184753, 0.209250, 688.800964, 0.213000, 708.481018, 0.216250, + 729.318665, 0.223000, 751.419250, 0.236500, 774.901123, 0.250000, 799.897949, + 0.254188, 826.561157, 0.260000, 855.063293, 0.280000, 885.601257, 0.300000, +]; + +pub const CU_K: [Float; 112] = [ + 298.757050, 1.662125, 302.400421, 1.687000, 306.133759, 1.703313, 309.960449, + 1.720000, 313.884003, 1.744563, 317.908142, 1.770000, 322.036835, 1.791625, + 326.274139, 1.810000, 330.624481, 1.822125, 335.092377, 1.834000, 339.682678, + 1.851750, 344.400482, 1.872000, 349.251221, 1.894250, 354.240509, 1.916000, + 359.374420, 1.931688, 364.659332, 1.950000, 370.102020, 1.972438, 375.709625, + 2.015000, 381.489777, 2.121562, 387.450562, 2.210000, 393.600555, 2.177188, + 399.948975, 2.130000, 406.505493, 2.160063, 413.280579, 2.210000, 420.285339, + 2.249938, 427.531647, 2.289000, 435.032196, 2.326000, 442.800629, 2.362000, + 450.851562, 2.397625, 459.200653, 2.433000, 467.864838, 2.469187, 476.862213, + 2.504000, 486.212463, 2.535875, 495.936707, 2.564000, 506.057861, 2.589625, + 516.600769, 2.605000, 527.592224, 2.595562, 539.061646, 2.583000, 551.040771, + 2.576500, 563.564453, 2.599000, 576.670593, 2.678062, 590.400818, 2.809000, + 604.800842, 3.010750, 619.920898, 3.240000, 635.816284, 3.458187, 652.548279, + 3.670000, 670.184753, 3.863125, 688.800964, 4.050000, 708.481018, 4.239563, + 729.318665, 4.430000, 751.419250, 4.619563, 774.901123, 4.817000, 799.897949, + 5.034125, 826.561157, 5.260000, 855.063293, 5.485625, 885.601257, 5.717000, +]; + +pub const CUZN_ETA: [Float; 122] = [ + 290., 1.358, 300., 1.388, 310., 1.419, 320., 1.446, 330., 1.473, 340., 1.494, 350., 1.504, + 360., 1.503, 370., 1.497, 380., 1.487, 390., 1.471, 400., 1.445, 410., 1.405, 420., 1.350, + 430., 1.278, 440., 1.191, 450., 1.094, 460., 0.994, 470., 0.900, 480., 0.816, 490., 0.745, + 500., 0.686, 510., 0.639, 520., 0.602, 530., 0.573, 540., 0.549, 550., 0.527, 560., 0.505, + 570., 0.484, 580., 0.468, 590., 0.460, 600., 0.450, 610., 0.452, 620., 0.449, 630., 0.445, + 640., 0.444, 650., 0.444, 660., 0.445, 670., 0.444, 680., 0.444, 690., 0.445, 700., 0.446, + 710., 0.448, 720., 0.450, 730., 0.452, 740., 0.455, 750., 0.457, 760., 0.458, 770., 0.460, + 780., 0.464, 790., 0.469, 800., 0.473, 810., 0.478, 820., 0.481, 830., 0.483, 840., 0.486, + 850., 0.490, 860., 0.494, 870., 0.500, 880., 0.507, 890., 0.515]; + +pub const CUZN_K: [Float; 122] = [ + 290., 1.688, 300., 1.731, 310., 1.764, 320., 1.789, 330., 1.807, 340., 1.815, 350., 1.815, + 360., 1.815, 370., 1.818, 380., 1.818, 390., 1.813, 400., 1.805, 410., 1.794, 420., 1.786, + 430., 1.784, 440., 1.797, 450., 1.829, 460., 1.883, 470., 1.957, 480., 2.046, 490., 2.145, + 500., 2.250, 510., 2.358, 520., 2.464, 530., 2.568, 540., 2.668, 550., 2.765, 560., 2.860, + 570., 2.958, 580., 3.059, 590., 3.159, 600., 3.253, 610., 3.345, 620., 3.434, 630., 3.522, + 640., 3.609, 650., 3.695, 660., 3.778, 670., 3.860, 680., 3.943, 690., 4.025, 700., 4.106, + 710., 4.186, 720., 4.266, 730., 4.346, 740., 4.424, 750., 4.501, 760., 4.579, 770., 4.657, + 780., 4.737, 790., 4.814, 800., 4.890, 810., 4.965, 820., 5.039, 830., 5.115, 840., 5.192, + 850., 5.269, 860., 5.346, 870., 5.423, 880., 5.500, 890., 5.575]; + +pub const MGO_ETA: [Float; 60] = [ + 309.950012, 1.798000, 330.613007, 1.785000, 351.118988, 1.776800, 355.549011, + 1.775500, 360.932007, 1.773200, 361.141998, 1.773180, 364.968994, 1.771860, + 382.065002, 1.766800, 386.712006, 1.765500, 393.337982, 1.763800, 404.634003, + 1.761040, 430.935028, 1.755700, 435.781982, 1.754710, 457.829010, 1.751200, + 477.949036, 1.748300, 486.004974, 1.747110, 487.918030, 1.746900, 499.919006, + 1.745400, 502.350006, 1.745300, 508.531982, 1.744460, 514.440002, 1.743900, + 546.166992, 1.740770, 589.258972, 1.737370, 632.874023, 1.734600, 643.718018, + 1.734000, 656.325989, 1.733350, 667.635986, 1.732770, 690.695984, 1.731910, + 706.439026, 1.731010, 767.677979, 1.728720, +]; + +pub const MGO_K: [Float; 60] = [ + 309.950012, 0.000000, 330.613007, 0.000000, 351.118988, 0.000000, 355.549011, + 0.000001, 360.932007, 0.000001, 361.141998, 0.000001, 364.968994, 0.000000, + 382.065002, 0.000000, 386.712006, 0.000000, 393.337982, 0.000000, 404.634003, + 0.000000, 430.935028, 0.000000, 435.781982, 0.000000, 457.829010, 0.000000, + 477.949036, 0.000000, 486.004974, 0.000000, 487.918030, 0.000000, 499.919006, + 0.000000, 502.350006, 0.000000, 508.531982, 0.000000, 514.440002, 0.000000, + 546.166992, 0.000000, 589.258972, 0.000000, 632.874023, 0.000000, 643.718018, + 0.000000, 656.325989, 0.000000, 667.635986, 0.000000, 690.695984, 0.000000, + 706.439026, 0.000000, 767.677979, 0.000000, +]; + +pub const TIO2_ETA: [Float; 68] = [ + 305.972015, 3.840000, 317.979004, 5.380000, 334.990997, 4.220000, 344.007019, + 4.360000, 359.988007, 3.870000, 388.044006, 3.490000, 399.935028, 3.400000, + 412.031006, 3.240000, 419.985992, 3.290000, 439.957001, 3.200000, 460.037018, + 3.130000, 479.985016, 3.080000, 499.919006, 3.030000, 520.049988, 3.000000, + 539.044006, 2.950000, 539.983032, 2.970000, 559.981995, 2.940000, 579.888000, + 2.920000, 600.097046, 2.900000, 619.900024, 2.880000, 640.062012, 2.870000, + 659.819031, 2.850000, 680.088013, 2.840000, 700.056030, 2.830000, 719.976990, + 2.820000, 740.179016, 2.810000, 760.147034, 2.810000, 779.747986, 2.800000, + 799.871033, 2.790000, 819.974060, 2.790000, 839.973083, 2.780000, 859.778015, + 2.780000, 879.915039, 2.770000, 899.709961, 2.770000, +]; + +pub const TIO2_K: [Float; 68] = [ + 305.972015, 1.950000, 317.979004, 2.180000, 334.990997, 0.788000, 344.007019, + 0.000000, 359.988007, 0.251000, 388.044006, 0.000000, 399.935028, 0.000000, + 412.031006, 0.022000, 419.985992, 0.000000, 439.957001, 0.000000, 460.037018, + 0.000000, 479.985016, 0.000000, 499.919006, 0.000000, 520.049988, 0.000000, + 539.044006, 0.000000, 539.983032, 0.000000, 559.981995, 0.000000, 579.888000, + 0.000000, 600.097046, 0.000000, 619.900024, 0.000000, 640.062012, 0.000000, + 659.819031, 0.000000, 680.088013, 0.000000, 700.056030, 0.000000, 719.976990, + 0.000000, 740.179016, 0.000000, 760.147034, 0.000000, 779.747986, 0.000000, + 799.871033, 0.000000, 819.974060, 0.000000, 839.973083, 0.000000, 859.778015, + 0.000000, 879.915039, 0.000000, 899.709961, 0.000000, +]; + +// https://refractiveindex.info, public domain CC0: +// https://creativecommons.org/publicdomain/zero/1.0/ +// PhysLight code and measurements contributed by Anders Langlands and Luca Fascione +// Copyright (c) 2020, Weta Digital, Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Data for various types of glass (refractive index eta vs. wavelength in nm). +pub const GLASSBK7_ETA: [f32; 58] = [ + 300.0, 1.5527702635739, 322.0, 1.5458699289209, 344.0, 1.5404466868331, + 366.0, 1.536090527917, 388.0, 1.53252773217, 410.0, 1.529568767224, + 432.0, 1.5270784291406, 454.0, 1.5249578457324, 476.0, 1.5231331738499, + 498.0, 1.5215482528369, 520.0, 1.5201596882463, 542.0, 1.5189334783109, + 564.0, 1.5178426478869, 586.0, 1.516865556749, 608.0, 1.5159846691816, + 630.0, 1.5151856452759, 652.0, 1.5144566604975, 674.0, 1.513787889767, + 696.0, 1.5131711117948, 718.0, 1.5125994024544, 740.0, 1.5120668948646, + 762.0, 1.5115685899969, 784.0, 1.5111002059336, 806.0, 1.5106580569705, + 828.0, 1.5102389559626, 850.0, 1.5098401349174, 872.0, 1.5094591800239, + 894.0, 1.5090939781792, 916.0, 1.5087426727363, +]; + +pub const GLASS_BAF10_ETA: [f32; 54] = [ + 350.0, 1.7126880848268, 371.0, 1.7044510025682, 393.0, 1.6978539633931, + 414.0, 1.6924597573902, 436.0, 1.6879747521657, 457.0, 1.6841935148947, + 479.0, 1.6809676313681, 500.0, 1.6781870617363, 522.0, 1.6757684467878, + 543.0, 1.6736474831891, 565.0, 1.6717737892968, 586.0, 1.6701073530462, + 608.0, 1.6686160168249, 629.0, 1.6672736605352, 651.0, 1.6660588657981, + 672.0, 1.6649539185393, 694.0, 1.6639440538738, 715.0, 1.6630168772865, + 737.0, 1.6621619159417, 758.0, 1.6613702672977, 780.0, 1.6606343213443, + 801.0, 1.6599475391478, 823.0, 1.6593042748862, 844.0, 1.6586996317841, + 866.0, 1.6581293446924, 887.0, 1.6575896837763, 909.0, 1.6570773750475, +]; + +pub const GLASS_FK51A_ETA: [f32; 58] = [ + 290.0, 1.5145777204082, 312.0, 1.5092112868865, 334.0, 1.5049961987453, + 356.0, 1.5016153970446, 378.0, 1.4988558885761, 400.0, 1.496569610433, + 422.0, 1.4946506898002, 444.0, 1.4930216011953, 466.0, 1.4916244098644, + 488.0, 1.49041505042, 511.0, 1.4893594837084, 533.0, 1.4884310526027, + 555.0, 1.4876086240083, 577.0, 1.486875258765, 599.0, 1.486217243501, + 621.0, 1.4856233753353, 643.0, 1.4850844262039, 665.0, 1.4845927367446, + 687.0, 1.484141904927, 709.0, 1.483726544853, 732.0, 1.4833420981287, + 754.0, 1.4829846850495, 776.0, 1.482650986233, 798.0, 1.4823381477539, + 820.0, 1.4820437045732, 842.0, 1.4817655183243, 864.0, 1.481501726448, + 886.0, 1.4812507003621, 908.0, 1.4810110108734, +]; + +pub const GLASS_LASF9_ETA: [f32; 52] = [ + 370.0, 1.9199725545705, 391.0, 1.9057858245373, 412.0, 1.8945401582481, + 433.0, 1.8854121949451, 455.0, 1.877863643024, 476.0, 1.8715257028176, + 497.0, 1.8661362648008, 519.0, 1.8615034773283, 540.0, 1.8574834752011, + 561.0, 1.8539661699122, 583.0, 1.8508658556, 604.0, 1.8481148099285, + 625.0, 1.8456588222442, 646.0, 1.8434539988324, 668.0, 1.8414644361915, + 689.0, 1.8396604975285, 710.0, 1.8380175167434, 732.0, 1.8365148106821, + 753.0, 1.8351349171703, 774.0, 1.8338630007484, 796.0, 1.8326863845545, + 817.0, 1.8315941782006, 838.0, 1.8305769794709, 859.0, 1.8296266333424, + 881.0, 1.8287360359155, 902.0, 1.8278989738228, +]; + +pub const GLASS_SF5_ETA: [f32; 52] = [ + 370.0, 1.7286549847245, 391.0, 1.7170151864402, 412.0, 1.7079037179421, + 433.0, 1.7005724270177, 455.0, 1.6945472844297, 476.0, 1.6895110487297, + 497.0, 1.685242265691, 519.0, 1.6815810964, 540.0, 1.678409006027, + 561.0, 1.6756360973958, 583.0, 1.6731928929908, 604.0, 1.6710248234743, + 625.0, 1.6690884260039, 646.0, 1.6673486579281, 668.0, 1.6657769585173, + 689.0, 1.6643498246044, 710.0, 1.6630477468358, 732.0, 1.6618544037398, + 753.0, 1.6607560432197, 774.0, 1.6597410023473, 796.0, 1.6587993305922, + 817.0, 1.6579224913632, 838.0, 1.6571031234995, 859.0, 1.6563348491305, + 881.0, 1.6556121177295, 902.0, 1.654930078671, +]; + +pub const GLASS_SF10_ETA: [f32; 52] = [ + 380.0, 1.7905788948419, 401.0, 1.7776074571692, 422.0, 1.7673620572474, + 443.0, 1.7590649148507, 464.0, 1.7522127524444, 486.0, 1.7464635698826, + 507.0, 1.741575877046, 528.0, 1.7373738218659, 549.0, 1.7337260730259, + 570.0, 1.7305324562829, 592.0, 1.7277151818026, 613.0, 1.7252129043714, + 634.0, 1.7229765939984, 655.0, 1.7209665988467, 676.0, 1.719150514229, + 698.0, 1.7175016091415, 719.0, 1.7159976462946, 740.0, 1.7146199848831, + 761.0, 1.7133528897994, 782.0, 1.7121829937648, 804.0, 1.7110988742233, + 825.0, 1.7100907173852, 846.0, 1.7091500491754, 867.0, 1.7082695180523, + 888.0, 1.7074427184169, 910.0, 1.7066640460471, +]; + +pub const GLASS_SF11_ETA: [f32; 52] = [ + 370.0, 1.8700216173234, 391.0, 1.8516255860581, 412.0, 1.8374707714715, + 433.0, 1.8262323798466, 455.0, 1.8170946940119, 476.0, 1.8095242343848, + 497.0, 1.803155581666, 519.0, 1.7977291183308, 540.0, 1.7930548640505, + 561.0, 1.7889903663666, 583.0, 1.7854266026774, 604.0, 1.7822786683156, + 625.0, 1.7794794394722, 646.0, 1.7769751487395, 668.0, 1.7747222267051, + 689.0, 1.7726850031375, 710.0, 1.770834004936, 732.0, 1.7691446766161, + 753.0, 1.7675964052635, 774.0, 1.7661717683505, 796.0, 1.764855947008, + 817.0, 1.7636362637211, 838.0, 1.7625018146862, 859.0, 1.7614431749629, + 881.0, 1.7604521601554, 902.0, 1.7595216323879, +]; + +// Camera sensor response data. +pub const CANON_EOS_100D_R: [f32; 70] = [ + 380.0, 0.0010345340999999945, 390.0, 0.0009932282000000006, 400.0, 0.0018438642000000011, + 410.0, 0.01028051149999999, 420.0, 0.018888578000000003, 430.0, 0.008353288399999994, + 440.0, 0.004348840900000002, 450.0, 0.004234005399999998, 460.0, 0.005510017599999996, + 470.0, 0.013791387400000006, 480.0, 0.0239978564, 490.0, 0.0337486995, 500.0, + 0.04718967219999999, 510.0, 0.0595009836, 520.0, 0.10054283370000004, 530.0, + 0.09959327540000007, 540.0, 0.12021106619999995, 550.0, 0.16941526599999987, 560.0, + 0.22021424980000007, 570.0, 0.2867606414000003, 580.0, 0.42683793049999974, 590.0, + 0.5070160297000001, 600.0, 0.5076168171999998, 610.0, 0.4854843454000001, 620.0, + 0.44547363129999995, 630.0, 0.35522478430000004, 640.0, 0.29193094230000016, 650.0, + 0.20514967430000022, 660.0, 0.15839285590000007, 670.0, 0.12412693239999992, 680.0, + 0.05972109030000003, 690.0, 0.014374949399999962, 700.0, 0.004008401600000015, 710.0, + 0.002686121, 720.0, 0.0014271277999999932, +]; +pub const CANON_EOS_100D_G: [f32; 70] = [ + 380.0, 0.0013633471000000048, 390.0, 0.0010626994999999998, 400.0, 0.002292138499999992, + 410.0, 0.019800222599999997, 420.0, 0.05279470360000005, 430.0, 0.04825121540000002, + 440.0, 0.07626763310000001, 450.0, 0.0955949721, 460.0, 0.12211854589999992, 470.0, + 0.3272022426999997, 480.0, 0.5387000284000003, 490.0, 0.6429640568000001, 500.0, + 0.7476649657000003, 510.0, 0.9164155277511032, 520.0, 0.9999999999999996, 530.0, + 0.8376770662000002, 540.0, 0.7823045828999999, 550.0, 0.7409162412000005, 560.0, + 0.7176894309, 570.0, 0.5934455464999996, 580.0, 0.501294976, 590.0, 0.3950402135999999, + 600.0, 0.25711312789999996, 610.0, 0.16052706920000007, 620.0, 0.0903173394999999, + 630.0, 0.05790517769999999, 640.0, 0.0412381485, 650.0, 0.026369013899999988, 660.0, + 0.02084280790000001, 670.0, 0.020156538299999978, 680.0, 0.013090085600000015, 690.0, + 0.004278728499999999, 700.0, 0.0016419162000000024, 710.0, 0.0011442523000000004, 720.0, + 0.0014271277999999932, +]; +pub const CANON_EOS_100D_B: [f32; 70] = [ + 380.0, 0.001134256300000011, 390.0, 0.0020822824000000083, 400.0, 0.013593639899999954, + 410.0, 0.17369660189999991, 420.0, 0.4995310214999999, 430.0, 0.5280868818999991, + 440.0, 0.6379977638999998, 450.0, 0.7444977194000005, 460.0, 0.7302740382999995, + 470.0, 0.7793694852999994, 480.0, 0.7501340391999995, 490.0, 0.5985683215999997, + 500.0, 0.4880152758000005, 510.0, 0.29927890899999987, 520.0, 0.26207153530000016, + 530.0, 0.14685510190000012, 540.0, 0.1016356638, 550.0, 0.0739006774, 560.0, + 0.055084158899999985, 570.0, 0.03956757719999993, 580.0, 0.03456439299999998, 590.0, + 0.02937775519999998, 600.0, 0.021473915400000005, 610.0, 0.016084294899999997, + 620.0, 0.012355475700000009, 630.0, 0.010610769999999997, 640.0, 0.0108006713, + 650.0, 0.0100642962, 660.0, 0.009919136899999997, 670.0, 0.009404730099999994, + 680.0, 0.0052184572000000045, 690.0, 0.001470561399999997, 700.0, 0.000523957, + 710.0, 0.00036819440000000075, 720.0, 0.0014271277999999932, +]; +pub const CANON_EOS_1DX_MKII_R: [f32; 70] = [ + 380.0, 0.0018162675000000048, 390.0, 0.0008884440999999994, 400.0, 0.0013519331, + 410.0, 0.006862395200000009, 420.0, 0.018209372700000004, 430.0, 0.010872048300000013, + 440.0, 0.007476543199999999, 450.0, 0.0082082228, 460.0, 0.009936214700000001, + 470.0, 0.019998990099999996, 480.0, 0.03289690019999998, 490.0, 0.0441015478, 500.0, + 0.05871237490000001, 510.0, 0.07086804510000003, 520.0, 0.11657400200000007, 530.0, + 0.11322348230000007, 540.0, 0.1334126516999999, 550.0, 0.1881571049000001, 560.0, + 0.2468919092, 570.0, 0.33262929869999985, 580.0, 0.5061241802000003, 590.0, + 0.6196121287999999, 600.0, 0.5905287510999997, 610.0, 0.5451206534000004, 620.0, + 0.4831930929000001, 630.0, 0.37750533169999995, 640.0, 0.30202893380000034, 650.0, + 0.19926189560000016, 660.0, 0.15022527210000017, 670.0, 0.1035556931, 680.0, + 0.03193851970000001, 690.0, 0.006272841300000002, 700.0, 0.0015903908000000058, 710.0, + 0.001106305900000002, 720.0, 0.0006460381, +]; +pub const CANON_EOS_1DX_MKII_G: [f32; 70] = [ + 380.0, 0.0016423542999999888, 390.0, 0.0009777616000000034, 400.0, 0.0019321217999999953, + 410.0, 0.015581394599999989, 420.0, 0.05820712490000001, 430.0, 0.057873061600000004, + 440.0, 0.09049534039999999, 450.0, 0.11289509579999989, 460.0, 0.13690262500000017, + 470.0, 0.3530679675000002, 480.0, 0.6294995037999993, 490.0, 0.7718022031000004, + 500.0, 0.8540351806000005, 510.0, 0.9830319643588216, 520.0, 0.9999999999999996, + 530.0, 0.8254704659999998, 540.0, 0.7617244276000004, 550.0, 0.7321744183999999, + 560.0, 0.7153673167, 570.0, 0.5877701213000004, 580.0, 0.4967957916999998, 590.0, + 0.38850882940000003, 600.0, 0.2524580149000001, 610.0, 0.1486040925, 620.0, + 0.08003808630000003, 630.0, 0.05004883910000005, 640.0, 0.03406882059999996, 650.0, + 0.020387360800000018, 660.0, 0.015922646800000002, 670.0, 0.013412824599999996, 680.0, + 0.005644151199999996, 690.0, 0.001566163900000003, 700.0, 0.0006628126999999997, + 710.0, 0.0005018839000000004, 720.0, 0.0006460381, +]; +pub const CANON_EOS_1DX_MKII_B: [f32; 70] = [ + 380.0, 0.0010143081000000276, 390.0, 0.0011417151000000098, 400.0, 0.008139566099999988, + 410.0, 0.10084776039999999, 420.0, 0.41232810599999986, 430.0, 0.48415738880000025, + 440.0, 0.6034903110000001, 450.0, 0.7013941569999995, 460.0, 0.7183791576999994, + 470.0, 0.7675886040999993, 480.0, 0.7629507671000009, 490.0, 0.6227254138999995, + 500.0, 0.5162875283000006, 510.0, 0.3290316179999999, 520.0, 0.2924844631000002, + 530.0, 0.16717138729999995, 540.0, 0.11770532209999993, 550.0, 0.08628073910000009, + 560.0, 0.06536573579999998, 570.0, 0.046387820999999975, 580.0, 0.04001332630000001, + 590.0, 0.0339862603, 600.0, 0.024995196400000006, 610.0, 0.018410626600000014, + 620.0, 0.01433101450000001, 630.0, 0.012280307800000004, 640.0, 0.0118934087, + 650.0, 0.010499538500000011, 660.0, 0.010198088599999999, 670.0, 0.008321526600000006, + 680.0, 0.0029917481000000085, 690.0, 0.0006789417000000017, 700.0, + 0.00025969219999999986, 710.0, 0.00019582540000000009, 720.0, 0.0006460381, +]; +pub const CANON_EOS_200D_R: [f32; 70] = [ + 380.0, 0.0029144141000000054, 390.0, 0.0035351966999999963, 400.0, 0.004550874099999999, + 410.0, 0.008062167499999998, 420.0, 0.012032691599999989, 430.0, 0.006558491, 440.0, + 0.004645926299999999, 450.0, 0.0054320686999999954, 460.0, 0.007664068699999996, + 470.0, 0.020832802399999996, 480.0, 0.038610929999999995, 490.0, 0.05467887510000001, + 500.0, 0.07355465870000003, 510.0, 0.08502357180000006, 520.0, 0.1392552936999999, + 530.0, 0.13355995020000008, 540.0, 0.15848134239999992, 550.0, 0.2119647969000001, + 560.0, 0.26662087939999984, 570.0, 0.3358138150999998, 580.0, 0.49721910860000024, + 590.0, 0.6029139215999996, 600.0, 0.5779837966999999, 610.0, 0.5339095878999993, + 620.0, 0.47174230130000044, 630.0, 0.3895140636, 640.0, 0.3256025399000001, 650.0, + 0.2247744232, 660.0, 0.1744590300000001, 670.0, 0.13294683720000014, 680.0, + 0.06903805069999998, 690.0, 0.015406350500000006, 700.0, 0.002479229899999994, 710.0, + 0.0017020056999999989, 720.0, 0.0009626299999999977, +]; +pub const CANON_EOS_200D_G: [f32; 70] = [ + 380.0, 0.0026624173999999917, 390.0, 0.0033443402000000013, 400.0, 0.006713123999999997, + 410.0, 0.022357985400000002, 420.0, 0.06043390070000006, 430.0, 0.0624431314, 440.0, + 0.09579255519999996, 450.0, 0.1206337524, 460.0, 0.14741111479999985, 470.0, + 0.3836219937000001, 480.0, 0.6535198087, 490.0, 0.7807410739000001, 500.0, + 0.8633800176000002, 510.0, 0.9705873672722316, 520.0, 1.0000000000000002, 530.0, + 0.8037266589999993, 540.0, 0.7449984985, 550.0, 0.7043569532, 560.0, 0.6821959164999997, + 570.0, 0.5614079233, 580.0, 0.47760447919999993, 590.0, 0.37920629600000005, 600.0, + 0.25203830239999997, 610.0, 0.15438021639999985, 620.0, 0.08788603450000002, 630.0, + 0.059415403399999976, 640.0, 0.04278553099999999, 650.0, 0.026760060900000028, 660.0, + 0.02201204859999998, 670.0, 0.02007961709999999, 680.0, 0.013885512900000008, 690.0, + 0.004068378600000006, 700.0, 0.0010167356000000036, 710.0, 0.0007390948000000004, 720.0, + 0.0009626299999999977, +]; +pub const CANON_EOS_200D_B: [f32; 70] = [ + 380.0, 0.005104666699999927, 390.0, 0.010771630799999994, 400.0, 0.03421659689999998, + 410.0, 0.13957153410000006, 420.0, 0.4169403182999999, 430.0, 0.5016156269000002, + 440.0, 0.5999764150999992, 450.0, 0.6902410122000003, 460.0, 0.6788661408000004, + 470.0, 0.7306828741000005, 480.0, 0.7099062746000008, 490.0, 0.5684429900000001, + 500.0, 0.4735602649999996, 510.0, 0.2957683109, 520.0, 0.2706484353999999, 530.0, + 0.1568601126000002, 540.0, 0.11619838960000001, 550.0, 0.09137537509999998, 560.0, + 0.07659973400000002, 570.0, 0.05981801589999993, 580.0, 0.05412082330000002, 590.0, + 0.047411972399999995, 600.0, 0.035406750900000006, 610.0, 0.0259375137, 620.0, + 0.01907139970000002, 630.0, 0.015858930999999986, 640.0, 0.014623586400000005, 650.0, + 0.012079224900000013, 660.0, 0.012531416399999998, 670.0, 0.011146442500000013, 680.0, + 0.006817560400000003, 690.0, 0.0016706942000000056, 700.0, 0.0003925581999999993, + 710.0, 0.00028437839999999976, 720.0, 0.0009626299999999977, +]; +pub const CANON_EOS_200D_MKII_R: [f32; 70] = [ + 380.0, 0.0010695662999999998, 390.0, 0.0010215157000000009, 400.0, 0.001800239800000001, + 410.0, 0.0051613802000000045, 420.0, 0.011597886699999999, 430.0, 0.005926944500000012, + 440.0, 0.004192133300000005, 450.0, 0.006086774399999995, 460.0, 0.008109192199999993, + 470.0, 0.020421660200000014, 480.0, 0.038730159400000005, 490.0, 0.055534453500000004, + 500.0, 0.07375381929999997, 510.0, 0.08544021930000009, 520.0, 0.13649820419999986, + 530.0, 0.13199794620000008, 540.0, 0.1574567821999999, 550.0, 0.2130652273999999, + 560.0, 0.2650621559000003, 570.0, 0.33511095299999966, 580.0, 0.49005720020000004, + 590.0, 0.5910423623000007, 600.0, 0.5664662960000003, 610.0, 0.5197442686999995, + 620.0, 0.4635490815000002, 630.0, 0.3689559083000002, 640.0, 0.3028632940000001, + 650.0, 0.20805997909999985, 660.0, 0.15794814150000017, 670.0, 0.11662015919999993, + 680.0, 0.04276142100000007, 690.0, 0.007449820500000029, 700.0, 0.0014657060000000067, + 710.0, 0.0010457857000000018, 720.0, 0.0006469792000000007, +]; +pub const CANON_EOS_200D_MKII_G: [f32; 70] = [ + 380.0, 0.0014539171999999878, 390.0, 0.001284022600000002, 400.0, 0.002941593299999994, + 410.0, 0.016774929899999978, 420.0, 0.0624618035, 430.0, 0.06511788900000004, 440.0, + 0.10110250370000004, 450.0, 0.12884613270000006, 460.0, 0.15989511789999977, 470.0, + 0.39647951929999986, 480.0, 0.6700279846, 490.0, 0.7986252647000002, 500.0, + 0.8715246625999998, 510.0, 0.9909546398809526, 520.0, 1.0, 530.0, 0.8072598946000004, + 540.0, 0.7470272242999992, 550.0, 0.7075300041000003, 560.0, 0.6775658558999996, + 570.0, 0.5623901828999998, 580.0, 0.47538353479999973, 590.0, 0.37786193000000023, + 600.0, 0.2490571137999999, 610.0, 0.15021893720000018, 620.0, 0.08689942939999998, + 630.0, 0.057137089500000016, 640.0, 0.04102159640000002, 650.0, 0.02551874050000001, + 660.0, 0.020873463900000014, 670.0, 0.01848571219999997, 680.0, 0.008851710700000008, + 690.0, 0.002129146599999997, 700.0, 0.0006863774999999984, 710.0, + 0.0005271906999999993, 720.0, 0.0006469792000000007, +]; +pub const CANON_EOS_200D_MKII_B: [f32; 70] = [ + 380.0, 0.0013501343999998139, 390.0, 0.0025226698999999894, 400.0, 0.013244975099999999, + 410.0, 0.10363723109999998, 420.0, 0.42571394960000014, 430.0, 0.5143865713000002, + 440.0, 0.6175732350999998, 450.0, 0.7100558570000005, 460.0, 0.7005341164999996, + 470.0, 0.7369353069999997, 480.0, 0.7211271347999998, 490.0, 0.5790573939000005, + 500.0, 0.47622906369999984, 510.0, 0.3026379523999999, 520.0, 0.27179261099999985, + 530.0, 0.15848085260000017, 540.0, 0.11638222859999989, 550.0, 0.09209505069999999, + 560.0, 0.07655394370000002, 570.0, 0.06091821180000004, 580.0, 0.055010111500000035, + 590.0, 0.04810872039999993, 600.0, 0.035926185799999996, 610.0, 0.02589726489999997, + 620.0, 0.02015258769999999, 630.0, 0.016046941800000006, 640.0, 0.014642890500000005, + 650.0, 0.011808713600000005, 660.0, 0.011757864999999994, 670.0, 0.01023048620000001, + 680.0, 0.004280065599999999, 690.0, 0.0008866921000000009, 700.0, + 0.0002705364000000005, 710.0, 0.0002062298999999998, 720.0, 0.0006469792000000007, +]; +pub const CANON_EOS_5D_R: [f32; 70] = [ + 380.0, 0.000785812000000002, 390.0, 0.0005182939999999997, 400.0, 0.0006975715999999995, + 410.0, 0.0006513088000000003, 420.0, 0.0012461199000000003, 430.0, 0.002361645199999999, + 440.0, 0.0011866939000000011, 450.0, 0.0009497114999999997, 460.0, 0.0010685680999999992, + 470.0, 0.0021031985999999986, 480.0, 0.0034310275000000007, 490.0, 0.005432385600000006, + 500.0, 0.00965392960000001, 510.0, 0.020180361100000013, 520.0, 0.06866548690000004, + 530.0, 0.10989503230000001, 540.0, 0.12993444770000012, 550.0, 0.10719849579999999, + 560.0, 0.1362709308000002, 570.0, 0.2424716482000001, 580.0, 0.3948191508, 590.0, + 0.48862970559999974, 600.0, 0.49795182830000007, 610.0, 0.4651712795999998, 620.0, + 0.4354870137999999, 630.0, 0.3574070533999998, 640.0, 0.29319137170000026, 650.0, + 0.19780686799999989, 660.0, 0.1463846321, 670.0, 0.11928431920000006, 680.0, + 0.0830921802, 690.0, 0.03679061709999996, 700.0, 0.010572395500000017, 710.0, + 0.007170058000000014, 720.0, 0.0039319256000000065, +]; +pub const CANON_EOS_5D_G: [f32; 70] = [ + 380.0, 0.0013068287999999997, 390.0, 0.000843798899999999, 400.0, 0.0018686632000000014, + 410.0, 0.002509625000000005, 420.0, 0.013869044900000014, 430.0, 0.04941043719999999, + 440.0, 0.07436638400000004, 450.0, 0.10807988309999988, 460.0, 0.1221226557, 470.0, + 0.2056079751, 480.0, 0.33529865130000014, 490.0, 0.4818002408000001, 500.0, + 0.6559223162000003, 510.0, 0.8826598056443744, 520.0, 1.0000000000000004, 530.0, + 0.8407994943999996, 540.0, 0.8104228931999997, 550.0, 0.7542687411000001, 560.0, + 0.7138494956000003, 570.0, 0.5702101856999995, 580.0, 0.44380900610000035, 590.0, + 0.29866680099999976, 600.0, 0.1787086678000001, 610.0, 0.09399271069999988, 620.0, + 0.04833385910000001, 630.0, 0.03147516569999997, 640.0, 0.022428237800000007, 650.0, + 0.0138810984, 660.0, 0.0114333411, 670.0, 0.012225814800000009, 680.0, + 0.01265468800000001, 690.0, 0.007709341299999995, 700.0, 0.0030865527999999996, 710.0, + 0.002140626899999997, 720.0, 0.0039319256000000065, +]; +pub const CANON_EOS_5D_B: [f32; 70] = [ + 380.0, 0.000758327000000017, 390.0, 0.0012576842999999938, 400.0, 0.010820649800000009, + 410.0, 0.019205195700000028, 420.0, 0.1270513262000001, 430.0, 0.5104267015999999, + 440.0, 0.663137615, 450.0, 0.7523775822000004, 460.0, 0.7700025961, 470.0, + 0.8340607439000001, 480.0, 0.8034507821000004, 490.0, 0.6184450975000001, 500.0, + 0.4900639065999999, 510.0, 0.27514909339999993, 520.0, 0.19960778159999987, 530.0, + 0.08455624810000015, 540.0, 0.04555814639999995, 550.0, 0.025387996400000022, 560.0, + 0.01422116559999999, 570.0, 0.007110021499999999, 580.0, 0.004975033999999996, 590.0, + 0.003437605000000003, 600.0, 0.002285168500000001, 610.0, 0.0017485192999999995, + 620.0, 0.001543357100000001, 630.0, 0.0014574705999999982, 640.0, 0.001605848200000001, + 650.0, 0.0019172778, 660.0, 0.0022209515999999977, 670.0, 0.0022082942000000005, + 680.0, 0.0017961151000000022, 690.0, 0.0009077389000000003, 700.0, + 0.00036932919999999926, 710.0, 0.0002659898999999999, 720.0, 0.0039319256000000065, +]; +pub const CANON_EOS_5D_MKII_R: [f32; 70] = [ + 380.0, 0.0008013842000000014, 390.0, 0.0006220757000000005, 400.0, 0.0016371479999999984, + 410.0, 0.0038748425999999983, 420.0, 0.008535767299999993, 430.0, 0.0061490956999999975, + 440.0, 0.0028872171999999993, 450.0, 0.0024148705999999984, 460.0, 0.002250863899999999, + 470.0, 0.004003229599999999, 480.0, 0.006830199799999999, 490.0, 0.009292882900000006, + 500.0, 0.01529337690000001, 510.0, 0.030524191099999987, 520.0, 0.09164994830000002, + 530.0, 0.13369090810000003, 540.0, 0.15096144730000005, 550.0, 0.13078283799999996, + 560.0, 0.16281881260000008, 570.0, 0.2568271304000001, 580.0, 0.38491230309999985, + 590.0, 0.4693487229, 600.0, 0.4692466342, 610.0, 0.4421441869, 620.0, + 0.3902777211000001, 630.0, 0.3173283070000002, 640.0, 0.26238596349999993, 650.0, + 0.17072365559999972, 660.0, 0.12280618299999993, 670.0, 0.0916867133000001, 680.0, + 0.05176563700000003, 690.0, 0.014707491100000027, 700.0, 0.0025296981999999857, 710.0, + 0.001690125899999998, 720.0, 0.0008906563, +]; +pub const CANON_EOS_5D_MKII_G: [f32; 70] = [ + 380.0, 0.0013209309999999835, 390.0, 0.0009869984000000043, 400.0, 0.0029762197000000033, + 410.0, 0.010172084700000004, 420.0, 0.03229437389999998, 430.0, 0.03903485090000001, + 440.0, 0.059805254400000014, 450.0, 0.07509233289999999, 460.0, 0.0912708802, 470.0, + 0.2487049523000001, 480.0, 0.4731934541000001, 490.0, 0.6032505440000001, 500.0, + 0.7303841968, 510.0, 0.928899999922136, 520.0, 0.9999999999999998, 530.0, + 0.8266072352000001, 540.0, 0.7706404756999998, 550.0, 0.7397716484000009, 560.0, + 0.7017391187999993, 570.0, 0.5729864942999998, 580.0, 0.4713093699000003, 590.0, + 0.35579091480000014, 600.0, 0.22708816140000013, 610.0, 0.12671256160000005, 620.0, + 0.060936668300000045, 630.0, 0.03582290699999997, 640.0, 0.022925379200000016, 650.0, + 0.012272660100000006, 660.0, 0.008882117299999989, 670.0, 0.008632280499999995, 680.0, + 0.006840578700000002, 690.0, 0.0027319055000000017, 700.0, 0.000806290999999999, + 710.0, 0.0005819094000000005, 720.0, 0.0008906563, +]; +pub const CANON_EOS_5D_MKII_B: [f32; 70] = [ + 380.0, 0.0010193366000000315, 390.0, 0.0019387474000000099, 400.0, 0.018413752700000015, + 410.0, 0.08808723720000004, 420.0, 0.3244567769999999, 430.0, 0.47336964679999993, + 440.0, 0.5952254136000005, 450.0, 0.7196279429999999, 460.0, 0.7247913875000006, + 470.0, 0.7656512659999998, 480.0, 0.7369036376000002, 490.0, 0.5623063473000004, + 500.0, 0.44160557339999995, 510.0, 0.25679258770000013, 520.0, 0.19639732809999982, + 530.0, 0.0897441871999999, 540.0, 0.05114181200000002, 550.0, 0.032225609099999976, + 560.0, 0.02106148029999999, 570.0, 0.01396219780000001, 580.0, 0.011410251200000002, + 590.0, 0.009473836199999986, 600.0, 0.007117135100000007, 610.0, 0.0046852384000000075, + 620.0, 0.003832668399999997, 630.0, 0.0032847416000000027, 640.0, 0.0035606685, + 650.0, 0.0026451064999999984, 660.0, 0.0025273598000000006, 670.0, + 0.0022779309000000012, 680.0, 0.001456518800000001, 690.0, 0.0004733991000000002, + 700.0, 0.0001661568999999997, 710.0, 0.00012751880000000013, 720.0, 0.0008906563, +]; +pub const CANON_EOS_5D_MKIII_R: [f32; 70] = [ + 380.0, 0.0011192165000000042, 390.0, 0.0006920671999999998, 400.0, 0.0016035032000000012, + 410.0, 0.004904116699999995, 420.0, 0.012417517800000001, 430.0, 0.0070674676, 440.0, + 0.003998602500000005, 450.0, 0.0029984458999999975, 460.0, 0.004558581400000003, + 470.0, 0.009029844599999998, 480.0, 0.01624787790000001, 490.0, 0.023378425999999983, + 500.0, 0.033973246700000015, 510.0, 0.04466546800000003, 520.0, 0.07737299579999998, + 530.0, 0.07511662659999996, 540.0, 0.09407468930000003, 550.0, 0.15072825789999997, + 560.0, 0.20702101610000007, 570.0, 0.2803427966999999, 580.0, 0.4365889557999997, + 590.0, 0.5173256208000001, 600.0, 0.5037044972999996, 610.0, 0.46184244280000003, + 620.0, 0.4190302513000004, 630.0, 0.32709231359999985, 640.0, 0.27481037689999954, + 650.0, 0.1959336332000001, 660.0, 0.14422431429999996, 670.0, 0.10282783189999987, + 680.0, 0.03735915659999999, 690.0, 0.008266283099999982, 700.0, 0.0018595146000000033, + 710.0, 0.0012591913000000003, 720.0, 0.0006878099999999997, +]; +pub const CANON_EOS_5D_MKIII_G: [f32; 70] = [ + 380.0, 0.001390841299999994, 390.0, 0.0008856476999999944, 400.0, 0.0019629780999999963, + 410.0, 0.008665688100000011, 420.0, 0.034662635299999994, 430.0, 0.03869156479999999, + 440.0, 0.06407182950000001, 450.0, 0.07711251990000002, 460.0, 0.1065891705, 470.0, + 0.29371702879999995, 480.0, 0.4914516651000001, 490.0, 0.6230910244999996, 500.0, + 0.7358019473, 510.0, 0.9225202689681099, 520.0, 0.9999999999999996, 530.0, + 0.8386261455000004, 540.0, 0.7873568719999999, 550.0, 0.7589399586, 560.0, + 0.7295377358000001, 570.0, 0.6013905526000003, 580.0, 0.5096794096999997, 590.0, + 0.3829056799000004, 600.0, 0.2475879427000002, 610.0, 0.14404165080000006, 620.0, + 0.07774218009999997, 630.0, 0.04894306939999999, 640.0, 0.03406563199999997, 650.0, + 0.021695099300000013, 660.0, 0.016572695699999994, 670.0, 0.014469584199999993, 680.0, + 0.0072150124, 690.0, 0.0022349690000000016, 700.0, 0.0008053623000000012, 710.0, + 0.0005887383000000002, 720.0, 0.0006878099999999997, +]; +pub const CANON_EOS_5D_MKIII_B: [f32; 70] = [ + 380.0, 0.0008019817999997403, 390.0, 0.00119836559999998, 400.0, 0.011613193399999984, + 410.0, 0.08003152100000005, 420.0, 0.36004589929999975, 430.0, 0.45821146420000003, + 440.0, 0.5638182208000003, 450.0, 0.6360781944, 460.0, 0.6465061665999996, 470.0, + 0.7027168592000002, 480.0, 0.6874472428000001, 490.0, 0.5788318148000007, 500.0, + 0.47492120750000005, 510.0, 0.29677079480000046, 520.0, 0.25750069519999985, 530.0, + 0.14089243890000003, 540.0, 0.09631467840000005, 550.0, 0.06973632990000002, 560.0, + 0.052008523100000044, 570.0, 0.037057567400000005, 580.0, 0.03213935529999997, 590.0, + 0.026613694900000012, 600.0, 0.019260524900000006, 610.0, 0.013890667899999994, + 620.0, 0.010354304899999995, 630.0, 0.008789459200000004, 640.0, 0.008488877600000007, + 650.0, 0.00832101159999999, 660.0, 0.008119739800000003, 670.0, 0.0067197686, 680.0, + 0.0028461444, 690.0, 0.0007309546000000002, 700.0, 0.00025481839999999984, 710.0, + 0.0001891476999999999, 720.0, 0.0006878099999999997, +]; +pub const CANON_EOS_5D_MKIV_R: [f32; 70] = [ + 380.0, 0.0014688574000000058, 390.0, 0.0006301996000000001, 400.0, 0.0005446494000000005, + 410.0, 0.0010474998999999968, 420.0, 0.007271954, 430.0, 0.008096808999999995, 440.0, + 0.005106568000000002, 450.0, 0.005919858199999997, 460.0, 0.008213295200000009, 470.0, + 0.02053213009999999, 480.0, 0.036392893600000004, 490.0, 0.04982201929999999, 500.0, + 0.06656237800000002, 510.0, 0.07778684230000005, 520.0, 0.12530491979999997, 530.0, + 0.11925549379999997, 540.0, 0.14044406019999997, 550.0, 0.1873441797, 560.0, + 0.24083037130000007, 570.0, 0.31384385, 580.0, 0.46505768259999986, 590.0, + 0.5565665276000004, 600.0, 0.5242783449000006, 610.0, 0.4816273192000001, 620.0, + 0.43049844099999995, 630.0, 0.34070986280000015, 640.0, 0.2764424227000001, 650.0, + 0.18549533799999993, 660.0, 0.13730679729999992, 670.0, 0.1058646462, 680.0, + 0.054022329, 690.0, 0.01325567779999999, 700.0, 0.0022729908000000045, 710.0, + 0.0015381574999999994, 720.0, 0.000838734099999999, +]; +pub const CANON_EOS_5D_MKIV_G: [f32; 70] = [ + 380.0, 0.0015237515000000312, 390.0, 0.0008358673999999998, 400.0, 0.0009421490000000002, + 410.0, 0.002435697700000003, 420.0, 0.033437543700000016, 430.0, 0.05929670160000006, + 440.0, 0.09450449019999997, 450.0, 0.1206681742, 460.0, 0.14639355869999987, 470.0, + 0.3766925461999998, 480.0, 0.6469848939000004, 490.0, 0.7731781067999999, 500.0, + 0.8558793734000002, 510.0, 0.9817116721884126, 520.0, 1.0000000000000002, 530.0, + 0.8142073200999993, 540.0, 0.7620984310999995, 550.0, 0.7142942967999997, 560.0, + 0.7009507217999997, 570.0, 0.5802500706000004, 580.0, 0.4941466722000002, 590.0, + 0.38636242969999995, 600.0, 0.2540146201999999, 610.0, 0.15332855060000009, 620.0, + 0.08758352909999992, 630.0, 0.057000320300000004, 640.0, 0.04034598770000004, 650.0, + 0.024884156400000006, 660.0, 0.01933671940000002, 670.0, 0.017802973500000006, 680.0, + 0.011886800400000003, 690.0, 0.0038683798999999963, 700.0, 0.000988854300000001, + 710.0, 0.0007121416000000001, 720.0, 0.000838734099999999, +]; +pub const CANON_EOS_5D_MKIV_B: [f32; 70] = [ + 380.0, 0.0008804458000000182, 390.0, 0.0006634113000000275, 400.0, 0.0022192932999999908, + 410.0, 0.014533080000000004, 420.0, 0.25912978270000003, 430.0, 0.5178014988999996, + 440.0, 0.6557940478000001, 450.0, 0.7682036950000002, 460.0, 0.7760495663000003, + 470.0, 0.8435406580999997, 480.0, 0.83436367, 490.0, 0.6703457991000003, 500.0, + 0.5604097880000006, 510.0, 0.3604426923999998, 520.0, 0.32979202660000007, 530.0, + 0.19310980120000001, 540.0, 0.14285532849999988, 550.0, 0.10938423930000014, 560.0, + 0.08927533429999997, 570.0, 0.06821238130000003, 580.0, 0.060708495199999976, 590.0, + 0.05189676310000004, 600.0, 0.0380560985, 610.0, 0.027820299800000024, 620.0, + 0.02119128640000002, 630.0, 0.017588021799999992, 640.0, 0.016320176400000014, 650.0, + 0.013006367199999985, 660.0, 0.012899307000000007, 670.0, 0.011723625199999997, 680.0, + 0.0067560379, 690.0, 0.0018653496999999936, 700.0, 0.0004341641000000008, 710.0, + 0.0003125274000000002, 720.0, 0.000838734099999999, +]; +pub const CANON_EOS_5DS_R: [f32; 70] = [ + 380.0, 0.003463662200000007, 390.0, 0.0012200567000000002, 400.0, 0.0008932422000000013, + 410.0, 0.0022058321999999975, 420.0, 0.007313711000000001, 430.0, 0.007224858000000004, + 440.0, 0.004142034400000001, 450.0, 0.004858831599999999, 460.0, 0.005958903600000003, + 470.0, 0.013928858600000005, 480.0, 0.02405038280000001, 490.0, 0.033728648299999955, + 500.0, 0.0462233718, 510.0, 0.056235266699999994, 520.0, 0.09274286100000007, 530.0, + 0.09019910450000004, 540.0, 0.10831501819999997, 550.0, 0.15283790730000005, 560.0, + 0.19736609519999992, 570.0, 0.26547280630000003, 580.0, 0.41026081539999976, 590.0, + 0.4752684552000002, 600.0, 0.4712417537999999, 610.0, 0.45950818080000033, 620.0, + 0.4168057398999999, 630.0, 0.32184036540000005, 640.0, 0.2586606401000001, 650.0, + 0.17538530579999986, 660.0, 0.13210742690000005, 670.0, 0.1025329793, 680.0, + 0.07265915549999993, 690.0, 0.025055912400000022, 700.0, 0.0040843608999999795, + 710.0, 0.002736315999999998, 720.0, 0.0014527802000000008, +]; +pub const CANON_EOS_5DS_G: [f32; 70] = [ + 380.0, 0.0024321118000000252, 390.0, 0.0011603053000000026, 400.0, 0.0014586812999999974, + 410.0, 0.005848239100000001, 420.0, 0.03375624640000003, 430.0, 0.05607674559999999, + 440.0, 0.0846017859, 450.0, 0.10770675599999996, 460.0, 0.1295210299, 470.0, + 0.35053868720000014, 480.0, 0.6241532651999997, 490.0, 0.7605110289000006, 500.0, + 0.8521902799999994, 510.0, 0.9695581266742302, 520.0, 1.0, 530.0, 0.8123828448999998, + 540.0, 0.7478506691999995, 550.0, 0.6964735281999996, 560.0, 0.6602772088999995, + 570.0, 0.5476419584, 580.0, 0.4649282929999997, 590.0, 0.35218355830000003, 600.0, + 0.22919496240000028, 610.0, 0.134097272, 620.0, 0.072415419, 630.0, + 0.04446532040000002, 640.0, 0.030194229400000015, 650.0, 0.0184560844, 660.0, + 0.014077427799999999, 670.0, 0.013573356400000004, 680.0, 0.013243815900000004, + 690.0, 0.006086989600000011, 700.0, 0.0014700930999999968, 710.0, + 0.0010293930999999996, 720.0, 0.0014527802000000008, +]; +pub const CANON_EOS_5DS_B: [f32; 70] = [ + 380.0, 0.0016633145999999654, 390.0, 0.001340098300000047, 400.0, 0.005383047400000007, + 410.0, 0.03784608160000001, 420.0, 0.2533419871999999, 430.0, 0.4900018845000002, + 440.0, 0.5810562696999996, 450.0, 0.6818367728000004, 460.0, 0.6765126459999999, + 470.0, 0.7165547088000004, 480.0, 0.6946298675000002, 490.0, 0.5484965628999999, + 500.0, 0.4497761827000004, 510.0, 0.2774052288000001, 520.0, 0.24014288610000006, + 530.0, 0.13318344770000012, 540.0, 0.09171393889999992, 550.0, 0.06569165239999998, + 560.0, 0.04860511640000002, 570.0, 0.035080472899999986, 580.0, 0.030499927200000027, + 590.0, 0.024808697100000002, 600.0, 0.017542954699999985, 610.0, 0.013419948300000003, + 620.0, 0.010319124299999994, 630.0, 0.007946026699999997, 640.0, 0.007422380400000004, + 650.0, 0.0066650459, 660.0, 0.006970440800000003, 670.0, 0.006664576900000007, 680.0, + 0.0057440859, 690.0, 0.0020653104000000013, 700.0, 0.0004425481000000011, 710.0, + 0.0003125414999999997, 720.0, 0.0014527802000000008, +]; +pub const CANON_EOS_M_R: [f32; 70] = [ + 380.0, 0.0017825097, 390.0, 0.0019753785999999988, 400.0, 0.004100101800000001, 410.0, + 0.008929812200000003, 420.0, 0.0145668084, 430.0, 0.007237694500000004, 440.0, + 0.003855200299999999, 450.0, 0.003886126599999995, 460.0, 0.004877787500000003, 470.0, + 0.011330336200000004, 480.0, 0.0196484051, 490.0, 0.027954188100000003, 500.0, + 0.04080796410000004, 510.0, 0.05042612749999999, 520.0, 0.08507520439999997, 530.0, + 0.0873563161000001, 540.0, 0.10640797910000002, 550.0, 0.1529411672, 560.0, + 0.2061841102999999, 570.0, 0.2790120689999999, 580.0, 0.4182408662999998, 590.0, + 0.4915893227, 600.0, 0.5000762640000002, 610.0, 0.49220253150000004, 620.0, + 0.4494744404000001, 630.0, 0.35879119520000013, 640.0, 0.3068667583999999, 650.0, + 0.2164303464000002, 660.0, 0.17272936760000004, 670.0, 0.13002819819999983, 680.0, + 0.06658933139999995, 690.0, 0.018666941599999987, 700.0, 0.003398739999999987, 710.0, + 0.002281207499999991, 720.0, 0.0012172206999999993, +]; +pub const CANON_EOS_M_G: [f32; 70] = [ + 380.0, 0.0017905516999999899, 390.0, 0.0017254255999999977, 400.0, 0.004472560000000003, + 410.0, 0.016386769200000033, 420.0, 0.045406872400000034, 430.0, 0.044619340300000004, + 440.0, 0.07018168919999992, 450.0, 0.08534570750000006, 460.0, 0.11183599910000007, + 470.0, 0.3083845786000001, 480.0, 0.5215927763000003, 490.0, 0.6369673738000006, + 500.0, 0.7744293836000002, 510.0, 0.9221216033454448, 520.0, 1.0000000000000004, + 530.0, 0.8411628455000009, 540.0, 0.7957783668999994, 550.0, 0.7260830566999998, + 560.0, 0.7230071004, 570.0, 0.6024775946000005, 580.0, 0.48953871380000014, 590.0, + 0.38181002219999977, 600.0, 0.25021458290000004, 610.0, 0.15210244230000003, 620.0, + 0.08422454469999992, 630.0, 0.052733458000000025, 640.0, 0.0380428832, 650.0, + 0.023444924900000004, 660.0, 0.019265180600000015, 670.0, 0.0179976151, 680.0, + 0.012667875199999986, 690.0, 0.004758467300000005, 700.0, 0.001297091799999998, + 710.0, 0.0009183253999999997, 720.0, 0.0012172206999999993, +]; +pub const CANON_EOS_M_B: [f32; 70] = [ + 380.0, 0.003102678299999717, 390.0, 0.006441905300000019, 400.0, 0.03350419549999997, + 410.0, 0.16056593750000026, 420.0, 0.4896949894999998, 430.0, 0.5552455991000006, + 440.0, 0.6625328295000008, 450.0, 0.7363917947000004, 460.0, 0.7343582241, 470.0, + 0.7722620448999996, 480.0, 0.7437871021000002, 490.0, 0.5961711094000002, 500.0, + 0.49513361940000034, 510.0, 0.29488019200000026, 520.0, 0.24732640349999988, 530.0, + 0.1379380441, 540.0, 0.09396642249999991, 550.0, 0.0663019286, 560.0, + 0.04943840660000002, 570.0, 0.03482119539999994, 580.0, 0.029069638799999974, 590.0, + 0.02420314450000001, 600.0, 0.017946503100000004, 610.0, 0.012455188600000007, + 620.0, 0.010445353599999999, 630.0, 0.009139727400000004, 640.0, 0.009216426200000002, + 650.0, 0.008432175, 660.0, 0.009083277800000001, 670.0, 0.008378730100000007, 680.0, + 0.0049738282999999975, 690.0, 0.0015761330000000008, 700.0, 0.00040506620000000054, + 710.0, 0.00029041780000000006, 720.0, 0.0012172206999999993, +]; +pub const HASSELBLAD_L1D_20C_R: [f32; 66] = [ + 380.0, 0.0005160850000000064, 390.0, 0.0004741290000000015, 400.0, 0.0013542525000000006, + 410.0, 0.002651428400000001, 420.0, 0.013228016999999991, 430.0, 0.021220543000000005, + 440.0, 0.01648968549999997, 450.0, 0.014766158400000007, 460.0, 0.01302493170000001, + 470.0, 0.018813151700000003, 480.0, 0.024834979800000007, 490.0, 0.024594191899999996, + 500.0, 0.027434059500000017, 510.0, 0.03159046840000003, 520.0, 0.0642708291, 530.0, + 0.06021589720000005, 540.0, 0.043860151699999954, 550.0, 0.028834285799999994, 560.0, + 0.028281900499999988, 570.0, 0.06338374090000007, 580.0, 0.24476407250000007, 590.0, + 0.5267533751999997, 600.0, 0.5122778399999998, 610.0, 0.45361160949999957, 620.0, + 0.3753260934000002, 630.0, 0.2802679135999999, 640.0, 0.21753109990000008, 650.0, + 0.14140760759999987, 660.0, 0.09254374290000016, 670.0, 0.06618278569999998, 680.0, + 0.004327748399999991, 710.0, 0.0028479460000000022, 720.0, 0.0014381367999999999, +]; +pub const HASSELBLAD_L1D_20C_G: [f32; 70] = [ + 380.0, 0.0012616548000000116, 390.0, 0.000869998999999998, 400.0, 0.0016204561000000004, + 410.0, 0.002761336399999998, 420.0, 0.01370713219999999, 430.0, 0.0213957252, 440.0, + 0.03152892629999999, 450.0, 0.03680976990000001, 460.0, 0.04389973789999997, 470.0, + 0.21597715529999986, 480.0, 0.5460662005000004, 490.0, 0.7476421494999995, 500.0, + 0.8408643559999998, 510.0, 0.9830170621111203, 520.0, 0.9999999999999996, 530.0, + 0.8182660584000002, 540.0, 0.7640229595999998, 550.0, 0.7181900096000002, 560.0, + 0.6885614276000002, 570.0, 0.5637842785999998, 580.0, 0.5022395519000006, 590.0, + 0.41359042550000014, 600.0, 0.2883105196999997, 610.0, 0.18964555439999992, 620.0, + 0.11295405230000004, 630.0, 0.0703244873, 640.0, 0.04674839129999998, 650.0, + 0.026782565700000014, 660.0, 0.017529164899999998, 670.0, 0.01472423499999999, 680.0, + 0.012615915900000007, 690.0, 0.010025204999999992, 700.0, 0.0021642569999999967, + 710.0, 0.0014748111000000034, 720.0, 0.0014381367999999999, +]; +pub const HASSELBLAD_L1D_20C_B: [f32; 70] = [ + 380.0, 0.0006132656000000236, 390.0, 0.0009732020000000237, 400.0, 0.007745828100000001, + 410.0, 0.024125668700000047, 420.0, 0.19449652359999978, 430.0, 0.4486349311000004, + 440.0, 0.5258907448000005, 450.0, 0.6380014230999997, 460.0, 0.6588667820999994, + 470.0, 0.7050964329000002, 480.0, 0.6988302508000005, 490.0, 0.5509589983000002, + 500.0, 0.44907074990000045, 510.0, 0.2717574566000001, 520.0, 0.23636045289999996, + 530.0, 0.13611382200000013, 540.0, 0.09542979950000005, 550.0, 0.06385320540000003, + 560.0, 0.04342015159999997, 570.0, 0.02741947690000002, 580.0, 0.02290197549999998, + 590.0, 0.019958974499999983, 600.0, 0.01512253870000002, 610.0, 0.0122479575, + 620.0, 0.010064754900000014, 630.0, 0.008851406800000003, 640.0, 0.008642274600000006, + 650.0, 0.007660237300000005, 660.0, 0.006849464800000003, 670.0, 0.006107482800000005, + 680.0, 0.004694325199999995, 690.0, 0.0033491955000000003, 700.0, + 0.0006783616999999987, 710.0, 0.00046401970000000173, 720.0, 0.0014381367999999999, +]; +pub const NIKON_D810_R: [f32; 70] = [ + 380.0, 0.0005731851999999926, 390.0, 0.0004202495000000042, 400.0, 0.0006569884, + 410.0, 0.003124341699999998, 420.0, 0.03478559979999997, 430.0, 0.0467667353, 440.0, + 0.04039734319999999, 450.0, 0.036156071299999995, 460.0, 0.032093358200000005, 470.0, + 0.04279075739999999, 480.0, 0.0504453006, 490.0, 0.043772606299999996, 500.0, + 0.047641361499999965, 510.0, 0.05400358580000003, 520.0, 0.11110384329999998, 530.0, + 0.1004639551, 540.0, 0.07089384939999993, 550.0, 0.0415326074, 560.0, + 0.03828426930000002, 570.0, 0.09233224890000002, 580.0, 0.35095206039999977, 590.0, + 0.6552644580000002, 600.0, 0.6198001747000003, 610.0, 0.5548819991999994, 620.0, + 0.4799853065999999, 630.0, 0.3735242712000003, 640.0, 0.29352035489999995, 650.0, + 0.19339049910000009, 660.0, 0.13755079240000004, 670.0, 0.07808684389999995, 680.0, + 0.022180720999999976, 690.0, 0.005934174699999999, 700.0, 0.0017230749000000027, + 710.0, 0.0012152623, 720.0, 0.0007327225999999986, +]; +pub const NIKON_D810_G: [f32; 70] = [ + 380.0, 0.0011608844999999451, 390.0, 0.0007257054999999998, 400.0, 0.0009183472, + 410.0, 0.00406119299999998, 420.0, 0.054717585800000024, 430.0, 0.09895888630000006, + 440.0, 0.13322969299999987, 450.0, 0.16512565030000007, 460.0, 0.21205885669999985, + 470.0, 0.35911410829999985, 480.0, 0.45832181790000026, 490.0, 0.47009864279999997, + 500.0, 0.5824303626999998, 510.0, 0.8423741353129776, 520.0, 0.9999999999999998, + 530.0, 0.8593352852000004, 540.0, 0.8087476004000003, 550.0, 0.7488264644999998, + 560.0, 0.7009741086000002, 570.0, 0.5433874952999995, 580.0, 0.4215144146999999, + 590.0, 0.30250479989999984, 600.0, 0.1816440982999999, 610.0, 0.10581981190000013, + 620.0, 0.0611571649, 630.0, 0.041325302600000044, 640.0, 0.029100126000000004, + 650.0, 0.01866241960000003, 660.0, 0.014658702299999996, 670.0, 0.010526794800000006, + 680.0, 0.004240209400000002, 690.0, 0.0016354801999999972, 700.0, + 0.0007563171999999998, 710.0, 0.0005702804999999992, 720.0, 0.0007327225999999986, +]; +pub const NIKON_D810_B: [f32; 70] = [ + 380.0, 0.0004069571999999466, 390.0, 0.00045112849999996707, 400.0, 0.001934275700000005, + 410.0, 0.022634881600000045, 420.0, 0.33371393260000015, 430.0, 0.5958112703999997, + 440.0, 0.7329865439000002, 450.0, 0.8543202487000001, 460.0, 0.8654431408999993, + 470.0, 0.9152655112000007, 480.0, 0.9072192065000002, 490.0, 0.6888820670000001, + 500.0, 0.5358193899999999, 510.0, 0.31005660729999995, 520.0, 0.2633803589999995, + 530.0, 0.1442775821000002, 540.0, 0.09473682579999998, 550.0, 0.05908464030000001, + 560.0, 0.03664517919999998, 570.0, 0.020960651600000005, 580.0, 0.015105264499999979, + 590.0, 0.011366031499999995, 600.0, 0.007888865099999995, 610.0, 0.006199090800000002, + 620.0, 0.005862770800000002, 630.0, 0.006164777800000001, 640.0, 0.006238094299999994, + 650.0, 0.006421072800000004, 660.0, 0.006368691900000001, 670.0, 0.0042242908000000015, + 680.0, 0.0015560332000000036, 690.0, 0.0005037126, 700.0, 0.00024318050000000018, + 710.0, 0.00018749549999999996, 720.0, 0.0007327225999999986, +]; +pub const NIKON_D850_R: [f32; 70] = [ + 380.0, 0.0013240464000000025, 390.0, 0.001664802299999998, 400.0, 0.0018789010000000014, + 410.0, 0.0016308618999999994, 420.0, 0.0056571581000000055, 430.0, 0.008393133499999993, + 440.0, 0.004227518899999998, 450.0, 0.0028648515000000018, 460.0, 0.002569232000000001, + 470.0, 0.0051624035, 480.0, 0.008173545200000003, 490.0, 0.010304027199999996, 500.0, + 0.013883361000000002, 510.0, 0.017953102200000015, 520.0, 0.03542821240000002, 530.0, + 0.03302219940000002, 540.0, 0.03118867760000001, 550.0, 0.031901360999999975, 560.0, + 0.0390879284, 570.0, 0.10512543899999993, 580.0, 0.3793366815999999, 590.0, + 0.6755740446000001, 600.0, 0.6466909972999996, 610.0, 0.5848997139000004, 620.0, + 0.5147311165999999, 630.0, 0.3940111731000007, 640.0, 0.3284688224999999, 650.0, + 0.2140636990999999, 660.0, 0.1614745290000001, 670.0, 0.1163921913, 680.0, + 0.0360276894, 690.0, 0.007949479500000002, 700.0, 0.0032346625999999986, 710.0, + 0.002353747299999999, 720.0, 0.0015179675000000004, +]; +pub const NIKON_D850_G: [f32; 70] = [ + 380.0, 0.0012335690999999982, 390.0, 0.0009321669999999996, 400.0, 0.0010096177000000002, + 410.0, 0.0008971459999999997, 420.0, 0.0045908158, 430.0, 0.009229739299999996, 440.0, + 0.01699101260000001, 450.0, 0.02081051710000002, 460.0, 0.025509773899999942, 470.0, + 0.15617959480000007, 480.0, 0.4214663342000001, 490.0, 0.6005823917000004, 500.0, + 0.7355325688999994, 510.0, 0.9049165564942032, 520.0, 1.0000000000000002, 530.0, + 0.8275694376999998, 540.0, 0.7684306507000003, 550.0, 0.7372563796000008, 560.0, + 0.7032310017000003, 570.0, 0.5553650895000004, 580.0, 0.4434085701000001, 590.0, + 0.3192455571999998, 600.0, 0.18047027620000014, 610.0, 0.08845294079999992, 620.0, + 0.03912544949999997, 630.0, 0.021046002799999998, 640.0, 0.013167303000000007, 650.0, + 0.007408521999999997, 660.0, 0.005699600199999998, 670.0, 0.005476612100000002, 680.0, + 0.0026113098999999977, 690.0, 0.0010251029999999999, 700.0, 0.0007592660000000001, + 710.0, 0.0005868197, 720.0, 0.0015179675000000004, +]; +pub const NIKON_D850_B: [f32; 70] = [ + 380.0, 0.002306390300000072, 390.0, 0.004649702299999999, 400.0, 0.01138898839999999, + 410.0, 0.01865001109999998, 420.0, 0.17380032480000004, 430.0, 0.4918920831999998, + 440.0, 0.6248905809999995, 450.0, 0.7459664084999997, 460.0, 0.7334870302999997, + 470.0, 0.816499691, 480.0, 0.8361376921000003, 490.0, 0.6571762634000001, 500.0, + 0.5384695229000008, 510.0, 0.3219031294000001, 520.0, 0.2783524585, 530.0, + 0.14865207189999993, 540.0, 0.0916177732999999, 550.0, 0.05258353919999997, 560.0, + 0.030905990900000013, 570.0, 0.016207124799999986, 580.0, 0.010996580299999994, + 590.0, 0.008064313200000006, 600.0, 0.004920121799999998, 610.0, 0.0033778512999999982, + 620.0, 0.0031200279999999978, 630.0, 0.0029481533000000016, 640.0, 0.003767808600000002, + 650.0, 0.0041100744, 660.0, 0.0046237632, 670.0, 0.0043260331999999966, 680.0, + 0.0016329502999999981, 690.0, 0.00045670859999999804, 700.0, 0.00028404719999999974, + 710.0, 0.0002151905000000003, 720.0, 0.0015179675000000004, +]; +pub const SONY_ILCE_6400_R: [f32; 70] = [ + 380.0, 0.0008597859000000069, 390.0, 0.000927841799999999, 400.0, 0.010423155, 410.0, + 0.040659155599999997, 420.0, 0.05142576210000001, 430.0, 0.03638583240000001, 440.0, + 0.03320779929999999, 450.0, 0.030828112499999973, 460.0, 0.027729497999999984, 470.0, + 0.03742202450000001, 480.0, 0.042911039799999995, 490.0, 0.03820684090000001, 500.0, + 0.04106552470000001, 510.0, 0.0458305926, 520.0, 0.09484196959999999, 530.0, + 0.08453127529999999, 540.0, 0.05937972679999999, 550.0, 0.03435563500000003, 560.0, + 0.03230314220000002, 570.0, 0.07941178869999997, 580.0, 0.30582470689999985, 590.0, + 0.5608974021999996, 600.0, 0.5135972888, 610.0, 0.4591064474999999, 620.0, + 0.3893383262999999, 630.0, 0.2950511454000003, 640.0, 0.22922521750000008, 650.0, + 0.14519603619999996, 660.0, 0.10628466160000004, 670.0, 0.07553227180000004, 680.0, + 0.020447339600000038, 690.0, 0.0033293148999999876, 700.0, 0.0008700692000000019, + 710.0, 0.0005986263999999983, 720.0, 0.0003404241999999994, +]; +pub const SONY_ILCE_6400_G: [f32; 70] = [ + 380.0, 0.0015642972999999338, 390.0, 0.001274163100000003, 400.0, 0.013766575700000006, + 410.0, 0.06250011129999994, 420.0, 0.10110871249999998, 430.0, 0.10420018420000007, + 440.0, 0.14705189879999997, 450.0, 0.19299833240000008, 460.0, 0.24752654540000013, + 470.0, 0.40345588360000006, 480.0, 0.4960352896000001, 490.0, 0.5330949547000005, + 500.0, 0.6404894884000001, 510.0, 0.8701427606994276, 520.0, 1.0000000000000002, + 530.0, 0.8589138679000005, 540.0, 0.8236767573999998, 550.0, 0.7868928516, 560.0, + 0.7664911883999996, 570.0, 0.6347097533000001, 580.0, 0.5389289617000006, 590.0, + 0.43021597269999967, 600.0, 0.2897516396000001, 610.0, 0.18356003860000014, 620.0, + 0.10433567079999989, 630.0, 0.06317784929999996, 640.0, 0.04045623670000003, 650.0, + 0.02229470409999999, 660.0, 0.01656388200000001, 670.0, 0.0143914719, 680.0, + 0.005153244699999998, 690.0, 0.0012222474999999985, 700.0, 0.0005738900000000015, + 710.0, 0.0004397159, 720.0, 0.0003404241999999994, +]; +pub const SONY_ILCE_6400_B: [f32; 70] = [ + 380.0, 0.0011045913000002905, 390.0, 0.002073269200000012, 400.0, 0.06082740049999992, + 410.0, 0.289777963, 420.0, 0.4624566591000001, 430.0, 0.4560433639999998, 440.0, + 0.5724276121, 450.0, 0.6848938255000006, 460.0, 0.6864410236999997, 470.0, + 0.7403867187000011, 480.0, 0.7114145443999997, 490.0, 0.5581266288999995, 500.0, + 0.43757066980000014, 510.0, 0.25936936389999987, 520.0, 0.22656733509999996, 530.0, + 0.12611574719999988, 540.0, 0.0846472619999999, 550.0, 0.052036732399999994, 560.0, + 0.03203097540000001, 570.0, 0.017736991399999964, 580.0, 0.0121201359, 590.0, + 0.009022487299999998, 600.0, 0.0058765781000000026, 610.0, 0.004450950300000004, + 620.0, 0.004018651600000007, 630.0, 0.004025195100000003, 640.0, 0.004566679200000003, + 650.0, 0.004691529900000001, 660.0, 0.0051649775, 670.0, 0.0045135279000000006, + 680.0, 0.0014096544000000016, 690.0, 0.0002927991000000003, 700.0, + 0.0001552085999999997, 710.0, 0.00012266730000000004, 720.0, 0.0003404241999999994, +]; +pub const SONY_ILCE_7M3_R: [f32; 70] = [ + 380.0, 0.0006375881000000166, 390.0, 0.0004937717999999817, 400.0, 0.004743166799999992, + 410.0, 0.03614611650000003, 420.0, 0.05911956349999996, 430.0, 0.04482576150000003, + 440.0, 0.03954487699999998, 450.0, 0.0351375862, 460.0, 0.03234058440000004, 470.0, + 0.04252573180000001, 480.0, 0.04798989120000001, 490.0, 0.041795863600000004, 500.0, + 0.044617591800000016, 510.0, 0.05023752070000005, 520.0, 0.10371327910000007, 530.0, + 0.0925879265, 540.0, 0.06404469849999997, 550.0, 0.037847078400000005, 560.0, + 0.03552543349999999, 570.0, 0.08215018410000001, 580.0, 0.3063447465000002, 590.0, + 0.5463160892000005, 600.0, 0.5121360249000001, 610.0, 0.4550823329999997, 620.0, + 0.38396282589999997, 630.0, 0.2877296382000001, 640.0, 0.22033884230000025, 650.0, + 0.1420660075, 660.0, 0.09576183879999993, 670.0, 0.06782851290000003, 680.0, + 0.033637187999999985, 690.0, 0.011028519699999986, 700.0, 0.0016261329000000066, + 710.0, 0.0011020213000000017, 720.0, 0.0006031914999999985, +]; +pub const SONY_ILCE_7M3_G: [f32; 70] = [ + 380.0, 0.0012680627999999972, 390.0, 0.000838339999999993, 400.0, 0.005621182299999998, + 410.0, 0.047760584599999996, 420.0, 0.09587075700000003, 430.0, 0.10377307180000012, + 440.0, 0.1412303807999999, 450.0, 0.1743932379000001, 460.0, 0.2257631787, 470.0, + 0.37730816170000003, 480.0, 0.4594855754, 490.0, 0.48044605660000034, 500.0, + 0.5950184070000002, 510.0, 0.8410050959002293, 520.0, 1.0, 530.0, 0.8641597597000003, + 540.0, 0.8132567331999998, 550.0, 0.7644830670999994, 560.0, 0.7252182930000003, + 570.0, 0.5563536644000001, 580.0, 0.43745561849999987, 590.0, 0.31640366589999996, + 600.0, 0.19140334189999983, 610.0, 0.11334390240000011, 620.0, 0.06398447620000002, + 630.0, 0.0412927935, 640.0, 0.028504837800000002, 650.0, 0.017887073000000014, + 660.0, 0.013505027599999993, 670.0, 0.011762506699999994, 680.0, 0.0080253417, + 690.0, 0.0035248307999999986, 700.0, 0.0008511416000000001, 710.0, + 0.0006187939000000001, 720.0, 0.0006031914999999985, +]; +pub const SONY_ILCE_7M3_B: [f32; 70] = [ + 380.0, 0.0005175077999999833, 390.0, 0.0007243315000000153, 400.0, 0.025095496900000028, + 410.0, 0.2376810725000001, 420.0, 0.4758576051000001, 430.0, 0.4974247385999997, + 440.0, 0.6065374909000003, 450.0, 0.6985169963999993, 460.0, 0.7088615229000006, + 470.0, 0.7650707164999995, 480.0, 0.7384176256000005, 490.0, 0.5698499565999997, + 500.0, 0.4485718744000001, 510.0, 0.2666880814999999, 520.0, 0.23697841150000007, + 530.0, 0.13314880079999986, 540.0, 0.08795393950000005, 550.0, 0.05460811009999999, + 560.0, 0.03409363389999998, 570.0, 0.018231708000000017, 580.0, 0.0125338757, + 590.0, 0.00928166130000001, 600.0, 0.006481619100000004, 610.0, 0.004770083199999996, + 620.0, 0.0042835064, 630.0, 0.004640298600000001, 640.0, 0.005079321700000001, + 650.0, 0.005196218499999993, 660.0, 0.005118058300000004, 670.0, 0.0044584280999999965, + 680.0, 0.002596682399999995, 690.0, 0.0009683184999999995, 700.0, + 0.00023614879999999988, 710.0, 0.0001765044000000002, 720.0, 0.0006031914999999985, +]; +pub const SONY_ILCE_7RM3_R: [f32; 70] = [ + 380.0, 0.0007139236999999972, 390.0, 0.00045857359999999965, 400.0, 0.0015378792000000016, + 410.0, 0.02228646410000001, 420.0, 0.05153578310000001, 430.0, 0.03703300020000005, + 440.0, 0.033030559900000006, 450.0, 0.029219711100000014, 460.0, 0.025538815899999998, + 470.0, 0.03453799389999995, 480.0, 0.03952965810000002, 490.0, 0.03393991410000002, + 500.0, 0.039002584499999986, 510.0, 0.04447273240000001, 520.0, 0.09354342150000003, + 530.0, 0.08586591360000001, 540.0, 0.06100304279999996, 550.0, 0.03552268509999997, + 560.0, 0.03305356690000007, 570.0, 0.07636615790000002, 580.0, 0.2878191173, 590.0, + 0.5407632108000004, 600.0, 0.5012375652999994, 610.0, 0.44302751060000056, 620.0, + 0.37462405069999993, 630.0, 0.28036972829999984, 640.0, 0.2149473513000002, 650.0, + 0.13502258210000018, 660.0, 0.09462211650000005, 670.0, 0.06582767029999997, 680.0, + 0.03758854699999997, 690.0, 0.012068946900000013, 700.0, 0.002302282799999994, 710.0, + 0.001542792999999999, 720.0, 0.0008196537999999986, +]; +pub const SONY_ILCE_7RM3_G: [f32; 70] = [ + 380.0, 0.0013278868999999976, 390.0, 0.0008243149999999982, 400.0, 0.002228918200000001, + 410.0, 0.03546070949999995, 420.0, 0.09618151439999997, 430.0, 0.10253094890000009, + 440.0, 0.14059514090000008, 450.0, 0.1801828229999999, 460.0, 0.22834910310000028, + 470.0, 0.37766030959999985, 480.0, 0.4605345706000001, 490.0, 0.47666353429999997, + 500.0, 0.6016131889000007, 510.0, 0.8414342136258034, 520.0, 1.0, 530.0, + 0.8691624516000002, 540.0, 0.8270284771999998, 550.0, 0.7721459899999992, 560.0, + 0.7287714451999999, 570.0, 0.5645261824999996, 580.0, 0.4403076697999995, 590.0, + 0.31873596029999995, 600.0, 0.1935573153000002, 610.0, 0.1127479418000001, 620.0, + 0.06527397209999991, 630.0, 0.042389510900000045, 640.0, 0.02937175639999998, 650.0, + 0.017878039900000024, 660.0, 0.014034656799999998, 670.0, 0.012144922699999998, 680.0, + 0.00932852569999999, 690.0, 0.004090707500000007, 700.0, 0.0011543123999999995, + 710.0, 0.0008180687000000006, 720.0, 0.0008196537999999986, +]; +pub const SONY_ILCE_7RM3_B: [f32; 70] = [ + 380.0, 0.0004888507000000014, 390.0, 0.0006043316999999986, 400.0, 0.007973077700000025, + 410.0, 0.1724873107000001, 420.0, 0.47350629050000004, 430.0, 0.4931205832000001, + 440.0, 0.6114025098000002, 450.0, 0.7320570008999998, 460.0, 0.7294567635999989, + 470.0, 0.7760136013000005, 480.0, 0.7453598369999996, 490.0, 0.5639984116999991, + 500.0, 0.4509121763999998, 510.0, 0.25963340380000033, 520.0, 0.22443465890000017, + 530.0, 0.12425239570000012, 540.0, 0.08267176779999995, 550.0, 0.05121479870000002, + 560.0, 0.031622774000000006, 570.0, 0.017702693600000003, 580.0, 0.011967608600000004, + 590.0, 0.009022450699999997, 600.0, 0.0059807488, 610.0, 0.0045296007, 620.0, + 0.003928876199999994, 630.0, 0.003981837800000003, 640.0, 0.004526280699999999, + 650.0, 0.004379785999999997, 660.0, 0.0046924875000000045, 670.0, + 0.0040511336999999965, 680.0, 0.002819445600000003, 690.0, 0.0010624849000000001, + 700.0, 0.00030333630000000045, 710.0, 0.00022071920000000007, 720.0, + 0.0008196537999999986, +]; +pub const SONY_ILCE_9_R: [f32; 70] = [ + 380.0, 0.0006438935000000756, 390.0, 0.0005447983000000049, 400.0, 0.02299526979999998, + 410.0, 0.06785958290000003, 420.0, 0.06794519749999997, 430.0, 0.0483549233, 440.0, + 0.042359627400000016, 450.0, 0.038817883399999954, 460.0, 0.03469990430000001, 470.0, + 0.0442857364, 480.0, 0.050288260299999964, 490.0, 0.04444333819999999, 500.0, + 0.048274595000000045, 510.0, 0.053186440499999994, 520.0, 0.10889700249999995, 530.0, + 0.09784726050000006, 540.0, 0.0695349656, 550.0, 0.0404080688, 560.0, 0.0375589101, + 570.0, 0.08471228520000003, 580.0, 0.30296528139999995, 590.0, 0.5654961054999998, + 600.0, 0.5360348881999998, 610.0, 0.4503096861000001, 620.0, 0.38271896330000005, + 630.0, 0.28594182180000005, 640.0, 0.21811219659999986, 650.0, 0.13900929300000014, + 660.0, 0.09586455429999988, 670.0, 0.06669439209999996, 680.0, 0.03276241960000002, + 690.0, 0.007589367800000005, 700.0, 0.0015700112999999958, 710.0, + 0.0010727753000000018, 720.0, 0.0005996684000000017, +]; +pub const SONY_ILCE_9_G: [f32; 70] = [ + 380.0, 0.001388401600000061, 390.0, 0.0009337119999999824, 400.0, 0.024139965599999993, + 410.0, 0.07977277519999992, 420.0, 0.10237714919999999, 430.0, 0.10433866789999999, + 440.0, 0.14010864840000004, 450.0, 0.17806537309999992, 460.0, 0.22968648349999993, + 470.0, 0.37464868480000013, 480.0, 0.4551516879000004, 490.0, 0.48235538010000006, + 500.0, 0.6018514934000005, 510.0, 0.8385314691918212, 520.0, 0.9999999999999996, + 530.0, 0.8668201131999997, 540.0, 0.8231361274999998, 550.0, 0.7555298987000002, + 560.0, 0.7089637483000004, 570.0, 0.5594261938999994, 580.0, 0.43572545049999983, + 590.0, 0.32768383179999966, 600.0, 0.20077908329999983, 610.0, 0.11347991330000001, + 620.0, 0.06618523440000006, 630.0, 0.04269942430000005, 640.0, 0.02943853210000001, + 650.0, 0.018065227499999975, 660.0, 0.01387175500000001, 670.0, 0.01185201039999999, + 680.0, 0.007972862600000004, 690.0, 0.0024712692000000034, 700.0, + 0.0008482921999999993, 710.0, 0.0006264448999999995, 720.0, 0.0005996684000000017, +]; +pub const SONY_ILCE_9_B: [f32; 70] = [ + 380.0, 0.0006061620999998629, 390.0, 0.0008762744000000156, 400.0, 0.11616656900000003, + 410.0, 0.39849326809999974, 420.0, 0.5148577316999999, 430.0, 0.5091090501999997, + 440.0, 0.6157524749999997, 450.0, 0.7252710085000006, 460.0, 0.7314879715000002, + 470.0, 0.7756312272999999, 480.0, 0.7517359417000001, 490.0, 0.5929571666000003, + 500.0, 0.4786500034999999, 510.0, 0.285801502, 520.0, 0.2572702881000001, 530.0, + 0.14684538190000013, 540.0, 0.09915005439999994, 550.0, 0.061411825800000035, 560.0, + 0.038522106699999996, 570.0, 0.021521229099999987, 580.0, 0.01531055420000001, + 590.0, 0.011856432600000003, 600.0, 0.008148272599999994, 610.0, 0.006297896800000003, + 620.0, 0.005856360400000002, 630.0, 0.005828849899999996, 640.0, 0.006199621000000001, + 650.0, 0.005941810599999998, 660.0, 0.005724522099999999, 670.0, 0.004867224499999995, + 680.0, 0.002801312599999999, 690.0, 0.0007574835000000022, 700.0, + 0.00025558289999999995, 710.0, 0.00019394020000000018, 720.0, 0.0005996684000000017, +]; diff --git a/src/core/film.rs b/src/core/film.rs index 49a0215..e9d3899 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -1,9 +1,110 @@ +use crate::core::pbrt::Float; +use crate::utils::color::RGB; +use crate::core::filter::Filter; +use crate::utils::geometry::{Point2i, Point2f, Bounds2f, Bounds2fi}; +use crate::utils::spectrum::{SampledSpectrum, SampledWavelengths}; + + +pub struct RGBFilm { + pub base: FilmBase, +} +pub struct GBufferBFilm { + pub base: FilmBase, +} +pub struct SpectralFilm { + pub base: FilmBase, +} + +pub struct VisibleSurface; +pub struct PixelSensor; +pub struct ImageMetadata; + +pub struct FilmBase { + pub full_resolution: Point2i, + pub pixel_bounds: Bounds2fi, + pub filter: Filter, + pub diagonal: Float, + pub sensor: Option, + pub filename: String, +} + +pub trait FilmTrait { + // Must be implemented + fn base(&self) -> &FilmBase; + fn base_mut(&mut self) -> &mut FilmBase; + fn add_sample(&mut self, + _p_filme: Point2i, + _l: SampledSpectrum, + _lambda: &SampledWavelengths, + _visible_surface: Option<&VisibleSurface>, + _weight: Float) {todo!()} + fn add_splat(&mut self, _p: Point2f, _v: SampledSpectrum, _lambda: &SampledWavelengths) { todo!() } + fn write_image(&self, _splat_scale: Float) { todo!() } + fn get_image(&self, _metadata: &ImageMetadata, _splat_scale: Float) { todo!() } + fn get_pixel_rgb(&self, _p: Point2i) -> RGB { todo!() } + fn reset_pixel(&mut self, _p: Point2i) { todo!() } + fn to_output_rgb(&self, _v: SampledSpectrum, _lambda: &SampledWavelengths) -> RGB { todo!() } + fn sample_wavelengths(&self) -> &SampledWavelengths { todo!() } + fn uses_visible_surface(&self) -> bool { todo!() } + + // Sensible defaults + fn full_resolution(&self) -> Point2i { self.base().full_resolution } + fn sample_bounds(&self) -> Bounds2f { Bounds2f::default() } + fn diagonal(&self) -> Float { self.base().diagonal } + fn get_filter(&self) -> &Filter { &self.base().filter } + fn get_pixel_sensor(&self) -> Option<&PixelSensor> { self.base().sensor.as_ref() } +} + pub enum Film { RGB(RGBFilm), GBuffer(GBufferBFilm), Spectral(SpectralFilm), } -pub struct RGBFilm; -pub struct GBufferBFilm; -pub struct SpectralFilm; +impl FilmTrait for RGBFilm { + fn base(&self) -> &FilmBase { + &self.base + } + + fn base_mut(&mut self) -> &mut FilmBase { + &mut self.base + } +} + +impl FilmTrait for GBufferBFilm { + fn base(&self) -> &FilmBase { + &self.base + } + + fn base_mut(&mut self) -> &mut FilmBase { + &mut self.base + } +} + +impl FilmTrait for SpectralFilm { + fn base(&self) -> &FilmBase { + &self.base + } + + fn base_mut(&mut self) -> &mut FilmBase { + &mut self.base + } +} + +impl FilmTrait for Film { + fn base(&self) -> &FilmBase { + match self { + Film::RGB(film) => film.base(), + Film::GBuffer(film) => film.base(), + Film::Spectral(film) => film.base(), + } + } + + fn base_mut(&mut self) -> &mut FilmBase { + match self { + Film::RGB(film) => film.base_mut(), + Film::GBuffer(film) => film.base_mut(), + Film::Spectral(film) => film.base_mut(), + } + } +} diff --git a/src/core/filter.rs b/src/core/filter.rs new file mode 100644 index 0000000..1ecacd6 --- /dev/null +++ b/src/core/filter.rs @@ -0,0 +1,344 @@ +use crate::utils::geometry::{Vector2f, Point2f, Point2i, Bounds2i, Bounds2f}; +use crate::core::pbrt::{Float, lerp}; +use crate::utils::math::{gaussian, gaussian_integral, sample_tent, windowed_sinc}; +use crate::core::sampler::PiecewiseConstant2D; +use crate::utils::containers::Array2D; + +use std::hash::Hash; +use rand::Rng; + +pub struct FilterSample { + p: Point2f, + weight: Float, +} +pub struct FilterSampler { + domain: Bounds2f, + distrib: PiecewiseConstant2D, + f: Array2D, +} + +impl FilterSampler { + /// A redesigned constructor that takes a closure to avoid initialization issues. + pub fn new(radius: Vector2f, resolution: Point2i, evaluate_fn: F) -> Self + where + F: Fn(Point2f) -> Float, + { + let domain = Bounds2f::from_points(Point2f::new(-radius.x(), -radius.y()), Point2f::new(radius.x(), radius.y())); + let array_bounds = Bounds2i::from_points(Point2i::new(0, 0), resolution); + let mut f = Array2D::new(array_bounds); + for j in 0..resolution.y() { + for i in 0..resolution.x() { + let p = domain.lerp(Point2f::new( + (i as Float + 0.5) / resolution.x() as Float, + (j as Float + 0.5) / resolution.y() as Float, + )); + f[Point2i::new(i, j)] = evaluate_fn(p); + } + } + let distrib = PiecewiseConstant2D::new(&f, domain); + Self { domain, f, distrib } + } + + /// Samples the filter's distribution. + pub fn sample(&self, u: Point2f) -> FilterSample { + let (p, pdf, pi) = self.distrib.sample(u); + if pdf == 0.0 { + return FilterSample { p, weight: 0.0 }; + } + let weight = self.f[pi] / pdf; + FilterSample { p, weight } + } +} + +pub trait FilterTrait { + fn radius(&self) -> Vector2f; + fn evaluate(&self, p: Point2f) -> Float; + fn integral(&self) -> Float; + fn sample(&self, u: Point2f) -> FilterSample; +} + +pub enum Filter { + Box(BoxFilter), + Gaussian(GaussianFilter), + Mitchell(MitchellFilter), + LanczosSinc(LanczosSincFilter), + Triangle(TriangleFilter), +} + +impl FilterTrait for Filter { + fn radius(&self) -> Vector2f { + match self { + Filter::Box(c) => c.radius(), + Filter::Gaussian(c) => c.radius(), + Filter::Mitchell(c) => c.radius(), + Filter::LanczosSinc(c) => c.radius(), + Filter::Triangle(c) => c.radius(), + } + } + + fn evaluate(&self, p: Point2f) -> Float { + match self { + Filter::Box(c) => c.evaluate(p), + Filter::Gaussian(c) => c.evaluate(p), + Filter::Mitchell(c) => c.evaluate(p), + Filter::LanczosSinc(c) => c.evaluate(p), + Filter::Triangle(c) => c.evaluate(p), + } + } + + fn integral(&self) -> Float { + match self { + Filter::Box(c) => c.integral(), + Filter::Gaussian(c) => c.integral(), + Filter::Mitchell(c) => c.integral(), + Filter::LanczosSinc(c) => c.integral(), + Filter::Triangle(c) => c.integral(), + } + } + + fn sample(&self, u: Point2f) -> FilterSample { + match self { + Filter::Box(c) => c.sample(u), + Filter::Gaussian(c) => c.sample(u), + Filter::Mitchell(c) => c.sample(u), + Filter::LanczosSinc(c) => c.sample(u), + Filter::Triangle(c) => c.sample(u), + } + } +} + +pub struct BoxFilter { + pub radius: Vector2f, +} + +impl BoxFilter { + pub fn new(radius: Vector2f) -> Self { + Self { radius } + } +} + +impl FilterTrait for BoxFilter { + fn radius(&self) -> Vector2f { + self.radius + } + + fn evaluate(&self, p: Point2f) -> Float { + if p.x().abs() <= self.radius.x() && p.y().abs() <= self.radius.y() { + 1. + } else { + 0. + } + } + + fn integral(&self) -> Float { + (2.0 * self.radius.x()) * (2.0 * self.radius.y()) + } + + fn sample(&self, u: Point2f) -> FilterSample { + let p = Point2f::new(lerp(u[0], -self.radius.x(), self.radius.x()), lerp(u[1], -self.radius.y(), self.radius.y())); + FilterSample { p, weight: 1.0 } + } +} + +pub struct GaussianFilter { + pub radius: Vector2f, + pub sigma: Float, + pub exp_x: Float, + pub exp_y: Float, + pub sampler: FilterSampler, +} + +impl GaussianFilter { + pub fn new(radius: Vector2f, sigma: Float) -> Self { + let exp_x = gaussian(radius.x(), 0., sigma); + let exp_y = gaussian(radius.y(), 0., sigma); + + let sampler = FilterSampler::new( + radius, + Point2i::new((32.0 * radius.x()) as i32, (32.0 * radius.y()) as i32), + |p: Point2f| { + (gaussian(p.x(), 0., sigma) - exp_x).max(0.) * + (gaussian(p.y(), 0., sigma) - exp_y).max(0.) + } + ); + Self { + radius, + sigma, + exp_x: gaussian(radius.x(), 0., sigma), + exp_y: gaussian(radius.y(), 0., sigma), + sampler + } + } +} + +impl FilterTrait for GaussianFilter { + fn radius(&self) -> Vector2f { + self.radius + } + + fn evaluate(&self, p: Point2f) -> Float { + (gaussian(p.x(), 0.0, self.sigma) - self.exp_x).max(0.0) * + (gaussian(p.y(), 0.0, self.sigma) - self.exp_y).max(0.0) + } + + fn integral(&self) -> Float { + (gaussian_integral(-self.radius.x(), self.radius.x(), 0.0, self.sigma) - 2.0 * self.radius.x() * self.exp_x) * + (gaussian_integral(-self.radius.y(), self.radius.y(), 0.0, self.sigma) - 2.0 * self.radius.y() * self.exp_y) + } + + fn sample(&self, u: Point2f) -> FilterSample { + self.sampler.sample(u) + } +} + +pub struct MitchellFilter { + pub radius: Vector2f, + pub b: Float, + pub c: Float, + pub sampler: FilterSampler, +} + +impl MitchellFilter { + pub fn new(radius: Vector2f, b: Float, c: Float) -> Self { + let sampler = FilterSampler::new( + radius, + Point2i::new((32.0 * radius.x()) as i32, (32.0 * radius.y()) as i32), + move |p: Point2f| { + let mitchell_1d = |x: Float| { + let x = x.abs(); + if x <= 1.0 { + ((12.0 - 9.0 * b - 6.0 * c) * x.powi(3) + + (-18.0 + 12.0 * b + 6.0 * c) * x.powi(2) + + (6.0 - 2.0 * b)) * (1.0 / 6.0) + } else if x <= 2.0 { + ((-b - 6.0 * c) * x.powi(3) + + (6.0 * b + 30.0 * c) * x.powi(2) + + (-12.0 * b - 48.0 * c) * x + + (8.0 * b + 24.0 * c)) * (1.0 / 6.0) + } else { + 0.0 + } + }; + mitchell_1d(2.0 * p.x() / radius.x()) * mitchell_1d(2.0 * p.y() / radius.y()) + }, + ); + Self { radius, b, c, sampler } + } + + fn mitchell_1d(&self, x: Float) -> Float { + let x = x.abs(); + if x <= 1.0 { + ((12.0 - 9.0 * self.b - 6.0 * self.c) * x.powi(3) + + (-18.0 + 12.0 * self.b + 6.0 * self.c) * x.powi(2) + + (6.0 - 2.0 * self.b)) * (1.0 / 6.0) + } else if x <= 2.0 { + ((-self.b - 6.0 * self.c) * x.powi(3) + + (6.0 * self.b + 30.0 * self.c) * x.powi(2) + + (-12.0 * self.b - 48.0 * self.c) * x + + (8.0 * self.b + 24.0 * self.c)) * (1.0 / 6.0) + } else { + 0.0 + } + } +} + +impl FilterTrait for MitchellFilter { + fn radius(&self) -> Vector2f { self.radius } + + fn evaluate(&self, p: Point2f) -> Float { + self.mitchell_1d(2.0 * p.x() / self.radius.x()) * self.mitchell_1d(2.0 * p.y() / self.radius.y()) + } + + fn integral(&self) -> Float { + self.radius.x() * self.radius.y() / 4.0 + } + + fn sample(&self, u: Point2f) -> FilterSample { + self.sampler.sample(u) + } +} + +pub struct LanczosSincFilter { + pub radius: Vector2f, + pub tau: Float, + pub sampler: FilterSampler, +} + +impl LanczosSincFilter { + pub fn new(radius: Vector2f, tau: Float) -> Self { + let sampler = FilterSampler::new( + radius, + Point2i::new((32.0 * radius.x()) as i32, (32.0 * radius.y()) as i32), + move |p: Point2f| { + windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau) + }, + ); + Self { radius, tau, sampler } + } +} + +impl FilterTrait for LanczosSincFilter { + fn radius(&self) -> Vector2f { self.radius } + + fn evaluate(&self, p: Point2f) -> Float { + windowed_sinc(p.x(), self.radius.x(), self.tau) * windowed_sinc(p.y(), self.radius.y(), self.tau) + } + + fn integral(&self) -> Float { + let sqrt_samples = 64; + let n_samples = sqrt_samples * sqrt_samples; + let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y()); + let mut sum = 0.0; + let mut rng = rand::rng(); + + for y in 0..sqrt_samples { + for x in 0..sqrt_samples { + let u = Point2f::new( + (x as Float + rng.random::()) / sqrt_samples as Float, + (y as Float + rng.random::()) / sqrt_samples as Float, + ); + let p = Point2f::new( + lerp(u.x(), -self.radius.x(), self.radius.x()), + lerp(u.y(), -self.radius.y(), self.radius.y()), + ); + sum += self.evaluate(p); + } + } + sum / n_samples as Float * area + } + + fn sample(&self, u: Point2f) -> FilterSample { + self.sampler.sample(u) + } +} + +pub struct TriangleFilter { + pub radius: Vector2f, +} + +impl TriangleFilter { + pub fn new(radius: Vector2f) -> Self { + Self { radius } + } +} + +impl FilterTrait for TriangleFilter { + fn radius(&self) -> Vector2f { self.radius } + + fn evaluate(&self, p: Point2f) -> Float { + (self.radius.x() - p.x().abs()).max(0.0) * + (self.radius.y() - p.y().abs()).max(0.0) + } + + fn integral(&self) -> Float { + self.radius.x().powi(2) * self.radius.y().powi(2) + } + + fn sample(&self, u: Point2f) -> FilterSample { + let p = Point2f::new( + sample_tent(u[0], self.radius.x()), + sample_tent(u[1], self.radius.y()), + ); + FilterSample { p, weight: 1.0 } + } +} diff --git a/src/core/interaction.rs b/src/core/interaction.rs index dbeb73c..90bf6d3 100644 --- a/src/core/interaction.rs +++ b/src/core/interaction.rs @@ -1,7 +1,7 @@ -use crate::core::geometry::{Vector3f, Normal3f, Point2f, Point3f, Point3fi, Normed, Ray, Dot}; use crate::core::pbrt::Float; use crate::core::medium::{Medium, MediumInterface}; use crate::core::light::Light; +use crate::utils::geometry::{Vector3f, Normal3f, Point2f, Point3f, Point3fi, Normed, Ray, Dot}; use std::any::Any; use std::sync::Arc; diff --git a/src/core/medium.rs b/src/core/medium.rs index 77f3160..9782b44 100644 --- a/src/core/medium.rs +++ b/src/core/medium.rs @@ -1,11 +1,17 @@ use std::sync::Arc; +#[derive(Debug, Clone)] pub struct HomogeneousMedium; +#[derive(Debug, Clone)] pub struct GridMedium; +#[derive(Debug, Clone)] pub struct RGBGridMedium; +#[derive(Debug, Clone)] pub struct CloudMedium; +#[derive(Debug, Clone)] pub struct NanoVDBMedium; +#[derive(Debug, Clone)] pub enum Medium { Homogeneous(HomogeneousMedium), Grid(GridMedium), diff --git a/src/core/mod.rs b/src/core/mod.rs index 0681321..2066845 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,18 +1,14 @@ -pub mod camera; -pub mod color; -pub mod colorspace; +pub mod bxdf; +pub mod cie; pub mod film; -pub mod geometry; +pub mod filter; pub mod integrator; pub mod light; pub mod interaction; -pub mod interval; pub mod material; pub mod medium; pub mod pbrt; pub mod primitive; -pub mod quaternion; +pub mod sampler; pub mod shape; pub mod texture; -pub mod transform; -pub mod spectrum; diff --git a/src/core/pbrt.rs b/src/core/pbrt.rs index b0b687b..5d35e41 100644 --- a/src/core/pbrt.rs +++ b/src/core/pbrt.rs @@ -1,10 +1,14 @@ use num_traits::Num; use std::ops::{Add, Mul}; +use crate::utils::geometry::{Point2f, Vector2f, Vector3f}; + pub type Float = f32; pub const MACHINE_EPSILON: Float = std::f32::EPSILON * 0.5; pub const SHADOW_EPSILON: Float = 0.0001; +pub const ONE_MINUS_EPSILON: Float = 0.99999994; +pub const PI: Float = std::f32::consts::PI; pub const INV_PI: Float = 0.318_309_886_183_790_671_54; pub const INV_2_PI: Float = 0.159_154_943_091_895_335_77; pub const INV_4_PI: Float = 0.079_577_471_545_947_667_88; @@ -41,6 +45,34 @@ where u * (a + b) } +pub fn sample_uniform_disk_polar(u: Point2f) -> Point2f { + let r = u[0].sqrt(); + let theta = 2. * PI * u[1]; + Point2f::new(r * theta.cos(), r * theta.sin()) +} + +pub fn sample_uniform_disk_concentric(u: Point2f) -> Vector2f { + // Map _u_ to $[-1,1]^2$ and handle degeneracy at the origin + let u_vector = u - Point2f::new(0., 0.); + let u_offset = 2. * u_vector - Vector2f::new(1., 1.); + if u_offset.x() == 0. && u_offset.y() == 0. { + return Vector2f::new(0., 0.) + } + + // Apply concentric mapping to point + let theta: Float; + let r: Float; + if u_offset.x().abs() > u_offset.y().abs() { + r = u_offset.x(); + theta = PI_OVER_4 * (u_offset.y() / u_offset.x()); + } else { + r = u_offset.y(); + theta = PI_OVER_2 - PI_OVER_4 * (u_offset.x() / u_offset.y()); + } + let s_vector = Point2f::new(theta.cos(), theta.sin()) - Point2f::new(0., 0.); + return r * s_vector; +} + pub fn clamp_t(val: T, low: T, high: T) -> T where T: PartialOrd, @@ -144,29 +176,16 @@ where -#[inline] -pub fn safe_asin(x: Float) -> Float { - if x >= -1.0001 && x <= 1.0001 { - clamp_t(x, -1., 1.).asin() - } else { - panic!("Not valid value for asin") - } -} - - -#[inline] -pub fn sinx_over_x(x: Float) -> Float { - if 1. - x * x == 1. { - return 1.; - } - return x.sin() / x; -} - #[inline] pub fn gamma(n: i32) -> Float { return (n as Float * MACHINE_EPSILON) / (1. - n as Float * MACHINE_EPSILON); } +#[inline] +pub fn square(n: T) -> T +where + T: Mul + Copy { n * n } + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RenderingCoordinateSystem { diff --git a/src/core/sampler.rs b/src/core/sampler.rs new file mode 100644 index 0000000..b65cedd --- /dev/null +++ b/src/core/sampler.rs @@ -0,0 +1,128 @@ +use crate::utils::geometry::{Point2f, Point2i, Vector2f, Bounds2f}; +use crate::core::pbrt::{find_interval, Float, PI, PI_OVER_2, PI_OVER_4, lerp}; +use crate::utils::containers::Array2D; + +#[derive(Debug, Clone, Copy)] +pub struct CameraSample { + pub p_film: Point2f, + pub p_lens: Point2f, + pub time: Float, + pub filter_weight: Float, +} + +impl Default for CameraSample { + fn default() -> Self { + Self { p_film: Point2f::default(), p_lens: Point2f::default(), time: 0.0, filter_weight: 1.0 } + } +} + +pub struct PiecewiseConstant1D { + pub func: Vec, + pub cdf: Vec, + pub min: Float, + pub max: Float, + pub func_integral: Float, +} +impl PiecewiseConstant1D { + pub fn new(f: &[Float]) -> Self { + Self::new_with_bounds(f, 0., 1.) + } + pub fn new_with_bounds(f: &[Float], min: Float, max: Float) -> Self { + assert!(max > min); + let n = f.len(); + let mut func = Vec::with_capacity(n); + for &val in f { + func.push(val.abs()); + } + let mut cdf = vec![0.; n + 1]; + for i in 1..=n { + debug_assert!(func[i - 1] >= 0.); + cdf[i] = cdf[i - 1] + func[i - 1] * (max - min) / n as Float; + } + let func_integral = cdf[n]; + if func_integral == 0. { + for i in 1..=n { + cdf[i] = i as Float / n as Float; + } + } else { + for i in 1..=n { + cdf[i] /= func_integral; + } + } + Self { func, cdf, func_integral, min, max } + } + pub fn integral(&self) -> Float { + self.func_integral + } + pub fn len(&self) -> usize { + self.func.len() + } + pub fn sample(&self, u: Float) -> (Float, Float, usize) { + let o = find_interval(self.cdf.len(), |idx| self.cdf[idx] <= u); + let mut du = u - self.cdf[o]; + if self.cdf[o + 1] - self.cdf[o] > 0. { + du /= self.cdf[o + 1] - self.cdf[o]; + } + debug_assert!(!du.is_nan()); + let value = lerp((o as Float + du) / self.len() as Float, self.min, self.max); + let pdf_val = if self.func_integral > 0. { self.func[o] / self.func_integral } else { 0. }; + (value, pdf_val, o) + } +} +pub struct PiecewiseConstant2D { + pub p_conditional_v: Vec, + pub p_marginal: PiecewiseConstant1D, + pub domain: Bounds2f, +} + +impl PiecewiseConstant2D { + pub fn new(data: &Array2D, domain: Bounds2f) -> Self { + let resolution = data.size(); + let nu = resolution.x as usize; + let nv = resolution.y as usize; + let mut p_conditional_v = Vec::with_capacity(nv); + for v in 0..nv { + let start = v * nu; + let end = start + nu; + p_conditional_v.push(PiecewiseConstant1D::new_with_bounds( + &data.as_slice()[start..end], + domain.p_min.x(), + domain.p_max.x(), + )); + } + // Compute marginal sampling distribution from the integrals of the conditional distributions + let marginal_func: Vec = p_conditional_v.iter().map(|p| p.integral()).collect(); + let p_marginal = PiecewiseConstant1D::new_with_bounds( + &marginal_func, + domain.p_min.y(), + domain.p_max.y(), + ); + Self { p_conditional_v, p_marginal, domain } + } + + pub fn integral(&self) -> f32 { + self.p_marginal.integral() + } + pub fn sample(&self, u: Point2f) -> (Point2f, f32, Point2i) { + let (d1, pdf1, off_y) = self.p_marginal.sample(u.y()); + let (d0, pdf0, off_x) = self.p_conditional_v[off_y].sample(u.x()); + let pdf = pdf0 * pdf1; + let offset = Point2i::new(off_x as i32, off_y as i32); + (Point2f::new(d0, d1), pdf, offset) + } + /// Calculates the PDF at a given point. + pub fn pdf(&self, p: Point2f) -> f32 { + let p_offset = self.domain.offset(&p); + let nu = self.p_conditional_v[0].len(); + let nv = self.p_marginal.len(); + + let iu = (p_offset.x() * nu as f32).clamp(0.0, nu as f32 - 1.0) as usize; + let iv = (p_offset.y() * nv as f32).clamp(0.0, nv as f32 - 1.0) as usize; + let integral = self.p_marginal.integral(); + if integral == 0.0 { + 0.0 + } else { + self.p_conditional_v[iv].func[iu] / integral + } + } +} diff --git a/src/core/transform.rs b/src/core/transform.rs deleted file mode 100644 index 4f2dc22..0000000 --- a/src/core/transform.rs +++ /dev/null @@ -1,847 +0,0 @@ -use num_traits::{Num, One, Zero, Signed, Float as NumFloat}; -use std::ops::{Add, Div, Index, IndexMut, Mul}; -use std::fmt; -use std::fmt::{Display, Debug}; - -use crate::core::geometry::{Vector, Point, Vector3f, Point3f, Point3fi}; -use crate::core::color::{RGB, XYZ}; -use crate::core::pbrt::{gamma, Float}; -use crate::core::quaternion::Quaternion; - -#[derive(Copy, Clone)] -pub struct SquareMatrix { - pub m: [[T; N]; N], -} - -impl SquareMatrix -{ - pub fn new(data: [[T; N]; N]) -> Self { - Self { m: data } - } - - fn identity() -> Self where T: Copy + Zero + One { - let mut m = [[T::zero(); N]; N]; - for i in 0..N { - m[i][i] = T::one(); - } - Self { m } - } - - - pub fn zero() -> Self where T: Copy + Zero { - SquareMatrix { m: [[T::zero(); N]; N] } - } - - pub fn is_identity(&self) -> bool where T: Copy + Zero + One + PartialEq { - for i in 0..N { - for j in 0..N { - if i == j { - if self.m[i][j] != T::one() { - return false; - } - } else if self.m[i][j] != T::zero() { - return false; - } - } - } - true - } - - fn trace(&self) -> T - where - T: Zero + Copy - { - let mut sum = T::zero(); - for i in 0..N { - for j in 0..N { - if i == j { - sum = sum + self.m[i][j]; - } - } - } - sum - } - - pub fn transpose(&self) -> Self where T: Copy + Zero { - let mut result = SquareMatrix::zero(); - for i in 0..N { - for j in 0..N { - result.m[j][i] = self.m[i][j]; - } - } - result - } -} - - -impl SquareMatrix - where - T: NumFloat -{ - pub fn inverse(&self) -> Option { - if N == 0 { - return Some(Self::identity()); - } - - let mut mat = self.m; - let mut inv = Self::identity(); - - for i in 0..N { - let mut pivot_row = i; - for j in (i + 1)..N { - if mat[j][i].abs() > mat[pivot_row][i].abs() { - pivot_row = j; - } - } - - if pivot_row != i { - mat.swap(i, pivot_row); - inv.m.swap(i, pivot_row); - } - - let pivot = mat[i][i]; - if pivot.is_zero() { - return None; - } - - for j in i..N { - mat[i][j] = mat[i][j] / pivot; - } - - for j in 0..N { - inv.m[i][j] = inv.m[i][j] / pivot; - } - - for j in 0..N { - if i != j { - let factor = mat[j][i]; - for k in i..N { - mat[j][k] = mat[j][k] - factor * mat[i][k]; - } - for k in 0..N { - inv.m[j][k] = inv.m[j][k] - factor * inv.m[i][k]; - } - } - } - } - Some(inv) - } - - pub fn determinant(&self) -> T { - let m = &self.m; - - match N { - 0 => T::one(), - 1 => m[0][0], - 2 => m[0][0] * m[1][1] - m[0][1] * m[1][0], - 3 => { - let a = m[0][0]; let b = m[0][1]; let c = m[0][2]; - let d = m[1][0]; let e = m[1][1]; let f = m[1][2]; - let g = m[2][0]; let h = m[2][1]; let i = m[2][2]; - - a * (e * i - f * h) - - b * (d * i - f * g) + - c * (d * h - e * g) - } - 4 => { - let det3 = |m11: T, m12: T, m13: T, - m21: T, m22: T, m23: T, - m31: T, m32: T, m33: T| -> T { - m11 * (m22 * m33 - m23 * m32) - - m12 * (m21 * m33 - m23 * m31) + - m13 * (m21 * m32 - m22 * m31) - }; - - let c0 = det3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); - let c1 = det3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); - let c2 = det3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); - let c3 = det3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); - - m[0][0] * c0 - m[0][1] * c1 + m[0][2] * c2 - m[0][3] * c3 - } - _ => { // Fallback to LU decomposition for N > 4 - let mut lum = self.m; - let mut parity = 0; - - for i in 0..N { - let mut max_row = i; - for k in (i + 1)..N { - if lum[k][i].abs() > lum[max_row][i].abs() { - max_row = k; - } - } - - if max_row != i { - lum.swap(i, max_row); - parity += 1; - } - - // Singular matrix - if lum[i][i] == T::zero() { - return T::zero(); - } - - // Gaussian elimination - for j in (i + 1)..N { - let factor = lum[j][i] / lum[i][i]; - for k in i..N { - lum[j][k] = lum[j][k] - factor * lum[i][k]; - } - } - } - - let mut det = T::one(); - for i in 0..N { - det = det * lum[i][i]; - } - - if parity % 2 != 0 { - det = -det; - } - - det - } - } - } -} - -impl Default for SquareMatrix -where - T: Copy + Zero + One, -{ - fn default() -> Self { - Self::identity() - } -} - -impl PartialEq for SquareMatrix -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.m == other.m - } -} - -impl Eq for SquareMatrix {} - - -impl Index for SquareMatrix { - type Output = [T; N]; - - fn index(&self, index: usize) -> &Self::Output { - &self.m[index] - } -} - -impl PartialOrd for SquareMatrix -where - T: PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.m.iter().flatten().partial_cmp(other.m.iter().flatten()) - } -} - -impl IndexMut for SquareMatrix { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.m[index] - } -} - -impl Debug for SquareMatrix { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SquareMatrix") - .field("m", &self.m) - .finish() - } -} - -impl Display for SquareMatrix -where - T: Display + Copy, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut col_widths = [0; N]; - for row in self.m.iter() { - for (j, element) in row.iter().enumerate() { - let width = format!("{}", element).len(); - if width > col_widths[j] { - col_widths[j] = width; - } - } - } - - for i in 0..N { - write!(f, "[")?; - for j in 0..N { - write!(f, "{: >width$} ", self.m[i][j], width = col_widths[j])?; - } - writeln!(f, "]")?; - } - Ok(()) - } -} - -impl Add for SquareMatrix -where - T: Copy + Add + Zero, -{ - type Output = Self; - fn add(mut self, rhs: Self) -> Self::Output { - for i in 0..N { - for j in 0..N { - self.m[i][j] = self.m[i][j] + rhs.m[i][j]; - } - } - self - } -} - -impl Mul for SquareMatrix -where - T: Copy + Num, -{ - type Output = Self; - fn mul(self, rhs: Self) -> Self::Output { - let mut result = SquareMatrix::zero(); - for i in 0..N { - for j in 0..N { - let mut sum = T::zero(); - for k in 0..N { - sum = sum + self.m[i][k] * rhs.m[k][j]; - } - result.m[i][j] = sum; - } - } - result - } -} - -impl Mul for SquareMatrix -where - T: Copy + Mul, -{ - type Output = Self; - fn mul(mut self, rhs: T) -> Self::Output { - for row in self.m.iter_mut() { - for element in row.iter_mut() { - *element = *element * rhs; - } - } - self - } -} - -impl Mul> for Float -{ - type Output = SquareMatrix; - fn mul(self, rhs: SquareMatrix) -> Self::Output { - rhs * self - } -} - -impl Div for SquareMatrix -where - T: Copy + Div, -{ - type Output = Self; - fn div(mut self, rhs: T) -> Self::Output { - for row in self.m.iter_mut() { - for element in row.iter_mut() { - *element = *element / rhs; - } - } - self - } -} - -impl Mul> for SquareMatrix -where - T: Num + Copy, -{ - type Output = Vector; - fn mul(self, rhs: Vector) -> Self::Output { - let mut result_arr = [T::zero(); N]; - for i in 0..N { - for j in 0..N { - result_arr[i] = result_arr[i] + self.m[i][j] * rhs.0[j]; - } - } - Vector(result_arr) - } -} - -impl Mul> for SquareMatrix -where - T: Num + Copy, -{ - type Output = Point; - - fn mul(self, rhs: Point) -> Self::Output { - let mut result_arr = [T::zero(); N]; - for i in 0..N { - for j in 0..N { - result_arr[i] = result_arr[i] + self.m[i][j] * rhs.0[j]; - } - } - Point(result_arr) - } -} - -impl SquareMatrix { - pub fn transform_to_xyz(&self, rgb: RGB) -> XYZ { - let r = rgb[0]; - let g = rgb[1]; - let b = rgb[2]; - - let x = self.m[0][0] * r + self.m[0][1] * g + self.m[0][2] * b; - let y = self.m[1][0] * r + self.m[1][1] * g + self.m[1][2] * b; - let z = self.m[2][0] * r + self.m[2][1] * g + self.m[2][2] * b; - - XYZ::new(x, y, z) - } - - // This name is also perfectly clear - pub fn transform_to_rgb(&self, xyz: XYZ) -> RGB { - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; - - let r = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; - let g = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; - let b = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; - - RGB::new(r, g, b) - } -} - -impl Mul for SquareMatrix -{ - type Output = XYZ; - fn mul(self, rhs: XYZ) -> Self::Output { - let mut result_arr = [0.0; 3]; - for i in 0..3 { - for j in 0..3 { - result_arr[i] = result_arr[i] + self.m[i][j] * rhs[j]; - } - } - XYZ::new(result_arr[0], result_arr[1], result_arr[2]) - } -} - -impl Mul for SquareMatrix -{ - type Output = RGB; - fn mul(self, rhs: RGB) -> Self::Output { - let mut result_arr = [0.0; 3]; - for i in 0..3 { - for j in 0..3 { - result_arr[i] = result_arr[i] + self.m[i][j] * rhs[j]; - } - } - RGB::new(result_arr[0], result_arr[1], result_arr[2]) - } -} - -#[derive(Copy, Clone)] -pub struct Transform { - m: SquareMatrix, - m_inv: SquareMatrix, -} - -impl Transform { - pub fn new(m: &SquareMatrix, m_inv: &SquareMatrix) -> Self { - Self { m: *m, m_inv: *m_inv } - } - - pub fn from_matrix(m: &SquareMatrix) -> Self { - let inv = match m.inverse() { - Some(inv) => inv , - None => SquareMatrix::zero(), - }; - - Self { m: *m, m_inv: inv } - } - - pub fn identity() -> Self { - let m: SquareMatrix = SquareMatrix::identity(); - let m_inv = m.inverse().expect("Singular matrix"); - Self { m, m_inv } - } - - pub fn is_identity(&self) -> bool { - self.m.is_identity() - } - - pub fn inv(&self) -> Self { - Self { m: self.m_inv, m_inv: self.m} - } - - pub fn apply_inverse(&self, p: Point) -> Point { - *self * p - } - - pub fn apply_inverse_vector(&self, v: Vector) -> Vector { - *self * v - } - -} - -impl Transform { - pub fn apply(&self, pi: &Point3fi) -> Point3fi { - let p = pi.midpoint(); - let x = p.x(); - let y = p.y(); - let z = p.z(); - let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; - let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; - let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; - let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; - let mut p_error = Vector3f::default(); - - if pi.is_exact() { - p_error[0] = gamma(3) * (self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + - (self.m[0][2] + z).abs() + self.m[0][3].abs(); - p_error[1] = gamma(3) * (self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + - (self.m[1][2] + z).abs() + self.m[1][3].abs(); - p_error[2] = gamma(3) * (self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + - (self.m[2][2] + z).abs() + self.m[2][3].abs(); - } - - if wp == 1. { - return Point3fi::new_with_error(Point([xp, yp, zp]), p_error); - } else { - return Point3fi::new_with_error(Point([xp/wp, yp/wp, zp/wp]), p_error) - } - } - - pub fn to_quaternion(&self) -> Quaternion { - let trace = self.m.trace(); - let mut quat = Quaternion::default(); - if trace > 0. { - let mut s = (trace + 1.).sqrt(); - quat.w = 0.5 * s; - s = 0.5 / s; - quat.v[0] = (self.m[2][1] - self.m[1][2]) * s; - quat.v[1] = (self.m[2][1] - self.m[1][2]) * s; - quat.v[2] = (self.m[2][1] - self.m[1][2]) * s; - } else { - let nxt = [1, 2, 0]; - let mut q = [0.; 3]; - let mut i = 0; - if self.m[1][1] > self.m[0][0] { - i = 1; - } - if self.m[2][2] > self.m[i][i] { - i = 2; - } - let j = nxt[i]; - let k = nxt[j]; - let mut s = (self.m[i][i] - (self.m[j][j] + self.m[k][k]) + 1.).sqrt(); - q[i] = 0.5 * s; - if s != 0. { - s = 0.5 / s; - } - quat.w = (self.m[k][j] - self.m[j][k]) * s; - q[j] = (self.m[j][i] + self.m[i][j]) * s; - q[k] = (self.m[k][i] + self.m[i][k]) * s; - quat.v[0] = q[0]; - quat.v[1] = q[1]; - quat.v[2] = q[2]; - } - quat - } - - pub fn translate(delta: Vector3f) -> Self { - Transform { - m: SquareMatrix { - m: [ - [1.0, 0.0, 0.0, delta.x()], - [0.0, 1.0, 0.0, delta.y()], - [0.0, 0.0, 1.0, delta.z()], - [0.0, 0.0, 0.0, 1.0], - ], - }, - m_inv: SquareMatrix { - m: [ - [1.0, 0.0, 0.0, -delta.x()], - [0.0, 1.0, 0.0, -delta.y()], - [0.0, 0.0, 1.0, -delta.z()], - [0.0, 0.0, 0.0, 1.0], - ], - }, - } - } - - - pub fn scale(x: Float, y: Float, z: Float) -> Self { - let m = SquareMatrix::new([ - [x, 0., 0., 0.], - [0., y, 0., 0.], - [0., 0., z, 0.], - [0., 0., 0., 1.], - ]); - - let m_inv = SquareMatrix::new([ - [1./x, 0., 0., 0.], - [0., 1./y, 0., 0.], - [0., 0., 1./z, 0.], - [0., 0., 0., 1.], - ]); - Self { m, m_inv } - } - - pub fn rotate_x(theta: Float) -> Self { - let sin_theta = theta.to_radians().sin(); - let cos_theta = theta.to_radians().cos(); - let m = SquareMatrix::new([ - [1., 0., 0., 0.], - [0., cos_theta, -sin_theta, 0.], - [0., sin_theta, cos_theta, 0.], - [0., 0., 0., 1.], - ]); - Self { m, m_inv: m.transpose() } - } - - pub fn rotate_y(theta: Float) -> Self { - let sin_theta = theta.to_radians().sin(); - let cos_theta = theta.to_radians().cos(); - let m = SquareMatrix::new([ - [cos_theta, 0., sin_theta, 0.], - [0., 1., 0., 0.], - [-sin_theta, 0., cos_theta, 0.], - [0., 0., 0., 1.], - ]); - Self { m, m_inv: m.transpose() } - } - - pub fn rotate_z(theta: Float) -> Self { - let sin_theta = theta.to_radians().sin(); - let cos_theta = theta.to_radians().cos(); - let m = SquareMatrix::new([ - [cos_theta, -sin_theta, 0., 0.], - [sin_theta, cos_theta, 0., 0.], - [0., 0., 1., 0.], - [0., 0., 0., 1.], - ]); - Self { m, m_inv: m.transpose() } - } -} - -impl PartialEq for Transform { - fn eq(&self, other: &Self) -> bool { - self.m == other.m && self.m_inv == other.m_inv - } -} - -impl Mul for Transform -where T: NumFloat -{ - type Output = Self; - fn mul(self, rhs: Self) -> Self::Output { - Transform { m: self.m * rhs.m, m_inv: rhs.m_inv * self.m_inv } - } -} - -impl Mul> for Float -{ - type Output = Transform; - fn mul(self, rhs: Transform) -> Self::Output { - Transform { m: rhs.m * self , m_inv: rhs.m_inv * self } - } -} - -impl Mul> for Transform -where - T: NumFloat -{ - type Output = Point; - fn mul(self, p: Point) -> Self::Output { - let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; - let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; - let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; - let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; - - if wp == T::one() { - Point([xp, yp, zp]) - } else { - Point([xp/wp, yp/wp, zp/wp]) - } - } -} - -impl Mul> for Transform -where - T: NumFloat -{ - type Output = Vector; - fn mul(self, p: Vector) -> Self::Output { - let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; - let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; - let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; - let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; - - if wp == T::one() { - Vector([xp, yp, zp]) - } else { - Vector([xp/wp, yp/wp, zp/wp]) - } - } -} - -impl From for Transform { - fn from(q: Quaternion) -> Self { - let xx = q.v.x() * q.v.x(); - let yy = q.v.y() * q.v.y(); - let zz = q.v.z() * q.v.z(); - let xy = q.v.x() * q.v.y(); - let xz = q.v.x() * q.v.z(); - let yz = q.v.y() * q.v.z(); - let wx = q.v.x() * q.w; - let wy = q.v.y() * q.w; - let wz = q.v.z() * q.w; - - let mut m = [[0.0; 4]; 4]; - - m[0][0] = 1. - 2. * (yy + zz); - m[0][1] = 2. * (xy - wz); - m[0][2] = 2. * (xz + wy); - - m[1][0] = 2. * (xy + wz); - m[1][1] = 1. - 2. * (xx + zz); - m[1][2] = 2. * (yz - wx); - - m[2][0] = 2. * (xz - wy); - m[2][1] = 2. * (yz + wx); - m[2][2] = 1. - 2. * (xx + yy); - - m[3][3] = 1.; - - let m_sq = SquareMatrix::new(m); - // For a pure rotation, the inverse is the transpose. - let m_inv_sq = m_sq.transpose(); - - Transform::new(&m_sq, &m_inv_sq) - } -} - -pub struct DerivativeTerm { - kc: Float, - kx: Float, - ky: Float, - kz: Float, -} - -impl DerivativeTerm { - pub fn new(kc: Float, kx: Float, ky: Float, kz: Float) -> Self { - Self { kc, kx, ky, kz } - } -} - -pub struct AnimatedTransform { - pub start_transform: Transform, - pub end_transform: Transform, - pub start_time: Float, - pub end_time: Float, - actually_animated: bool, - t: [Vector3f; 2], - r: [Quaternion; 2], - s: [SquareMatrix; 2], - has_rotation: bool, - c1: [DerivativeTerm; 3], - c2: [DerivativeTerm; 3], - c3: [DerivativeTerm; 3], - c4: [DerivativeTerm; 3], - c5: [DerivativeTerm; 3], -} - -impl AnimatedTransform { - pub fn interpolate(&self, time: Float) -> Transform { - if !self.actually_animated || time <= self.start_time { return self.start_transform } - if time >= self.end_time { return self.end_transform } - - let dt = (time - self.start_time) / (self.end_time - self.start_time); - let trans = (1.0 - dt) * self.t[0] + dt * self.t[1]; - - let rotate = Quaternion::slerp(dt, self.r[0], self.r[1]); - - let scale = (1.0 - dt) * self.s[0] + dt * self.s[1]; - - // Return interpolated matrix as product of interpolated components - return Transform::translate(trans) * Transform::from(rotate) * Transform::from_matrix(&scale); - } - - pub fn apply_inverse_point(&self, p: Point3f, time: Float) -> Point3f { - if !self.actually_animated { - return self.start_transform.apply_inverse(p); - } - return self.interpolate(time).apply_inverse(p) - } - - pub fn apply_inverse_vector(&self, v: Vector3f, time: Float) -> Vector3f { - if !self.actually_animated { - return self.start_transform.apply_inverse_vector(v); - } - return self.interpolate(time).apply_inverse_vector(v) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn assert_matrix_approx_eq(a: &SquareMatrix, b: &SquareMatrix) { - const EPSILON: f64 = 1e-9; - for i in 0..N { - for j in 0..N { - assert!( - (a[i][j] - b[i][j]).abs() < EPSILON, - "Matrices differ at ({},{}): {} vs {}\nLeft:\n{}\nRight:\n{}", - i, j, a[i][j], b[i][j], a, b - ); - } - } - } - - #[test] - fn test_inverse_2x2() { - let m = SquareMatrix { m: [[4.0, 7.0], [2.0, 6.0]] }; - let identity = SquareMatrix::identity(); - let inv = m.inverse().expect("Matrix should be invertible"); - let product = m * inv; - assert_matrix_approx_eq(&product, &identity); - } - - #[test] - fn test_inverse_3x3() { - let m = SquareMatrix { m: [[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]] }; - let identity = SquareMatrix::identity(); - let inv = m.inverse().expect("Matrix should be invertible"); - let product = m * inv; - let product_inv = inv * m; - assert_matrix_approx_eq(&product, &identity); - assert_matrix_approx_eq(&product_inv, &identity); - } - - #[test] - fn test_singular_inverse() { - let m = SquareMatrix { m: [[1.0, 2.0], [2.0, 4.0]] }; // Determinant is 0 - assert!(m.inverse().is_none()); - } - - #[test] - fn test_multiplication_2x2() { - let a = SquareMatrix { m: [[1.0, 2.0], [3.0, 4.0]] }; - let b = SquareMatrix { m: [[2.0, 0.0], [1.0, 2.0]] }; - let expected = SquareMatrix { m: [[4.0, 4.0], [10.0, 8.0]] }; - let result = a * b; - assert_matrix_approx_eq(&result, &expected); - } - - #[test] - fn test_determinant_3x3() { - let m = SquareMatrix { m: [[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]] }; - assert_eq!(m.determinant(), 1.0); - } -} diff --git a/src/lib.rs b/src/lib.rs index 71e8aab..b2f737f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ #![allow(unused_imports, dead_code)] +#![feature(float_erf)] +mod camera; mod core; mod lights; +mod utils; diff --git a/src/lights/diffuse.rs b/src/lights/diffuse.rs index 989af06..1df9ef3 100644 --- a/src/lights/diffuse.rs +++ b/src/lights/diffuse.rs @@ -1,6 +1,6 @@ use crate::core::medium::MediumInterface; use crate::core::pbrt::Float; -use crate::core::spectrum::Spectrum; +use crate::utils::spectrum::Spectrum; pub struct DiffuseAreaLight { pub l_emit: Spectrum, diff --git a/src/core/color.rs b/src/utils/color.rs similarity index 99% rename from src/core/color.rs rename to src/utils/color.rs index d2f69ac..fbc49ee 100644 --- a/src/core/color.rs +++ b/src/utils/color.rs @@ -2,8 +2,8 @@ use std::ops::{Index, IndexMut, Add, AddAssign, Sub, SubAssign, Mul, MulAssign, use std::fmt; use crate::core::pbrt::{Float, lerp}; -use crate::core::geometry::Point2f; -use crate::core::spectrum::Spectrum; +use super::geometry::Point2f; +use super::spectrum::Spectrum; #[derive(Debug, Clone)] pub struct XYZ { diff --git a/src/core/colorspace.rs b/src/utils/colorspace.rs similarity index 90% rename from src/core/colorspace.rs rename to src/utils/colorspace.rs index 05e095e..fa9f794 100644 --- a/src/core/colorspace.rs +++ b/src/utils/colorspace.rs @@ -1,8 +1,8 @@ use crate::core::pbrt::Float; -use crate::core::geometry::Point2f; -use crate::core::transform::SquareMatrix; -use crate::core::color::{RGBSigmoidPolynomial, RGBToSpectrumTable, RGB, XYZ}; -use crate::core::spectrum::{DenselySampledSpectrum, Spectrum, SampledSpectrum}; +use super::geometry::Point2f; +use super::transform::SquareMatrix; +use super::color::{RGBSigmoidPolynomial, RGBToSpectrumTable, RGB, XYZ}; +use super::spectrum::{DenselySampledSpectrum, Spectrum, SampledSpectrum}; use std::cmp::{Eq, PartialEq}; use std::sync::Arc; diff --git a/src/utils/containers.rs b/src/utils/containers.rs new file mode 100644 index 0000000..540bc39 --- /dev/null +++ b/src/utils/containers.rs @@ -0,0 +1,75 @@ +use std::collections::hash_map::RandomState; +use std::hash::{BuildHasher, Hash, Hasher}; +use std::ops::{Index, IndexMut}; +use std::sync::RwLock; + +use crate::utils::geometry::{Bounds2i, Bounds3i, Point2i, Point3i, Point3f, Vector3i, Vector3f}; + +pub struct Array2D { + values: Vec, + extent: Bounds2i, +} + +impl Array2D { + pub fn new(extent: Bounds2i) -> Self + where + T: Default, + { + let size = extent.area() as usize; + let mut values = Vec::with_capacity(size); + values.resize_with(size, T::default); + Self { values, extent } + } + + pub fn with_default(extent: Bounds2i, default_val: T) -> Self + where + T: Clone, + { + let size = extent.area() as usize; + let values = vec![default_val; size]; + Self { values, extent } + } + + pub fn x_size(&self) -> i32 { + self.extent.p_max.x() - self.extent.p_min.x() + } + + pub fn y_size(&self) -> i32 { + self.extent.p_max.y() - self.extent.p_min.y() + } + + pub fn size(&self) -> usize { + self.values.len() + } + + pub fn as_slice(&self) -> &[T] { + &self.values + } + + pub fn as_mut_slice(&mut self) -> &mut [T] { + &mut self.values + } + + fn get_index(&self, p: Point2i) -> usize { + debug_assert!(p.x() >= self.extent.p_min.x() && p.x() < self.extent.p_max.x()); + debug_assert!(p.y() >= self.extent.p_min.y() && p.y() < self.extent.p_max.y()); + let width = self.x_size(); + let pp = Point2i::new(p.x() - self.extent.p_min.x(), p.y() - self.extent.p_min.y()); + (pp.y() * width + pp.x()) as usize + } +} + +impl Index for Array2D { + type Output = T; + fn index(&self, p: Point2i) -> &Self::Output { + let idx = self.get_index(p); + &self.values[idx] + } +} + +impl IndexMut for Array2D { + fn index_mut(&mut self, p: Point2i) -> &mut Self::Output { + let idx = self.get_index(p); + &mut self.values[idx] + } +} diff --git a/src/core/geometry.rs b/src/utils/geometry.rs similarity index 90% rename from src/core/geometry.rs rename to src/utils/geometry.rs index 35ce9cf..0a83a31 100644 --- a/src/core/geometry.rs +++ b/src/utils/geometry.rs @@ -3,10 +3,9 @@ use std::ops::{Sub, SubAssign, Add, AddAssign, Div, DivAssign, Mul, MulAssign, N use std::sync::Arc; use std::f32::consts::PI; -use crate::core::pbrt::{Float, next_float_up, next_float_down}; -use crate::core::pbrt; -use crate::core::interval::Interval; use crate::core::medium::Medium; +use crate::core::pbrt::{Float, next_float_up, next_float_down, lerp, clamp_t}; +use super::interval::Interval; pub trait Tuple: Sized + Copy + Index + IndexMut @@ -44,7 +43,14 @@ macro_rules! impl_tuple_core { impl $Struct where T: Zero + Copy { #[inline] pub fn zero() -> Self { Self([T::zero(); N]) } } - + + impl $Struct where T: Copy { + #[inline] + pub fn fill(value: T) -> Self { + Self([value; N]) + } + } + impl Index for $Struct { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { &self.0[index] } @@ -191,6 +197,8 @@ impl_dot_for!(Normal, Vector); impl_dot_for!(Vector, Normal); impl_dot_for!(Vector, Vector); impl_dot_for!(Normal, Normal); +impl_dot_for!(Normal, Point); +impl_dot_for!(Vector, Point); impl From> for Normal { fn from(v: Vector) -> Self { Self(v.0) } @@ -199,6 +207,13 @@ impl From> for Vector { fn from(n: Normal) -> Self { Self(n.0) } } +impl From> for Point { + fn from(v: Vector) -> Self { Self(v.0) } +} + +impl From> for Vector { + fn from(n: Point) -> Self { Self(n.0) } +} macro_rules! impl_accessors { ($Struct:ident) => { impl $Struct { @@ -421,8 +436,16 @@ where T: Num + PartialOrd + Copy + Neg } } +pub fn abs_cos_theta(w: Vector3f) -> Float { + w.z().abs() +} + // SPHERICAL GEOMETRY -pub fn spherical_direction(sin_theta: Float, cos_theta: Float, phi: Float) -> Vector3f { +pub fn same_hemisphere(w: Vector3f, wp: Vector3f) -> bool { + w.z() * wp.z() > 0. +} + +pub fn spherical_direction(sin_theta: Float, cos_theta: Float, phi: Float) -> Vector3f { Vector3f::new(sin_theta * phi.cos(), sin_theta * phi.sin(), cos_theta) } @@ -431,7 +454,7 @@ pub fn spherical_triangle_area(a: Vector3f, b: Vector3f, c: Vector3 } pub fn spherical_theta(v: Vector3f) -> Float { - pbrt::clamp_t(v.z(), -1.0, 1.0).acos() + clamp_t(v.z(), -1.0, 1.0).acos() } pub fn spherical_phi(v: Vector3f) -> Float { @@ -504,11 +527,19 @@ where d.0.iter().fold(T::one(), |acc, &val| acc * val) } + pub fn expand(&self, delta: T) -> Self { + let mut p_min = self.p_min; + let mut p_max = self.p_max; + p_min = p_min - Vector::fill(delta); + p_max = p_max + Vector::fill(delta); + Self { p_min, p_max } + } + pub fn lerp(&self, t: Point) -> Point { let mut results_arr = [T::zero(); N]; for i in 0..N { - results_arr[i] = pbrt::lerp(t[i], self.p_min[i], self.p_max[i]) + results_arr[i] = lerp(t[i], self.p_min[i], self.p_max[i]) } Point(results_arr) @@ -576,8 +607,13 @@ where } pub type Bounds2 = Bounds; +pub type Bounds2f = Bounds2; +pub type Bounds2i = Bounds2; +pub type Bounds2fi = Bounds2; pub type Bounds3 = Bounds; +pub type Bounds3i = Bounds3; pub type Bounds3f = Bounds3; +pub type Bounds3fi = Bounds3; impl Bounds3 where @@ -600,6 +636,16 @@ where } } +impl Bounds2 +where + T: Num + Copy + Default +{ + pub fn area(&self) -> T { + let d: Vector2 = self.p_max - self.p_min; + d.x() * d.y() + } +} + #[derive(Copy, Clone, Default, PartialEq)] pub struct Frame { pub x: Vector3f, @@ -632,7 +678,7 @@ impl Frame { } } -#[derive(Clone, Default)] +#[derive(Clone, Debug)] pub struct Ray { pub o: Point3f, pub d: Vector3f, @@ -642,8 +688,24 @@ pub struct Ray { pub differential: Option, } +impl Default for Ray { + fn default() -> Self { + Self { + o: Point3f::new(0.0, 0.0, 0.0), + d: Vector3f::new(0.0, 0.0, 0.0), + medium: None, + time: 0.0, + differential: None, + } + } +} + impl Ray { - pub fn operator(&self, t: Float) -> Point3f { + pub fn new(o: Point3f, d: Vector3f, time: Option, medium: Option>) -> Self { + Self { o, d, time: time.unwrap_or_else(|| Self::default().time), medium, ..Self::default() } + } + + pub fn evaluate(&self, t: Float) -> Point3f { self.o + self.d * t } diff --git a/src/core/interval.rs b/src/utils/interval.rs similarity index 100% rename from src/core/interval.rs rename to src/utils/interval.rs diff --git a/src/utils/math.rs b/src/utils/math.rs new file mode 100644 index 0000000..29faa79 --- /dev/null +++ b/src/utils/math.rs @@ -0,0 +1,229 @@ +use super::geometry::{Point2f, Vector3f}; +use crate::core::pbrt::{PI, PI_OVER_4, square, lerp, Float, ONE_MINUS_EPSILON, clamp_t}; +use std::mem; +use std::ops::{Mul, Neg, Add}; + +#[inline] +fn fma(a: T, b: T, c: T) -> T +where + T: Mul + Add + Copy +{ + a * b + c +} + +#[inline] +fn difference_of_products(a: T, b: T, c: T, d: T) -> T +where + T: Mul + Add + Neg + Copy +{ + let cd = c * d; + let difference_of_products = fma(a, b, -cd); + let error = fma(-c, d, cd); + return difference_of_products + error; +} + +#[inline] +pub fn safe_asin(x: Float) -> Float { + if x >= -1.0001 && x <= 1.0001 { + clamp_t(x, -1., 1.).asin() + } else { + panic!("Not valid value for asin") + } +} + +#[inline] +pub fn safe_acos(x: Float) -> Float { + if x >= -1.0001 && x <= 1.0001 { + clamp_t(x, -1., 1.).asin() + } else { + panic!("Not valid value for asin") + } +} + +#[inline] +pub fn sinx_over_x(x: Float) -> Float { + if 1. - x * x == 1. { + return 1.; + } + return x.sin() / x; +} + +pub fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float { + if x.abs() > radius { + return 0.; + } + return sinc(x) * sinc(x / tau); +} + +pub fn sinc(x: Float) -> Float { + return sinx_over_x(PI * x); +} + +pub fn quadratic(a: Float, b: Float, c: Float) -> Option<(Float, Float)> { + if a == 0. { + if b == 0. { + return None; + } + let t0 = -c / b; + let t1 = - c / b; + return Some((t0, t1)); + } + + let discrim = difference_of_products(b, b, 4. * a, c); + if discrim < 0. { + return None; + } + let root_discrim = discrim.sqrt(); + + let q = -0.5 * (b + root_discrim.copysign(b)); + let mut t0 = q / a; + let mut t1 = c / q; + if t0 > t1 { + mem::swap(&mut t0, &mut t1); + } + + Some((t0, t1)) +} + +pub fn wrap_equal_area_square(uv: &mut Point2f) -> Point2f { + if uv[0] < 0. { + uv[0] = -uv[0]; // mirror across u = 0 + uv[1] = 1. - uv[1]; // mirror across v = 0.5 + } else if uv[0] > 1. + { + uv[0] = 2. - uv[0]; // mirror across u = 1 + uv[1] = 1. - uv[1]; // mirror across v = 0.5 + } + if uv[1] < 0. { + uv[0] = 1. - uv[0]; // mirror across u = 0.5 + uv[1] = -uv[1]; // mirror across v = 0; + } else if uv[1] > 1. + { + uv[0] = 1. - uv[0]; // mirror across u = 0.5 + uv[1] = 2. - uv[1]; // mirror across v = 1 + } + *uv +} + +pub fn equal_area_square_to_sphere(p: Point2f) -> Vector3f { + assert!(p.x() >= 0. && p.x() <= 1. && p.y() >= 0. && p.y() <= 1.); + + // Transform _p_ to $[-1,1]^2$ and compute absolute values + let u = 2. * p.x() - 1.; + let v = 2. * p.y() - 1.; + let up = u.abs(); + let vp = v.abs(); + + // Compute radius _r_ as signed distance from diagonal + let signed_distance = 1. - (up + vp); + let d = signed_distance.abs(); + let r = 1. - d; + + // Compute angle $\phi$ for square to sphere mapping + let mut phi = if r == 0. { 1. } else {(vp - up) / r + 1.}; + phi /= PI_OVER_4; + + // Find $z$ coordinate for spherical direction + let z = (1. - square(r)).copysign(signed_distance); + + // Compute $\cos\phi$ and $\sin\phi$ for original quadrant and return vector + let cos_phi = phi.cos().copysign(u); + let sin_phi = phi.sin().copysign(v); + return Vector3f::new(cos_phi * r * (2. - square(r)).sqrt(), + sin_phi * r * (2. - square(r)).sqrt(), z); +} + +pub fn gaussian(x: Float, y: Float, sigma: Float) -> Float +{ + (-(x * x + y * y) / (2. * sigma * sigma)).exp() +} + +pub fn gaussian_integral(x0: Float, x1: Float, mu: Float, sigma: Float) -> Float { + assert!(sigma > 0.); + let sigma_root2 = sigma * 1.414213562373095; + 0.5 * (((mu - x0) / sigma_root2).erf() - ((mu - x1) / sigma_root2).erf()) +} + +pub fn sample_linear(u: Float, a: Float, b: Float) -> Float { + assert!(a >= 0. && b >= 0.); + if u == 0. && a == 0. { + return 0. + } + let x = u * (a + b) / (a + (lerp(u, square(a), square(b)))); + x.min(ONE_MINUS_EPSILON) +} + +fn next_float_down(a: f32) -> f32 { + if a.is_infinite() && a < 0.0 { + return a; + } + if a == 0.0 { + return -0.0; + } + let bits = a.to_bits(); + if a > 0.0 { + f32::from_bits(bits - 1) + } else { + f32::from_bits(bits + 1) + } +} + +pub fn sample_discrete(weights: &[f32], u: f32, pmf: Option<&mut f32>, u_remapped: Option<&mut f32>, +) -> Option { + // Handle empty weights for discrete sampling. + if weights.is_empty() { + if let Some(p) = pmf { + *p = 0.0; + } + return None; + } + + // Compute the sum of all weights. + let sum_weights: f32 = weights.iter().sum(); + + // If the total weight is zero, sampling is not possible. + if sum_weights == 0.0 { + if let Some(p) = pmf { + *p = 0.0; + } + return None; + } + + // Compute rescaled u' sample, ensuring it's strictly less than sum_weights. + let mut up = u * sum_weights; + if up >= sum_weights { + up = next_float_down(up); + } + + // Find the offset in weights corresponding to the rescaled sample u'. + let mut offset = 0; + let mut sum = 0.0; + while sum + weights[offset] <= up { + sum += weights[offset]; + offset += 1; + // This assertion should hold true due to the guard `up = next_float_down(up)`. + debug_assert!(offset < weights.len()); + } + + // Compute PMF and remapped u value, if requested. + if let Some(p) = pmf { + *p = weights[offset] / sum_weights; + } + if let Some(ur) = u_remapped { + let weight = weights[offset]; + // The logic guarantees weight > 0 here if sum_weights > 0. + *ur = ((up - sum) / weight).min(ONE_MINUS_EPSILON); + } + + Some(offset) +} + +pub fn sample_tent(u: Float, r: Float) -> Float { + let mut u_remapped = 0.0; + let offset = sample_discrete(&[0.5, 0.5], u, None, Some(&mut u_remapped)).expect("Discrete sampling shouldn't fail"); + if offset == 0 { + return -r + r * sample_linear(u, 0., 1.); + } else { + return r * sample_linear(u, 1., 0.); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..57a77d8 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,11 @@ +pub mod color; +pub mod colorspace; +pub mod containers; +pub mod geometry; +pub mod interval; +pub mod math; +pub mod quaternion; +pub mod spectrum; +pub mod transform; +pub mod scattering; +pub mod sampling; diff --git a/src/core/quaternion.rs b/src/utils/quaternion.rs similarity index 92% rename from src/core/quaternion.rs rename to src/utils/quaternion.rs index fe7652d..6c3c08a 100644 --- a/src/core/quaternion.rs +++ b/src/utils/quaternion.rs @@ -1,10 +1,11 @@ -use crate::core::geometry::{Vector3f, Dot, Normed}; -use crate::core::pbrt::{safe_asin, sinx_over_x, Float}; use std::ops::{Index, IndexMut}; use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg}; use std::f32::consts::PI; -#[derive(Copy, Clone, PartialEq)] +use crate::core::pbrt::{safe_asin, sinx_over_x, Float}; +use super::geometry::{Vector3f, Dot, Normed}; + +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Quaternion { pub v: Vector3f, pub w: Float, @@ -106,4 +107,12 @@ impl Quaternion { return q1 * (1. - t) * sinx_over_x((1. - t) * theta) / sin_theta_over_theta + q2 * t * sinx_over_x(t * theta) / sin_theta_over_theta; } + + pub fn length(&self) -> Float { + self.v.norm() + } + + pub fn normalize(&self) -> Self { + Quaternion { v: self.v.normalize(), w: self.w} + } } diff --git a/src/utils/sampling.rs b/src/utils/sampling.rs new file mode 100644 index 0000000..b1ad503 --- /dev/null +++ b/src/utils/sampling.rs @@ -0,0 +1,13 @@ +use super::geometry::{Point2f, Vector3f}; +use crate::core::pbrt::{square, PI, INV_2_PI}; + +pub fn sample_uniform_hemisphere(u: Point2f) -> Vector3f { + let z = u[0]; + let r = SafeSqrt(1 - square(z)); + let phi = 2 * PI * u[1]; + Vector3f::new(r * phi.cos(), r * phi.sin(), z); +} + +pub fn uniform_hemisphere_pdf() -> Float { + INV_2_PI +} diff --git a/src/utils/scattering.rs b/src/utils/scattering.rs new file mode 100644 index 0000000..54b5934 --- /dev/null +++ b/src/utils/scattering.rs @@ -0,0 +1,31 @@ +use crate::utils::geometry::{Normal3f, Vector3f, Dot}; +use crate::core::pbrt::{Float, square}; + +pub fn refract(wi: Vector3f, n: Normal3f, eta_ratio: Float) -> Option<(Vector3f, Float)> { + // Make local mutable copies of variables that may be changed + let mut n_interface = n; + let mut eta = eta_ratio; + + let mut cos_theta_i = n_interface.dot(wi); + + // Potentially flip interface orientation for Snell's law + if cos_theta_i < 0.0 { + eta = 1.0 / eta; + cos_theta_i = -cos_theta_i; + n_interface = -n_interface; + } + + // Compute sin^2(theta_t) using Snell's law + let sin2_theta_i = (1.0 - square(cos_theta_i)).max(0.0); + let sin2_theta_t = sin2_theta_i / square(eta); + + // Handle total internal reflection + if sin2_theta_t >= 1.0 { + return None; + } + + let cos_theta_t = (1.0 - sin2_theta_t).sqrt(); + + let wt = -wi / eta + (cos_theta_i / eta - cos_theta_t) * Vector3f::from(n_interface); + Some((wt, eta)) +} diff --git a/src/core/spectrum.rs b/src/utils/spectrum.rs similarity index 92% rename from src/core/spectrum.rs rename to src/utils/spectrum.rs index 31a9c31..84d2ab2 100644 --- a/src/core/spectrum.rs +++ b/src/utils/spectrum.rs @@ -3,9 +3,10 @@ use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, N use once_cell::sync::Lazy; use std::sync::Arc; -use crate::core::colorspace::RGBColorspace; use crate::core::pbrt::Float; -use crate::core::color::{RGB, XYZ, RGBSigmoidPolynomial}; +use crate::core::cie; +use super::color::{RGB, XYZ, RGBSigmoidPolynomial}; +use super::colorspace::RGBColorspace; pub const CIE_Y_INTEGRAL: Float = 106.856895; pub const N_SPECTRUM_SAMPLES: usize = 1200; @@ -167,6 +168,13 @@ impl Mul for SampledSpectrum { } } +impl Mul for Float { + type Output = SampledSpectrum; + fn mul(self, rhs: SampledSpectrum) -> SampledSpectrum { + rhs * self + } +} + impl MulAssign for SampledSpectrum { fn mul_assign(&mut self, rhs: Float) { for i in 0..N_SPECTRUM_SAMPLES { @@ -184,6 +192,14 @@ impl DivAssign for SampledSpectrum { } } +impl Div for SampledSpectrum { + type Output = Self; + fn div(self, rhs: Self) -> Self::Output { + let mut ret = self; + ret /= rhs; + ret + } +} impl Div for SampledSpectrum { type Output = Self; @@ -404,13 +420,8 @@ impl UnboundedRGBSpectrum { pub fn new(cs: RGBColorspace, rgb: RGB) -> Self { let m = rgb.r.max(rgb.g).max(rgb.b); let scale = 2.0 * m; - let lambda; - if scale == 1.0 { - lambda = rgb / scale; - } else { - lambda = RGB::new(0.0, 0.0, 0.0); - } - Self { scale, rsp: cs.to_rgb_coeffs(lambda) } + let scaled_rgb = if scale != 0.0 { rgb / scale } else { RGB::new(0.0, 0.0, 0.0) }; + Self { scale, rsp: cs.to_rgb_coeffs(scaled_rgb) } } pub fn sample_at(&self, lambda: Float) -> Float { self.scale * self.rsp.evaluate(lambda) @@ -577,15 +588,31 @@ pub struct RGBUnboundedSpectrum(pub RGBSpectrum); pub mod spectra { use super::*; pub static X: Lazy = Lazy::new(|| { - let dss = DenselySampledSpectrum::from_piecewise_linear(&CIE_X_DATA); + let pls = PiecewiseLinearSpectrum::from_interleaved(&cie::CIE_X); + let dss = DenselySampledSpectrum::from_spectrum( + &Spectrum::PiecewiseLinear(pls), + LAMBDA_MIN, + LAMBDA_MAX, + ); Spectrum::DenselySampled(dss) }); pub static Y: Lazy = Lazy::new(|| { - let dss = DenselySampledSpectrum::from_piecewise_linear(&CIE_Y_DATA); + let pls = PiecewiseLinearSpectrum::from_interleaved(&cie::CIE_Y); + let dss = DenselySampledSpectrum::from_spectrum( + &Spectrum::PiecewiseLinear(pls), + LAMBDA_MIN, + LAMBDA_MAX, + ); Spectrum::DenselySampled(dss) }); pub static Z: Lazy = Lazy::new(|| { - let dss = DenselySampledSpectrum::from_piecewise_linear(&CIE_Z_DATA); + let pls = PiecewiseLinearSpectrum::from_interleaved(&cie::CIE_Z); + let dss = DenselySampledSpectrum::from_spectrum( + &Spectrum::PiecewiseLinear(pls), + LAMBDA_MIN, + LAMBDA_MAX, + ); Spectrum::DenselySampled(dss) }); -} + + } diff --git a/src/utils/transform.rs b/src/utils/transform.rs new file mode 100644 index 0000000..e539ea8 --- /dev/null +++ b/src/utils/transform.rs @@ -0,0 +1,1661 @@ +use num_traits::{Num, One, Zero, Signed, Float as NumFloat}; +use std::ops::{Add, Div, Index, IndexMut, Mul}; +use std::fmt; +use std::fmt::{Display, Debug}; + +use crate::core::pbrt::{gamma, safe_acos, Float}; +use super::geometry::{Normed, Point, Point3f, Point3fi, Vector, Vector3f, Dot, Normal3f, Normal, Ray, Vector3fi}; +use super::color::{RGB, XYZ}; +use super::quaternion::Quaternion; + +#[derive(Copy, Clone)] +pub struct SquareMatrix { + pub m: [[T; N]; N], +} + +impl SquareMatrix +{ + pub fn new(data: [[T; N]; N]) -> Self { + Self { m: data } + } + + fn identity() -> Self where T: Copy + Zero + One { + let mut m = [[T::zero(); N]; N]; + for i in 0..N { + m[i][i] = T::one(); + } + Self { m } + } + + + pub fn zero() -> Self where T: Copy + Zero { + SquareMatrix { m: [[T::zero(); N]; N] } + } + + pub fn is_identity(&self) -> bool where T: Copy + Zero + One + PartialEq { + for i in 0..N { + for j in 0..N { + if i == j { + if self.m[i][j] != T::one() { + return false; + } + } else if self.m[i][j] != T::zero() { + return false; + } + } + } + true + } + + + fn trace(&self) -> T + where + T: Zero + Copy + { + let mut sum = T::zero(); + for i in 0..N { + for j in 0..N { + if i == j { + sum = sum + self.m[i][j]; + } + } + } + sum + } + + pub fn transpose(&self) -> Self where T: Copy + Zero { + let mut result = SquareMatrix::zero(); + for i in 0..N { + for j in 0..N { + result.m[j][i] = self.m[i][j]; + } + } + result + } +} + + +impl SquareMatrix + where + T: NumFloat +{ + pub fn inverse(&self) -> Option { + if N == 0 { + return Some(Self::identity()); + } + + let mut mat = self.m; + let mut inv = Self::identity(); + + for i in 0..N { + let mut pivot_row = i; + for j in (i + 1)..N { + if mat[j][i].abs() > mat[pivot_row][i].abs() { + pivot_row = j; + } + } + + if pivot_row != i { + mat.swap(i, pivot_row); + inv.m.swap(i, pivot_row); + } + + let pivot = mat[i][i]; + if pivot.is_zero() { + return None; + } + + for j in i..N { + mat[i][j] = mat[i][j] / pivot; + } + + for j in 0..N { + inv.m[i][j] = inv.m[i][j] / pivot; + } + + for j in 0..N { + if i != j { + let factor = mat[j][i]; + for k in i..N { + mat[j][k] = mat[j][k] - factor * mat[i][k]; + } + for k in 0..N { + inv.m[j][k] = inv.m[j][k] - factor * inv.m[i][k]; + } + } + } + } + Some(inv) + } + + pub fn determinant(&self) -> T { + let m = &self.m; + + match N { + 0 => T::one(), + 1 => m[0][0], + 2 => m[0][0] * m[1][1] - m[0][1] * m[1][0], + 3 => { + let a = m[0][0]; let b = m[0][1]; let c = m[0][2]; + let d = m[1][0]; let e = m[1][1]; let f = m[1][2]; + let g = m[2][0]; let h = m[2][1]; let i = m[2][2]; + + a * (e * i - f * h) - + b * (d * i - f * g) + + c * (d * h - e * g) + } + 4 => { + let det3 = |m11: T, m12: T, m13: T, + m21: T, m22: T, m23: T, + m31: T, m32: T, m33: T| -> T { + m11 * (m22 * m33 - m23 * m32) - + m12 * (m21 * m33 - m23 * m31) + + m13 * (m21 * m32 - m22 * m31) + }; + + let c0 = det3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + let c1 = det3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + let c2 = det3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + let c3 = det3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + + m[0][0] * c0 - m[0][1] * c1 + m[0][2] * c2 - m[0][3] * c3 + } + _ => { // Fallback to LU decomposition for N > 4 + let mut lum = self.m; + let mut parity = 0; + + for i in 0..N { + let mut max_row = i; + for k in (i + 1)..N { + if lum[k][i].abs() > lum[max_row][i].abs() { + max_row = k; + } + } + + if max_row != i { + lum.swap(i, max_row); + parity += 1; + } + + // Singular matrix + if lum[i][i] == T::zero() { + return T::zero(); + } + + // Gaussian elimination + for j in (i + 1)..N { + let factor = lum[j][i] / lum[i][i]; + for k in i..N { + lum[j][k] = lum[j][k] - factor * lum[i][k]; + } + } + } + + let mut det = T::one(); + for i in 0..N { + det = det * lum[i][i]; + } + + if parity % 2 != 0 { + det = -det; + } + + det + } + } + } +} + +impl Default for SquareMatrix +where + T: Copy + Zero + One, +{ + fn default() -> Self { + Self::identity() + } +} + +impl PartialEq for SquareMatrix +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.m == other.m + } +} + +impl Eq for SquareMatrix {} + + +impl Index for SquareMatrix { + type Output = [T; N]; + + fn index(&self, index: usize) -> &Self::Output { + &self.m[index] + } +} + +impl PartialOrd for SquareMatrix +where + T: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + self.m.iter().flatten().partial_cmp(other.m.iter().flatten()) + } +} + +impl IndexMut for SquareMatrix { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.m[index] + } +} + +impl Debug for SquareMatrix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SquareMatrix") + .field("m", &self.m) + .finish() + } +} + +impl Display for SquareMatrix +where + T: Display + Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut col_widths = [0; N]; + for row in self.m.iter() { + for (j, element) in row.iter().enumerate() { + let width = format!("{}", element).len(); + if width > col_widths[j] { + col_widths[j] = width; + } + } + } + + for i in 0..N { + write!(f, "[")?; + for j in 0..N { + write!(f, "{: >width$} ", self.m[i][j], width = col_widths[j])?; + } + writeln!(f, "]")?; + } + Ok(()) + } +} + +impl Add for SquareMatrix +where + T: Copy + Add + Zero, +{ + type Output = Self; + fn add(mut self, rhs: Self) -> Self::Output { + for i in 0..N { + for j in 0..N { + self.m[i][j] = self.m[i][j] + rhs.m[i][j]; + } + } + self + } +} + +impl Mul for SquareMatrix +where + T: Copy + Num, +{ + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { + let mut result = SquareMatrix::zero(); + for i in 0..N { + for j in 0..N { + let mut sum = T::zero(); + for k in 0..N { + sum = sum + self.m[i][k] * rhs.m[k][j]; + } + result.m[i][j] = sum; + } + } + result + } +} + +impl Mul for SquareMatrix +where + T: Copy + Mul, +{ + type Output = Self; + fn mul(mut self, rhs: T) -> Self::Output { + for row in self.m.iter_mut() { + for element in row.iter_mut() { + *element = *element * rhs; + } + } + self + } +} + +impl Mul> for Float +{ + type Output = SquareMatrix; + fn mul(self, rhs: SquareMatrix) -> Self::Output { + rhs * self + } +} + +impl Div for SquareMatrix +where + T: Copy + Div, +{ + type Output = Self; + fn div(mut self, rhs: T) -> Self::Output { + for row in self.m.iter_mut() { + for element in row.iter_mut() { + *element = *element / rhs; + } + } + self + } +} + +impl Mul> for SquareMatrix +where + T: Num + Copy, +{ + type Output = Vector; + fn mul(self, rhs: Vector) -> Self::Output { + let mut result_arr = [T::zero(); N]; + for i in 0..N { + for j in 0..N { + result_arr[i] = result_arr[i] + self.m[i][j] * rhs.0[j]; + } + } + Vector(result_arr) + } +} + +impl Mul> for SquareMatrix +where + T: Num + Copy, +{ + type Output = Point; + + fn mul(self, rhs: Point) -> Self::Output { + let mut result_arr = [T::zero(); N]; + for i in 0..N { + for j in 0..N { + result_arr[i] = result_arr[i] + self.m[i][j] * rhs.0[j]; + } + } + Point(result_arr) + } +} + +impl SquareMatrix { + pub fn transform_to_xyz(&self, rgb: RGB) -> XYZ { + let r = rgb[0]; + let g = rgb[1]; + let b = rgb[2]; + + let x = self.m[0][0] * r + self.m[0][1] * g + self.m[0][2] * b; + let y = self.m[1][0] * r + self.m[1][1] * g + self.m[1][2] * b; + let z = self.m[2][0] * r + self.m[2][1] * g + self.m[2][2] * b; + + XYZ::new(x, y, z) + } + + // This name is also perfectly clear + pub fn transform_to_rgb(&self, xyz: XYZ) -> RGB { + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; + + let r = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z; + let g = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z; + let b = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z; + + RGB::new(r, g, b) + } +} + +impl Mul for SquareMatrix +{ + type Output = XYZ; + fn mul(self, rhs: XYZ) -> Self::Output { + let mut result_arr = [0.0; 3]; + for i in 0..3 { + for j in 0..3 { + result_arr[i] = result_arr[i] + self.m[i][j] * rhs[j]; + } + } + XYZ::new(result_arr[0], result_arr[1], result_arr[2]) + } +} + +impl Mul for SquareMatrix +{ + type Output = RGB; + fn mul(self, rhs: RGB) -> Self::Output { + let mut result_arr = [0.0; 3]; + for i in 0..3 { + for j in 0..3 { + result_arr[i] = result_arr[i] + self.m[i][j] * rhs[j]; + } + } + RGB::new(result_arr[0], result_arr[1], result_arr[2]) + } +} + +#[derive(Copy, Clone)] +pub struct Transform { + m: SquareMatrix, + m_inv: SquareMatrix, +} + +impl Transform { + pub fn new(m: &SquareMatrix, m_inv: &SquareMatrix) -> Self { + Self { m: *m, m_inv: *m_inv } + } + + pub fn from_matrix(m: &SquareMatrix) -> Self { + let inv = match m.inverse() { + Some(inv) => inv , + None => SquareMatrix::zero(), + }; + + Self { m: *m, m_inv: inv } + } + + pub fn identity() -> Self { + let m: SquareMatrix = SquareMatrix::identity(); + let m_inv = m.inverse().expect("Singular matrix"); + Self { m, m_inv } + } + + pub fn is_identity(&self) -> bool { + self.m.is_identity() + } + + pub fn inverse(&self) -> Self { + Self { m: self.m_inv, m_inv: self.m} + } + + pub fn apply_inverse(&self, p: Point) -> Point { + *self * p + } + + pub fn apply_inverse_vector(&self, v: Vector) -> Vector { + *self * v + } + + pub fn apply_inverse_normal(&self, v: Normal) -> Normal { + *self * v + } +} + +impl Transform { + pub fn apply_to_point(&self, p: Point3f) -> Point3f { + let x = p.x(); + let y = p.y(); + let z = p.z(); + let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; + + if wp == 1. { + return Point3f::new(xp, yp, zp) + } else { + return Point3f::new(xp/wp, yp/wp, zp/wp) + } + } + + pub fn apply_to_vector(&self, p: Vector3f) -> Vector3f { + let x = p.x(); + let y = p.y(); + let z = p.z(); + let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; + + if wp == 1. { + return Vector3f::new(xp, yp, zp) + } else { + return Vector3f::new(xp/wp, yp/wp, zp/wp) + } + } + + pub fn apply_to_normal(&self, p: Normal3f) -> Normal3f { + let x = p.x(); + let y = p.y(); + let z = p.z(); + let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; + + if wp == 1. { + return Normal3f::new(xp, yp, zp) + } else { + return Normal3f::new(xp/wp, yp/wp, zp/wp) + } + } + + pub fn apply_to_ray(&self, r: &Ray, t_max: &mut Option) -> Ray { + let norm_squared = r.d.norm_squared(); + let mut o = Point3fi::new_from_point(r.o); + if norm_squared > 0. { + let dt = r.d.abs().dot(o.error()) / norm_squared; + let offset = Vector3fi::new_from_vector(r.d * dt); + o = o + offset; + + if let Some(t) = t_max.as_mut() { + *t -= dt; + } + } + Ray::new(o.into(), r.d, Some(r.time), r.medium.clone()) + } + + pub fn apply_to_interval(&self, pi: &Point3fi) -> Point3fi { + let p = pi.midpoint(); + let x = p.x(); + let y = p.y(); + let z = p.z(); + let xp = self.m[0][0] * x + self.m[0][1] * y + self.m[0][2] * z + self.m[0][3]; + let yp = self.m[1][0] * x + self.m[1][1] * y + self.m[1][2] * z + self.m[1][3]; + let zp = self.m[2][0] * x + self.m[2][1] * y + self.m[2][2] * z + self.m[2][3]; + let wp = self.m[3][0] * x + self.m[3][1] * y + self.m[3][2] * z + self.m[3][3]; + let mut p_error = Vector3f::default(); + + if pi.is_exact() { + p_error[0] = gamma(3) * (self.m[0][0] * x).abs() + (self.m[0][1] * y).abs() + + (self.m[0][2] + z).abs() + self.m[0][3].abs(); + p_error[1] = gamma(3) * (self.m[1][0] * x).abs() + (self.m[1][1] * y).abs() + + (self.m[1][2] + z).abs() + self.m[1][3].abs(); + p_error[2] = gamma(3) * (self.m[2][0] * x).abs() + (self.m[2][1] * y).abs() + + (self.m[2][2] + z).abs() + self.m[2][3].abs(); + } + + if wp == 1. { + return Point3fi::new_with_error(Point([xp, yp, zp]), p_error); + } else { + return Point3fi::new_with_error(Point([xp/wp, yp/wp, zp/wp]), p_error) + } + } + + pub fn to_quaternion(&self) -> Quaternion { + let trace = self.m.trace(); + let mut quat = Quaternion::default(); + if trace > 0. { + let mut s = (trace + 1.).sqrt(); + quat.w = 0.5 * s; + s = 0.5 / s; + quat.v[0] = (self.m[2][1] - self.m[1][2]) * s; + quat.v[1] = (self.m[2][1] - self.m[1][2]) * s; + quat.v[2] = (self.m[2][1] - self.m[1][2]) * s; + } else { + let nxt = [1, 2, 0]; + let mut q = [0.; 3]; + let mut i = 0; + if self.m[1][1] > self.m[0][0] { + i = 1; + } + if self.m[2][2] > self.m[i][i] { + i = 2; + } + let j = nxt[i]; + let k = nxt[j]; + let mut s = (self.m[i][i] - (self.m[j][j] + self.m[k][k]) + 1.).sqrt(); + q[i] = 0.5 * s; + if s != 0. { + s = 0.5 / s; + } + quat.w = (self.m[k][j] - self.m[j][k]) * s; + q[j] = (self.m[j][i] + self.m[i][j]) * s; + q[k] = (self.m[k][i] + self.m[i][k]) * s; + quat.v[0] = q[0]; + quat.v[1] = q[1]; + quat.v[2] = q[2]; + } + quat + } + + pub fn translate(delta: Vector3f) -> Self { + Transform { + m: SquareMatrix { + m: [ + [1.0, 0.0, 0.0, delta.x()], + [0.0, 1.0, 0.0, delta.y()], + [0.0, 0.0, 1.0, delta.z()], + [0.0, 0.0, 0.0, 1.0], + ], + }, + m_inv: SquareMatrix { + m: [ + [1.0, 0.0, 0.0, -delta.x()], + [0.0, 1.0, 0.0, -delta.y()], + [0.0, 0.0, 1.0, -delta.z()], + [0.0, 0.0, 0.0, 1.0], + ], + }, + } + } + + + pub fn scale(x: Float, y: Float, z: Float) -> Self { + let m = SquareMatrix::new([ + [x, 0., 0., 0.], + [0., y, 0., 0.], + [0., 0., z, 0.], + [0., 0., 0., 1.], + ]); + + let m_inv = SquareMatrix::new([ + [1./x, 0., 0., 0.], + [0., 1./y, 0., 0.], + [0., 0., 1./z, 0.], + [0., 0., 0., 1.], + ]); + Self { m, m_inv } + } + + pub fn orthographic(z_near: Float, z_far: Float) -> Self { + Self::scale(1., 1., 1. / (z_far - z_near)) * + Self::translate(Vector3f::new(0., 0., -z_near)) +} + + pub fn rotate_around_axis(theta: Float, axis: Vector3f) -> Self { + let sin_theta = theta.to_radians().sin(); + let cos_theta = theta.to_radians().cos(); + Transform::rotate(sin_theta, cos_theta, axis) + } + + pub fn rotate(sin_theta: Float, cos_theta: Float, axis: Vector3f) -> Self { + let a = axis.normalize(); + let mut m: SquareMatrix = SquareMatrix::default(); + m[0][0] = a.x() * a.x() + (1. - a.x() * a.x()) * cos_theta; + m[0][1] = a.x() * a.y() * (1. - cos_theta) - a.z() * sin_theta; + m[0][2] = a.x() * a.z() * (1. - cos_theta) + a.y() * sin_theta; + m[0][3] = 0.; + + m[1][0] = a.x() * a.y() * (1. - cos_theta) + a.z() * sin_theta; + m[1][1] = a.y() * a.y() + (1. - a.y() * a.y()) * cos_theta; + m[1][2] = a.y() * a.z() * (1. - cos_theta) - a.x() * sin_theta; + m[1][3] = 0.; + + m[2][0] = a.x() * a.z() * (1. - cos_theta) - a.y() * sin_theta; + m[2][1] = a.y() * a.z() * (1. - cos_theta) + a.x() * sin_theta; + m[2][2] = a.z() * a.z() + (1. - a.z() * a.z()) * cos_theta; + m[2][3] = 0.; + Transform::new(&m, &m.transpose()) + } + + pub fn rotate_from_to(from: Vector3f, to: Vector3f) -> Self { + let refl; + if from.x().abs() < 0.72 && to.x().abs() < 0.72 { + refl = Vector3f::new(1., 0., 0.); + } else if from.y().abs() < 0.72 && to.y().abs() < 0.72 { + refl = Vector3f::new(0., 1., 0.); + } else { + refl = Vector3f::new(0., 0., 1.); + } + + let u = refl - from; + let v = refl - to; + let mut r: SquareMatrix = SquareMatrix::default(); + for i in 0..3 { + for j in 0..3 { + let k = if i == j { 1. } else { 0. }; + r[i][j] = k - 2. / u.dot(v) * u[i] * u[j] - 2. * v.dot(v) * v[i] * v[j] + + 4. * u.dot(v) / (u.dot(u) * v.dot(v)) * v[i] * u[j]; + } + } + Transform::new(&r, &r.transpose()) + } + + pub fn rotate_x(theta: Float) -> Self { + let sin_theta = theta.to_radians().sin(); + let cos_theta = theta.to_radians().cos(); + let m = SquareMatrix::new([ + [1., 0., 0., 0.], + [0., cos_theta, -sin_theta, 0.], + [0., sin_theta, cos_theta, 0.], + [0., 0., 0., 1.], + ]); + Self { m, m_inv: m.transpose() } + } + + pub fn rotate_y(theta: Float) -> Self { + let sin_theta = theta.to_radians().sin(); + let cos_theta = theta.to_radians().cos(); + let m = SquareMatrix::new([ + [cos_theta, 0., sin_theta, 0.], + [0., 1., 0., 0.], + [-sin_theta, 0., cos_theta, 0.], + [0., 0., 0., 1.], + ]); + Self { m, m_inv: m.transpose() } + } + + pub fn rotate_z(theta: Float) -> Self { + let sin_theta = theta.to_radians().sin(); + let cos_theta = theta.to_radians().cos(); + let m = SquareMatrix::new([ + [cos_theta, -sin_theta, 0., 0.], + [sin_theta, cos_theta, 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.], + ]); + Self { m, m_inv: m.transpose() } + } + + pub fn decompose(&self) -> (Vector3f, SquareMatrix, SquareMatrix) { + let t = Vector3f::new(self.m[0][3], self.m[1][3], self.m[2][3]); + let mut m = self.m; + for i in 0..3 { + m[i][3] = 0.; + m[3][i] = m[i][3]; + } + + m[3][3] = 1.; + + let mut norm: Float; + let mut r = m.clone(); + let mut count = 0; + loop { + let rit = r.transpose().inverse().expect("Transform is not decomposable"); + let rnext = (r + rit) / 2.; + norm = 0.0; + for i in 0..3 { + let n = (r[i][0] - rnext[i][0]).abs() + (r[i][1] - rnext[i][1]).abs() + (r[i][2] - rnext[i][2]); + norm = norm.max(n); + } + r = rnext; + count += 1; + if count > 100 || norm < 0.0001 { + break + } + } + let s = r.transpose().inverse().expect("Part of decomposition is singular") * m; + (t, r, s) + } + +} + +impl PartialEq for Transform { + fn eq(&self, other: &Self) -> bool { + self.m == other.m && self.m_inv == other.m_inv + } +} + +impl Mul for Transform +where T: NumFloat +{ + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { + Transform { m: self.m * rhs.m, m_inv: rhs.m_inv * self.m_inv } + } +} + +impl Mul> for Float +{ + type Output = Transform; + fn mul(self, rhs: Transform) -> Self::Output { + Transform { m: rhs.m * self , m_inv: rhs.m_inv * self } + } +} + +impl Mul> for Transform +where + T: NumFloat +{ + type Output = Point; + fn mul(self, p: Point) -> Self::Output { + let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; + let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; + let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; + let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; + + if wp == T::one() { + Point([xp, yp, zp]) + } else { + Point([xp/wp, yp/wp, zp/wp]) + } + } +} + +impl Mul> for Transform +where + T: NumFloat +{ + type Output = Vector; + fn mul(self, p: Vector) -> Self::Output { + let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; + let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; + let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; + let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; + + if wp == T::one() { + Vector([xp, yp, zp]) + } else { + Vector([xp/wp, yp/wp, zp/wp]) + } + } +} + +impl Mul> for Transform +where + T: NumFloat +{ + type Output = Normal; + fn mul(self, p: Normal) -> Self::Output { + let xp = self.m[0][0] * p.x() + self.m[0][1] * p.y() + self.m[0][2] * p.z() + self.m[0][3]; + let yp = self.m[1][0] * p.x() + self.m[1][1] * p.y() + self.m[1][2] * p.z() + self.m[1][3]; + let zp = self.m[2][0] * p.x() + self.m[2][1] * p.y() + self.m[2][2] * p.z() + self.m[2][3]; + let wp = self.m[3][0] * p.x() + self.m[3][1] * p.y() + self.m[3][2] * p.z() + self.m[3][3]; + + if wp == T::one() { + Normal([xp, yp, zp]) + } else { + Normal([xp/wp, yp/wp, zp/wp]) + } + } +} +impl From for Transform { + fn from(q: Quaternion) -> Self { + let xx = q.v.x() * q.v.x(); + let yy = q.v.y() * q.v.y(); + let zz = q.v.z() * q.v.z(); + let xy = q.v.x() * q.v.y(); + let xz = q.v.x() * q.v.z(); + let yz = q.v.y() * q.v.z(); + let wx = q.v.x() * q.w; + let wy = q.v.y() * q.w; + let wz = q.v.z() * q.w; + + let mut m = [[0.0; 4]; 4]; + + m[0][0] = 1. - 2. * (yy + zz); + m[0][1] = 2. * (xy - wz); + m[0][2] = 2. * (xz + wy); + + m[1][0] = 2. * (xy + wz); + m[1][1] = 1. - 2. * (xx + zz); + m[1][2] = 2. * (yz - wx); + + m[2][0] = 2. * (xz - wy); + m[2][1] = 2. * (yz + wx); + m[2][2] = 1. - 2. * (xx + yy); + + m[3][3] = 1.; + + let m_sq = SquareMatrix::new(m); + // For a pure rotation, the inverse is the transpose. + let m_inv_sq = m_sq.transpose(); + + Transform::new(&m_sq, &m_inv_sq) + } +} + + +#[derive(Default, Debug, Copy, Clone)] +pub struct DerivativeTerm { + kc: Float, + kx: Float, + ky: Float, + kz: Float, +} + +impl DerivativeTerm { + pub fn new(kc: Float, kx: Float, ky: Float, kz: Float) -> Self { + Self { kc, kx, ky, kz } + } + + pub fn from_matrix(m: [Float; 4]) -> Self { + Self { kc: m[0], kx: m[1], ky: m[2], kz: m[3] } + } +} + +pub struct AnimatedTransform { + pub start_transform: Transform, + pub end_transform: Transform, + pub start_time: Float, + pub end_time: Float, + actually_animated: bool, + t: [Vector3f; 2], + r: [Quaternion; 2], + s: [SquareMatrix; 2], + has_rotation: bool, + c1: [DerivativeTerm; 3], + c2: [DerivativeTerm; 3], + c3: [DerivativeTerm; 3], + c4: [DerivativeTerm; 3], + c5: [DerivativeTerm; 3], +} + +impl AnimatedTransform { + pub fn new(start_transform: &Transform, start_time: Float, end_transform: &Transform, end_time: Float) -> Option { + let actually_animated = start_transform != end_transform; + + if !actually_animated { + return None + } + + let (t0, r_temp, s0) = start_transform.decompose(); + let mut rm = r_temp; + let r0 = Transform::from_matrix(&rm).to_quaternion(); + let (t1, r_temp, s1) = end_transform.decompose(); + rm = r_temp; + let mut r1 = Transform::from_matrix(&rm).to_quaternion(); + if r0.dot(r1) < 0. { + r1 -= r1; + } + let has_rotation = r0.dot(r1) < 0.9995; + let t = [t0, t1]; + let r = [r0, r1]; + let s = [s0, s1]; + if has_rotation { + let mut c1: [DerivativeTerm; 3] = [Default::default(); 3]; + let mut c2: [DerivativeTerm; 3] = [Default::default(); 3]; + let mut c3: [DerivativeTerm; 3] = [Default::default(); 3]; + let mut c4: [DerivativeTerm; 3] = [Default::default(); 3]; + let mut c5: [DerivativeTerm; 3] = [Default::default(); 3]; + let cos_theta = r0.dot(r1); + let theta = safe_acos(cos_theta); + let qperp: Quaternion = (r0 - r1 * cos_theta).normalize(); + + let t0x = t0.x(); + let t0y = t0.y(); + let t0z = t0.z(); + let t1x = t1.x(); + let t1y = t1.y(); + let t1z = t1.z(); + let q0x = r0.v.x(); + let q0y = r0.v.y(); + let q0z = r0.v.z(); + let q0w = r0.w; + let qperpx = qperp.v.x(); + let qperpy = qperp.v.y(); + let qperpz = qperp.v.z(); + let qperpw = qperp.w; + let s000 = s0[0][0]; + let s001 = s0[0][1]; + let s002 = s0[0][2]; + let s010 = s0[1][0]; + let s011 = s0[1][1]; + let s012 = s0[1][2]; + let s020 = s0[2][0]; + let s021 = s0[2][1]; + let s022 = s0[2][2]; + let s100 = s1[0][0]; + let s101 = s1[0][1]; + let s102 = s1[0][2]; + let s110 = s1[1][0]; + let s111 = s1[1][1]; + let s112 = s1[1][2]; + let s120 = s1[2][0]; + let s121 = s1[2][1]; + let s122 = s1[2][2]; + + c1[0] = DerivativeTerm::new( + -t0x + t1x, + (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s000 + + q0w * q0z * s010 - qperpx * qperpy * s010 + qperpw * qperpz * s010 - + q0w * q0y * s020 - qperpw * qperpy * s020 - qperpx * qperpz * s020 + + s100 - q0y * q0y * s100 - q0z * q0z * s100 - qperpy * qperpy * s100 - + qperpz * qperpz * s100 - q0w * q0z * s110 + qperpx * qperpy * s110 - + qperpw * qperpz * s110 + q0w * q0y * s120 + qperpw * qperpy * s120 + + qperpx * qperpz * s120 + + q0x * (-(q0y * s010) - q0z * s020 + q0y * s110 + q0z * s120), + (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s001 + + q0w * q0z * s011 - qperpx * qperpy * s011 + qperpw * qperpz * s011 - + q0w * q0y * s021 - qperpw * qperpy * s021 - qperpx * qperpz * s021 + + s101 - q0y * q0y * s101 - q0z * q0z * s101 - qperpy * qperpy * s101 - + qperpz * qperpz * s101 - q0w * q0z * s111 + qperpx * qperpy * s111 - + qperpw * qperpz * s111 + q0w * q0y * s121 + qperpw * qperpy * s121 + + qperpx * qperpz * s121 + + q0x * (-(q0y * s011) - q0z * s021 + q0y * s111 + q0z * s121), + (-1. + q0y * q0y + q0z * q0z + qperpy * qperpy + qperpz * qperpz) * s002 + + q0w * q0z * s012 - qperpx * qperpy * s012 + qperpw * qperpz * s012 - + q0w * q0y * s022 - qperpw * qperpy * s022 - qperpx * qperpz * s022 + + s102 - q0y * q0y * s102 - q0z * q0z * s102 - qperpy * qperpy * s102 - + qperpz * qperpz * s102 - q0w * q0z * s112 + qperpx * qperpy * s112 - + qperpw * qperpz * s112 + q0w * q0y * s122 + qperpw * qperpy * s122 + + qperpx * qperpz * s122 + + q0x * (-(q0y * s012) - q0z * s022 + q0y * s112 + q0z * s122)); + + c2[0] = DerivativeTerm::new( + 0., + -(qperpy * qperpy * s000) - qperpz * qperpz * s000 + qperpx * qperpy * s010 - + qperpw * qperpz * s010 + qperpw * qperpy * s020 + qperpx * qperpz * s020 + + q0y * q0y * (s000 - s100) + q0z * q0z * (s000 - s100) + + qperpy * qperpy * s100 + qperpz * qperpz * s100 - qperpx * qperpy * s110 + + qperpw * qperpz * s110 - qperpw * qperpy * s120 - qperpx * qperpz * s120 + + 2. * q0x * qperpy * s010 * theta - 2. * q0w * qperpz * s010 * theta + + 2. * q0w * qperpy * s020 * theta + 2. * q0x * qperpz * s020 * theta + + q0y * (q0x * (-s010 + s110) + q0w * (-s020 + s120) + + 2. * (-2. * qperpy * s000 + qperpx * s010 + qperpw * s020) * theta) + + q0z * (q0w * (s010 - s110) + q0x * (-s020 + s120) - + 2. * (2. * qperpz * s000 + qperpw * s010 - qperpx * s020) * theta), + -(qperpy * qperpy * s001) - qperpz * qperpz * s001 + qperpx * qperpy * s011 - + qperpw * qperpz * s011 + qperpw * qperpy * s021 + qperpx * qperpz * s021 + + q0y * q0y * (s001 - s101) + q0z * q0z * (s001 - s101) + + qperpy * qperpy * s101 + qperpz * qperpz * s101 - qperpx * qperpy * s111 + + qperpw * qperpz * s111 - qperpw * qperpy * s121 - qperpx * qperpz * s121 + + 2. * q0x * qperpy * s011 * theta - 2. * q0w * qperpz * s011 * theta + + 2. * q0w * qperpy * s021 * theta + 2. * q0x * qperpz * s021 * theta + + q0y * (q0x * (-s011 + s111) + q0w * (-s021 + s121) + + 2. * (-2. * qperpy * s001 + qperpx * s011 + qperpw * s021) * theta) + + q0z * (q0w * (s011 - s111) + q0x * (-s021 + s121) - + 2. * (2. * qperpz * s001 + qperpw * s011 - qperpx * s021) * theta), + -(qperpy * qperpy * s002) - qperpz * qperpz * s002 + qperpx * qperpy * s012 - + qperpw * qperpz * s012 + qperpw * qperpy * s022 + qperpx * qperpz * s022 + + q0y * q0y * (s002 - s102) + q0z * q0z * (s002 - s102) + + qperpy * qperpy * s102 + qperpz * qperpz * s102 - qperpx * qperpy * s112 + + qperpw * qperpz * s112 - qperpw * qperpy * s122 - qperpx * qperpz * s122 + + 2. * q0x * qperpy * s012 * theta - 2. * q0w * qperpz * s012 * theta + + 2. * q0w * qperpy * s022 * theta + 2. * q0x * qperpz * s022 * theta + + q0y * (q0x * (-s012 + s112) + q0w * (-s022 + s122) + + 2. * (-2. * qperpy * s002 + qperpx * s012 + qperpw * s022) * theta) + + q0z * (q0w * (s012 - s112) + q0x * (-s022 + s122) - + 2. * (2. * qperpz * s002 + qperpw * s012 - qperpx * s022) * theta)); + + c3[0] = DerivativeTerm::new( + 0., + -2. * + (q0x * qperpy * s010 - q0w * qperpz * s010 + q0w * qperpy * s020 + + q0x * qperpz * s020 - q0x * qperpy * s110 + q0w * qperpz * s110 - + q0w * qperpy * s120 - q0x * qperpz * s120 + + q0y * (-2. * qperpy * s000 + qperpx * s010 + qperpw * s020 + + 2. * qperpy * s100 - qperpx * s110 - qperpw * s120) + + q0z * (-2. * qperpz * s000 - qperpw * s010 + qperpx * s020 + + 2. * qperpz * s100 + qperpw * s110 - qperpx * s120)) * + theta, + -2. * + (q0x * qperpy * s011 - q0w * qperpz * s011 + q0w * qperpy * s021 + + q0x * qperpz * s021 - q0x * qperpy * s111 + q0w * qperpz * s111 - + q0w * qperpy * s121 - q0x * qperpz * s121 + + q0y * (-2. * qperpy * s001 + qperpx * s011 + qperpw * s021 + + 2. * qperpy * s101 - qperpx * s111 - qperpw * s121) + + q0z * (-2. * qperpz * s001 - qperpw * s011 + qperpx * s021 + + 2. * qperpz * s101 + qperpw * s111 - qperpx * s121)) * + theta, + -2. * + (q0x * qperpy * s012 - q0w * qperpz * s012 + q0w * qperpy * s022 + + q0x * qperpz * s022 - q0x * qperpy * s112 + q0w * qperpz * s112 - + q0w * qperpy * s122 - q0x * qperpz * s122 + + q0y * (-2. * qperpy * s002 + qperpx * s012 + qperpw * s022 + + 2. * qperpy * s102 - qperpx * s112 - qperpw * s122) + + q0z * (-2. * qperpz * s002 - qperpw * s012 + qperpx * s022 + + 2. * qperpz * s102 + qperpw * s112 - qperpx * s122)) * + theta); + + c4[0] = DerivativeTerm::new( + 0., + -(q0x * qperpy * s010) + q0w * qperpz * s010 - q0w * qperpy * s020 - + q0x * qperpz * s020 + q0x * qperpy * s110 - q0w * qperpz * s110 + + q0w * qperpy * s120 + q0x * qperpz * s120 + 2. * q0y * q0y * s000 * theta + + 2. * q0z * q0z * s000 * theta - 2. * qperpy * qperpy * s000 * theta - + 2. * qperpz * qperpz * s000 * theta + 2. * qperpx * qperpy * s010 * theta - + 2. * qperpw * qperpz * s010 * theta + 2. * qperpw * qperpy * s020 * theta + + 2. * qperpx * qperpz * s020 * theta + + q0y * (-(qperpx * s010) - qperpw * s020 + 2. * qperpy * (s000 - s100) + + qperpx * s110 + qperpw * s120 - 2. * q0x * s010 * theta - + 2. * q0w * s020 * theta) + + q0z * (2. * qperpz * s000 + qperpw * s010 - qperpx * s020 - + 2. * qperpz * s100 - qperpw * s110 + qperpx * s120 + + 2. * q0w * s010 * theta - 2. * q0x * s020 * theta), + -(q0x * qperpy * s011) + q0w * qperpz * s011 - q0w * qperpy * s021 - + q0x * qperpz * s021 + q0x * qperpy * s111 - q0w * qperpz * s111 + + q0w * qperpy * s121 + q0x * qperpz * s121 + 2. * q0y * q0y * s001 * theta + + 2. * q0z * q0z * s001 * theta - 2. * qperpy * qperpy * s001 * theta - + 2. * qperpz * qperpz * s001 * theta + 2. * qperpx * qperpy * s011 * theta - + 2. * qperpw * qperpz * s011 * theta + 2. * qperpw * qperpy * s021 * theta + + 2. * qperpx * qperpz * s021 * theta + + q0y * (-(qperpx * s011) - qperpw * s021 + 2. * qperpy * (s001 - s101) + + qperpx * s111 + qperpw * s121 - 2. * q0x * s011 * theta - + 2. * q0w * s021 * theta) + + q0z * (2. * qperpz * s001 + qperpw * s011 - qperpx * s021 - + 2. * qperpz * s101 - qperpw * s111 + qperpx * s121 + + 2. * q0w * s011 * theta - 2. * q0x * s021 * theta), + -(q0x * qperpy * s012) + q0w * qperpz * s012 - q0w * qperpy * s022 - + q0x * qperpz * s022 + q0x * qperpy * s112 - q0w * qperpz * s112 + + q0w * qperpy * s122 + q0x * qperpz * s122 + 2. * q0y * q0y * s002 * theta + + 2. * q0z * q0z * s002 * theta - 2. * qperpy * qperpy * s002 * theta - + 2. * qperpz * qperpz * s002 * theta + 2. * qperpx * qperpy * s012 * theta - + 2. * qperpw * qperpz * s012 * theta + 2. * qperpw * qperpy * s022 * theta + + 2. * qperpx * qperpz * s022 * theta + + q0y * (-(qperpx * s012) - qperpw * s022 + 2. * qperpy * (s002 - s102) + + qperpx * s112 + qperpw * s122 - 2. * q0x * s012 * theta - + 2. * q0w * s022 * theta) + + q0z * (2. * qperpz * s002 + qperpw * s012 - qperpx * s022 - + 2. * qperpz * s102 - qperpw * s112 + qperpx * s122 + + 2. * q0w * s012 * theta - 2. * q0x * s022 * theta)); + + c5[0] = DerivativeTerm::new( + 0., + 2. * + (qperpy * qperpy * s000 + qperpz * qperpz * s000 - + qperpx * qperpy * s010 + qperpw * qperpz * s010 - + qperpw * qperpy * s020 - qperpx * qperpz * s020 - + qperpy * qperpy * s100 - qperpz * qperpz * s100 + + q0y * q0y * (-s000 + s100) + q0z * q0z * (-s000 + s100) + + qperpx * qperpy * s110 - qperpw * qperpz * s110 + + q0y * (q0x * (s010 - s110) + q0w * (s020 - s120)) + + qperpw * qperpy * s120 + qperpx * qperpz * s120 + + q0z * (-(q0w * s010) + q0x * s020 + q0w * s110 - q0x * s120)) * + theta, + 2. * + (qperpy * qperpy * s001 + qperpz * qperpz * s001 - + qperpx * qperpy * s011 + qperpw * qperpz * s011 - + qperpw * qperpy * s021 - qperpx * qperpz * s021 - + qperpy * qperpy * s101 - qperpz * qperpz * s101 + + q0y * q0y * (-s001 + s101) + q0z * q0z * (-s001 + s101) + + qperpx * qperpy * s111 - qperpw * qperpz * s111 + + q0y * (q0x * (s011 - s111) + q0w * (s021 - s121)) + + qperpw * qperpy * s121 + qperpx * qperpz * s121 + + q0z * (-(q0w * s011) + q0x * s021 + q0w * s111 - q0x * s121)) * + theta, + 2. * + (qperpy * qperpy * s002 + qperpz * qperpz * s002 - + qperpx * qperpy * s012 + qperpw * qperpz * s012 - + qperpw * qperpy * s022 - qperpx * qperpz * s022 - + qperpy * qperpy * s102 - qperpz * qperpz * s102 + + q0y * q0y * (-s002 + s102) + q0z * q0z * (-s002 + s102) + + qperpx * qperpy * s112 - qperpw * qperpz * s112 + + q0y * (q0x * (s012 - s112) + q0w * (s022 - s122)) + + qperpw * qperpy * s122 + qperpx * qperpz * s122 + + q0z * (-(q0w * s012) + q0x * s022 + q0w * s112 - q0x * s122)) * + theta); + + c1[1] = DerivativeTerm::new( + -t0y + t1y, + -(qperpx * qperpy * s000) - qperpw * qperpz * s000 - s010 + q0z * q0z * s010 + + qperpx * qperpx * s010 + qperpz * qperpz * s010 - q0y * q0z * s020 + + qperpw * qperpx * s020 - qperpy * qperpz * s020 + qperpx * qperpy * s100 + + qperpw * qperpz * s100 + q0w * q0z * (-s000 + s100) + + q0x * q0x * (s010 - s110) + s110 - q0z * q0z * s110 - + qperpx * qperpx * s110 - qperpz * qperpz * s110 + + q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120)) + q0y * q0z * s120 - + qperpw * qperpx * s120 + qperpy * qperpz * s120, + -(qperpx * qperpy * s001) - qperpw * qperpz * s001 - s011 + q0z * q0z * s011 + + qperpx * qperpx * s011 + qperpz * qperpz * s011 - q0y * q0z * s021 + + qperpw * qperpx * s021 - qperpy * qperpz * s021 + qperpx * qperpy * s101 + + qperpw * qperpz * s101 + q0w * q0z * (-s001 + s101) + + q0x * q0x * (s011 - s111) + s111 - q0z * q0z * s111 - + qperpx * qperpx * s111 - qperpz * qperpz * s111 + + q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121)) + q0y * q0z * s121 - + qperpw * qperpx * s121 + qperpy * qperpz * s121, + -(qperpx * qperpy * s002) - qperpw * qperpz * s002 - s012 + q0z * q0z * s012 + + qperpx * qperpx * s012 + qperpz * qperpz * s012 - q0y * q0z * s022 + + qperpw * qperpx * s022 - qperpy * qperpz * s022 + qperpx * qperpy * s102 + + qperpw * qperpz * s102 + q0w * q0z * (-s002 + s102) + + q0x * q0x * (s012 - s112) + s112 - q0z * q0z * s112 - + qperpx * qperpx * s112 - qperpz * qperpz * s112 + + q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122)) + q0y * q0z * s122 - + qperpw * qperpx * s122 + qperpy * qperpz * s122); + + c2[1] = DerivativeTerm::new( + 0., + qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010 - + qperpx * qperpx * s010 - qperpz * qperpz * s010 - q0y * q0z * s020 - + qperpw * qperpx * s020 + qperpy * qperpz * s020 - qperpx * qperpy * s100 - + qperpw * qperpz * s100 + q0x * q0x * (s010 - s110) - q0z * q0z * s110 + + qperpx * qperpx * s110 + qperpz * qperpz * s110 + q0y * q0z * s120 + + qperpw * qperpx * s120 - qperpy * qperpz * s120 + + 2. * q0z * qperpw * s000 * theta + 2. * q0y * qperpx * s000 * theta - + 4. * q0z * qperpz * s010 * theta + 2. * q0z * qperpy * s020 * theta + + 2. * q0y * qperpz * s020 * theta + + q0x * (q0w * s020 + q0y * (-s000 + s100) - q0w * s120 + + 2. * qperpy * s000 * theta - 4. * qperpx * s010 * theta - + 2. * qperpw * s020 * theta) + + q0w * (-(q0z * s000) + q0z * s100 + 2. * qperpz * s000 * theta - + 2. * qperpx * s020 * theta), + qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011 - + qperpx * qperpx * s011 - qperpz * qperpz * s011 - q0y * q0z * s021 - + qperpw * qperpx * s021 + qperpy * qperpz * s021 - qperpx * qperpy * s101 - + qperpw * qperpz * s101 + q0x * q0x * (s011 - s111) - q0z * q0z * s111 + + qperpx * qperpx * s111 + qperpz * qperpz * s111 + q0y * q0z * s121 + + qperpw * qperpx * s121 - qperpy * qperpz * s121 + + 2. * q0z * qperpw * s001 * theta + 2. * q0y * qperpx * s001 * theta - + 4. * q0z * qperpz * s011 * theta + 2. * q0z * qperpy * s021 * theta + + 2. * q0y * qperpz * s021 * theta + + q0x * (q0w * s021 + q0y * (-s001 + s101) - q0w * s121 + + 2. * qperpy * s001 * theta - 4. * qperpx * s011 * theta - + 2. * qperpw * s021 * theta) + + q0w * (-(q0z * s001) + q0z * s101 + 2. * qperpz * s001 * theta - + 2. * qperpx * s021 * theta), + qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012 - + qperpx * qperpx * s012 - qperpz * qperpz * s012 - q0y * q0z * s022 - + qperpw * qperpx * s022 + qperpy * qperpz * s022 - qperpx * qperpy * s102 - + qperpw * qperpz * s102 + q0x * q0x * (s012 - s112) - q0z * q0z * s112 + + qperpx * qperpx * s112 + qperpz * qperpz * s112 + q0y * q0z * s122 + + qperpw * qperpx * s122 - qperpy * qperpz * s122 + + 2. * q0z * qperpw * s002 * theta + 2. * q0y * qperpx * s002 * theta - + 4. * q0z * qperpz * s012 * theta + 2. * q0z * qperpy * s022 * theta + + 2. * q0y * qperpz * s022 * theta + + q0x * (q0w * s022 + q0y * (-s002 + s102) - q0w * s122 + + 2. * qperpy * s002 * theta - 4. * qperpx * s012 * theta - + 2. * qperpw * s022 * theta) + + q0w * (-(q0z * s002) + q0z * s102 + 2. * qperpz * s002 * theta - + 2. * qperpx * s022 * theta)); + + c3[1] = DerivativeTerm::new( + 0., + 2. * + (-(q0x * qperpy * s000) - q0w * qperpz * s000 + 2. * q0x * qperpx * s010 + + q0x * qperpw * s020 + q0w * qperpx * s020 + q0x * qperpy * s100 + + q0w * qperpz * s100 - 2. * q0x * qperpx * s110 - q0x * qperpw * s120 - + q0w * qperpx * s120 + + q0z * (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100) - + 2. * qperpz * s110 + qperpy * s120) + + q0y * + (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120)) * + theta, + 2. * + (-(q0x * qperpy * s001) - q0w * qperpz * s001 + 2. * q0x * qperpx * s011 + + q0x * qperpw * s021 + q0w * qperpx * s021 + q0x * qperpy * s101 + + q0w * qperpz * s101 - 2. * q0x * qperpx * s111 - q0x * qperpw * s121 - + q0w * qperpx * s121 + + q0z * (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101) - + 2. * qperpz * s111 + qperpy * s121) + + q0y * + (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121)) * + theta, + 2. * + (-(q0x * qperpy * s002) - q0w * qperpz * s002 + 2. * q0x * qperpx * s012 + + q0x * qperpw * s022 + q0w * qperpx * s022 + q0x * qperpy * s102 + + q0w * qperpz * s102 - 2. * q0x * qperpx * s112 - q0x * qperpw * s122 - + q0w * qperpx * s122 + + q0z * (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102) - + 2. * qperpz * s112 + qperpy * s122) + + q0y * + (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122)) * + theta); + + c4[1] = DerivativeTerm::new( + 0., + -(q0x * qperpy * s000) - q0w * qperpz * s000 + 2. * q0x * qperpx * s010 + + q0x * qperpw * s020 + q0w * qperpx * s020 + q0x * qperpy * s100 + + q0w * qperpz * s100 - 2. * q0x * qperpx * s110 - q0x * qperpw * s120 - + q0w * qperpx * s120 + 2. * qperpx * qperpy * s000 * theta + + 2. * qperpw * qperpz * s000 * theta + 2. * q0x * q0x * s010 * theta + + 2. * q0z * q0z * s010 * theta - 2. * qperpx * qperpx * s010 * theta - + 2. * qperpz * qperpz * s010 * theta + 2. * q0w * q0x * s020 * theta - + 2. * qperpw * qperpx * s020 * theta + 2. * qperpy * qperpz * s020 * theta + + q0y * (-(qperpx * s000) - qperpz * s020 + qperpx * s100 + qperpz * s120 - + 2. * q0x * s000 * theta) + + q0z * (2. * qperpz * s010 - qperpy * s020 + qperpw * (-s000 + s100) - + 2. * qperpz * s110 + qperpy * s120 - 2. * q0w * s000 * theta - + 2. * q0y * s020 * theta), + -(q0x * qperpy * s001) - q0w * qperpz * s001 + 2. * q0x * qperpx * s011 + + q0x * qperpw * s021 + q0w * qperpx * s021 + q0x * qperpy * s101 + + q0w * qperpz * s101 - 2. * q0x * qperpx * s111 - q0x * qperpw * s121 - + q0w * qperpx * s121 + 2. * qperpx * qperpy * s001 * theta + + 2. * qperpw * qperpz * s001 * theta + 2. * q0x * q0x * s011 * theta + + 2. * q0z * q0z * s011 * theta - 2. * qperpx * qperpx * s011 * theta - + 2. * qperpz * qperpz * s011 * theta + 2. * q0w * q0x * s021 * theta - + 2. * qperpw * qperpx * s021 * theta + 2. * qperpy * qperpz * s021 * theta + + q0y * (-(qperpx * s001) - qperpz * s021 + qperpx * s101 + qperpz * s121 - + 2. * q0x * s001 * theta) + + q0z * (2. * qperpz * s011 - qperpy * s021 + qperpw * (-s001 + s101) - + 2. * qperpz * s111 + qperpy * s121 - 2. * q0w * s001 * theta - + 2. * q0y * s021 * theta), + -(q0x * qperpy * s002) - q0w * qperpz * s002 + 2. * q0x * qperpx * s012 + + q0x * qperpw * s022 + q0w * qperpx * s022 + q0x * qperpy * s102 + + q0w * qperpz * s102 - 2. * q0x * qperpx * s112 - q0x * qperpw * s122 - + q0w * qperpx * s122 + 2. * qperpx * qperpy * s002 * theta + + 2. * qperpw * qperpz * s002 * theta + 2. * q0x * q0x * s012 * theta + + 2. * q0z * q0z * s012 * theta - 2. * qperpx * qperpx * s012 * theta - + 2. * qperpz * qperpz * s012 * theta + 2. * q0w * q0x * s022 * theta - + 2. * qperpw * qperpx * s022 * theta + 2. * qperpy * qperpz * s022 * theta + + q0y * (-(qperpx * s002) - qperpz * s022 + qperpx * s102 + qperpz * s122 - + 2. * q0x * s002 * theta) + + q0z * (2. * qperpz * s012 - qperpy * s022 + qperpw * (-s002 + s102) - + 2. * qperpz * s112 + qperpy * s122 - 2. * q0w * s002 * theta - + 2. * q0y * s022 * theta)); + + c5[1] = DerivativeTerm::new( + 0., + -2. * + (qperpx * qperpy * s000 + qperpw * qperpz * s000 + q0z * q0z * s010 - + qperpx * qperpx * s010 - qperpz * qperpz * s010 - q0y * q0z * s020 - + qperpw * qperpx * s020 + qperpy * qperpz * s020 - + qperpx * qperpy * s100 - qperpw * qperpz * s100 + + q0w * q0z * (-s000 + s100) + q0x * q0x * (s010 - s110) - + q0z * q0z * s110 + qperpx * qperpx * s110 + qperpz * qperpz * s110 + + q0x * (q0y * (-s000 + s100) + q0w * (s020 - s120)) + q0y * q0z * s120 + + qperpw * qperpx * s120 - qperpy * qperpz * s120) * + theta, + -2. * + (qperpx * qperpy * s001 + qperpw * qperpz * s001 + q0z * q0z * s011 - + qperpx * qperpx * s011 - qperpz * qperpz * s011 - q0y * q0z * s021 - + qperpw * qperpx * s021 + qperpy * qperpz * s021 - + qperpx * qperpy * s101 - qperpw * qperpz * s101 + + q0w * q0z * (-s001 + s101) + q0x * q0x * (s011 - s111) - + q0z * q0z * s111 + qperpx * qperpx * s111 + qperpz * qperpz * s111 + + q0x * (q0y * (-s001 + s101) + q0w * (s021 - s121)) + q0y * q0z * s121 + + qperpw * qperpx * s121 - qperpy * qperpz * s121) * + theta, + -2. * + (qperpx * qperpy * s002 + qperpw * qperpz * s002 + q0z * q0z * s012 - + qperpx * qperpx * s012 - qperpz * qperpz * s012 - q0y * q0z * s022 - + qperpw * qperpx * s022 + qperpy * qperpz * s022 - + qperpx * qperpy * s102 - qperpw * qperpz * s102 + + q0w * q0z * (-s002 + s102) + q0x * q0x * (s012 - s112) - + q0z * q0z * s112 + qperpx * qperpx * s112 + qperpz * qperpz * s112 + + q0x * (q0y * (-s002 + s102) + q0w * (s022 - s122)) + q0y * q0z * s122 + + qperpw * qperpx * s122 - qperpy * qperpz * s122) * + theta); + + c1[2] = DerivativeTerm::new( + -t0z + t1z, + qperpw * qperpy * s000 - qperpx * qperpz * s000 - q0y * q0z * s010 - + qperpw * qperpx * s010 - qperpy * qperpz * s010 - s020 + q0y * q0y * s020 + + qperpx * qperpx * s020 + qperpy * qperpy * s020 - qperpw * qperpy * s100 + + qperpx * qperpz * s100 + q0x * q0z * (-s000 + s100) + q0y * q0z * s110 + + qperpw * qperpx * s110 + qperpy * qperpz * s110 + + q0w * (q0y * (s000 - s100) + q0x * (-s010 + s110)) + + q0x * q0x * (s020 - s120) + s120 - q0y * q0y * s120 - + qperpx * qperpx * s120 - qperpy * qperpy * s120, + qperpw * qperpy * s001 - qperpx * qperpz * s001 - q0y * q0z * s011 - + qperpw * qperpx * s011 - qperpy * qperpz * s011 - s021 + q0y * q0y * s021 + + qperpx * qperpx * s021 + qperpy * qperpy * s021 - qperpw * qperpy * s101 + + qperpx * qperpz * s101 + q0x * q0z * (-s001 + s101) + q0y * q0z * s111 + + qperpw * qperpx * s111 + qperpy * qperpz * s111 + + q0w * (q0y * (s001 - s101) + q0x * (-s011 + s111)) + + q0x * q0x * (s021 - s121) + s121 - q0y * q0y * s121 - + qperpx * qperpx * s121 - qperpy * qperpy * s121, + qperpw * qperpy * s002 - qperpx * qperpz * s002 - q0y * q0z * s012 - + qperpw * qperpx * s012 - qperpy * qperpz * s012 - s022 + q0y * q0y * s022 + + qperpx * qperpx * s022 + qperpy * qperpy * s022 - qperpw * qperpy * s102 + + qperpx * qperpz * s102 + q0x * q0z * (-s002 + s102) + q0y * q0z * s112 + + qperpw * qperpx * s112 + qperpy * qperpz * s112 + + q0w * (q0y * (s002 - s102) + q0x * (-s012 + s112)) + + q0x * q0x * (s022 - s122) + s122 - q0y * q0y * s122 - + qperpx * qperpx * s122 - qperpy * qperpy * s122); + + c2[2] = DerivativeTerm::new( + 0., + q0w * q0y * s000 - q0x * q0z * s000 - qperpw * qperpy * s000 + + qperpx * qperpz * s000 - q0w * q0x * s010 - q0y * q0z * s010 + + qperpw * qperpx * s010 + qperpy * qperpz * s010 + q0x * q0x * s020 + + q0y * q0y * s020 - qperpx * qperpx * s020 - qperpy * qperpy * s020 - + q0w * q0y * s100 + q0x * q0z * s100 + qperpw * qperpy * s100 - + qperpx * qperpz * s100 + q0w * q0x * s110 + q0y * q0z * s110 - + qperpw * qperpx * s110 - qperpy * qperpz * s110 - q0x * q0x * s120 - + q0y * q0y * s120 + qperpx * qperpx * s120 + qperpy * qperpy * s120 - + 2. * q0y * qperpw * s000 * theta + 2. * q0z * qperpx * s000 * theta - + 2. * q0w * qperpy * s000 * theta + 2. * q0x * qperpz * s000 * theta + + 2. * q0x * qperpw * s010 * theta + 2. * q0w * qperpx * s010 * theta + + 2. * q0z * qperpy * s010 * theta + 2. * q0y * qperpz * s010 * theta - + 4. * q0x * qperpx * s020 * theta - 4. * q0y * qperpy * s020 * theta, + q0w * q0y * s001 - q0x * q0z * s001 - qperpw * qperpy * s001 + + qperpx * qperpz * s001 - q0w * q0x * s011 - q0y * q0z * s011 + + qperpw * qperpx * s011 + qperpy * qperpz * s011 + q0x * q0x * s021 + + q0y * q0y * s021 - qperpx * qperpx * s021 - qperpy * qperpy * s021 - + q0w * q0y * s101 + q0x * q0z * s101 + qperpw * qperpy * s101 - + qperpx * qperpz * s101 + q0w * q0x * s111 + q0y * q0z * s111 - + qperpw * qperpx * s111 - qperpy * qperpz * s111 - q0x * q0x * s121 - + q0y * q0y * s121 + qperpx * qperpx * s121 + qperpy * qperpy * s121 - + 2. * q0y * qperpw * s001 * theta + 2. * q0z * qperpx * s001 * theta - + 2. * q0w * qperpy * s001 * theta + 2. * q0x * qperpz * s001 * theta + + 2. * q0x * qperpw * s011 * theta + 2. * q0w * qperpx * s011 * theta + + 2. * q0z * qperpy * s011 * theta + 2. * q0y * qperpz * s011 * theta - + 4. * q0x * qperpx * s021 * theta - 4. * q0y * qperpy * s021 * theta, + q0w * q0y * s002 - q0x * q0z * s002 - qperpw * qperpy * s002 + + qperpx * qperpz * s002 - q0w * q0x * s012 - q0y * q0z * s012 + + qperpw * qperpx * s012 + qperpy * qperpz * s012 + q0x * q0x * s022 + + q0y * q0y * s022 - qperpx * qperpx * s022 - qperpy * qperpy * s022 - + q0w * q0y * s102 + q0x * q0z * s102 + qperpw * qperpy * s102 - + qperpx * qperpz * s102 + q0w * q0x * s112 + q0y * q0z * s112 - + qperpw * qperpx * s112 - qperpy * qperpz * s112 - q0x * q0x * s122 - + q0y * q0y * s122 + qperpx * qperpx * s122 + qperpy * qperpy * s122 - + 2. * q0y * qperpw * s002 * theta + 2. * q0z * qperpx * s002 * theta - + 2. * q0w * qperpy * s002 * theta + 2. * q0x * qperpz * s002 * theta + + 2. * q0x * qperpw * s012 * theta + 2. * q0w * qperpx * s012 * theta + + 2. * q0z * qperpy * s012 * theta + 2. * q0y * qperpz * s012 * theta - + 4. * q0x * qperpx * s022 * theta - 4. * q0y * qperpy * s022 * theta); + + c3[2] = DerivativeTerm::new( + 0., + -2. * + (-(q0w * qperpy * s000) + q0x * qperpz * s000 + q0x * qperpw * s010 + + q0w * qperpx * s010 - 2. * q0x * qperpx * s020 + q0w * qperpy * s100 - + q0x * qperpz * s100 - q0x * qperpw * s110 - q0w * qperpx * s110 + + q0z * (qperpx * s000 + qperpy * s010 - qperpx * s100 - qperpy * s110) + + 2. * q0x * qperpx * s120 + + q0y * (qperpz * s010 - 2. * qperpy * s020 + qperpw * (-s000 + s100) - + qperpz * s110 + 2. * qperpy * s120)) * + theta, + -2. * + (-(q0w * qperpy * s001) + q0x * qperpz * s001 + q0x * qperpw * s011 + + q0w * qperpx * s011 - 2. * q0x * qperpx * s021 + q0w * qperpy * s101 - + q0x * qperpz * s101 - q0x * qperpw * s111 - q0w * qperpx * s111 + + q0z * (qperpx * s001 + qperpy * s011 - qperpx * s101 - qperpy * s111) + + 2. * q0x * qperpx * s121 + + q0y * (qperpz * s011 - 2. * qperpy * s021 + qperpw * (-s001 + s101) - + qperpz * s111 + 2. * qperpy * s121)) * + theta, + -2. * + (-(q0w * qperpy * s002) + q0x * qperpz * s002 + q0x * qperpw * s012 + + q0w * qperpx * s012 - 2. * q0x * qperpx * s022 + q0w * qperpy * s102 - + q0x * qperpz * s102 - q0x * qperpw * s112 - q0w * qperpx * s112 + + q0z * (qperpx * s002 + qperpy * s012 - qperpx * s102 - qperpy * s112) + + 2. * q0x * qperpx * s122 + + q0y * (qperpz * s012 - 2. * qperpy * s022 + qperpw * (-s002 + s102) - + qperpz * s112 + 2. * qperpy * s122)) * + theta); + + c4[2] = DerivativeTerm::new( + 0., + q0w * qperpy * s000 - q0x * qperpz * s000 - q0x * qperpw * s010 - + q0w * qperpx * s010 + 2. * q0x * qperpx * s020 - q0w * qperpy * s100 + + q0x * qperpz * s100 + q0x * qperpw * s110 + q0w * qperpx * s110 - + 2. * q0x * qperpx * s120 - 2. * qperpw * qperpy * s000 * theta + + 2. * qperpx * qperpz * s000 * theta - 2. * q0w * q0x * s010 * theta + + 2. * qperpw * qperpx * s010 * theta + 2. * qperpy * qperpz * s010 * theta + + 2. * q0x * q0x * s020 * theta + 2. * q0y * q0y * s020 * theta - + 2. * qperpx * qperpx * s020 * theta - 2. * qperpy * qperpy * s020 * theta + + q0z * (-(qperpx * s000) - qperpy * s010 + qperpx * s100 + qperpy * s110 - + 2. * q0x * s000 * theta) + + q0y * (-(qperpz * s010) + 2. * qperpy * s020 + qperpw * (s000 - s100) + + qperpz * s110 - 2. * qperpy * s120 + 2. * q0w * s000 * theta - + 2. * q0z * s010 * theta), + q0w * qperpy * s001 - q0x * qperpz * s001 - q0x * qperpw * s011 - + q0w * qperpx * s011 + 2. * q0x * qperpx * s021 - q0w * qperpy * s101 + + q0x * qperpz * s101 + q0x * qperpw * s111 + q0w * qperpx * s111 - + 2. * q0x * qperpx * s121 - 2. * qperpw * qperpy * s001 * theta + + 2. * qperpx * qperpz * s001 * theta - 2. * q0w * q0x * s011 * theta + + 2. * qperpw * qperpx * s011 * theta + 2. * qperpy * qperpz * s011 * theta + + 2. * q0x * q0x * s021 * theta + 2. * q0y * q0y * s021 * theta - + 2. * qperpx * qperpx * s021 * theta - 2. * qperpy * qperpy * s021 * theta + + q0z * (-(qperpx * s001) - qperpy * s011 + qperpx * s101 + qperpy * s111 - + 2. * q0x * s001 * theta) + + q0y * (-(qperpz * s011) + 2. * qperpy * s021 + qperpw * (s001 - s101) + + qperpz * s111 - 2. * qperpy * s121 + 2. * q0w * s001 * theta - + 2. * q0z * s011 * theta), + q0w * qperpy * s002 - q0x * qperpz * s002 - q0x * qperpw * s012 - + q0w * qperpx * s012 + 2. * q0x * qperpx * s022 - q0w * qperpy * s102 + + q0x * qperpz * s102 + q0x * qperpw * s112 + q0w * qperpx * s112 - + 2. * q0x * qperpx * s122 - 2. * qperpw * qperpy * s002 * theta + + 2. * qperpx * qperpz * s002 * theta - 2. * q0w * q0x * s012 * theta + + 2. * qperpw * qperpx * s012 * theta + 2. * qperpy * qperpz * s012 * theta + + 2. * q0x * q0x * s022 * theta + 2. * q0y * q0y * s022 * theta - + 2. * qperpx * qperpx * s022 * theta - 2. * qperpy * qperpy * s022 * theta + + q0z * (-(qperpx * s002) - qperpy * s012 + qperpx * s102 + qperpy * s112 - + 2. * q0x * s002 * theta) + + q0y * (-(qperpz * s012) + 2. * qperpy * s022 + qperpw * (s002 - s102) + + qperpz * s112 - 2. * qperpy * s122 + 2. * q0w * s002 * theta - + 2. * q0z * s012 * theta)); + + c5[2] = DerivativeTerm::new( + 0., + 2. * + (qperpw * qperpy * s000 - qperpx * qperpz * s000 + q0y * q0z * s010 - + qperpw * qperpx * s010 - qperpy * qperpz * s010 - q0y * q0y * s020 + + qperpx * qperpx * s020 + qperpy * qperpy * s020 + + q0x * q0z * (s000 - s100) - qperpw * qperpy * s100 + + qperpx * qperpz * s100 + + q0w * (q0y * (-s000 + s100) + q0x * (s010 - s110)) - q0y * q0z * s110 + + qperpw * qperpx * s110 + qperpy * qperpz * s110 + q0y * q0y * s120 - + qperpx * qperpx * s120 - qperpy * qperpy * s120 + + q0x * q0x * (-s020 + s120)) * + theta, + 2. * + (qperpw * qperpy * s001 - qperpx * qperpz * s001 + q0y * q0z * s011 - + qperpw * qperpx * s011 - qperpy * qperpz * s011 - q0y * q0y * s021 + + qperpx * qperpx * s021 + qperpy * qperpy * s021 + + q0x * q0z * (s001 - s101) - qperpw * qperpy * s101 + + qperpx * qperpz * s101 + + q0w * (q0y * (-s001 + s101) + q0x * (s011 - s111)) - q0y * q0z * s111 + + qperpw * qperpx * s111 + qperpy * qperpz * s111 + q0y * q0y * s121 - + qperpx * qperpx * s121 - qperpy * qperpy * s121 + + q0x * q0x * (-s021 + s121)) * + theta, + 2. * + (qperpw * qperpy * s002 - qperpx * qperpz * s002 + q0y * q0z * s012 - + qperpw * qperpx * s012 - qperpy * qperpz * s012 - q0y * q0y * s022 + + qperpx * qperpx * s022 + qperpy * qperpy * s022 + + q0x * q0z * (s002 - s102) - qperpw * qperpy * s102 + + qperpx * qperpz * s102 + + q0w * (q0y * (-s002 + s102) + q0x * (s012 - s112)) - q0y * q0z * s112 + + qperpw * qperpx * s112 + qperpy * qperpz * s112 + q0y * q0y * s122 - + qperpx * qperpx * s122 - qperpy * qperpy * s122 + + q0x * q0x * (-s022 + s122)) * + theta); + + return Some(AnimatedTransform{ start_transform: *start_transform, end_transform: *end_transform, start_time, end_time, actually_animated, t, r, s, has_rotation, c1, c2, c3, c4, c5 }) + } + None + } + + pub fn apply_point(&self, p: Point3f, time: Float) -> Point3f { + if !self.actually_animated || time <= self.start_time { + return self.start_transform.apply_to_point(p.into()); + } + else if time >= self.end_time { + return self.end_transform.apply_to_point(p); + } + let t = self.interpolate(time); + t.apply_to_point(p) + } + + pub fn apply_vector(&self, p: Vector3f, time: Float) -> Vector3f { + if !self.actually_animated || time <= self.start_time { + return self.start_transform.apply_to_vector(p); + } + else if time >= self.end_time { + return self.end_transform.apply_to_vector(p); + } + let t = self.interpolate(time); + t.apply_to_vector(p) + } + + pub fn apply_ray(&self, r: &Ray, t_max: &mut Option) -> Ray { + if !self.actually_animated || r.time <= self.start_time { + return self.start_transform.apply_to_ray(r, t_max); + } + else if r.time >= self.end_time { + return self.end_transform.apply_to_ray(r, t_max); + } + let t = self.interpolate(r.time); + t.apply_to_ray(r, t_max) + + } + + pub fn interpolate(&self, time: Float) -> Transform { + if !self.actually_animated || time <= self.start_time { return self.start_transform } + if time >= self.end_time { return self.end_transform } + + let dt = (time - self.start_time) / (self.end_time - self.start_time); + let trans = (1.0 - dt) * self.t[0] + dt * self.t[1]; + + let rotate = Quaternion::slerp(dt, self.r[0], self.r[1]); + + let scale = (1.0 - dt) * self.s[0] + dt * self.s[1]; + + // Return interpolated matrix as product of interpolated components + return Transform::translate(trans) * Transform::from(rotate) * Transform::from_matrix(&scale); + } + + pub fn apply_inverse_point(&self, p: Point3f, time: Float) -> Point3f { + if !self.actually_animated { + return self.start_transform.apply_inverse(p); + } + return self.interpolate(time).apply_inverse(p) + } + + pub fn apply_inverse_vector(&self, v: Vector3f, time: Float) -> Vector3f { + if !self.actually_animated { + return self.start_transform.apply_inverse_vector(v); + } + return self.interpolate(time).apply_inverse_vector(v) + } + + pub fn apply_inverse_normal(&self, n: Normal3f, time: Float) -> Normal3f { + if !self.actually_animated { + return self.start_transform.apply_inverse_normal(n); + } + return self.interpolate(time).apply_inverse_normal(n) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_matrix_approx_eq(a: &SquareMatrix, b: &SquareMatrix) { + const EPSILON: f64 = 1e-9; + for i in 0..N { + for j in 0..N { + assert!( + (a[i][j] - b[i][j]).abs() < EPSILON, + "Matrices differ at ({},{}): {} vs {}\nLeft:\n{}\nRight:\n{}", + i, j, a[i][j], b[i][j], a, b + ); + } + } + } + + #[test] + fn test_inverse_2x2() { + let m = SquareMatrix { m: [[4.0, 7.0], [2.0, 6.0]] }; + let identity = SquareMatrix::identity(); + let inv = m.inverse().expect("Matrix should be invertible"); + let product = m * inv; + assert_matrix_approx_eq(&product, &identity); + } + + #[test] + fn test_inverse_3x3() { + let m = SquareMatrix { m: [[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]] }; + let identity = SquareMatrix::identity(); + let inv = m.inverse().expect("Matrix should be invertible"); + let product = m * inv; + let product_inv = inv * m; + assert_matrix_approx_eq(&product, &identity); + assert_matrix_approx_eq(&product_inv, &identity); + } + + #[test] + fn test_singular_inverse() { + let m = SquareMatrix { m: [[1.0, 2.0], [2.0, 4.0]] }; // Determinant is 0 + assert!(m.inverse().is_none()); + } + + #[test] + fn test_multiplication_2x2() { + let a = SquareMatrix { m: [[1.0, 2.0], [3.0, 4.0]] }; + let b = SquareMatrix { m: [[2.0, 0.0], [1.0, 2.0]] }; + let expected = SquareMatrix { m: [[4.0, 4.0], [10.0, 8.0]] }; + let result = a * b; + assert_matrix_approx_eq(&result, &expected); + } + + #[test] + fn test_determinant_3x3() { + let m = SquareMatrix { m: [[1.0, 2.0, 3.0], [0.0, 1.0, 4.0], [5.0, 6.0, 0.0]] }; + assert_eq!(m.determinant(), 1.0); + } +}